Skip to content

Commit

Permalink
feat: document API with OpenAPI
Browse files Browse the repository at this point in the history
Each instance now hosts a SwaggerUI under /api that you can use for
interacting with the API.
  • Loading branch information
mKeRix committed May 31, 2020
1 parent b96f2ee commit 4d6d4de
Show file tree
Hide file tree
Showing 22 changed files with 174 additions and 16 deletions.
3 changes: 2 additions & 1 deletion docs/.vuepress/config.js
Expand Up @@ -57,7 +57,8 @@ module.exports = {
'/guide/configuration',
'/guide/cluster',
'/guide/entities',
'/guide/cli'
'/guide/cli',
'/guide/api'
]
},
{
Expand Down
12 changes: 12 additions & 0 deletions docs/guide/api.md
@@ -0,0 +1,12 @@
# API

::: warning

The API is still a work in progress and some features are missing. The existing endpoints will stay compatible within the same major version though.

:::

Each instance of room-assistant exposes an HTTP API that you can use for debugging or connecting it to different services. The API is accessible under port `6415` (cannot be changed currently).

The API is documented with an OpenAPI schema that you can retrieve under `/api-json`. For a visual representation navigate to `/api`. You can make all available API calls directly in your browser from this page.

5 changes: 4 additions & 1 deletion nest-cli.json
@@ -1,4 +1,7 @@
{
"collection": "@nestjs/schematics",
"sourceRoot": "src"
"sourceRoot": "src",
"compilerOptions": {
"plugins": ["@nestjs/swagger/plugin"]
}
}
59 changes: 59 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Expand Up @@ -47,9 +47,12 @@
"@nestjs/core": "^7.1.0",
"@nestjs/platform-express": "^7.1.0",
"@nestjs/schedule": "^0.4.0",
"@nestjs/swagger": "^4.5.9",
"@nestjs/terminus": "^7.0.1",
"async-mqtt": "^2.6.0",
"chalk": "^4.0.0",
"class-transformer": "^0.2.3",
"class-validator": "^0.12.2",
"command-line-args": "^5.1.1",
"command-line-usage": "^6.1.0",
"config": "^3.3.1",
Expand All @@ -64,6 +67,7 @@
"reflect-metadata": "^0.1.13",
"rxjs": "^6.5.5",
"slugify": "^1.4.0",
"swagger-ui-express": "^4.1.4",
"systeminformation": "^4.26.4",
"update-notifier": "^4.1.0",
"winston": "^3.2.1"
Expand Down
2 changes: 1 addition & 1 deletion src/entities/binary-sensor.ts
@@ -1,4 +1,4 @@
import { Entity } from './entity';
import { Entity } from './entity.dto';

export class BinarySensor extends Entity {
state: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/entities/camera.ts
@@ -1,4 +1,4 @@
import { Entity } from './entity';
import { Entity } from './entity.dto';

export class Camera extends Entity {
state: Buffer;
Expand Down
2 changes: 1 addition & 1 deletion src/entities/device-tracker.ts
@@ -1,4 +1,4 @@
import { Entity } from './entity';
import { Entity } from './entity.dto';

export class DeviceTracker extends Entity {
state: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/entities/entities.controller.spec.ts
Expand Up @@ -3,7 +3,7 @@ import { EntitiesController } from './entities.controller';
import { EntitiesService } from './entities.service';
import { Sensor } from './sensor';
import { Switch } from './switch';
import { Entity } from './entity';
import { Entity } from './entity.dto';
import { NestEmitterModule } from 'nest-emitter';
import { ClusterModule } from '../cluster/cluster.module';
import { EventEmitter } from 'events';
Expand Down
42 changes: 41 additions & 1 deletion src/entities/entities.controller.ts
@@ -1,13 +1,53 @@
import { Controller, Get } from '@nestjs/common';
import { Entity } from './entity';
import { Entity } from './entity.dto';
import { EntitiesService } from './entities.service';
import { Camera } from './camera';
import {
ApiExtraModels,
ApiOkResponse,
ApiOperation,
ApiTags,
getSchemaPath,
} from '@nestjs/swagger';

@ApiTags('entities')
@ApiExtraModels(Entity)
@Controller('entities')
export class EntitiesController {
constructor(private readonly entitiesService: EntitiesService) {}

@Get()
@ApiOperation({
summary: 'Retrieve all entities and their states',
description:
'The entities will always match the schema, but may also provide some additional information depending on the integration they are from. Note that camera entities are filtered from this list.',
})
@ApiOkResponse({
description: 'All registered entities are listed.',
content: {
'application/json': {
schema: { type: 'array', items: { $ref: getSchemaPath(Entity) } },
example: [
{
id: 'example-sensor',
name: 'Example Sensor',
distributed: true,
state: 'instance-name',
attributes: {
distance: 4.2,
},
},
{
id: 'example-switch',
name: 'Example Switch',
distributed: false,
state: true,
attributes: {},
},
],
},
},
})
getAll(): Entity[] {
return this.entitiesService
.getAll()
Expand Down
2 changes: 1 addition & 1 deletion src/entities/entities.events.ts
@@ -1,4 +1,4 @@
import { Entity } from './entity';
import { Entity } from './entity.dto';
import { StrictEventEmitter } from 'nest-emitter';
import EventEmitter = NodeJS.EventEmitter;
import { EntityCustomization } from './entity-customization.interface';
Expand Down
2 changes: 1 addition & 1 deletion src/entities/entities.service.ts
@@ -1,5 +1,5 @@
import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common';
import { Entity } from './entity';
import { Entity } from './entity.dto';
import { EntityProxyHandler } from './entity.proxy';
import { InjectEventEmitter } from 'nest-emitter';
import { EntitiesEventEmitter } from './entities.events';
Expand Down
9 changes: 9 additions & 0 deletions src/entities/entity.ts → src/entities/entity.dto.ts
@@ -1,3 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';

export abstract class Entity {
constructor(id: string, name: string, distributed = false) {
this.id = id;
Expand All @@ -7,7 +9,14 @@ export abstract class Entity {

readonly id: string;
name: string;

@ApiProperty({
oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }],
})
state: string | number | boolean | Buffer;

@ApiProperty({ type: 'object' })
attributes: { [key: string]: any } = {};

readonly distributed: boolean;
}
2 changes: 1 addition & 1 deletion src/entities/entity.proxy.ts
@@ -1,4 +1,4 @@
import { Entity } from './entity';
import { Entity } from './entity.dto';
import { AttributesProxyHandler } from './attributes.proxy';
import { EntitiesEventEmitter } from './entities.events';
import { EntityBehavior } from './entities.config';
Expand Down
2 changes: 1 addition & 1 deletion src/entities/sensor.ts
@@ -1,4 +1,4 @@
import { Entity } from './entity';
import { Entity } from './entity.dto';

export class Sensor extends Entity {
state: number | string;
Expand Down
2 changes: 1 addition & 1 deletion src/entities/switch.ts
@@ -1,4 +1,4 @@
import { Entity } from './entity';
import { Entity } from './entity.dto';

export class Switch extends Entity {
state: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/integrations/grid-eye/grid-eye.service.ts
Expand Up @@ -5,7 +5,7 @@ import {
OnApplicationShutdown,
} from '@nestjs/common';
import i2cBus, { PromisifiedBus } from 'i2c-bus';
import { Entity } from '../../entities/entity';
import { Entity } from '../../entities/entity.dto';
import { EntitiesService } from '../../entities/entities.service';
import { Sensor } from '../../entities/sensor';
import * as math from 'mathjs';
Expand Down
Expand Up @@ -15,7 +15,7 @@ import * as mqtt from 'async-mqtt';
import { system, Systeminformation } from 'systeminformation';
import SystemData = Systeminformation.SystemData;
import { SensorConfig } from './sensor-config';
import { Entity } from '../../entities/entity';
import { Entity } from '../../entities/entity.dto';
import { Sensor } from '../../entities/sensor';
import { BinarySensor } from '../../entities/binary-sensor';
import { Switch } from '../../entities/switch';
Expand Down
2 changes: 1 addition & 1 deletion src/integrations/home-assistant/home-assistant.service.ts
Expand Up @@ -5,7 +5,7 @@ import {
OnApplicationShutdown,
OnModuleInit,
} from '@nestjs/common';
import { Entity } from '../../entities/entity';
import { Entity } from '../../entities/entity.dto';
import { Sensor } from '../../entities/sensor';
import { EntityConfig } from './entity-config';
import { SensorConfig } from './sensor-config';
Expand Down
2 changes: 1 addition & 1 deletion src/integrations/omron-d6t/omron-d6t.service.ts
Expand Up @@ -11,7 +11,7 @@ import i2cBus, { PromisifiedBus } from 'i2c-bus';
import { Interval } from '@nestjs/schedule';
import * as math from 'mathjs';
import { Sensor } from '../../entities/sensor';
import { Entity } from '../../entities/entity';
import { Entity } from '../../entities/entity.dto';
import { I2CError } from './i2c.error';
import { SensorConfig } from '../home-assistant/sensor-config';
import { ThermopileOccupancyService } from '../thermopile/thermopile-occupancy.service';
Expand Down
25 changes: 25 additions & 0 deletions src/main.ts
Expand Up @@ -2,12 +2,37 @@ import './env';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { WINSTON_LOGGER } from './logger';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const pkg = require('../package.json');

async function bootstrap(): Promise<void> {
const app = await NestFactory.create(AppModule, {
logger: WINSTON_LOGGER,
});
app.enableShutdownHooks();

const options = new DocumentBuilder()
.setTitle('room-assistant')
.setDescription(
'Presence tracking and more for automation on the room-level.'
)
.setVersion(pkg.version)
.setExternalDoc('Website', 'https://room-assistant.io')
.setLicense(
'MIT License',
'https://github.com/mKeRix/room-assistant/blob/master/LICENSE'
)
.addTag('entities', 'Access to the internal entity registry', {
description: 'Configuration',
url: 'https://www.room-assistant.io/guide/entities.html',
})
.addTag('status', 'Status information about the room-assistant instance')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api', app, document);

await app.listenAsync(6415);
}

Expand Down
5 changes: 5 additions & 0 deletions src/status/status.controller.ts
Expand Up @@ -5,7 +5,9 @@ import {
HealthCheckService,
} from '@nestjs/terminus';
import { HealthIndicatorService } from './health-indicator.service';
import { ApiOperation, ApiTags } from '@nestjs/swagger';

@ApiTags('status')
@Controller('status')
export class StatusController {
constructor(
Expand All @@ -14,6 +16,9 @@ export class StatusController {
) {}

@Get()
@ApiOperation({
summary: 'Check if this instance is healthy',
})
@HealthCheck()
check(): Promise<HealthCheckResult> {
return this.health.check(this.healthIndicatorService.getIndicators());
Expand Down

0 comments on commit 4d6d4de

Please sign in to comment.