Skip to content

Commit

Permalink
refactor(core): use # instead __ as internal handler prefix (#1104)
Browse files Browse the repository at this point in the history
* refactor(core): use # instead __ as internal handler prefix

* chore(binding-opcua): adjust readPropertyHandler test code

* chore(core): adjust readPropertyHandler test code

* fixup! refactor(core): use # instead __ as internal handler prefix
  • Loading branch information
JKRhb authored Oct 4, 2023
1 parent b51ef19 commit 7cbe593
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 74 deletions.
10 changes: 3 additions & 7 deletions packages/binding-opcua/test/full-opcua-thing-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// node-wot implementation of W3C WoT Servient

import { expect } from "chai";
import { ExposedThing, Servient, createLoggers } from "@node-wot/core";
import { Servient, createLoggers } from "@node-wot/core";
import { InteractionOptions } from "wot-typescript-definitions";

import { OPCUAServer } from "node-opcua";
Expand Down Expand Up @@ -325,13 +325,9 @@ describe("Full OPCUA Thing Test", () => {
thing.expose();

let temperature = 10;
thing.setPropertyReadHandler("temperature", async () => temperature);
const readHandler = async () => temperature;
thing.setPropertyReadHandler("temperature", readHandler);

const expThing = thing as ExposedThing;
const readHandler = expThing.__propertyHandlers.get("temperature")?.readHandler;
if (!readHandler) {
expect.fail("must have a readHandler");
}
const temperatureCheck1 = await readHandler();
expect(temperatureCheck1).to.equal(10);

Expand Down
125 changes: 68 additions & 57 deletions packages/core/src/exposed-thing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,45 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
[key: string]: TDT.EventElement;
};

/** A map of property (read & write) handler callback functions */
__propertyHandlers: PropertyHandlerMap = new Map<string, PropertyHandlers>();
/**
* A map of property (read & write) handler callback functions.
*
* By using the private modifier `#`, this class member is excluded from
* the Thing Description generated by {@link getThingDescription}.
*/
#propertyHandlers: PropertyHandlerMap = new Map<string, PropertyHandlers>();

/** A map of action handler callback functions */
__actionHandlers: ActionHandlerMap = new Map<string, WoT.ActionHandler>();
/**
* A map of action handler callback functions.
*
* By using the private modifier `#`, this class member is excluded from
* the Thing Description generated by {@link getThingDescription}.
*/
#actionHandlers: ActionHandlerMap = new Map<string, WoT.ActionHandler>();

/** A map of event handler callback functions */
__eventHandlers: EventHandlerMap = new Map<string, EventHandlers>();
/**
* A map of event handler callback functions.
*
* By using the private modifier `#`, this class member is excluded from
* the Thing Description generated by {@link getThingDescription}.
*/
#eventHandlers: EventHandlerMap = new Map<string, EventHandlers>();

/** A map of property listener callback functions */
__propertyListeners: ProtocolListenerRegistry = new ProtocolListenerRegistry();
/**
* A map of property listener callback functions.
*
* By using the private modifier `#`, this class member is excluded from
* the Thing Description generated by {@link getThingDescription}.
*/
#propertyListeners: ProtocolListenerRegistry = new ProtocolListenerRegistry();

/** A map of event listener callback functions */
__eventListeners: ProtocolListenerRegistry = new ProtocolListenerRegistry();
/**
* A map of event listener callback functions.
*
* By using the private modifier `#`, this class member is excluded from
* the Thing Description generated by {@link getThingDescription}.
*/
#eventListeners: ProtocolListenerRegistry = new ProtocolListenerRegistry();

private getServient: () => Servient;

Expand Down Expand Up @@ -123,27 +148,13 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
}

public getThingDescription(): WoT.ThingDescription {
return JSON.parse(TD.serializeTD(this), (key, value) => {
// Check if key matches internals like "__propertyHandlers", "__actionHandlers", ...
// if matched return value "undefined"
if (
key === "__propertyHandlers" ||
key === "__actionHandlers" ||
key === "__eventHandlers" ||
key === "__propertyListeners" ||
key === "__eventListeners"
) {
return undefined;
}
// else return the value itself
return value;
});
return JSON.parse(TD.serializeTD(this));
}

