mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-04-14 14:36:36 +00:00
device-api: Add v2/device/tags api endpoint
This endpoint will fetch the device tags from the balena api Change-type: minor Closes: #890 Signed-off-by: Cameron Diver <cameron@balena.io>
This commit is contained in:
parent
3f231e8ff3
commit
b922789dee
26
docs/API.md
26
docs/API.md
@ -1126,3 +1126,29 @@ Response:
|
||||
"deviceName": "holy-wildflower"
|
||||
}
|
||||
```
|
||||
|
||||
#### Device tags
|
||||
|
||||
Added in supervisor version v9.11.0
|
||||
|
||||
Retrieve any device tags from the balena API. Note that this endpoint will not work when
|
||||
the device does not have an available connection to the balena API.
|
||||
|
||||
From an application container:
|
||||
```
|
||||
$ curl "$BALENA_SUPERVISOR_ADDRESS/v2/device/tags?apikey=$BALENA_SUPERVISOR_API_KEY"
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"tags": [
|
||||
{
|
||||
"id": 188303,
|
||||
"name": "DeviceLocation",
|
||||
"value": "warehouse #3"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
@ -1,6 +1,8 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as bodyParser from 'body-parser';
|
||||
import * as express from 'express';
|
||||
import { isLeft } from 'fp-ts/lib/Either';
|
||||
import * as t from 'io-ts';
|
||||
import * as _ from 'lodash';
|
||||
import * as Path from 'path';
|
||||
import { PinejsClientRequest, StatusError } from 'pinejs-client-request';
|
||||
@ -57,6 +59,12 @@ interface DevicePinInfo {
|
||||
commit: string;
|
||||
}
|
||||
|
||||
interface DeviceTag {
|
||||
id: number;
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
type KeyExchangeOpts = ConfigSchemaType<'provisioningOptions'>;
|
||||
|
||||
export class APIBinder {
|
||||
@ -376,6 +384,39 @@ export class APIBinder {
|
||||
return this.reportCurrentState();
|
||||
}
|
||||
|
||||
public async fetchDeviceTags(): Promise<DeviceTag[]> {
|
||||
if (this.balenaApi == null) {
|
||||
throw new Error(
|
||||
'Attempt to communicate with API, without initialized client',
|
||||
);
|
||||
}
|
||||
const tags = (await this.balenaApi.get({
|
||||
resource: 'device_tag',
|
||||
options: {
|
||||
$select: ['id', 'tag_key', 'value'],
|
||||
},
|
||||
})) as Array<Dictionary<unknown>>;
|
||||
|
||||
return tags.map(tag => {
|
||||
// Do some type safe decoding and throw if we get an unexpected value
|
||||
const id = t.number.decode(tag.id);
|
||||
const name = t.string.decode(tag.tag_key);
|
||||
const value = t.string.decode(tag.value);
|
||||
if (isLeft(id) || isLeft(name) || isLeft(value)) {
|
||||
throw new Error(
|
||||
`There was an error parsing device tags from the api. Device tag: ${JSON.stringify(
|
||||
tag,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
id: id.value,
|
||||
name: name.value,
|
||||
value: value.value,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private getStateDiff(): DeviceApplicationState {
|
||||
const lastReportedLocal = this.lastReportedState.local;
|
||||
const lastReportedDependent = this.lastReportedState.dependent;
|
||||
|
2
src/application-manager.d.ts
vendored
2
src/application-manager.d.ts
vendored
@ -12,6 +12,7 @@ import DB from './db';
|
||||
|
||||
import { Service } from './compose/service';
|
||||
import Config from './config';
|
||||
import { APIBinder } from './api-binder';
|
||||
|
||||
declare interface Options {
|
||||
force?: boolean;
|
||||
@ -37,6 +38,7 @@ export class ApplicationManager extends EventEmitter {
|
||||
public logger: Logger;
|
||||
public deviceState: any;
|
||||
public eventTracker: EventTracker;
|
||||
public apiBinder: APIBinder;
|
||||
|
||||
public services: ServiceManager;
|
||||
public config: Config;
|
||||
|
@ -451,4 +451,19 @@ export function createV2Api(router: Router, applications: ApplicationManager) {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/v2/device/tags', async (_req, res) => {
|
||||
try {
|
||||
const tags = await applications.apiBinder.fetchDeviceTags();
|
||||
return res.json({
|
||||
status: 'success',
|
||||
tags,
|
||||
});
|
||||
} catch (e) {
|
||||
res.status(503).json({
|
||||
status: 'failed',
|
||||
message: messageFromError(e),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ module.exports = class Supervisor extends EventEmitter
|
||||
# FIXME: rearchitect proxyvisor to avoid this circular dependency
|
||||
# by storing current state and having the APIBinder query and report it / provision devices
|
||||
@deviceState.applications.proxyvisor.bindToAPI(@apiBinder)
|
||||
# We could also do without the below dependency, but it's part of a much larger refactor
|
||||
@deviceState.applications.apiBinder = @apiBinder
|
||||
|
||||
@api = new SupervisorAPI({
|
||||
@config,
|
||||
@eventTracker,
|
||||
|
Loading…
x
Reference in New Issue
Block a user