diff --git a/packages/binding-coap/src/coap-client.ts b/packages/binding-coap/src/coap-client.ts index 4d0740ace..c7156d7d6 100644 --- a/packages/binding-coap/src/coap-client.ts +++ b/packages/binding-coap/src/coap-client.ts @@ -55,6 +55,21 @@ export default class CoapClient implements ProtocolClient { registerFormat(ContentSerdes.JSON_LD, 2100); } + discoverDirectly(uri: string): Promise { + const options: CoapRequestParams = this.uriToOptions(uri); + const req = this.agent.request(options); + + req.setOption("Accept", "application/td+json"); + return new Promise((resolve, reject) => { + req.on("response", (res: IncomingMessage) => { + const contentType = (res.headers["Content-Format"] as string) ?? "application/td+json"; + resolve(new Content(contentType, Readable.from(res.payload))); + }); + req.on("error", (err: Error) => reject(err)); + req.end(); + }); + } + public toString(): string { return "[CoapClient]"; } diff --git a/packages/binding-coap/src/coaps-client.ts b/packages/binding-coap/src/coaps-client.ts index 2113c4f4f..ea93d3318 100644 --- a/packages/binding-coap/src/coaps-client.ts +++ b/packages/binding-coap/src/coaps-client.ts @@ -235,4 +235,8 @@ export default class CoapsClient implements ProtocolClient { return req; } + + async discoverDirectly(uri: string): Promise { + throw new Error("Method not implemented."); + } } diff --git a/packages/binding-file/src/file-client.ts b/packages/binding-file/src/file-client.ts index 31c3d005a..0b721adf5 100644 --- a/packages/binding-file/src/file-client.ts +++ b/packages/binding-file/src/file-client.ts @@ -90,4 +90,8 @@ export default class FileClient implements ProtocolClient { } public setSecurity = (metadata: Array): boolean => false; + + async discoverDirectly(uri: string): Promise { + throw new Error("Method not implemented."); + } } diff --git a/packages/binding-http/src/http-client-impl.ts b/packages/binding-http/src/http-client-impl.ts index de5c93182..f489f77e0 100644 --- a/packages/binding-http/src/http-client-impl.ts +++ b/packages/binding-http/src/http-client-impl.ts @@ -409,4 +409,15 @@ export default class HttpClient implements ProtocolClient { return url; } + + async discoverDirectly(uri: string): Promise { + // Note: This is still work in progress + const headers: HeadersInit = { + Accept: "application/td+json", + }; + const response = await fetch(uri, { headers }); + // TODO: Result should be validated + const body = ProtocolHelpers.toNodeStream(response.body as Readable); + return new Content(response.headers.get("content-type") ?? "application/td+json", body); + } } diff --git a/packages/binding-mbus/src/mbus-client.ts b/packages/binding-mbus/src/mbus-client.ts index 63a8c24cb..ebd021c18 100644 --- a/packages/binding-mbus/src/mbus-client.ts +++ b/packages/binding-mbus/src/mbus-client.ts @@ -144,4 +144,8 @@ export default class MBusClient implements ProtocolClient { return result; } + + async discoverDirectly(uri: string): Promise { + throw new Error("Method not implemented."); + } } diff --git a/packages/binding-modbus/src/modbus-client.ts b/packages/binding-modbus/src/modbus-client.ts index f375d227d..207676aaa 100644 --- a/packages/binding-modbus/src/modbus-client.ts +++ b/packages/binding-modbus/src/modbus-client.ts @@ -309,4 +309,8 @@ export default class ModbusClient implements ProtocolClient { return result as ModbusFormWithDefaults; } + + async discoverDirectly(uri: string): Promise { + throw new Error("Method not implemented."); + } } diff --git a/packages/binding-mqtt/src/mqtt-client.ts b/packages/binding-mqtt/src/mqtt-client.ts index ef3b5d1c9..8d7aba6b1 100644 --- a/packages/binding-mqtt/src/mqtt-client.ts +++ b/packages/binding-mqtt/src/mqtt-client.ts @@ -199,4 +199,8 @@ export default class MqttClient implements ProtocolClient { return (qos = 0); } } + + async discoverDirectly(uri: string): Promise { + throw new Error("Method not implemented."); + } } diff --git a/packages/binding-netconf/src/netconf-client.ts b/packages/binding-netconf/src/netconf-client.ts index 90d8892cb..93d7cbdcf 100644 --- a/packages/binding-netconf/src/netconf-client.ts +++ b/packages/binding-netconf/src/netconf-client.ts @@ -175,4 +175,8 @@ export default class NetconfClient implements ProtocolClient { this.credentials = credentials; return true; } + + async discoverDirectly(uri: string): Promise { + throw new Error("Method not implemented."); + } } diff --git a/packages/binding-opcua/src/opcua-protocol-client.ts b/packages/binding-opcua/src/opcua-protocol-client.ts index 524564c1c..37a1ae019 100644 --- a/packages/binding-opcua/src/opcua-protocol-client.ts +++ b/packages/binding-opcua/src/opcua-protocol-client.ts @@ -620,4 +620,8 @@ export class OPCUAProtocolClient implements ProtocolClient { return new Content("application/json", Readable.from(JSON.stringify(body))); } + + async discoverDirectly(uri: string): Promise { + throw new Error("Method not implemented."); + } } diff --git a/packages/binding-websockets/src/ws-client.ts b/packages/binding-websockets/src/ws-client.ts index cdf79dd12..7938f35f9 100644 --- a/packages/binding-websockets/src/ws-client.ts +++ b/packages/binding-websockets/src/ws-client.ts @@ -29,6 +29,10 @@ export default class WebSocketClient implements ProtocolClient { // TODO: implement and remove eslint-ignore-useless-constructor } + async discoverDirectly(uri: string): Promise { + throw new Error("Method not implemented."); + } + public toString(): string { return `[WebSocketClient]`; } diff --git a/packages/core/example/example.js b/packages/core/example/example.js new file mode 100644 index 000000000..c4bd661ba --- /dev/null +++ b/packages/core/example/example.js @@ -0,0 +1,28 @@ +const { Servient } = require("@node-wot/core"); +const { HttpClientFactory } = require("@node-wot/binding-http"); +const { CoapClientFactory } = require("@node-wot/binding-coap"); + +// Note: This example is just for testing/demonstration purposes and +// will eventually be removed/moved to the examples package. +async function discover() { + const servient = new Servient(); + servient.addClientFactory(new HttpClientFactory()); + servient.addClientFactory(new CoapClientFactory()); + const wot = await servient.start(); + + const httpUrl = "http://plugfest.thingweb.io:8083/smart-coffee-machine"; + const coapUrl = "coap://plugfest.thingweb.io:5683/smart-coffee-machine"; + + for (const url of [httpUrl, coapUrl]) { + console.log(await wot.discovery.direct(url)); + } + + // Alternative approach + for (const url of [httpUrl, coapUrl]) { + for await (const result of wot.discovery.directIterator(url)) { + console.log(result); + } + } +} + +discover(); diff --git a/packages/core/src/protocol-interfaces.ts b/packages/core/src/protocol-interfaces.ts index 68ded1233..5eb246866 100644 --- a/packages/core/src/protocol-interfaces.ts +++ b/packages/core/src/protocol-interfaces.ts @@ -59,6 +59,8 @@ export interface ProtocolClient { /** this client is requested to perform an "unlink" on the resource with the given URI */ unlinkResource(form: TD.Form): Promise; + discoverDirectly(uri: string): Promise; + subscribeResource( form: TD.Form, next: (content: Content) => void, diff --git a/packages/core/src/wot-impl.ts b/packages/core/src/wot-impl.ts index 55ac7265b..1b9a05339 100644 --- a/packages/core/src/wot-impl.ts +++ b/packages/core/src/wot-impl.ts @@ -19,28 +19,73 @@ import Servient from "./servient"; import ExposedThing from "./exposed-thing"; import ConsumedThing from "./consumed-thing"; import Helpers from "./helpers"; +import { ThingDescription } from "wot-thing-description-types"; import { createLoggers } from "./logger"; +import ContentManager from "./content-serdes"; const { debug } = createLoggers("core", "wot-impl"); +class ThingDiscoveryProcess implements WoT.ThingDiscoveryProcess { + #done = false; + + get done() { + return this.#done; + } + + #error?: Error; + + get error() { + return this.#error; + } + + #filter: WoT.ThingFilter; + + constructor(filter?: WoT.ThingFilter) { + this.#filter = filter ?? {}; + } + + filter?: WoT.ThingFilter | undefined; + + [Symbol.asyncIterator](): AsyncIterator { + throw new Error("Method not implemented."); + } + + stop(): void { + this.#done = true; + } +} + export default class WoTImpl { private srv: Servient; + constructor(srv: Servient) { this.srv = srv; } /** @inheritDoc */ async discover(filter?: WoT.ThingFilter): Promise { - throw new Error("not implemented"); + // TODO: Implement this function + return new ThingDiscoveryProcess(filter); } /** @inheritDoc */ async exploreDirectory(url: string, filter?: WoT.ThingFilter): Promise { - throw new Error("not implemented"); + return new ThingDiscoveryProcess(filter); } - async requestThingDescription(url: string): Promise { - throw new Error("not implemented"); + /** @inheritDoc */ + async requestThingDescription(url: string): Promise { + const uriScheme = new URL(url).protocol.split(":")[0]; + const client = this.srv.getClientFor(uriScheme); + const result = await client.discoverDirectly(url); + + const value = ContentManager.contentToValue({ type: result.type, body: await result.toBuffer() }, {}); + + if (value instanceof Object) { + return value as ThingDescription; + } + + throw new Error("Not found."); } /** @inheritDoc */ diff --git a/packages/core/test/ClientTest.ts b/packages/core/test/ClientTest.ts index 56a28678a..380114273 100644 --- a/packages/core/test/ClientTest.ts +++ b/packages/core/test/ClientTest.ts @@ -169,6 +169,10 @@ class TDClient implements ProtocolClient { public toString(): string { return "TDClient"; } + + async discoverDirectly(uri: string): Promise { + throw new Error("discoverDirectly not implemented."); + } } class TDClientFactory implements ProtocolClientFactory { @@ -239,6 +243,10 @@ class TrapClient implements ProtocolClient { } public setSecurity = (metadata: SecurityScheme[]) => false; + + async discoverDirectly(uri: string): Promise { + throw new Error("discoverDirectly not implemented."); + } } class TrapClientFactory implements ProtocolClientFactory { @@ -306,6 +314,10 @@ class TestProtocolClient implements ProtocolClient { this.securitySchemes = securitySchemes; return true; } + + async discoverDirectly(uri: string): Promise { + throw new Error("discoverDirectly not implemented."); + } } @suite("client flow of servient") diff --git a/tsconfig.json b/tsconfig.json index f60306d3c..80c0127d3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es6", + "target": "es2018", "lib": ["dom"], "skipLibCheck": false, "module": "commonjs",