public emitEvent(name: string, data: WoT.InteractionInput): void {
if (this.events[name] != null) {
const eventAffordance = this.events[name];
this.__eventListeners.notify(eventAffordance, data, eventAffordance.data);
this.#eventListeners.notify(eventAffordance, data, eventAffordance.data);
} else {
// NotFoundError
throw new Error("NotFoundError for event '" + name + "'");
Expand All @@ -153,7 +164,7 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
public async emitPropertyChange(name: string): Promise<void> {
if (this.properties[name] != null) {
const property = this.properties[name];
const readHandler = this.__propertyHandlers.get(name)?.readHandler;
const readHandler = this.#propertyHandlers.get(name)?.readHandler;

if (!readHandler) {
throw new Error(
Expand All @@ -162,7 +173,7 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
}

const data = await readHandler();
this.__propertyListeners.notify(property, data, property);
this.#propertyListeners.notify(property, data, property);
} else {
// NotFoundError
throw new Error("NotFoundError for property '" + name + "'");
Expand All @@ -189,11 +200,11 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
debug(`ExposedThing '${this.title}' destroying the thing and its interactions`);
await this.getServient().destroyThing(this.id);

this.__eventListeners.unregisterAll();
this.__propertyListeners.unregisterAll();
this.__eventHandlers.clear();
this.__propertyHandlers.clear();
this.__eventHandlers.clear();
this.#eventListeners.unregisterAll();
this.#propertyListeners.unregisterAll();
this.#eventHandlers.clear();
this.#propertyHandlers.clear();
this.#eventHandlers.clear();
}

/** @inheritDoc */
Expand All @@ -207,14 +218,14 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
`ExposedThing '${this.title}' cannot set read handler for property '${propertyName}' due to writeOnly flag`
);
} else {
let propertyHandler = this.__propertyHandlers.get(propertyName);
let propertyHandler = this.#propertyHandlers.get(propertyName);
if (propertyHandler) {
propertyHandler.readHandler = handler;
} else {
propertyHandler = { readHandler: handler };
}

this.__propertyHandlers.set(propertyName, propertyHandler);
this.#propertyHandlers.set(propertyName, propertyHandler);
}
} else {
throw new Error(`ExposedThing '${this.title}' has no Property '${propertyName}'`);
Expand All @@ -232,14 +243,14 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
`ExposedThing '${this.title}' cannot set write handler for property '${propertyName}' due to readOnly flag`
);
} else {
let propertyHandler = this.__propertyHandlers.get(propertyName);
let propertyHandler = this.#propertyHandlers.get(propertyName);
if (propertyHandler) {
propertyHandler.writeHandler = handler;
} else {
propertyHandler = { writeHandler: handler };
}

this.__propertyHandlers.set(propertyName, propertyHandler);
this.#propertyHandlers.set(propertyName, propertyHandler);
}
} else {
throw new Error(`ExposedThing '${this.title}' has no Property '${propertyName}'`);
Expand All @@ -257,13 +268,13 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
`ExposedThing '${this.title}' cannot set observe handler for property '${name}' since the observable flag is set to false`
);
} else {
let propertyHandler = this.__propertyHandlers.get(name);
let propertyHandler = this.#propertyHandlers.get(name);
if (propertyHandler) {
propertyHandler.observeHandler = handler;
} else {
propertyHandler = { observeHandler: handler };
}
this.__propertyHandlers.set(name, propertyHandler);
this.#propertyHandlers.set(name, propertyHandler);
}
} else {
throw new Error(`ExposedThing '${this.title}' has no Property '${name}'`);
Expand All @@ -281,13 +292,13 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
`ExposedThing '${this.title}' cannot set unobserve handler for property '${name}' due to missing observable flag`
);
} else {
let propertyHandler = this.__propertyHandlers.get(name);
let propertyHandler = this.#propertyHandlers.get(name);
if (propertyHandler) {
propertyHandler.unobserveHandler = handler;
} else {
propertyHandler = { unobserveHandler: handler };
}
this.__propertyHandlers.set(name, propertyHandler);
this.#propertyHandlers.set(name, propertyHandler);
}
} else {
throw new Error(`ExposedThing '${this.title}' has no Property '${name}'`);
Expand All @@ -300,7 +311,7 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
debug(`ExposedThing '${this.title}' setting action handler for '${actionName}'`);

if (this.actions[actionName] != null) {
this.__actionHandlers.set(actionName, handler);
this.#actionHandlers.set(actionName, handler);
} else {
throw new Error(`ExposedThing '${this.title}' has no Action '${actionName}'`);
}
Expand All @@ -312,14 +323,14 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
debug(`ExposedThing '${this.title}' setting event subscribe handler for '${name}'`);

if (this.events[name] != null) {
let eventHandler = this.__eventHandlers.get(name);
let eventHandler = this.#eventHandlers.get(name);
if (eventHandler) {
eventHandler.subscribe = handler;
} else {
eventHandler = { subscribe: handler };
}

this.__eventHandlers.set(name, eventHandler);
this.#eventHandlers.set(name, eventHandler);
} else {
throw new Error(`ExposedThing '${this.title}' has no Event '${name}'`);
}
Expand All @@ -331,14 +342,14 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
debug(`ExposedThing '${this.title}' setting event unsubscribe handler for '${name}'`);

if (this.events[name] != null) {
let eventHandler = this.__eventHandlers.get(name);
let eventHandler = this.#eventHandlers.get(name);
if (eventHandler) {
eventHandler.unsubscribe = handler;
} else {
eventHandler = { unsubscribe: handler };
}

this.__eventHandlers.set(name, eventHandler);
this.#eventHandlers.set(name, eventHandler);
} else {
throw new Error(`ExposedThing '${this.title}' has no Event '${name}'`);
}
Expand All @@ -358,7 +369,7 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
if (this.actions[name] != null) {
debug(`ExposedThing '${this.title}' has Action state of '${name}'`);

const handler = this.__actionHandlers.get(name);
const handler = this.#actionHandlers.get(name);
if (handler != null) {
debug(`ExposedThing '${this.title}' calls registered handler for Action '${name}'`);
Helpers.validateInteractionOptions(this, this.actions[name], options);
Expand Down Expand Up @@ -390,7 +401,7 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
if (this.properties[propertyName] != null) {
debug(`ExposedThing '${this.title}' has Action state of '${propertyName}'`);

const readHandler = this.__propertyHandlers.get(propertyName)?.readHandler;
const readHandler = this.#propertyHandlers.get(propertyName)?.readHandler;

if (readHandler != null) {
debug(`ExposedThing '${this.title}' calls registered readHandler for Property '${propertyName}'`);
Expand Down Expand Up @@ -480,7 +491,7 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
throw new Error(`ExposedThing '${this.title}', property '${propertyName}' is readOnly`);
}
Helpers.validateInteractionOptions(this, this.properties[propertyName], options);
const writeHandler = this.__propertyHandlers.get(propertyName)?.writeHandler;
const writeHandler = this.#propertyHandlers.get(propertyName)?.writeHandler;
const form = this.properties[propertyName]?.forms[options.formIndex] ?? {};
// call write handler (if any)
if (writeHandler != null) {
Expand Down Expand Up @@ -543,15 +554,15 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
);

if (formIndex !== -1) {
this.__eventListeners.register(this.events[name], formIndex, listener);
this.#eventListeners.register(this.events[name], formIndex, listener);
debug(`ExposedThing '${this.title}' subscribes to event '${name}'`);
} else {
throw new Error(
`ExposedThing '${this.title}', no property listener from found for '${name}' with form index '${options.formIndex}'`
);
}

const subscribe = this.__eventHandlers.get(name)?.subscribe;
const subscribe = this.#eventHandlers.get(name)?.subscribe;
if (subscribe) {
await subscribe(options);
}
Expand Down Expand Up @@ -580,13 +591,13 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
options.formIndex
);
if (formIndex !== -1) {
this.__eventListeners.unregister(this.events[name], formIndex, listener);
this.#eventListeners.unregister(this.events[name], formIndex, listener);
} else {
throw new Error(
`ExposedThing '${this.title}', no event listener from found for '${name}' with form index '${options.formIndex}'`
);
}
const unsubscribe = this.__eventHandlers.get(name)?.unsubscribe;
const unsubscribe = this.#eventHandlers.get(name)?.unsubscribe;
if (unsubscribe) {
unsubscribe(options);
}
Expand Down Expand Up @@ -615,15 +626,15 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
);

if (formIndex !== -1) {
this.__propertyListeners.register(this.properties[name], formIndex, listener);
this.#propertyListeners.register(this.properties[name], formIndex, listener);
debug(`ExposedThing '${this.title}' subscribes to property '${name}'`);
} else {
throw new Error(
`ExposedThing '${this.title}', no property listener from found for '${name}' with form index '${options.formIndex}'`
);
}

const observeHandler = this.__propertyHandlers.get(name)?.observeHandler;
const observeHandler = this.#propertyHandlers.get(name)?.observeHandler;
if (observeHandler) {
await observeHandler(options);
}
Expand All @@ -647,14 +658,14 @@ export default class ExposedThing extends TD.Thing implements WoT.ExposedThing {
);

if (formIndex !== -1) {
this.__propertyListeners.unregister(this.properties[name], formIndex, listener);
this.#propertyListeners.unregister(this.properties[name], formIndex, listener);
} else {
throw new Error(
`ExposedThing '${this.title}', no property listener from found for '${name}' with form index '${options.formIndex}'`
);
}

const unobserveHandler = this.__propertyHandlers.get(name)?.unobserveHandler;
const unobserveHandler = this.#propertyHandlers.get(name)?.unobserveHandler;
if (unobserveHandler) {
unobserveHandler(options);
}
Expand Down
21 changes: 11 additions & 10 deletions packages/core/test/ServerTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,11 @@ class WoTServerTest {
expect(thing.getThingDescription()).to.have.property("support").that.equals("none");
expect(thing.getThingDescription()).to.have.property("test:custom").that.equals("test");
// should not share internals
expect(thing.getThingDescription()).to.not.have.property("__propertyHandlers");
expect(thing.getThingDescription()).to.not.have.property("__actionHandlers");
expect(thing.getThingDescription()).to.not.have.property("__eventHandlers");
expect(thing.getThingDescription()).to.not.have.property("__propertyListeners");
expect(thing.getThingDescription()).to.not.have.property("__eventListeners");
expect(thing.getThingDescription()).to.not.have.property("#propertyHandlers");
expect(thing.getThingDescription()).to.not.have.property("#actionHandlers");
expect(thing.getThingDescription()).to.not.have.property("#eventHandlers");
expect(thing.getThingDescription()).to.not.have.property("#propertyListeners");
expect(thing.getThingDescription()).to.not.have.property("#eventListeners");
// direct access
expect(thing).to.have.property("title").that.equals("myFragmentThing");
expect(thing).to.have.property("support").that.equals("none");
Expand Down Expand Up @@ -278,17 +278,18 @@ class WoTServerTest {
},
});
const number: WoT.DataSchemaValue = 1; // init
thing.setPropertyReadHandler("my number", () => {
return new Promise((resolve, reject) => {

const readHandler = () => {
return new Promise<InteractionInput>((resolve) => {
resolve(number);
});
});
};

thing.setPropertyReadHandler("my number", readHandler);

expect(thing).to.have.property("properties").to.have.property("my number");

// Check internals, how to to check handlers properly with *some* type-safety
const expThing = thing as ExposedThing;
const readHandler = expThing.__propertyHandlers.get("my number")?.readHandler;
const ff = await readHandler?.();
expect(ff).to.equal(1);
}
Expand Down

0 comments on commit 7cbe593

Please sign in to comment.