Skip to content

Commit

Permalink
fix(td-tools): fixing build errors caused by eclipse-thingweb#758
Browse files Browse the repository at this point in the history
Signed-off-by: reluc <relu.cri@gmail.com>
  • Loading branch information
relu91 committed May 20, 2022
1 parent 22f7b2a commit 0bb7453
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 49 deletions.
2 changes: 2 additions & 0 deletions packages/td-tools/src/thing-description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export default class Thing implements TDT.ThingDescription {
constructor() {
this["@context"] = [DEFAULT_CONTEXT_V1, DEFAULT_CONTEXT_V11];
this["@type"] = DEFAULT_THING_TYPE;
this.title = "";
this.securityDefinitions = {};
this.security = "";
this.properties = {};
this.actions = {};
Expand Down
106 changes: 72 additions & 34 deletions packages/td-tools/src/thing-model-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class ThingModelHelpers {
static tsSchemaValidator = ajv.compile(tmSchema) as ValidateFunction;

private deps: string[] = [] as string[];
private resolver: Resolver = undefined;
private resolver?: Resolver = undefined;

constructor(_resolver?: Resolver) {
if (_resolver) {
Expand Down Expand Up @@ -129,9 +129,9 @@ export class ThingModelHelpers {
*
* @experimental
*/
public static getModelVersion(data: ThingModel): string {
if (!("version" in data) || !("model" in data.version)) {
return null;
public static getModelVersion(data: ThingModel): string | undefined {
if (!("version" in data) || !(data.version && "model" in data.version)) {
return undefined;
}
return data.version.model as string;
}
Expand All @@ -146,11 +146,11 @@ export class ThingModelHelpers {
*
* @experimental
*/
public static validateThingModel(data: ThingModel): { valid: boolean; errors: string } {
public static validateThingModel(data: ThingModel): { valid: boolean; errors?: string } {
const isValid = ThingModelHelpers.tsSchemaValidator(data);
let errors;
if (!isValid) {
errors = ThingModelHelpers.tsSchemaValidator.errors.map((o: ErrorObject) => o.message).join("\n");
errors = ThingModelHelpers.tsSchemaValidator.errors?.map((o: ErrorObject) => o.message).join("\n");
}
return {
valid: isValid,
Expand Down Expand Up @@ -239,7 +239,7 @@ export class ThingModelHelpers {
console.debug("[td-tools]", "http fetched:", parsedData);
resolve(parsedData);
} catch (e) {
console.error(e.message);
console.error("[td-tools]", (e as Error).message);
}
});
}).on("error", (e) => {
Expand All @@ -262,7 +262,7 @@ export class ThingModelHelpers {
console.debug("[td-tools]", "https fetched:", parsedData);
resolve(parsedData);
} catch (e) {
console.error(e.message);
console.error("[td-tools]", (e as Error).message);
}
});
})
Expand Down Expand Up @@ -322,10 +322,13 @@ export class ThingModelHelpers {
for (const aff in affRefs) {
const affUri = affRefs[aff] as string;
const refObj = this.parseTmRef(affUri);
if (!refObj.uri) {
throw new Error(`Missing remote path in ${affUri}`);
}
let source = await this.fetchModel(refObj.uri);
[source] = await this._getPartialTDs(source);
delete (data[affType] as DataSchema)[aff]["tm:ref"];
const importedAffordance = this.getRefAffordance(refObj, source);
const importedAffordance = this.getRefAffordance(refObj, source) ?? {};
refObj.name = aff; // update the name of the affordance
modelInput.imports.push({ affordance: importedAffordance, ...refObj });
}
Expand All @@ -348,7 +351,7 @@ export class ThingModelHelpers {
options?: CompositionOptions
): Promise<ThingModel[]> {
let tmpThingModels = [] as ThingModel[];
const title = data.title.replace(/ /g, "");
const title = (data.title ?? "").replace(/ /g, "");
if (!options) {
options = {} as CompositionOptions;
}
Expand All @@ -358,19 +361,22 @@ export class ThingModelHelpers {
const newTMHref = this.returnNewTMHref(options.baseUrl, title);
const newTDHref = this.returnNewTDHref(options.baseUrl, title);
if ("extends" in modelObject) {
const extendObjs = modelObject.extends;
for (const key in extendObjs) {
const el = extendObjs[key];
data = ThingModelHelpers.extendThingModel(el, data);
const extendObjs = modelObject.extends ?? [];
for (const extendObj of extendObjs) {
data = ThingModelHelpers.extendThingModel(extendObj, data);
}
// remove the tm:extends links
data.links = data.links.filter((link) => link.rel !== "tm:extends");
data.links = data.links?.filter((link) => link.rel !== "tm:extends");
}
if ("imports" in modelObject) {
const importObjs = modelObject.imports;
for (const key in importObjs) {
const el = importObjs[key];
data = ThingModelHelpers.importAffordance(el.type, el.name, el.affordance, data);
const importObjs = modelObject.imports ?? [];
for (const importedObj of importObjs) {
data = ThingModelHelpers.importAffordance(
importedObj.type,
importedObj.name,
importedObj.affordance,
data
);
}
}
if ("submodel" in modelObject) {
Expand All @@ -379,6 +385,12 @@ export class ThingModelHelpers {
for (const key in submodelObj) {
const sub = submodelObj[key];
if (options.selfComposition) {
if (!data.links) {
throw new Error(
"You used self composition but links are missing; they are needed to extract the instance name"
);
}

const index = data.links.findIndex((el) => el.href === key);
const el = data.links[index];
const instanceName = el.instanceName;
Expand All @@ -400,9 +412,9 @@ export class ThingModelHelpers {
}
}
} else {
const subTitle = sub.title.replace(/ /g, "");
const subTitle = (sub.title ?? "").replace(/ /g, "");
const subNewHref = this.returnNewTDHref(options.baseUrl, subTitle);
if (!("links" in sub)) {
if (!sub.links) {
sub.links = [];
}
sub.links.push({
Expand All @@ -416,7 +428,7 @@ export class ThingModelHelpers {
}
}
}
if (!("links" in data) || options.selfComposition) {
if (!data.links || options.selfComposition) {
data.links = [];
}
// add reference to the thing model
Expand All @@ -433,7 +445,7 @@ export class ThingModelHelpers {
data = this.fillPlaceholder(data, options.map);
}
tmpThingModels.unshift(data); // put itself as first element
tmpThingModels = tmpThingModels.map((el) => this.fillPlaceholder(el, options.map)); // TODO: make more efficient, since repeated each recursive call
tmpThingModels = tmpThingModels.map((el) => this.fillPlaceholder(el, options?.map)); // TODO: make more efficient, since repeated each recursive call
if (this.deps.length > 0) {
this.removeDependency();
}
Expand Down Expand Up @@ -471,15 +483,21 @@ export class ThingModelHelpers {
extendedModel = { ...source, ...dest };
// TODO: implement validation for extending
if (properties) {
if (!extendedModel.properties) {
extendedModel.properties = {};
}
for (const key in properties) {
if (dest.properties && key in dest.properties) {
if (dest.properties && dest.properties[key]) {
extendedModel.properties[key] = { ...properties[key], ...dest.properties[key] };
} else {
extendedModel.properties[key] = properties[key];
}
}
}
if (actions) {
if (!extendedModel.actions) {
extendedModel.actions = {};
}
for (const key in actions) {
if (dest.actions && key in dest.actions) {
extendedModel.actions[key] = { ...actions[key], ...dest.actions[key] };
Expand All @@ -489,6 +507,9 @@ export class ThingModelHelpers {
}
}
if (events) {
if (!extendedModel.events) {
extendedModel.events = {};
}
for (const key in events) {
if (dest.events && key in dest.events) {
extendedModel.events[key] = { ...events[key], ...dest.events[key] };
Expand All @@ -506,18 +527,32 @@ export class ThingModelHelpers {
source: DataSchema,
dest: ThingModel
): ThingModel {
const d = dest[affordanceType][affordanceName];
dest[affordanceType][affordanceName] = { ...source, ...d };
for (const key in dest[affordanceType][affordanceName]) {
if (dest[affordanceType][affordanceName][key] === null) {
delete dest[affordanceType][affordanceName][key];
if (!dest[affordanceType]) {
dest[affordanceType] = {};
}
/* eslint-disable @typescript-eslint/no-non-null-assertion */
// tsc doesn't know that dest[affordanceType] is not null
const d = dest[affordanceType]![affordanceName];
dest[affordanceType]![affordanceName] = { ...source, ...d };
for (const key in dest[affordanceType]![affordanceName]) {
if (dest[affordanceType]![affordanceName][key] === undefined) {
delete dest[affordanceType]![affordanceName][key];
}
}
/* eslint-enable @typescript-eslint/no-non-null-assertion */
return dest;
}

private static formatSubmodelLink(source: ThingModel, oldHref: string, newHref: string) {
if (!source.links) {
throw new Error("Links are missing");
}

const index = source.links.findIndex((el) => el.href === oldHref);
if (index === -1) {
throw new Error("Link not found");
}

const el = source.links[index];
if ("instanceName" in el) {
delete el.instanceName;
Expand All @@ -539,26 +574,29 @@ export class ThingModelHelpers {
return { uri: thingModelUri, type: affordaceType, name: affordaceName };
}

private getRefAffordance(obj: ModelImportsInput, thing: ThingModel): DataSchema {
private getRefAffordance(obj: ModelImportsInput, thing: ThingModel): DataSchema | undefined {
const affordanceType = obj.type;
const affordanceKey = obj.name;
if (!(affordanceType in thing)) {
return null;
return undefined;
}
const affordances = thing[affordanceType] as DataSchema;
if (!(affordanceKey in affordances)) {
return null;
return undefined;
}
return affordances[affordanceKey];
}

private fillPlaceholder(data: Record<string, unknown>, map: Record<string, unknown>): ThingModel {
private fillPlaceholder(data: Record<string, unknown>, map: Record<string, unknown> = {}): ThingModel {
const placeHolderReplacer = new JsonPlaceholderReplacer();
placeHolderReplacer.addVariableMap(map);
return placeHolderReplacer.replace(data) as ThingModel;
}

private checkPlaceholderMap(model: ThingModel, map: Record<string, unknown>): { valid: boolean; errors: string } {
private checkPlaceholderMap(
model: ThingModel,
map: Record<string, unknown> = {}
): { valid: boolean; errors?: string } {
const regex = "{{.*?}}";
const modelString = JSON.stringify(model);
// first check if model needs map
Expand Down
15 changes: 7 additions & 8 deletions packages/td-tools/test/ThingModelHelperCompositionTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ import { ExposedThingInit } from "wot-typescript-definitions";
chai.use(chaiAsPromised);
@suite("tests to verify the composition feature of Thing Model Helper")
class ThingModelHelperCompositionTest {
private thingModelHelpers: ThingModelHelpers;
async before() {
this.thingModelHelpers = new ThingModelHelpers();
}
private thingModelHelpers: ThingModelHelpers = new ThingModelHelpers();

async fetch(uri: string): Promise<unknown> {
const data = await fsPromises.readFile(uri, "utf-8");
Expand Down Expand Up @@ -102,9 +99,9 @@ class ThingModelHelperCompositionTest {
// eslint-disable-next-line dot-notation
const extendedModel = await this.thingModelHelpers["composeModel"](model, modelInput, options);
expect(extendedModel.length).to.be.equal(3);
expect(extendedModel[0]).to.be.deep.equal(finalModel);
expect(extendedModel[1]).to.be.deep.equal(finalModel1);
expect(extendedModel[2]).to.be.deep.equal(finalModel2);
expect(extendedModel[0]).to.be.deep.equal(finalModel, "FinalModel is not deep equal");
expect(extendedModel[1]).to.be.deep.equal(finalModel1, "FinalModel1 is not deep equal");
expect(extendedModel[2]).to.be.deep.equal(finalModel2, "FinalModel2 is not deep equal");
}

@test async "should correctly compose recursively a Thing Model with multiple partialTDs and extend/import"() {
Expand All @@ -123,7 +120,9 @@ class ThingModelHelperCompositionTest {
baseUrl: "http://test.com",
selfComposition: false,
};
finalModel2.links[0].href = "http://test.com/VentilatorThingModelRecursive.td.jsonld";

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- the links in the model are not null
finalModel2.links![0]!.href = "http://test.com/VentilatorThingModelRecursive.td.jsonld";
// eslint-disable-next-line dot-notation
const extendedModel = await this.thingModelHelpers["composeModel"](model, modelInput, options);
expect(extendedModel.length).to.be.equal(3);
Expand Down
11 changes: 4 additions & 7 deletions packages/td-tools/test/ThingModelHelperTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ import { ThingModelHelpers, CompositionOptions, modelComposeInput } from "../src
import { promises as fs } from "fs";
@suite("tests to verify the Thing Model Helper")
class ThingModelHelperTest {
private thingModelHelpers: ThingModelHelpers;
async before() {
this.thingModelHelpers = new ThingModelHelpers();
}
private thingModelHelpers = new ThingModelHelpers();

@test "should correctly validate tm schema with ThingModel in @type"() {
const model = {
Expand Down Expand Up @@ -135,7 +132,7 @@ class ThingModelHelperTest {
};

version = ThingModelHelpers.getModelVersion(thing);
expect(version).to.be.null;
expect(version).to.be.undefined;

thing = {
title: "thingTest",
Expand All @@ -144,7 +141,7 @@ class ThingModelHelperTest {
};

version = ThingModelHelpers.getModelVersion(thing);
expect(version).to.be.null;
expect(version).to.be.undefined;
}

@test async "should correctly extend a thing model with properties"() {
Expand Down Expand Up @@ -223,7 +220,7 @@ class ThingModelHelperTest {
properties: {
timestamp1: {
"tm:ref": "file://./test/thing-model/tmodels/OnOff.jsonld#/properties/timestamp",
description: null,
description: undefined,
},
},
};
Expand Down
3 changes: 3 additions & 0 deletions packages/td-tools/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"rootDir": ".."
},
"include": ["*.ts", "**/*.ts", "../src/**/*.ts"]
}
2 changes: 2 additions & 0 deletions packages/td-tools/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"strict": true,
"strictFunctionTypes": true,
"resolveJsonModule": true,
"types": ["node", "readable-stream"],
"outDir": "dist",
Expand Down

0 comments on commit 0bb7453

Please sign in to comment.