Skip to content

Commit

Permalink
fix: report allproperties with proper type (#985)
Browse files Browse the repository at this point in the history
Co-authored-by: danielpeintner <daniel.peintner@gmail.com>
Co-authored-by: Cristiano Aguzzi <relu91@users.noreply.github.com>
  • Loading branch information
danielpeintner and relu91 authored May 11, 2023
1 parent dc13ba1 commit 55a2615
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 26 deletions.
9 changes: 6 additions & 3 deletions packages/binding-http/src/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -758,13 +758,16 @@ export default class HttpServer implements ProtocolServer {
const propMap: PropertyContentMap = await thing.handleReadAllProperties({
formIndex: 0,
});
res.setHeader("Content-Type", "application/json"); // contentType handling?
res.setHeader("Content-Type", ContentSerdes.DEFAULT); // contentType handling?
res.writeHead(200);
const recordResponse: Record<string, unknown> = {};
for (const key of propMap.keys()) {
const content: Content = propMap.get(key);
const data = await content.toBuffer();
recordResponse[key] = data.toString();
const value = ContentSerdes.get().contentToValue(
{ type: ContentSerdes.DEFAULT, body: await content.toBuffer() },
{}
);
recordResponse[key] = value;
}
res.end(JSON.stringify(recordResponse));
} catch (err) {
Expand Down
102 changes: 98 additions & 4 deletions packages/binding-http/test/http-server-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ class HttpServerTest {
resp = await (await fetch(uri + "properties/test")).text();
expect(resp).to.equal('"off"');

resp = await (await fetch(uri + "properties")).text();
expect(resp).to.equal('{"test":"\\"off\\""}');
resp = await (await fetch(uri + "properties")).json();
expect(resp).to.deep.equal({ test: "off" });

resp = await (await fetch(uri + "properties/test", { method: "PUT", body: "on" })).text();
expect(resp).to.equal("");
Expand Down Expand Up @@ -271,8 +271,8 @@ class HttpServerTest {
resp = await (await fetch(uri + "properties/test")).text();
expect(resp).to.equal("{}");

resp = await (await fetch(uri + "properties")).text();
expect(resp).to.equal('{"test":"{}"}');
resp = await (await fetch(uri + "properties")).json();
expect(resp).to.deep.equal({ test: {} });

resp = await (
await fetch(uri + "properties/test", {
Expand Down Expand Up @@ -549,6 +549,100 @@ class HttpServerTest {
return httpServer.stop();
}

@test async "should report allproperties excluding non-JSON properties"() {
const httpServer = new HttpServer({ port: 0 });

await httpServer.start(null);

const tdTemplate: WoT.ExposedThingInit = {
title: "TestA",
properties: {
image: {
forms: [
{
contentType: "image/svg+xml",
},
],
},
testInteger: {
type: "integer",
},
testBoolean: {
type: "boolean",
},
testString: {
type: "string",
},
testObject: {
type: "object",
},
testArray: {
type: "array",
},
},
};
const testThing = new ExposedThing(null, tdTemplate);

const image = "<svg xmlns='http://www.w3.org/2000/svg'><text>FOO</text></svg>";
const integer = 123;
const boolean = true;
const string = "ABCD";
const object = { t1: "xyz", i: 77 };
const array = ["x", "y", "z"];
testThing.setPropertyReadHandler("image", (_) => Promise.resolve(image));
testThing.setPropertyReadHandler("testInteger", (_) => Promise.resolve(integer));
testThing.setPropertyReadHandler("testBoolean", (_) => Promise.resolve(boolean));
testThing.setPropertyReadHandler("testString", (_) => Promise.resolve(string));
testThing.setPropertyReadHandler("testObject", (_) => Promise.resolve(object));
testThing.setPropertyReadHandler("testArray", (_) => Promise.resolve(array));

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
testThing.properties.image.forms = [];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
testThing.properties.testInteger.forms = [];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
testThing.properties.testBoolean.forms = [];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
testThing.properties.testString.forms = [];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
testThing.properties.testObject.forms = [];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
testThing.properties.testArray.forms = [];

await httpServer.expose(testThing, tdTemplate);

// check values one by one first
const responseInteger = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties/testInteger`);
expect(await responseInteger.json()).to.equal(integer);
const responseBoolean = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties/testBoolean`);
expect(await responseBoolean.json()).to.equal(boolean);
const responseString = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties/testString`);
expect(await responseString.json()).to.equal(string);
const responseObject = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties/testObject`);
expect(await responseObject.json()).to.deep.equal(object);
const responseArray = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties/testArray`);
expect(await responseArray.json()).to.deep.equal(array);

// check values of readallproperties
const responseAll = await fetch(`http://localhost:${httpServer.getPort()}/testa/properties`);
expect(await responseAll.json()).to.deep.equal({
// "image": image, // Note: No support for contentTypes other than JSON -> not included
testInteger: integer,
testBoolean: boolean,
testString: string,
testObject: object,
testArray: array,
});

return httpServer.stop();
}

@test async "should support setting SVG contentType"() {
const httpServer = new HttpServer({ port: 0 });

Expand Down
31 changes: 12 additions & 19 deletions packages/core/src/exposed-thing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { InteractionOutput } from "./interaction-output";
import { Readable } from "stream";
import ProtocolHelpers from "./protocol-helpers";
import { ReadableStream as PolyfillStream } from "web-streams-polyfill/ponyfill/es2018";
import { Content, PropertyContentMap } from "./core";
import { Content, ContentSerdes, PropertyContentMap } from "./core";
import ContentManager from "./content-serdes";
import {
ActionHandlerMap,
Expand Down Expand Up @@ -422,26 +422,19 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
propertyNames: string[],
options: WoT.InteractionOptions & { formIndex: number }
): Promise<PropertyContentMap> {
// collect all single promises into array
const promises: Promise<Content>[] = [];
for (const propertyName of propertyNames) {
// Note: currently only DataSchema properties are supported
const form = this.properties[propertyName].forms.find(
(form) => form.contentType === "application/json" || !form.contentType
);
if (!form) {
continue;
}

promises.push(this.handleReadProperty(propertyName, options));
}
try {
// wait for all promises to succeed and create response
// collect all single promises
const output = new Map<string, Content>();
const results = await Promise.all(promises);

for (let i = 0; i < results.length; i++) {
output.set(propertyNames[i], results[i]);
for (const propertyName of propertyNames) {
// Note: currently only JSON DataSchema properties are supported
const form = this.properties[propertyName].forms.find(
(form) => form.contentType === ContentSerdes.DEFAULT || !form.contentType
);
if (!form) {
continue;
}
const contentResponse = await this.handleReadProperty(propertyName, options);
output.set(propertyName, contentResponse);
}
return output;
} catch (error) {
Expand Down

0 comments on commit 55a2615

Please sign in to comment.