Skip to content

Commit

Permalink
Standardise @context of Thing Descriptions - closes #2809
Browse files Browse the repository at this point in the history
  • Loading branch information
benfrancis committed Mar 17, 2023
1 parent 1fd7c40 commit 0817eec
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 35 deletions.
8 changes: 7 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,26 @@ export enum LogSeverity {
PROMPT = 4,
}

// Thing description things
export enum WoTOperation {
READ_ALL_PROPERTIES = 'readallproperties',
WRITE_MULTIPLE_PROPERTIES = 'writemultipleproperties',
SUBSCRIBE_ALL_EVENTS = 'subscribeallevents',
UNSUBSCRIBE_ALL_EVENTS = 'unsubscribeallevents',
QUERY_ALL_ACTIONS = 'queryallactions',
}

export enum ActionStatusValues {
PENDING = 'pending',
RUNNING = 'running',
COMPLETED = 'completed',
FAILED = 'failed',
}
export const MOZ_IOT_CONTEXT = 'https://iot.mozilla.org/schemas';
export const WEBTHINGS_CONTEXT = 'https://webthings.io/schemas';
export const WOT_TD_NS_CONTEXT = 'http://www.w3.org/ns/td';
export const WOT_TD_1_CONTEXT = 'https://www.w3.org/2019/wot/td/v1';
export const WOT_TD_1_1_CONTEXT = 'https://www.w3.org/2022/wot/td/v1.1';
export const DEFAULT_CONTEXT = [WOT_TD_1_1_CONTEXT, WEBTHINGS_CONTEXT];

