Skip to content

Commit

Permalink
fix: pass along rejects for handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
danielpeintner committed Sep 25, 2020
1 parent f131625 commit 86c80a0
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 26 deletions.
57 changes: 31 additions & 26 deletions packages/core/src/exposed-thing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,20 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
reject(new Error(`ExposedThing '${this.title}', property '${propertyName}' is writeOnly`));
}

let ps: PropertyState = this.properties[propertyName].getState();
// call read handler (if any)
if (this.properties[propertyName].getState().readHandler != null) {
if (ps.readHandler != null) {
console.debug("[core/exposed-thing]",`ExposedThing '${this.title}' calls registered readHandler for Property '${propertyName}'`);
let ps: PropertyState = this.properties[propertyName].getState();
ps.readHandler(options).then((customValue) => {
resolve(customValue);
});
ps.readHandler(options)
.then((customValue) => {
resolve(customValue);
})
.catch((err) => {
reject(err);
});
} else {
console.debug("[core/exposed-thing]",`ExposedThing '${this.title}' gets internal value '${this.properties[propertyName].getState().value}' for Property '${propertyName}'`);
resolve(this.properties[propertyName].getState().value);
console.debug("[core/exposed-thing]",`ExposedThing '${this.title}' gets internal value '${ps.value}' for Property '${propertyName}'`);
resolve(ps.value);
}
} else {
reject(new Error(`ExposedThing '${this.title}', no property found for '${propertyName}'`));
Expand Down Expand Up @@ -227,23 +231,23 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
reject(new Error(`ExposedThing '${this.title}', property '${propertyName}' is readOnly`));
}

// call write handler (if any)
if (this.properties[propertyName].getState().writeHandler != null) {
let ps: PropertyState = this.properties[propertyName].getState();

// call write handler (if any)
if (ps.writeHandler != null) {
// be generous when no promise is returned
let ps: PropertyState = this.properties[propertyName].getState();
let promiseOrValueOrNil = ps.writeHandler(value, options);

if (promiseOrValueOrNil !== undefined) {
if (typeof promiseOrValueOrNil.then === "function") {
promiseOrValueOrNil.then((customValue) => {
console.debug("[core/exposed-thing]", `ExposedThing '${this.title}' write handler for Property '${propertyName}' sets custom value '${customValue}'`);
/** notify state change */
// notify state change
// FIXME object comparison
if (this.properties[propertyName].getState().value !== customValue) {
this.properties[propertyName].getState().subject.next(customValue);
if (ps.value !== customValue) {
ps.subject.next(customValue);
}
this.properties[propertyName].getState().value = customValue;
ps.value = customValue;
resolve();
})
.catch((customError) => {
Expand All @@ -252,28 +256,28 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
});
} else {
console.warn("[core/exposed-thing]", `ExposedThing '${this.title}' write handler for Property '${propertyName}' does not return promise`);
if (this.properties[propertyName].getState().value !== promiseOrValueOrNil) {
this.properties[propertyName].getState().subject.next(<any>promiseOrValueOrNil);
if (ps.value !== promiseOrValueOrNil) {
ps.subject.next(<any>promiseOrValueOrNil);
}
this.properties[propertyName].getState().value = <any>promiseOrValueOrNil;
ps.value = <any>promiseOrValueOrNil;
resolve();
}
} else {
console.warn("[core/exposed-thing]", `ExposedThing '${this.title}' write handler for Property '${propertyName}' does not return custom value, using direct value '${value}'`);

if (this.properties[propertyName].getState().value !== value) {
this.properties[propertyName].getState().subject.next(value);
if (ps.value !== value) {
ps.subject.next(value);
}
this.properties[propertyName].getState().value = value;
ps.value = value;
resolve();
}
} else {
console.debug("[core/exposed-thing]", `ExposedThing '${this.title}' directly sets Property '${propertyName}' to value '${value}'`);
/** notify state change */
if (this.properties[propertyName].getState().value !== value) {
this.properties[propertyName].getState().subject.next(value);
// notify state change
if (ps.value !== value) {
ps.subject.next(value);
}
this.properties[propertyName].getState().value = value;
ps.value = value;
resolve();
}
} else {
Expand Down Expand Up @@ -305,9 +309,10 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
if (this.actions[actionName]) {
console.debug("[core/exposed-thing]",`ExposedThing '${this.title}' has Action state of '${actionName}'`);

if (this.actions[actionName].getState().handler != null) {
let as: ActionState = this.actions[actionName].getState();
if (as.handler != null) {
console.debug("[core/exposed-thing]",`ExposedThing '${this.title}' calls registered handler for Action '${actionName}'`);
resolve(this.actions[actionName].getState().handler(parameter, options));
resolve(as.handler(parameter, options));
} else {
reject(new Error(`ExposedThing '${this.title}' has no handler for Action '${actionName}'`));
}
Expand Down
88 changes: 88 additions & 0 deletions packages/core/test/ServerTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,94 @@ class WoTServerTest {
expect(thing).to.have.property("@context").to.deep.include({"@language": "xx"});
}

@test async "should reject if property read handler fails"() {
let thing = await WoTServerTest.WoT.produce({
title: "Thing",
properties: {
uriProp: {
type: 'number'
}
}
});

thing.setPropertyReadHandler('uriProp', (options) => {
return new Promise((resolve, reject) => {
// Note: test reject
return reject('Fail expected');
});
});

let readingPossible = false;
try {
await thing.readProperty("uriProp");
readingPossible = true;
} catch (e) {
// as expected
}

if (readingPossible) {
fail("reading property 'uriProp' should throw error")
}
}

@test async "should reject if property write handler fails"() {
let thing = await WoTServerTest.WoT.produce({
title: "Thing",
properties: {
uriProp: {
type: 'number'
}
}
});

thing.setPropertyWriteHandler('uriProp', (options) => {
return new Promise((resolve, reject) => {
// Note: test reject
return reject('Fail expected');
});
});

let writingPossible = false;
try {
await thing.writeProperty("uriProp", 123);
writingPossible = true;
} catch (e) {
// as expected
}

if (writingPossible) {
fail("writing property 'uriProp' should throw error")
}
}

@test async "should reject if action handler fails"() {
let thing = await WoTServerTest.WoT.produce({
title: "Thing",
actions: {
toggle: {}
}
});

thing.setActionHandler('toggle', (options) => {
return new Promise((resolve, reject) => {
// Note: test reject
return reject('Fail expected');
});
});

let actionPossible = false;
try {
await thing.invokeAction("toggle");
actionPossible = true;
} catch (e) {
// as expected
}

if (actionPossible) {
fail("invoking action 'toggle' should throw error")
}
}

// TODO add Event and subscribe locally (based on addEvent)
// TODO add Event and subscribe locally (based on WoT.ThingFragment)
// TODO add Event and subscribe locally (based on WoT.ThingDescription)
Expand Down

0 comments on commit 86c80a0

Please sign in to comment.