Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion node/aas-aid/src/asset-interfaces-description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1213,12 +1213,15 @@ export class AssetInterfacesDescription {
for (const propertyKey in td.properties) {
const property: PropertyElement = td.properties[propertyKey];

// we get all the property keys and remove known keys by the TD spec at each if clause
let propertyKeys: any = Object.keys(property);
// check whether form exists for a given protocol (prefix)
const formElementPicked = this.getFormForProtocol(property, protocol);
if (formElementPicked === undefined) {
// do not add this property, since there will be no href of interest
continue;
}
propertyKeys.splice(propertyKeys.indexOf("forms"), 1);

const propertyValues: Array<unknown> = [];
// type
Expand All @@ -1230,6 +1233,7 @@ export class AssetInterfacesDescription {
value: property.type,
modelType: "Property",
});
propertyKeys.splice(propertyKeys.indexOf("type"), 1);
// special AID treatment
if (property.minimum != null || property.maximum != null) {
const minMax: { [k: string]: unknown } = {
Expand All @@ -1246,12 +1250,14 @@ export class AssetInterfacesDescription {
(minMax.supplementalSemanticIds as Array<unknown>).push(
this.createSemanticId("https://www.w3.org/2019/wot/json-schema#minimum")
);
propertyKeys.splice(propertyKeys.indexOf("minimum"), 1);
}
if (property.maximum != null) {
minMax.max = property.maximum.toString();
(minMax.supplementalSemanticIds as Array<unknown>).push(
this.createSemanticId("https://www.w3.org/2019/wot/json-schema#maximum")
);
propertyKeys.splice(propertyKeys.indexOf("maximum"), 1);
}
propertyValues.push(minMax);
}
Expand All @@ -1270,12 +1276,14 @@ export class AssetInterfacesDescription {
(itemsRange.supplementalSemanticIds as Array<unknown>).push(
this.createSemanticId("https://www.w3.org/2019/wot/json-schema#minItems")
);
propertyKeys.splice(propertyKeys.indexOf("minItems"), 1);
}
if (property.maxItems != null) {
itemsRange.max = property.maxItems.toString();
(itemsRange.supplementalSemanticIds as Array<unknown>).push(
this.createSemanticId("https://www.w3.org/2019/wot/json-schema#maxItems")
);
propertyKeys.splice(propertyKeys.indexOf("maxItems"), 1);
}
propertyValues.push(itemsRange);
}
Expand All @@ -1294,12 +1302,14 @@ export class AssetInterfacesDescription {
(lengthRange.supplementalSemanticIds as Array<unknown>).push(
this.createSemanticId("https://www.w3.org/2019/wot/json-schema#minLength")
);
propertyKeys.splice(propertyKeys.indexOf("minLength"), 1);
}
if (property.maxLength != null) {
lengthRange.max = property.maxLength.toString();
(lengthRange.supplementalSemanticIds as Array<unknown>).push(
this.createSemanticId("https://www.w3.org/2019/wot/json-schema#maxLength")
);
propertyKeys.splice(propertyKeys.indexOf("maxLength"), 1);
}
propertyValues.push(lengthRange);
}
Expand All @@ -1313,10 +1323,12 @@ export class AssetInterfacesDescription {
value: property.title,
modelType: "Property",
});
propertyKeys.splice(propertyKeys.indexOf("title"), 1);
}
// description
if (property.description != null) {
// AID deals with description in level above
propertyKeys.splice(propertyKeys.indexOf("description"), 1);
}
// observable (if it deviates from the default == false only)
if (property.observable != null && property.observable === true) {
Expand All @@ -1327,6 +1339,7 @@ export class AssetInterfacesDescription {
value: `${property.observable}`, // in AID represented as string
modelType: "Property",
});
propertyKeys.splice(propertyKeys.indexOf("observable"), 1);
}
// contentMediaType
if (property.contentMediaType != null) {
Expand All @@ -1339,6 +1352,7 @@ export class AssetInterfacesDescription {
value: property.contentMediaType,
modelType: "Property",
});
propertyKeys.splice(propertyKeys.indexOf("contentMediaType"), 1);
}
// TODO enum
// const
Expand All @@ -1349,6 +1363,7 @@ export class AssetInterfacesDescription {
value: property.const,
modelType: "Property",
});
propertyKeys.splice(propertyKeys.indexOf("const"), 1);
}
// default
if (property.default != null) {
Expand All @@ -1359,19 +1374,31 @@ export class AssetInterfacesDescription {
value: property.default,
modelType: "Property",
});
propertyKeys.splice(propertyKeys.indexOf("default"), 1);
}
// unit
if (property.unit != null) {
// TODO: handle if value has : in it
propertyValues.push({
idShort: "unit",
valueType: "xs:string",
value: property.unit,
modelType: "Property",
});
propertyKeys.splice(propertyKeys.indexOf("unit"), 1);
}
// TODO items

// readOnly and writeOnly marked as EXTERNAL in AID spec
if (property.readOnly != null) {
propertyKeys.splice(propertyKeys.indexOf("readOnly"), 1);
// TODO: readOnly marked as EXTERNAL in AID spec
}

if (property.writeOnly != null) {
propertyKeys.splice(propertyKeys.indexOf("writeOnly"), 1);
// TODO: writeOnly marked as EXTERNAL in AID spec
}

// range and others? Simply add them as is?