export interface LogMessage {
severity: LogSeverity;
Expand Down
16 changes: 12 additions & 4 deletions src/models/thing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export interface Router {
export interface ThingDescription {
id: string;
title: string;
'@context': string;
'@context': string | string[];
'@type': string[];
description: string;
base: string;
Expand Down Expand Up @@ -79,7 +79,7 @@ export default class Thing extends EventEmitter {

private title: string;

private '@context': string;
private '@context': string | string[];

private '@type': string[];

Expand Down Expand Up @@ -133,7 +133,11 @@ export default class Thing extends EventEmitter {
// Parse the Thing Description
this.id = id;
this.title = description.title || (<Record<string, string>>(<unknown>description)).name || '';
this['@context'] = description['@context'] || 'https://webthings.io/schemas';
if (description['@context']) {
this['@context'] = Utils.standardizeContext(description['@context']);
} else {
this['@context'] = Constants.DEFAULT_CONTEXT;
}
this['@type'] = description['@type'] || [];
this.description = description.description || '';
this.href = `${Constants.THINGS_PATH}/${encodeURIComponent(this.id)}`;
Expand Down Expand Up @@ -707,7 +711,11 @@ export default class Thing extends EventEmitter {
const oldDescription = JSON.stringify(this.getDescription());

// Update @context
this['@context'] = description['@context'] || 'https://webthings.io/schemas';
if (description['@context']) {
this['@context'] = Utils.standardizeContext(description['@context']);
} else {
this['@context'] = Constants.DEFAULT_CONTEXT;
}

// Update @type
this['@type'] = description['@type'] || [];
Expand Down
2 changes: 1 addition & 1 deletion src/plugin/device-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default class DeviceProxy extends Device {
super(adapter, deviceDict.id);

this.setTitle(deviceDict.title ?? '');
this['@context'] = deviceDict['@context'] || 'https://webthings.io/schemas';
this['@context'] = deviceDict['@context'] || Constants.DEFAULT_CONTEXT;
this['@type'] = deviceDict['@type'] || [];
this.setDescription(deviceDict.description ?? '');
(<{ links: Link[] }>(<unknown>this)).links = deviceDict.links || [];
Expand Down
30 changes: 15 additions & 15 deletions src/test/browser/things-view/thing-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Thing', () => {
const desc = {
id: 'UnknownThing',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': [],
properties: {
numberProp: {
Expand Down Expand Up @@ -114,7 +114,7 @@ describe('Thing', () => {
const desc = {
id: 'spacedPropertyThings',
title: 'battery sensor',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': [],
properties: {
'spaced number': {
Expand Down Expand Up @@ -175,7 +175,7 @@ describe('Thing', () => {
const desc = {
id: 'UnknownThing',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': [],
properties: {
rejectPropertyNum: {
Expand Down Expand Up @@ -258,7 +258,7 @@ describe('Thing', () => {
const desc = {
id: 'onOffLight',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['Light', 'OnOffSwitch'],
properties: {
power: {
Expand Down Expand Up @@ -299,7 +299,7 @@ describe('Thing', () => {
const desc = {
id: 'onOffSwitch',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['OnOffSwitch'],
properties: {
power: {
Expand Down Expand Up @@ -341,7 +341,7 @@ describe('Thing', () => {
const desc = {
id: 'dimmableLight',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['Light', 'OnOffSwitch'],
properties: {
power: {
Expand Down Expand Up @@ -435,7 +435,7 @@ describe('Thing', () => {
const desc = {
id: 'onOffColorLight',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['Light', 'ColorControl', 'OnOffSwitch'],
properties: {
power: {
Expand Down Expand Up @@ -511,7 +511,7 @@ describe('Thing', () => {
const desc = {
id: 'dimmableColorLight',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['Light', 'ColorControl', 'OnOffSwitch'],
properties: {
power: {
Expand Down Expand Up @@ -617,7 +617,7 @@ describe('Thing', () => {
const desc = {
id: 'multiLevelSwitch',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['MultiLevelSwitch', 'OnOffSwitch'],
properties: {
power: {
Expand Down Expand Up @@ -711,7 +711,7 @@ describe('Thing', () => {
const desc = {
id: 'smartPlug',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['SmartPlug', 'EnergyMonitor', 'MultiLevelSwitch', 'OnOffSwitch'],
properties: {
power: {
Expand Down Expand Up @@ -873,7 +873,7 @@ describe('Thing', () => {
const desc = {
id: 'binarySensor',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['BinarySensor'],
properties: {
active: {
Expand Down Expand Up @@ -911,7 +911,7 @@ describe('Thing', () => {
const desc = {
id: 'multiLevelSensor',
title: 'foofoo',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['MultiLevelSensor'],
properties: {
active: {
Expand Down Expand Up @@ -956,7 +956,7 @@ describe('Thing', () => {
const desc = {
id: 'humiditySensor',
title: 'Humidity',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['HumiditySensor'],
properties: {
humidity: {
Expand Down Expand Up @@ -1009,7 +1009,7 @@ describe('Thing', () => {
const desc = {
id: 'Camera',
title: 'Camera',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['Camera'],
properties: {
photo: {
Expand Down Expand Up @@ -1059,7 +1059,7 @@ describe('Thing', () => {
const desc = {
id: 'VideoCamera',
title: 'VideoCamera',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['VideoCamera'],
properties: {
video: {
Expand Down
2 changes: 1 addition & 1 deletion src/test/integration/actions-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('actions/', () => {
const thingLight = {
id: 'light',
title: 'light',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['OnOffSwitch', 'Light'],
properties: {
power: {
Expand Down
2 changes: 1 addition & 1 deletion src/test/integration/logs-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import sleep from '../../sleep';
const thingLight1 = {
id: 'light1',
title: 'light1',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['OnOffSwitch'],
properties: {
on: {
Expand Down
2 changes: 1 addition & 1 deletion src/test/integration/oauth-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const CLIENT_SERVER_PORT = 31338;
const TEST_THING = {
id: 'test-1',
title: 'kitchen',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['OnOffSwitch'],
properties: {
power: {
Expand Down
8 changes: 4 additions & 4 deletions src/test/integration/things-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Events from '../../models/events';
const TEST_THING = {
id: 'test-1',
title: 'test-1',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['OnOffSwitch'],
properties: {
power: {
Expand All @@ -31,7 +31,7 @@ const TEST_THING = {
const VALIDATION_THING = {
id: 'validation-1',
title: 'validation-1',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
properties: {
readOnlyProp: {
type: 'boolean',
Expand Down Expand Up @@ -62,7 +62,7 @@ const VALIDATION_THING = {
const EVENT_THING = {
id: 'event-thing1',
title: 'Event Thing',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
events: {
overheated: {
type: 'number',
Expand All @@ -74,7 +74,7 @@ const EVENT_THING = {
const piDescr = {
id: 'pi-1',
title: 'pi-1',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['OnOffSwitch'],
properties: {
power: {
Expand Down
6 changes: 3 additions & 3 deletions src/test/rules-engine/index-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const thingLight1 = {
id: 'light1',
title: 'light1',
type: 'onOffSwitch',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['OnOffSwitch'],
properties: {
on: { type: 'boolean', value: false },
Expand All @@ -35,7 +35,7 @@ const thingLight2 = {
id: 'light2',
title: 'light2',
type: 'onOffSwitch',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['OnOffSwitch'],
properties: {
on: { type: 'boolean', value: false },
Expand All @@ -49,7 +49,7 @@ const thingLight3 = {
id: 'light3',
title: 'light3',
type: 'onOffSwitch',
'@context': 'https://webthings.io/schemas',
'@context': ['https://www.w3.org/2022/wot/td/v1.1', 'https://webthings.io/schemas'],
'@type': ['OnOffSwitch'],
properties: {
on: { type: 'boolean', value: false },
Expand Down
51 changes: 47 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import fs from 'fs';
import * as Platform from './platform';
import pkg from './package.json';
import { Event as EventSchema } from 'gateway-addon/lib/schema';
import * as Constants from './constants';

/**
* Compute a SHA-256 checksum of a file.
Expand Down Expand Up @@ -75,21 +76,63 @@ export function isW3CThingDescription(thingDescription: {
}): boolean {
if (typeof thingDescription['@context'] === 'string') {
return (
thingDescription['@context'] === 'https://www.w3.org/2019/wot/td/v1' ||
thingDescription['@context'] === 'http://www.w3.org/ns/td'
thingDescription['@context'] === Constants.WOT_TD_1_CONTEXT ||
thingDescription['@context'] === Constants.WOT_TD_1_1_CONTEXT ||
thingDescription['@context'] === Constants.WOT_TD_NS_CONTEXT
);
}

if (Array.isArray(thingDescription['@context'])) {
return (
thingDescription['@context'].includes('https://www.w3.org/2019/wot/td/v1') ||
thingDescription['@context'].includes('http://www.w3.org/ns/td')
thingDescription['@context'].includes(Constants.WOT_TD_1_CONTEXT) ||
thingDescription['@context'].includes(Constants.WOT_TD_1_1_CONTEXT) ||
thingDescription['@context'].includes(Constants.WOT_TD_NS_CONTEXT)
);
}

return false;
}

/**
* Make sure the @context value of a Thing Description contains both
* W3C WoT TD 1.1 and WebThings contexts.
*
* @param context {(string|string[])} The non-standardised context value.
* @returns string[] The standardised context array.
*/
export function standardizeContext(context: string | string[]): string[] {
// If expected contexts already present then return null
if (
Array.isArray(context) &&
context.includes(Constants.WOT_TD_1_1_CONTEXT) &&
context.includes(Constants.WEBTHINGS_CONTEXT)
) {
return context;
}

// If @context is a string, make it an array
if (typeof context === 'string') {
context = [context];
}

// Remove legacy iot.mozilla.org @context
if (context.includes(Constants.MOZ_IOT_CONTEXT)) {
context.splice(context.indexOf(Constants.MOZ_IOT_CONTEXT));
}

// If @context doesn't contain W3C TD 1.1 context then add it to the start
if (!context.includes(Constants.WOT_TD_1_1_CONTEXT)) {
context.unshift(Constants.WOT_TD_1_1_CONTEXT);
}

// If @context doesn't contain WebThings context then add it to the end
if (!context.includes(Constants.WEBTHINGS_CONTEXT)) {
context.push(Constants.WEBTHINGS_CONTEXT);
}

return context;
}

/**
* Convert event descriptions from the legacy format in the Web Thing API
* (https://webthings.io/api/#event-object) to the W3C WoT TD standard format
Expand Down

0 comments on commit 0817eec

Please sign in to comment.