Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(coap-server): add support for URI variables #1078

Merged
merged 7 commits into from
Sep 12, 2023
49 changes: 39 additions & 10 deletions packages/binding-coap/src/coap-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ export default class CoapServer implements ProtocolServer {
this.PROPERTY_DIR,
propertyName,
offeredMediaType,
opValues
opValues,
property.uriVariables,
thing.uriVariables
);

property.forms.push(form);
Expand All @@ -204,7 +206,9 @@ export default class CoapServer implements ProtocolServer {
this.ACTION_DIR,
actionName,
offeredMediaType,
"invokeaction"
"invokeaction",
action.uriVariables,
thing.uriVariables
);

action.forms.push(form);
Expand All @@ -215,10 +219,15 @@ export default class CoapServer implements ProtocolServer {

private fillInEventBindingData(thing: ExposedThing, base: string, port: number, offeredMediaType: string) {
for (const [eventName, event] of Object.entries(thing.events)) {
const [href, form] = this.createHrefAndForm(base, this.EVENT_DIR, eventName, offeredMediaType, [
"subscribeevent",
"unsubscribeevent",
]);
const [href, form] = this.createHrefAndForm(
base,
this.EVENT_DIR,
eventName,
offeredMediaType,
["subscribeevent", "unsubscribeevent"],
event.uriVariables,
thing.uriVariables
);

event.forms.push(form);

Expand All @@ -231,16 +240,36 @@ export default class CoapServer implements ProtocolServer {
affordancePathSegment: string,
affordanceName: string,
offeredMediaType: string,
opValues: string | string[]
opValues: string | string[],
affordanceUriVariables: PropertyElement["uriVariables"] = {},
thingUriVariables: PropertyElement["uriVariables"] = {}
): [string, TD.Form] {
const href = this.createFormHref(base, affordancePathSegment, affordanceName);
const href = this.createFormHref(
base,
affordancePathSegment,
affordanceName,
affordanceUriVariables,
thingUriVariables
);
const form = this.createAffordanceForm(href, offeredMediaType, opValues);

return [href, form];
}

private createFormHref(base: string, affordancePathSegment: string, affordanceName: string) {
return `${base}/${affordancePathSegment}/${encodeURIComponent(affordanceName)}`;
private createFormHref(
base: string,
affordancePathSegment: string,
affordanceName: string,
affordanceUriVariables: PropertyElement["uriVariables"] = {},
thingUriVariables: PropertyElement["uriVariables"] = {}
) {
const affordanceNamePattern = Helpers.updateInteractionNameWithUriVariablePattern(
affordanceName,
affordanceUriVariables,
thingUriVariables
);

return `${base}/${affordancePathSegment}/${encodeURIComponent(affordanceNamePattern)}`;
}

private createAffordanceForm(href: string, offeredMediaType: string, op: string[] | string) {
Expand Down
76 changes: 75 additions & 1 deletion packages/binding-coap/test/coap-server-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Servient, { ExposedThing, Content } from "@node-wot/core";

import { suite, test } from "@testdeck/mocha";
import { expect, should } from "chai";
import { DataSchemaValue, InteractionInput } from "wot-typescript-definitions";
import { DataSchemaValue, InteractionInput, InteractionOptions } from "wot-typescript-definitions";
import * as TD from "@node-wot/td-tools";
import CoapServer from "../src/coap-server";
import { CoapClient } from "../src/coap";
Expand Down Expand Up @@ -389,4 +389,78 @@ class CoapServerTest {
});
req.end();
}

@test async "should check uriVariables consistency"() {
const portNumber = 9003;
const coapServer = new CoapServer(portNumber);
const servient = new Servient();

const baseUri = `coap://localhost:${portNumber}/test`;

await coapServer.start(servient);

const testThing = new ExposedThing(servient, {
title: "Test",
properties: {
test: {
type: "string",
uriVariables: {
id: {
type: "string",
},
},
},
},
actions: {
try: {
output: { type: "string" },
uriVariables: {
step: { type: "integer" },
},
},
},
});

let test: DataSchemaValue;
testThing.setPropertyReadHandler("test", (options) => {
expect(options?.uriVariables).to.deep.equal({ id: "testId" });
return new Promise<InteractionInput>((resolve, reject) => {
resolve(test);
});
});
testThing.setPropertyWriteHandler("test", async (value, options) => {
expect(options?.uriVariables).to.deep.equal({ id: "testId" });
test = await value.value();
expect(test?.valueOf()).to.deep.equal("on");
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
testThing.properties.test.forms = [];
testThing.setActionHandler("try", (input: WoT.InteractionOutput, params?: InteractionOptions) => {
return new Promise<string>((resolve, reject) => {
expect(params?.uriVariables).to.deep.equal({ step: 5 });
resolve("TEST");
});
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
testThing.actions.try.forms = [];

await coapServer.expose(testThing);

const coapClient = new CoapClient(coapServer);

const propertyUri = `${baseUri}/properties/test?id=testId`;

await coapClient.writeResource(new TD.Form(propertyUri), new Content("text/plain", Readable.from("on")));

const response1 = await coapClient.readResource(new TD.Form(propertyUri));
expect((await response1.toBuffer()).toString()).to.equal('"on"');

const response2 = await coapClient.invokeResource(new TD.Form(`${baseUri}/actions/try?step=5`));
expect((await response2.toBuffer()).toString()).to.equal('"TEST"');

await coapClient.stop();
await coapServer.stop();
}
}
40 changes: 3 additions & 37 deletions packages/binding-http/src/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,40 +261,6 @@ export default class HttpServer implements ProtocolServer {
}
}

private updateInteractionNameWithUriVariablePattern(
interactionName: string,
uriVariables: PropertyElement["uriVariables"] = {},
thingVariables: PropertyElement["uriVariables"] = {}
): string {
const variables = Object.assign({}, uriVariables, thingVariables);
if (Object.keys(variables).length > 0) {
let pattern = "{?";
let index = 0;
if (uriVariables) {
for (const key in uriVariables) {
if (index !== 0) {
pattern += ",";
}
pattern += encodeURIComponent(key);
index++;
}
}
if (thingVariables) {
for (const key in thingVariables) {
if (index !== 0) {
pattern += ",";
}
pattern += encodeURIComponent(key);
index++;
}
}
pattern += "}";
return encodeURIComponent(interactionName) + pattern;
} else {
return encodeURIComponent(interactionName);
}
}

public async expose(thing: ExposedThing, tdTemplate: WoT.ExposedThingInit = {}): Promise<void> {
let urlPath = slugify(thing.title, { lower: true });

Expand Down Expand Up @@ -400,7 +366,7 @@ export default class HttpServer implements ProtocolServer {
}

for (const propertyName in thing.properties) {
const propertyNamePattern = this.updateInteractionNameWithUriVariablePattern(
const propertyNamePattern = Helpers.updateInteractionNameWithUriVariablePattern(
propertyName,
thing.properties[propertyName].uriVariables,
thing.uriVariables
Expand Down Expand Up @@ -453,7 +419,7 @@ export default class HttpServer implements ProtocolServer {
}

for (const actionName in thing.actions) {
const actionNamePattern = this.updateInteractionNameWithUriVariablePattern(
const actionNamePattern = Helpers.updateInteractionNameWithUriVariablePattern(
actionName,
thing.actions[actionName].uriVariables,
thing.uriVariables
Expand All @@ -475,7 +441,7 @@ export default class HttpServer implements ProtocolServer {
}

for (const eventName in thing.events) {
const eventNamePattern = this.updateInteractionNameWithUriVariablePattern(
const eventNamePattern = Helpers.updateInteractionNameWithUriVariablePattern(
eventName,
thing.events[eventName].uriVariables,
thing.uriVariables
Expand Down
19 changes: 18 additions & 1 deletion packages/core/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { DataSchemaValue, ExposedThingInit } from "wot-typescript-definitions";
import { SomeJSONSchema } from "ajv/dist/types/json-schema";
import { ThingInteraction, ThingModelHelpers } from "@node-wot/td-tools";
import { Resolver } from "@node-wot/td-tools/src/resolver-interface";
import { DataSchema } from "wot-thing-description-types";
import { PropertyElement, DataSchema } from "wot-thing-description-types";
import { createLoggers } from "./logger";

const { debug, error, warn } = createLoggers("core", "helpers");
Expand Down Expand Up @@ -386,4 +386,21 @@ export default class Helpers implements Resolver {

return params;
}

public static updateInteractionNameWithUriVariablePattern(
interactionName: string,
affordanceUriVariables: PropertyElement["uriVariables"] = {},
thingUriVariables: PropertyElement["uriVariables"] = {}
): string {
const encodedInteractionName = encodeURIComponent(interactionName);
const uriVariables = [...Object.keys(affordanceUriVariables), ...Object.keys(thingUriVariables)];

if (uriVariables.length === 0) {
return encodedInteractionName;
}

const pattern = uriVariables.map(encodeURIComponent).join(",");

return encodedInteractionName + "{?" + pattern + "}";
}
}