// forms
Expand Down Expand Up @@ -1498,6 +1525,59 @@ export class AssetInterfacesDescription {
});
}

// Handling of any key that has : in it by creating semantic ids for each
// no need to bother if there is no object in the context or it is a string
const tdContext = td["@context"];
let prefixObject: any = {};

if (Array.isArray(tdContext)) {
// iterate through the array and add key-value pairs of each object to a single one
tdContext.forEach((arrayItem) => {
if (typeof arrayItem === "object") {
// insert every pair into the prefixObject
for (const key in arrayItem) {
prefixObject[key] = arrayItem[key as keyof typeof arrayItem];
}
} else if (typeof arrayItem === "string") {
// it is either the standard context or another non-prefix term.
// in second case, it is not useful without full rdf processing
} else {
//probably a json-ld error?
}
});
} else {
// then it is a simple string
}

// iterate through the remaining property keys to add extended semantic ids
propertyKeys.forEach((element: string) => {
if (element.includes(":")) {
// find the prefix
const colonLoc = element.indexOf(":");
const prefix = element.slice(0, colonLoc);

// check if the prefix is in the @context
if (prefixObject.hasOwnProperty(prefix)) {
const baseUri = prefixObject[prefix];

const semanticId = baseUri + element.slice(colonLoc + 1);

propertyValues.push({
idShort: this.sanitizeIdShort(element),
semanticId: this.createSemanticId(semanticId),
valueType: this.getSimpleValueTypeXsd(property.element),
value: property[element],
modelType: "Property",
});
} else {
// TODO: What to do for unknown keys by the TD spec, have a : but not present in the @context?
// Is this a JSON-LD bug? No error in JSON-LD playground
}
} else {
// TODO: What to do for unknown keys by the TD spec and do not have a :, thus not in the context with a prefix. Should we do full json-ld processing?
}
});

properties.push({
idShort: propertyKey,
description,
Expand Down
86 changes: 86 additions & 0 deletions node/aas-aid/test/asset-interfaces-description-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1327,4 +1327,90 @@ class AssetInterfaceDescriptionTest {
expect(isValid.valid, isValid.errors).to.equal(true);
expect(submodel).to.have.property("submodelElements").to.be.an("array").to.have.lengthOf(1);
}

td4: ThingDescription = {
"@context": [
"https://www.w3.org/2022/wot/td/v1.1",
{
myPrefix1: "https://example.com/myContext1",
myPrefix2: "https://example.com/myContext2",
},
],
title: "test thing",
"@type": ["myPrefix1:suffix1", "myPrefix2:suffix2"],
securityDefinitions: {
nosec_sc: {
scheme: "nosec",
},
},
base: "http://example.com:3003",
security: ["nosec_sc"],
"myPrefix2:suffix3": "value1",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not part of the test?

properties: {
myProperty1: {
type: "number",
title: "Property Title",
"myPrefix1:suffix4": "myPrefix2:value4",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand the idea/system/schema behind the attached numbers but it is fine by me

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was just to make sure I create new terms in the AID to make it easier to search where they end up at

unit: "myPrefix2:value2",
forms: [
{
href: "relative/value",
contentType: "application/json",
"htv:methodName": "GET",
},
],
},
},
};

@test async "should correctly include semantic annotations into AAS from TD"() {
const sm = this.assetInterfacesDescription.transformTD2AID(JSON.stringify(this.td4));

const smObj = JSON.parse(sm);
console.log("###\n\n" + JSON.stringify(smObj) + "\n\n###");
// const isValid = this.validateAID(smObj);
// expect(isValid.valid, isValid.errors).to.equal(true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove console log and commented code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

expect(smObj).to.have.property("idShort").that.equals("AssetInterfacesDescription");
const smInterface = smObj.submodelElements[0];

// InteractionMetadata with properties etc
let hasInteractionMetadata = false;
for (const smValue of smInterface.value) {
if (smValue.idShort === "InteractionMetadata") {
hasInteractionMetadata = true;
expect(smValue).to.have.property("value").to.be.an("array").to.have.lengthOf.greaterThan(0);
let hasProperties = false;
for (const interactionValues of smValue.value) {
if (interactionValues.idShort === "properties") {
hasProperties = true;
expect(interactionValues)
.to.have.property("value")
.to.be.an("array")
.to.have.lengthOf.greaterThan(0);
let hasProperty1 = false;
for (const propertyValue of interactionValues.value) {
if (propertyValue.idShort === "myProperty1") {
hasProperty1 = true;
expect(propertyValue)
.to.have.property("value")
.to.be.an("array")
.to.have.lengthOf.greaterThan(0);
let hasMyPrefix1_suffix4 = false;
for (const propProperty of propertyValue.value) {
if (propProperty.idShort === "myPrefix1_suffix4") {
hasMyPrefix1_suffix4 = true;
expect(propProperty.value).to.equal("myPrefix2:value4");
}
}
expect(hasMyPrefix1_suffix4).to.equal(true);
}
}
expect(hasProperty1).to.equal(true);
}
}
expect(hasProperties).to.equal(true);
}
}
expect(hasInteractionMetadata, "No InteractionMetadata").to.equal(true);
}
}
2 changes: 1 addition & 1 deletion node/aas-aid/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@
"strictNullChecks": true
},
"include": ["*.ts", "**/*.ts", "../src/**/*.ts", "**/AIDSchema.json"],
"exclude": []
"exclude": ["./dist/**/*"]
}