From 7e2b57e08b8e420c87e220d91f01d33a77f80427 Mon Sep 17 00:00:00 2001 From: Monika Singh Date: Fri, 18 Aug 2023 13:16:34 +0200 Subject: [PATCH 1/9] TD to Asyncapi conversion: adding the security conversion Signed-off-by: Monika Singh --- packages/td_to_asyncapi/index.js | 9 +- packages/td_to_asyncapi/src/genChannels.js | 16 +- packages/td_to_asyncapi/src/genRoot.js | 4 +- packages/td_to_asyncapi/src/mapSecurity.js | 415 +++++++++++++++++++++ 4 files changed, 435 insertions(+), 9 deletions(-) create mode 100644 packages/td_to_asyncapi/src/mapSecurity.js diff --git a/packages/td_to_asyncapi/index.js b/packages/td_to_asyncapi/index.js index 445f7d1f5..27c0e823c 100644 --- a/packages/td_to_asyncapi/index.js +++ b/packages/td_to_asyncapi/index.js @@ -18,6 +18,8 @@ const genChannels = require("./src/genChannels"); const { genInfo, genTags, genBaseServer } = require("./src/genRoot"); const defaults = require("@thing-description-playground/defaults"); +const {mapSecurity} = require("./src/mapSecurity"); + /** * Create an AsyncAPI instance from a Web of Things Thing Description * @param {object} td A Thing Description object as input @@ -28,14 +30,19 @@ function toAsyncAPI(td) { if (typeof td !== "object") { rej("TD has wrong type, should be an object"); } + const {securitySchemes, security} = mapSecurity(td.securityDefinitions, td.security); + const components = { + securitySchemes + }; defaults.addDefaults(td); - const servers = genBaseServer(td); + const servers = genBaseServer(td, security); const asyncApiInstance = new AsyncAPI("2.0.0", genInfo(td), genChannels(td, servers), { id: td.id, servers, tags: genTags(td), + components: components, externalDocs: new ExternalDocs( "http://plugfest.thingweb.io/playground/", "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" diff --git a/packages/td_to_asyncapi/src/genChannels.js b/packages/td_to_asyncapi/src/genChannels.js index f03103aa0..ad8bfb06b 100644 --- a/packages/td_to_asyncapi/src/genChannels.js +++ b/packages/td_to_asyncapi/src/genChannels.js @@ -23,7 +23,8 @@ const { Channel, Operation, Tag, Message, MqttOperationBinding, Server } = requi * @param {object} td The Thing Description input * @param {object} servers The AsyncAPI servers map */ -function genChannels(td, servers) { +function genChannels(td, servers, security) { + const channels = {}; // Scan Properties @@ -53,7 +54,7 @@ function genChannels(td, servers) { interaction.forms.forEach((form) => { const dataSchema = interactionInfo.getDataSchema(interaction); const payload = dataToAsyncSchema(dataSchema); - scanPropForm(form, channels, interactionName, payload, servers, interactionInfo); + scanPropForm(form, channels, interactionName, payload, servers, interactionInfo, security); }); } }); @@ -97,7 +98,7 @@ const tryProtocols = [ * Check if form is relevant * @param {object} form One form */ -function scanPropForm(form, channels, propertyName, payload, servers, interactionInfo) { +function scanPropForm(form, channels, propertyName, payload, servers, interactionInfo, security) { const hasBase = servers.base !== undefined ? true : false; const isRelative = form.href && form.href.search("://") === -1 ? true : false; @@ -109,8 +110,11 @@ function scanPropForm(form, channels, propertyName, payload, servers, interactio tryProtocol.checkForm(form) ) { const { channel, server } = extractChannel(form.href); + let newOptionalPara = { + "security": security + } // add server - addServer(servers, server, tryProtocol.id); + addServer(servers, server, tryProtocol.id, newOptionalPara) // add channel if (!channels[channel]) { @@ -141,7 +145,7 @@ function scanPropForm(form, channels, propertyName, payload, servers, interactio * @param {string|undefined} newServerUri * @param {string} protocol */ -function addServer(servers, newServerUri, newServerProtocol) { +function addServer(servers, newServerUri, newServerProtocol, newOptionalPara) { if ( newServerUri && Object.keys(servers).every( @@ -153,7 +157,7 @@ function addServer(servers, newServerUri, newServerProtocol) { while (Object.keys(servers).some((asyncServer) => asyncServer === i.toString())) { i++; } - servers[i.toString()] = new Server(newServerUri, newServerProtocol); + servers[i.toString()] = new Server(newServerUri, newServerProtocol, newOptionalPara); } } diff --git a/packages/td_to_asyncapi/src/genRoot.js b/packages/td_to_asyncapi/src/genRoot.js index e7f902ba5..533e07641 100644 --- a/packages/td_to_asyncapi/src/genRoot.js +++ b/packages/td_to_asyncapi/src/genRoot.js @@ -91,11 +91,11 @@ function genTags(td) { return tags; } -function genBaseServer(td) { +function genBaseServer(td, security) { const servers = {}; if (td.base) { if (td.base.startsWith("mqtt")) { - servers.base = new Server(td.base, "mqtt"); + servers.base = new Server(td.base, "mqtt", security); } } return servers; diff --git a/packages/td_to_asyncapi/src/mapSecurity.js b/packages/td_to_asyncapi/src/mapSecurity.js new file mode 100644 index 000000000..aa2a6b5bc --- /dev/null +++ b/packages/td_to_asyncapi/src/mapSecurity.js @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2023 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and + * Document License (2015-05-13) which is available at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document. + * + * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 + */ + +module.exports = { mapSecurity, mapSecurityString, mapSecurityDefinitions, hasNoSec, mapFormSecurity }; + +/** + * Convert the TD security Definitions and Security to + * AsyncAPI components->securitySchemes and security objects + * @param {object} tdDefinitions the definitions for all security schemes of the TD + * @param {string|string[]} tdSecurity security scheme names that apply per default + */ +function mapSecurity(tdDefinitions, tdSecurity) { + const { securitySchemes, scopes } = mapSecurityDefinitions(tdDefinitions); + const security = mapSecurityString(tdSecurity, securitySchemes, scopes); + + if (security.length === 0 && hasNoSec(tdDefinitions, tdSecurity)) { + security.push({}); + } + + for (const key in securitySchemes) { + if (key.includes("combo_sc")) { + delete securitySchemes[key]; + } + } + + return { securitySchemes, security }; +} + +/** + * Convert the TD security and scopes information of a single form + * to an AsyncAPI security object + * @param {object} tdDefinitions the definitions for all security schemes of the TD + * @param {string|string[]|undefined} tdSecurity security scheme names that apply to this form + * @param {string|string[]|undefined} tdFormScopes oauth2 scopes that apply to this form + */ +function mapFormSecurity(tdDefinitions, tdSecurity, tdFormScopes) { + const { securitySchemes, scopes } = mapSecurityDefinitions(tdDefinitions); + let aapScopes = scopes; + if (typeof tdFormScopes === "string") { + tdFormScopes = [tdFormScopes]; + } + + if (typeof tdFormScopes === "object") { + const scopeSecurities = []; + // filter TD scopes that are not supported by the conversion + aapScopes = {}; + + Object.keys(scopes).forEach((supportedSecurity) => { + scopeSecurities.push(supportedSecurity); + const addScope = tdFormScopes.find((tdFormScope) => + scopes[supportedSecurity].some((supportedScope) => supportedScope === tdFormScope) + ); + if (addScope !== undefined) { + if (aapScopes[supportedSecurity] === undefined) { + aapScopes[supportedSecurity] = [addScope]; + } else { + aapScopes[supportedSecurity].push(addScope); + } + } + }); + + // add security scheme names if only a scope was given in the TD + scopeSecurities.forEach((scopeSecurity) => { + if (typeof tdSecurity === "string") { + tdSecurity = [tdSecurity]; + } + if (typeof tdSecurity === "object") { + if (!tdSecurity.some((tdSecString) => tdSecString === scopeSecurity)) { + tdSecurity.push(scopeSecurity); + } + } else if (tdSecurity === undefined) { + tdSecurity = scopeSecurities; + } + }); + } + const security = mapSecurityString(tdSecurity, securitySchemes, aapScopes); + + if (security.length === 0 && hasNoSec(tdDefinitions, tdSecurity)) { + security.push({}); + } + + return { security }; +} + +/** + * Mapping the TD security object to AsyncAPI + * @param {object} tdSecurity the TD security options to apply + * @param {object} aapSecuritySchemes the already mapped security schemes + * @param {object} tdScopes the found scopes as map {string: string[]} + */ +function mapSecurityString(tdSecurity, aapSecuritySchemes, tdScopes) { + const aapSecurityContainer = []; + + if (typeof tdSecurity === "string") { + tdSecurity = [tdSecurity]; + } + + if (typeof tdSecurity === "object") { + tdSecurity.forEach((tdSecurityKey) => { + let aapSecurity = {}; + const securityObject = aapSecuritySchemes[tdSecurityKey]; + + if (securityObject && securityObject.type === "allOf") { + securityObject.secdef.forEach((def) => { + // get scopes + let thisScopes = []; + if (tdScopes[def] !== undefined) { + thisScopes = tdScopes[def]; + } + + const supportedSchemes = Object.keys(aapSecuritySchemes); + + if (supportedSchemes.some((supportedScheme) => supportedScheme === def)) { + aapSecurity[def] = thisScopes; + } + }); + + if (Object.keys(aapSecurity).length > 0) { + aapSecurityContainer.push(aapSecurity); + } + } else if (securityObject && securityObject.type === "oneOf") { + securityObject.secdef.forEach((def) => { + aapSecurity = {}; + // get scopes + let thisScopes = []; + if (tdScopes[def] !== undefined) { + thisScopes = tdScopes[def]; + } + + const supportedSchemes = Object.keys(aapSecuritySchemes); + + if (supportedSchemes.some((supportedScheme) => supportedScheme === def)) { + aapSecurity[def] = thisScopes; + } + + if (Object.keys(aapSecurity).length > 0) { + aapSecurityContainer.push(aapSecurity); + } + }); + } else { + // get scopes + let thisScopes = []; + if (tdScopes[tdSecurityKey] !== undefined) { + thisScopes = tdScopes[tdSecurityKey]; + } + + const supportedSchemes = Object.keys(aapSecuritySchemes); + + if (supportedSchemes.some((supportedScheme) => supportedScheme === tdSecurityKey)) { + aapSecurity[tdSecurityKey] = thisScopes; + } + + if (Object.keys(aapSecurity).length > 0) { + aapSecurityContainer.push(aapSecurity); + } + } + }); + } + + return aapSecurityContainer; +} + +/** + * Map the TD security definitions to + * AsyncAPI security Schemes in components + * and return all used scopes in addition + * @param {object|undefined} tdDefinitions if no object is given, empty securitySchemes and scopes are returned + */ +function mapSecurityDefinitions(tdDefinitions) { + const securitySchemes = {}; + const scopes = {}; + + if (typeof tdDefinitions === "object") { + Object.keys(tdDefinitions).forEach((key) => { + if (typeof tdDefinitions[key].scheme === "string") { + const { aapDefinition, scope } = genaapDefinition(tdDefinitions[key]); + if (Object.keys(aapDefinition).length > 0) { + securitySchemes[key] = aapDefinition; + if (typeof scope === "object") { + scopes[key] = scope; + } + } + } + }); + } + + return { securitySchemes, scopes }; +} + +/** + * Generate an AsyncAPI securityScheme part from a given TD security definiton part + * @param {object} tdDefinition one security definition object + */ +function genaapDefinition(tdDefinition) { + const aapDefinition = {}; + const tdScheme = tdDefinition.scheme.toLowerCase(); + let addOptionals = true; + const httpSchemes = ["basic", "digest", "bearer"]; + let scope; + + if (httpSchemes.some((httpScheme) => httpScheme === tdScheme)) { + aapDefinition.type = "http"; + aapDefinition.scheme = tdScheme; + if (tdDefinition.in && tdDefinition.in !== "header") { + throw new Error("Cannot represent " + tdScheme + " authentication outside the header in AsyncAPI"); + } + } + + switch (tdScheme) { + + case "nosec": + case "basic": + if (tdDefinition.name) { + aapDefinition["x-name"] = tdDefinition.name; + } + // should the x-in parameter be added if in = header? + if (tdDefinition.in) { + aapDefinition["x-in"] = tdDefinition.in; + } + case "psk": + // do nothing? + break; + + case "digest": + aapDefinition["x-qop"] = (tdDefinition.qop === undefined) ? "auth" : tdDefinition.qop; + if (tdDefinition.name) { + aapDefinition["x-name"] = tdDefinition.name; + } + // should the x-in parameter be added if in = header? + if (tdDefinition.in) { + aapDefinition["x-in"] = tdDefinition.in; + } + break + + case "bearer": + aapDefinition.bearerFormat = (tdDefinition.format === undefined) ? "jwt" : tdDefinition.format; + aapDefinition["x-alg"] = (tdDefinition.alg === undefined) ? "ES256" : tdDefinition.alg; + if (tdDefinition.name) { + aapDefinition["x-name"] = tdDefinition.name; + } + // should the x-in parameter be added if in = header? + if (tdDefinition.in) { + aapDefinition["x-in"] = tdDefinition.in; + } + if (tdDefinition.authorization) { + aapDefinition["x-authorization"] = tdDefinition.authorization; + } + break + + case "apikey": + aapDefinition.type = "httpApiKey"; + aapDefinition.in = tdDefinition.in === undefined ? "query" : tdDefinition.in; + aapDefinition.name = (tdDefinition.name === undefined) ? "UNKNOWN" : tdDefinition.name; + if (aapDefinition.in != "query" && aapDefinition.in != "header" && aapDefinition.in != "cookie") { + throw new Error("Cannot represent ApiKey in" + aapDefinition.in +" with AsyncAPI"); + } + break + + case "oauth2": + if (typeof tdDefinition.scopes === "string") { + scope = [tdDefinition.scopes]; + } else if (typeof tdDefinition.scopes === "object") { + scope = tdDefinition.scopes; + } + + aapDefinition.type = "oauth2"; + aapDefinition.flows = genOAuthFlows(tdDefinition); + break; + /* + case "openidconnect": + aapDefinition.type = "openIdConnect" + aapDefinition.openIdConnectUrl = (tdDefinition.openIdConnectUrl === undefined) ? "UNKNOWN" : tdDefinition.openIdConnectUrl + //aapDefinition.flows = genOAuthFlows(tdDefinition) + break +*/ + case "combo": + // todo Implement combo security scheme + if (tdDefinition.allOf) { + aapDefinition.type = "allOf"; + aapDefinition.secdef = tdDefinition.allOf; + } else { + aapDefinition.type = "oneOf"; + aapDefinition.secdef = tdDefinition.oneOf; + } + break; + + default: + console.log("unknown security definition: " + tdScheme); + addOptionals = false; + } + + // add optional fields + if (addOptionals) { + if (tdDefinition.description) { + aapDefinition.description = tdDefinition.description; + } + if (tdDefinition.descriptions) { + aapDefinition["x-descriptions"] = tdDefinition.descriptions; + } + if (tdDefinition.proxy) { + aapDefinition["x-proxy"] = tdDefinition.proxy; + } + } + + return { aapDefinition, scope }; +} + +/** + * Map the oauth2 fields of a TD to an AsyncAPI instance + * @param {object} tdDefinition The security definition object of a TD + */ +function genOAuthFlows(tdDefinition) { + const aapFlow = {}; + if (typeof tdDefinition.flow !== "string") { + throw new Error("the oauth2 object has no flow of type string"); + } + + const tdFlow = tdDefinition.flow.toLowerCase(); + const mapTdToAap = { + implicit: ["implicit"], + password: ["password", "ropc"], + clientCredentials: ["application", "client", "clientcredentials", "clientcredential"], + authorizationCode: ["accesscode", "code", "authorizationcode"], + "x-device": ["device"], + }; + + Object.keys(mapTdToAap).forEach((key) => { + if (mapTdToAap[key].some((arrayElement) => arrayElement === tdFlow)) { + const protoFlow = {}; + if (key === "implicit" || key === "authorizationCode" || key === "x-device") { + if (tdDefinition.authorization === undefined) { + throw new Error("the authorization URI is required for oauth2 flow: " + key); + } else { + protoFlow.authorizationUrl = tdDefinition.authorization; + } + } + if ( + key === "password" || + key === "clientCredentials" || + key === "authorizationCode" || + key === "x-device" + ) { + if (tdDefinition.token === undefined) { + throw new Error("the token URI is required for oauth2 flow: " + key); + } else { + protoFlow.tokenUrl = tdDefinition.token; + } + } + if (typeof tdDefinition.refresh === "string") { + protoFlow.refreshUrl = tdDefinition.refresh; + } + if (tdDefinition.scopes === undefined) { + protoFlow.scopes = { + /* "default": "autogenerated default scope" */ + }; // TODO: is one scope required? (I don't think so) + } else { + protoFlow.scopes = {}; + if (typeof tdDefinition.scopes === "string") { + tdDefinition.scopes = [tdDefinition.scopes]; + } + tdDefinition.scopes.forEach((scope) => { + protoFlow.scopes[scope] = ""; + }); + } + aapFlow[key] = protoFlow; + } + }); + return aapFlow; +} + +/** + * Check if all applying security schemes are of type nosec + * @param {object} tdDefinitions the definitions for all security schemes of the TD + * @param {string|string[]} tdSecurity security scheme names that apply to this TD part + */ +function hasNoSec(tdDefinitions, tdSecurity) { + let foundNoSec = false; + + // find all noSec names + const noSecNames = []; + if (typeof tdDefinitions === "object") { + Object.keys(tdDefinitions).forEach((key) => { + const tdScheme = tdDefinitions[key].scheme; + if (typeof tdScheme === "string" && tdScheme.toLowerCase() === "nosec") { + noSecNames.push(key); + } + }); + } + + if (typeof tdSecurity === "string") { + tdSecurity = [tdSecurity]; + } + if (typeof tdSecurity === "undefined") { + tdSecurity = []; + } + + // check if all security Schemes are of type noSec + if (tdSecurity.length > 0) { + foundNoSec = tdSecurity.every((securityString) => noSecNames.some((noSecName) => noSecName === securityString)); + } + + return foundNoSec; +} From 22feedd65f2df922659f009a79839f91029c738f Mon Sep 17 00:00:00 2001 From: Monika Singh Date: Mon, 21 Aug 2023 18:30:40 +0200 Subject: [PATCH 2/9] Adding test cases --- packages/td_to_asyncapi/src/mapSecurity.js | 28 +- .../td_to_asyncapi/tests/mapSecurity.test.js | 263 ++++++++++++++++++ 2 files changed, 277 insertions(+), 14 deletions(-) create mode 100644 packages/td_to_asyncapi/tests/mapSecurity.test.js diff --git a/packages/td_to_asyncapi/src/mapSecurity.js b/packages/td_to_asyncapi/src/mapSecurity.js index aa2a6b5bc..4984f713c 100644 --- a/packages/td_to_asyncapi/src/mapSecurity.js +++ b/packages/td_to_asyncapi/src/mapSecurity.js @@ -218,20 +218,20 @@ function genaapDefinition(tdDefinition) { } } - switch (tdScheme) { - - case "nosec": - case "basic": - if (tdDefinition.name) { - aapDefinition["x-name"] = tdDefinition.name; - } - // should the x-in parameter be added if in = header? - if (tdDefinition.in) { - aapDefinition["x-in"] = tdDefinition.in; - } - case "psk": - // do nothing? - break; + switch (tdScheme) { + case "nosec": + case "basic": + if (tdDefinition.name) { + aapDefinition["x-name"] = tdDefinition.name; + } + // should the x-in parameter be added if in = header? + if (tdDefinition.in) { + aapDefinition["x-in"] = tdDefinition.in; + } + break; + /*case "psk": + // do nothing? + break;*/ case "digest": aapDefinition["x-qop"] = (tdDefinition.qop === undefined) ? "auth" : tdDefinition.qop; diff --git a/packages/td_to_asyncapi/tests/mapSecurity.test.js b/packages/td_to_asyncapi/tests/mapSecurity.test.js new file mode 100644 index 000000000..dc187e3b6 --- /dev/null +++ b/packages/td_to_asyncapi/tests/mapSecurity.test.js @@ -0,0 +1,263 @@ +/** + * @file Modules test for mapSecurity.js, test its functionality by hardcoded example -> expected output pairs + */ + +const { + mapSecurity, + mapSecurityString, + mapSecurityDefinitions, + hasNoSec, + mapFormSecurity, + } = require("../src/mapSecurity"); + + // reused definitions + const oauth2Definitions = { + oauth2_sc: { + scheme: "oauth2", + flow: "code", + scopes: ["limited", "special"], + refresh: "https://refreshServer.com/", + token: "https://tokenServer.com/", + authorization: "https://authServer.com/", + }, + }; + + const oauth2Aap = { + securitySchemes: { + oauth2_sc: { + type: "oauth2", + flows: { + authorizationCode: { + authorizationUrl: "https://authServer.com/", + tokenUrl: "https://tokenServer.com/", + refreshUrl: "https://refreshServer.com/", + scopes: { + limited: "", + special: "", + }, + }, + }, + }, + }, + scopes: { + oauth2_sc: ["limited", "special"], + }, + }; + + const basicDefinitions = { + basic_sc: { + scheme: "basic", + in: "header", + }, + }; + const basicAap = { + securitySchemes: { + basic_sc: { + type: "http", + scheme: "basic", + 'x-in': "header", + }, + }, + scopes: {}, + }; +const digestDefinitions = { + digest_sc: { + scheme: "digest", + in: "header", + qop: "auth" + }, +} +const digestAap = { + "scopes": { }, +"securitySchemes": { + digest_sc: { + type: "http", + scheme: "digest", + "x-in": "header", + "x-qop": "auth" + }, +}, +} +const apikeyDefinitions = { + apikey_sc: { + scheme: "apikey", + in: "header", + }, +} +const apikeyAap = { + "scopes": {}, + "securitySchemes": { + apikey_sc: { + type: "httpApiKey", + in: "header", + name: "UNKNOWN", + }, + }, +} + const comboAllOfAap = { + securitySchemes: { + basic_sc: { + type: "http", + scheme: "basic", + }, + basic_sc2: { + type: "http", + scheme: "basic", + }, + combo_sc: { + type: "allOf", + secdef: ["basic_sc", "basic_sc2"], + }, + }, + }; + + const comboOneOfAap = { + securitySchemes: { + basic_sc: { + type: "http", + scheme: "basic", + }, + basic_sc2: { + type: "http", + scheme: "basic", + }, + combo_sc: { + type: "oneOf", + secdef: ["basic_sc", "basic_sc2"], + }, + }, + }; + + const noSecDefinitions = { + nosec_sc: { + scheme: "nosec", + }, + }; + + describe("mapSecurity unit tests", () => { + describe("mapSecurityString", () => { + test("oauth2", () => { + const result = [{ oauth2_sc: ["limited"] }]; + const computed = mapSecurityString( + ["oauth2_sc"], + oauth2Aap.securitySchemes, + { oauth2_sc: ["limited"] } + ); + const computed2 = mapSecurityString( + "oauth2_sc", + oauth2Aap.securitySchemes, + { oauth2_sc: ["limited"] } + ); + expect(computed).toEqual(result); + expect(computed2).toEqual(result); + }); + + test("comboAllOf", () => { + const result = [{ basic_sc: [], basic_sc2: [] }]; + const computed = mapSecurityString( + "combo_sc", + comboAllOfAap.securitySchemes, + {} + ); + expect(computed).toEqual(result); + }); + + test("comboOneOf", () => { + const result = [{ basic_sc: [] }, { basic_sc2: [] }]; + const computed = mapSecurityString( + "combo_sc", + comboOneOfAap.securitySchemes, + {} + ); + expect(computed).toEqual(result); + }); + }); + + describe("mapSecurityDefinitions", () => { + test("basic", () => { + console.log(mapSecurityDefinitions(basicDefinitions)) + expect(mapSecurityDefinitions(basicDefinitions)).toEqual(basicAap); + }); + + test("digest", () => { + expect(mapSecurityDefinitions(digestDefinitions)).toEqual(digestAap); + }); + + test("apikey", () => { + expect(mapSecurityDefinitions(apikeyDefinitions)).toEqual(apikeyAap); + }); + + test("oauth2", () => { + expect(mapSecurityDefinitions(oauth2Definitions)).toEqual(oauth2Aap); + }); + }); + }); + + describe("mapSecurity integration tests", () => { + describe("mapFormSecurity", () => { + const result = { security: [{ oauth2_sc: ["limited"] }] }; + + test("oauth2 single scope no security", () => { + expect( + mapFormSecurity(oauth2Definitions, undefined, ["limited"]) + ).toEqual(result); + expect(mapFormSecurity(oauth2Definitions, undefined, "limited")).toEqual( + result + ); + }); + test("oauth2 single scope with security", () => { + const security = ["oauth2_sc"]; + + expect(mapFormSecurity(oauth2Definitions, security, ["limited"])).toEqual( + result + ); + expect(mapFormSecurity(oauth2Definitions, security, "limited")).toEqual( + result + ); + }); + }); + + describe("mapSecurity", () => { + test("oauth2", () => { + const result = { + securitySchemes: oauth2Aap.securitySchemes, + security: [ + { + oauth2_sc: ["limited", "special"], + }, + ], + }; + expect(mapSecurity(oauth2Definitions, "oauth2_sc")).toEqual(result); + }); + + test("basic", () => { + const result = { + securitySchemes: basicAap.securitySchemes, + security: [ + { + basic_sc: [], + }, + ], + }; + expect(mapSecurity(basicDefinitions, "basic_sc")).toEqual(result); + expect(mapSecurity(basicDefinitions, ["basic_sc"])).toEqual(result); + }); + + test("nosec", () => { + const result = { + securitySchemes: {}, + security: [{}], + }; + expect(mapSecurity(noSecDefinitions, "nosec_sc")).toEqual(result); + }); + + test("empty", () => { + const result = { + securitySchemes: {}, + security: [], + }; + expect(mapSecurity(noSecDefinitions, undefined)).toEqual(result); + }); + }); + }); + \ No newline at end of file From 8ece7766f3d82fa426d29eeed2ffe1b416254146 Mon Sep 17 00:00:00 2001 From: Monika Singh Date: Tue, 22 Aug 2023 13:46:44 +0200 Subject: [PATCH 3/9] updating the test case --- packages/td_to_asyncapi/examples/asyncapi.json | 9 +++++++++ packages/td_to_asyncapi/examples/asyncapi.yaml | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/packages/td_to_asyncapi/examples/asyncapi.json b/packages/td_to_asyncapi/examples/asyncapi.json index 78acaa579..92ac5a099 100644 --- a/packages/td_to_asyncapi/examples/asyncapi.json +++ b/packages/td_to_asyncapi/examples/asyncapi.json @@ -132,6 +132,15 @@ } } ], + "components": { + "securitySchemes": { + "basic_sc": { + "type": "http", + "scheme": "basic", + "x-in": "header" + } + } + }, "externalDocs": { "url": "http://plugfest.thingweb.io/playground/", "description": "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" diff --git a/packages/td_to_asyncapi/examples/asyncapi.yaml b/packages/td_to_asyncapi/examples/asyncapi.yaml index 91f8927aa..2b54a30e0 100644 --- a/packages/td_to_asyncapi/examples/asyncapi.yaml +++ b/packages/td_to_asyncapi/examples/asyncapi.yaml @@ -81,6 +81,12 @@ tags: externalDocs: url: "https://www.w3.org/TR/wot-thing-description/#eventaffordance" description: "Find out more about Event Affordances." +components: + securitySchemes: + basic_sc: + type: "http" + scheme: "basic" + x-in: "header" externalDocs: url: "http://plugfest.thingweb.io/playground/" description: "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" From 21094a04c4fbfa86f167399edc48b49c18314896 Mon Sep 17 00:00:00 2001 From: Monika Singh Date: Tue, 22 Aug 2023 13:48:55 +0200 Subject: [PATCH 4/9] Removing unused code --- packages/td_to_asyncapi/src/mapSecurity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/td_to_asyncapi/src/mapSecurity.js b/packages/td_to_asyncapi/src/mapSecurity.js index 4984f713c..15dbe568b 100644 --- a/packages/td_to_asyncapi/src/mapSecurity.js +++ b/packages/td_to_asyncapi/src/mapSecurity.js @@ -13,7 +13,7 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 */ -module.exports = { mapSecurity, mapSecurityString, mapSecurityDefinitions, hasNoSec, mapFormSecurity }; +module.exports = { mapSecurity, mapSecurityString, mapSecurityDefinitions, mapFormSecurity }; /** * Convert the TD security Definitions and Security to From fa18fc5ebedf12c0ad7e7b376546124878217253 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Thu, 24 Aug 2023 14:19:13 +0200 Subject: [PATCH 5/9] Update packages/td_to_asyncapi/tests/mapSecurity.test.js --- packages/td_to_asyncapi/tests/mapSecurity.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/td_to_asyncapi/tests/mapSecurity.test.js b/packages/td_to_asyncapi/tests/mapSecurity.test.js index dc187e3b6..b3fa90a5c 100644 --- a/packages/td_to_asyncapi/tests/mapSecurity.test.js +++ b/packages/td_to_asyncapi/tests/mapSecurity.test.js @@ -6,7 +6,6 @@ const { mapSecurity, mapSecurityString, mapSecurityDefinitions, - hasNoSec, mapFormSecurity, } = require("../src/mapSecurity"); From 93892479a112bb3136aa88dbaebc362040258197 Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Thu, 24 Aug 2023 16:43:34 +0200 Subject: [PATCH 6/9] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 92b394777..dd74261a5 100644 --- a/README.md +++ b/README.md @@ -81,3 +81,8 @@ All packages are licensed under the Eclipse Public License v. 2.0. You find a co 1. Run `lerna bootstrap` to install dependencies among the packages, even if a package has never been published before. Make sure you have not increased the dependency versions yet, e.g., you have a new package _newExample_ and the _oldExample_ depends on it. The _newExample_ is on version `0.0.0` (since you want to publish it as `1.0.0`) then in the _oldExample_ package.json the dependency has to be on the same version (or lower) so `"dependencies" { newExample: "^0.0.0"}}`. **Otherwise lerna will not accept linking the local _newExample_.** 2. If `lerna bootstrap` was successful you can now bump dependency versions (if required), e.g., you could now do `"dependencies" { newExample: "^1.0.0"}}` in the _oldExample_ package.json. 3. Run `lerna publish` to publish all new package versions. Lerna will then ask for every changed package whether it received a patch, minor or major update. In our example you should now select major for the _newExample_ so that it will be published as `1.0.0` version. You should login to npm via `npm login` before doing this. + +## Adapting Licenses + +One can use the VS Code plugin at to updating the copyright fields on top of each file. + From c4c2a026185fde31bd399eab97b2504d14ff449c Mon Sep 17 00:00:00 2001 From: Monika Singh Date: Tue, 29 Aug 2023 15:33:50 +0200 Subject: [PATCH 7/9] Addition of the security parameter in Asyncapi --- .../td_to_asyncapi/examples/asyncapi.json | 274 ++++---- packages/td_to_asyncapi/examples/td.json | 302 ++++---- packages/td_to_asyncapi/index.js | 66 +- packages/td_to_asyncapi/src/mapSecurity.js | 645 ++++++++++-------- 4 files changed, 665 insertions(+), 622 deletions(-) diff --git a/packages/td_to_asyncapi/examples/asyncapi.json b/packages/td_to_asyncapi/examples/asyncapi.json index 92ac5a099..8d6887d81 100644 --- a/packages/td_to_asyncapi/examples/asyncapi.json +++ b/packages/td_to_asyncapi/examples/asyncapi.json @@ -1,148 +1,148 @@ { - "asyncapi": "2.0.0", - "info": { - "title": "MyCoffeeMaker-Home", - "version": "unknown", - "description": "Order your coffee remotely!", - "x-AT-context": [ - "https://www.w3.org/2022/wot/td/v1.1", - { - "cov": "http://www.example.org/coap-binding#", - "mqv": "http://www.example.org/mqtt-binding#" - }, - { - "@language": "en" - } + "asyncapi": "2.0.0", + "info": { + "title": "MyCoffeeMaker-Home", + "version": "unknown", + "description": "Order your coffee remotely!", + "x-AT-context": [ + "https://www.w3.org/2022/wot/td/v1.1", + { + "cov": "http://www.example.org/coap-binding#", + "mqv": "http://www.example.org/mqtt-binding#" + }, + { + "@language": "en" + } + ], + "x-AT-type": "Thing", + "x-name": "MyCoffeeMaker" + }, + "channels": { + "/oneOfTest": { + "subscribe": { + "operationId": "observeoneOfTestproperty", + "tags": [ + { + "name": "properties" + } ], - "x-AT-type": "Thing", - "x-name": "MyCoffeeMaker" - }, - "channels": { - "/oneOfTest": { - "subscribe": { - "operationId": "observeoneOfTestproperty", - "tags": [ - { - "name": "properties" - } - ], - "message": { - "contentType": "application/json", - "payload": { - "oneOf": [ - { - "type": "string", - "readOnly": false, - "writeOnly": false - }, - { - "type": "integer", - "readOnly": false, - "writeOnly": false - }, - { - "type": "null", - "readOnly": false, - "writeOnly": false - } - ], - "readOnly": true, - "writeOnly": false - } - }, - "bindings": { - "mqtt": { - "bindingVersion": "0.1.0", - "qos": 1 - } - } - } + "message": { + "contentType": "application/json", + "payload": { + "oneOf": [ + { + "type": "string", + "readOnly": false, + "writeOnly": false + }, + { + "type": "integer", + "readOnly": false, + "writeOnly": false + }, + { + "type": "null", + "readOnly": false, + "writeOnly": false + } + ], + "readOnly": true, + "writeOnly": false + } }, - "/maxItemsTest": { - "subscribe": { - "operationId": "observemaxItemsTestproperty", - "tags": [ - { - "name": "properties" - } - ], - "message": { - "contentType": "application/json", - "payload": { - "type": "array", - "readOnly": true, - "writeOnly": false - } - } - } - }, - "/binfull": { - "subscribe": { - "operationId": "subscribebinFullevent", - "tags": [ - { - "name": "events" - } - ], - "message": { - "contentType": "application/json", - "payload": { - "type": "number", - "readOnly": false, - "writeOnly": false - } - } - } + "bindings": { + "mqtt": { + "bindingVersion": "0.1.0", + "qos": 1 + } } + } }, - "id": "urn:dev:home:coff:type123-SNR123456", - "servers": { - "0": { - "url": "mqtt://iot.eclipse.org", - "protocol": "mqtt" - }, - "1": { - "url": "mqtt://mycoffeemaker.example.com", - "protocol": "mqtt" + "/maxItemsTest": { + "subscribe": { + "operationId": "observemaxItemsTestproperty", + "tags": [ + { + "name": "properties" + } + ], + "message": { + "contentType": "application/json", + "payload": { + "type": "array", + "readOnly": true, + "writeOnly": false + } } + } }, - "tags": [ - { - "name": "properties", - "description": "A property can expose a variable of a Thing, this variable might be readable, writable and/or observable.", - "externalDocs": { - "url": "https://www.w3.org/TR/wot-thing-description/#propertyaffordance", - "description": "Find out more about Property Affordances." - } - }, - { - "name": "actions", - "description": "An action can expose something to be executed by a Thing, an action can be invoked.", - "externalDocs": { - "url": "https://www.w3.org/TR/wot-thing-description/#actionaffordance", - "description": "Find out more about Action Affordances." - } - }, - { - "name": "events", - "description": "An event can expose a notification by a Thing, this notification can be subscribed and/or unsubscribed.", - "externalDocs": { - "url": "https://www.w3.org/TR/wot-thing-description/#eventaffordance", - "description": "Find out more about Event Affordances." - } - } - ], - "components": { - "securitySchemes": { - "basic_sc": { - "type": "http", - "scheme": "basic", - "x-in": "header" - } + "/binfull": { + "subscribe": { + "operationId": "subscribebinFullevent", + "tags": [ + { + "name": "events" + } + ], + "message": { + "contentType": "application/json", + "payload": { + "type": "number", + "readOnly": false, + "writeOnly": false + } } + } + } + }, + "id": "urn:dev:home:coff:type123-SNR123456", + "servers": { + "0": { + "url": "mqtt://iot.eclipse.org", + "protocol": "mqtt" }, - "externalDocs": { - "url": "http://plugfest.thingweb.io/playground/", - "description": "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" + "1": { + "url": "mqtt://mycoffeemaker.example.com", + "protocol": "mqtt" + } + }, + "tags": [ + { + "name": "properties", + "description": "A property can expose a variable of a Thing, this variable might be readable, writable and/or observable.", + "externalDocs": { + "url": "https://www.w3.org/TR/wot-thing-description/#propertyaffordance", + "description": "Find out more about Property Affordances." + } + }, + { + "name": "actions", + "description": "An action can expose something to be executed by a Thing, an action can be invoked.", + "externalDocs": { + "url": "https://www.w3.org/TR/wot-thing-description/#actionaffordance", + "description": "Find out more about Action Affordances." + } + }, + { + "name": "events", + "description": "An event can expose a notification by a Thing, this notification can be subscribed and/or unsubscribed.", + "externalDocs": { + "url": "https://www.w3.org/TR/wot-thing-description/#eventaffordance", + "description": "Find out more about Event Affordances." + } + } + ], + "components": { + "securitySchemes": { + "basic_sc": { + "type": "http", + "scheme": "basic", + "x-in": "header" + } } + }, + "externalDocs": { + "url": "http://plugfest.thingweb.io/playground/", + "description": "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" + } } diff --git a/packages/td_to_asyncapi/examples/td.json b/packages/td_to_asyncapi/examples/td.json index 8c6a0d72e..e18358f30 100644 --- a/packages/td_to_asyncapi/examples/td.json +++ b/packages/td_to_asyncapi/examples/td.json @@ -1,163 +1,163 @@ { - "@context": [ - "https://www.w3.org/2022/wot/td/v1.1", + "@context": [ + "https://www.w3.org/2022/wot/td/v1.1", + { + "cov": "http://www.example.org/coap-binding#", + "mqv": "http://www.example.org/mqtt-binding#" + }, + { "@language": "en" } + ], + "id": "urn:dev:home:coff:type123-SNR123456", + "name": "MyCoffeeMaker", + "@type": "Thing", + "title": "MyCoffeeMaker-Home", + "description": "Order your coffee remotely!", + "securityDefinitions": { + "basic_sc": { "scheme": "basic", "in": "header" }, + "psk_sc": { "scheme": "psk" }, + "nosec_sc": { "scheme": "nosec" } + }, + "security": ["nosec_sc"], + "properties": { + "oneOfTest": { + "readOnly": true, + "writeOnly": false, + "oneOf": [ { - "cov": "http://www.example.org/coap-binding#", - "mqv": "http://www.example.org/mqtt-binding#" + "type": "string" }, - { "@language": "en" } - ], - "id": "urn:dev:home:coff:type123-SNR123456", - "name": "MyCoffeeMaker", - "@type": "Thing", - "title": "MyCoffeeMaker-Home", - "description": "Order your coffee remotely!", - "securityDefinitions": { - "basic_sc": { "scheme": "basic", "in": "header" }, - "psk_sc": { "scheme": "psk" }, - "nosec_sc": { "scheme": "nosec" } - }, - "security": ["nosec_sc"], - "properties": { - "oneOfTest": { - "readOnly": true, - "writeOnly": false, - "oneOf": [ - { - "type": "string" - }, - { - "type": "integer" - }, - { - "type": "null" - } - ], - "forms": [ - { - "href": "mqtt://iot.eclipse.org/oneOfTest", - "op": "observeproperty", - "mqv:controlPacketValue": "SUBSCRIBE", - "mqv:options": [ - { - "mqv:optionName": "qos", - "mqv:optionValue": 1 - } - ] - }, - { - "href": "mqtt://iot.eclipse.org/oneOfTest", - "op": "unobserveproperty", - "mqv:controlPacketValue": "UNSUBSCRIBE" - } - ] + { + "type": "integer" }, - "maxItemsTest": { - "readOnly": true, - "writeOnly": false, - "type": "array", - "items": { "type": "integer" }, - "maxItems": 5, - "minItems": 2, - "forms": [ - { - "href": "mqtt://iot.eclipse.org/maxItemsTest", - "op": "observeproperty" - }, - { - "href": "mqtt://iot.eclipse.org/maxItemsTest", - "op": "unobserveproperty" - } - ] + { + "type": "null" } - }, - "actions": { - "brewCoffee": { - "input": { - "type": "string", - "enum": ["Espresso", "EspressoDoppio", "Coffee", "HotWater"] - }, - "forms": [ - { - "href": "http://mycoffeemaker.example.com/brewcoffee", - "op": "invokeaction", - "contentType": "application/json" - } - ], - "safe": false, - "idempotent": false - }, - "stopBrewing": { - "forms": [ - { - "href": "coap://mycoffeemaker.example.com/stopbrewing", - "cov:methodName": "GET", - "op": "invokeaction", - "contentType": "application/json" - } - ], - "safe": false, - "idempotent": false + ], + "forms": [ + { + "href": "mqtt://iot.eclipse.org/oneOfTest", + "op": "observeproperty", + "mqv:controlPacketValue": "SUBSCRIBE", + "mqv:options": [ + { + "mqv:optionName": "qos", + "mqv:optionValue": 1 + } + ] }, - "switchOff": { - "forms": [ - { - "href": "http://mycoffeemaker.example.com/switchoff", - "op": "invokeaction", - "contentType": "application/json" - } - ], - "safe": false, - "idempotent": false + { + "href": "mqtt://iot.eclipse.org/oneOfTest", + "op": "unobserveproperty", + "mqv:controlPacketValue": "UNSUBSCRIBE" } + ] }, - "events": { - "waterEmpty": { - "description": "The water fillstate is below a certain level!", - "data": { - "type": "number", - "minimum": 0, - "maximum": 100 - }, - "forms": [ - { - "href": "http://mycoffeemaker.example.com/waterempty", - "op": "subscribeevent", - "contentType": "application/json", - "subprotocol": "longpoll" - } - ] - }, - "coffeeEmpty": { - "description": "The coffee bean fillstate is below a certain amount!", - "data": { - "type": "number", - "minimum": 0, - "maximum": 100 - }, - "forms": [ - { - "href": "http://mycoffeemaker.example.com/coffeeempty", - "op": "subscribeevent", - "contentType": "application/json", - "subprotocol": "longpoll" - } - ] + "maxItemsTest": { + "readOnly": true, + "writeOnly": false, + "type": "array", + "items": { "type": "integer" }, + "maxItems": 5, + "minItems": 2, + "forms": [ + { + "href": "mqtt://iot.eclipse.org/maxItemsTest", + "op": "observeproperty" }, - "binFull": { - "description": "The bin fillstate is above a certain level!", - "data": { - "type": "number", - "minimum": 0, - "maximum": 100 - }, - "forms": [ - { - "href": "mqtt://mycoffeemaker.example.com/binfull", - "op": "subscribeevent", - "contentType": "application/json" - } - ] + { + "href": "mqtt://iot.eclipse.org/maxItemsTest", + "op": "unobserveproperty" + } + ] + } + }, + "actions": { + "brewCoffee": { + "input": { + "type": "string", + "enum": ["Espresso", "EspressoDoppio", "Coffee", "HotWater"] + }, + "forms": [ + { + "href": "http://mycoffeemaker.example.com/brewcoffee", + "op": "invokeaction", + "contentType": "application/json" + } + ], + "safe": false, + "idempotent": false + }, + "stopBrewing": { + "forms": [ + { + "href": "coap://mycoffeemaker.example.com/stopbrewing", + "cov:methodName": "GET", + "op": "invokeaction", + "contentType": "application/json" + } + ], + "safe": false, + "idempotent": false + }, + "switchOff": { + "forms": [ + { + "href": "http://mycoffeemaker.example.com/switchoff", + "op": "invokeaction", + "contentType": "application/json" + } + ], + "safe": false, + "idempotent": false + } + }, + "events": { + "waterEmpty": { + "description": "The water fillstate is below a certain level!", + "data": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "forms": [ + { + "href": "http://mycoffeemaker.example.com/waterempty", + "op": "subscribeevent", + "contentType": "application/json", + "subprotocol": "longpoll" + } + ] + }, + "coffeeEmpty": { + "description": "The coffee bean fillstate is below a certain amount!", + "data": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "forms": [ + { + "href": "http://mycoffeemaker.example.com/coffeeempty", + "op": "subscribeevent", + "contentType": "application/json", + "subprotocol": "longpoll" + } + ] + }, + "binFull": { + "description": "The bin fillstate is above a certain level!", + "data": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "forms": [ + { + "href": "mqtt://mycoffeemaker.example.com/binfull", + "op": "subscribeevent", + "contentType": "application/json" } + ] } + } } diff --git a/packages/td_to_asyncapi/index.js b/packages/td_to_asyncapi/index.js index 27c0e823c..a68851881 100644 --- a/packages/td_to_asyncapi/index.js +++ b/packages/td_to_asyncapi/index.js @@ -18,7 +18,7 @@ const genChannels = require("./src/genChannels"); const { genInfo, genTags, genBaseServer } = require("./src/genRoot"); const defaults = require("@thing-description-playground/defaults"); -const {mapSecurity} = require("./src/mapSecurity"); +const { mapSecurity } = require("./src/mapSecurity"); /** * Create an AsyncAPI instance from a Web of Things Thing Description @@ -26,39 +26,41 @@ const {mapSecurity} = require("./src/mapSecurity"); * @returns {Promise<{json:object, yaml:String}>} Resolves as object containing the OAP document or rejects */ function toAsyncAPI(td) { - return new Promise((res, rej) => { - if (typeof td !== "object") { - rej("TD has wrong type, should be an object"); - } - const {securitySchemes, security} = mapSecurity(td.securityDefinitions, td.security); - const components = { - securitySchemes - }; + return new Promise((res, rej) => { + if (typeof td !== "object") { + rej("TD has wrong type, should be an object"); + } + const { securitySchemes, security } = mapSecurity(td.securityDefinitions, td.security); + const components = { + securitySchemes + }; - defaults.addDefaults(td); - - const servers = genBaseServer(td, security); - const asyncApiInstance = new AsyncAPI("2.0.0", genInfo(td), genChannels(td, servers), { - id: td.id, - servers, - tags: genTags(td), - components: components, - externalDocs: new ExternalDocs( - "http://plugfest.thingweb.io/playground/", - "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" - ), - }); - - asyncApiInstance.parse().then( - (apiExport) => { - res(apiExport); - }, - (err) => { - console.log(asyncApiInstance.asYaml()); - rej(err); - } - ); + defaults.addDefaults(td); + let securityPara = { + security: security, + }; + const servers = genBaseServer(td, securityPara); + const asyncApiInstance = new AsyncAPI("2.0.0", genInfo(td), genChannels(td, servers), { + id: td.id, + servers, + tags: genTags(td), + components: components, + externalDocs: new ExternalDocs( + "http://plugfest.thingweb.io/playground/", + "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" + ), }); + + asyncApiInstance.parse().then( + (apiExport) => { + res(apiExport); + }, + (err) => { + console.log(asyncApiInstance.asYaml()); + rej(err); + } + ); + }); } module.exports = toAsyncAPI; diff --git a/packages/td_to_asyncapi/src/mapSecurity.js b/packages/td_to_asyncapi/src/mapSecurity.js index 15dbe568b..a507d5f84 100644 --- a/packages/td_to_asyncapi/src/mapSecurity.js +++ b/packages/td_to_asyncapi/src/mapSecurity.js @@ -13,7 +13,13 @@ * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 */ -module.exports = { mapSecurity, mapSecurityString, mapSecurityDefinitions, mapFormSecurity }; +module.exports = { + mapSecurity, + mapSecurityString, + mapSecurityDefinitions, + hasNoSec, + mapFormSecurity, +}; /** * Convert the TD security Definitions and Security to @@ -22,20 +28,19 @@ module.exports = { mapSecurity, mapSecurityString, mapSecurityDefinitions, mapFo * @param {string|string[]} tdSecurity security scheme names that apply per default */ function mapSecurity(tdDefinitions, tdSecurity) { - const { securitySchemes, scopes } = mapSecurityDefinitions(tdDefinitions); - const security = mapSecurityString(tdSecurity, securitySchemes, scopes); - - if (security.length === 0 && hasNoSec(tdDefinitions, tdSecurity)) { - security.push({}); - } - - for (const key in securitySchemes) { - if (key.includes("combo_sc")) { - delete securitySchemes[key]; - } + const { securitySchemes, scopes } = mapSecurityDefinitions(tdDefinitions); + const security = mapSecurityString(tdSecurity, securitySchemes, scopes); + if (security.length === 0 && hasNoSec(tdDefinitions, tdSecurity)) { + security.push({}); + } + + for (const key in securitySchemes) { + if (key.includes("combo_sc")) { + delete securitySchemes[key]; } + } - return { securitySchemes, security }; + return { securitySchemes, security }; } /** @@ -46,52 +51,54 @@ function mapSecurity(tdDefinitions, tdSecurity) { * @param {string|string[]|undefined} tdFormScopes oauth2 scopes that apply to this form */ function mapFormSecurity(tdDefinitions, tdSecurity, tdFormScopes) { - const { securitySchemes, scopes } = mapSecurityDefinitions(tdDefinitions); - let aapScopes = scopes; - if (typeof tdFormScopes === "string") { - tdFormScopes = [tdFormScopes]; - } - - if (typeof tdFormScopes === "object") { - const scopeSecurities = []; - // filter TD scopes that are not supported by the conversion - aapScopes = {}; - - Object.keys(scopes).forEach((supportedSecurity) => { - scopeSecurities.push(supportedSecurity); - const addScope = tdFormScopes.find((tdFormScope) => - scopes[supportedSecurity].some((supportedScope) => supportedScope === tdFormScope) - ); - if (addScope !== undefined) { - if (aapScopes[supportedSecurity] === undefined) { - aapScopes[supportedSecurity] = [addScope]; - } else { - aapScopes[supportedSecurity].push(addScope); - } - } - }); + const { securitySchemes, scopes } = mapSecurityDefinitions(tdDefinitions); + let aapScopes = scopes; + if (typeof tdFormScopes === "string") { + tdFormScopes = [tdFormScopes]; + } + + if (typeof tdFormScopes === "object") { + const scopeSecurities = []; + // filter TD scopes that are not supported by the conversion + aapScopes = {}; + + Object.keys(scopes).forEach((supportedSecurity) => { + scopeSecurities.push(supportedSecurity); + const addScope = tdFormScopes.find((tdFormScope) => + scopes[supportedSecurity].some( + (supportedScope) => supportedScope === tdFormScope + ) + ); + if (addScope !== undefined) { + if (aapScopes[supportedSecurity] === undefined) { + aapScopes[supportedSecurity] = [addScope]; + } else { + aapScopes[supportedSecurity].push(addScope); + } + } + }); - // add security scheme names if only a scope was given in the TD - scopeSecurities.forEach((scopeSecurity) => { - if (typeof tdSecurity === "string") { - tdSecurity = [tdSecurity]; - } - if (typeof tdSecurity === "object") { - if (!tdSecurity.some((tdSecString) => tdSecString === scopeSecurity)) { - tdSecurity.push(scopeSecurity); - } - } else if (tdSecurity === undefined) { - tdSecurity = scopeSecurities; - } - }); - } - const security = mapSecurityString(tdSecurity, securitySchemes, aapScopes); + // add security scheme names if only a scope was given in the TD + scopeSecurities.forEach((scopeSecurity) => { + if (typeof tdSecurity === "string") { + tdSecurity = [tdSecurity]; + } + if (typeof tdSecurity === "object") { + if (!tdSecurity.some((tdSecString) => tdSecString === scopeSecurity)) { + tdSecurity.push(scopeSecurity); + } + } else if (tdSecurity === undefined) { + tdSecurity = scopeSecurities; + } + }); + } + const security = mapSecurityString(tdSecurity, securitySchemes, aapScopes); - if (security.length === 0 && hasNoSec(tdDefinitions, tdSecurity)) { - security.push({}); - } + if (security.length === 0 && hasNoSec(tdDefinitions, tdSecurity)) { + security.push({}); + } - return { security }; + return { security }; } /** @@ -101,75 +108,81 @@ function mapFormSecurity(tdDefinitions, tdSecurity, tdFormScopes) { * @param {object} tdScopes the found scopes as map {string: string[]} */ function mapSecurityString(tdSecurity, aapSecuritySchemes, tdScopes) { - const aapSecurityContainer = []; - - if (typeof tdSecurity === "string") { - tdSecurity = [tdSecurity]; - } + const aapSecurityContainer = []; + if (typeof tdSecurity === "string") { + tdSecurity = [tdSecurity]; + } + + if (typeof tdSecurity === "object") { + tdSecurity.forEach((tdSecurityKey) => { + let aapSecurity = {}; + const securityObject = aapSecuritySchemes[tdSecurityKey]; + if (securityObject && securityObject.type === "allOf") { + securityObject.secdef.forEach((def) => { + // get scopes + let thisScopes = []; + if (tdScopes[def] !== undefined) { + thisScopes = tdScopes[def]; + } + + const supportedSchemes = Object.keys(aapSecuritySchemes); + + if ( + supportedSchemes.some((supportedScheme) => supportedScheme === def) + ) { + aapSecurity[def] = thisScopes; + } + }); - if (typeof tdSecurity === "object") { - tdSecurity.forEach((tdSecurityKey) => { - let aapSecurity = {}; - const securityObject = aapSecuritySchemes[tdSecurityKey]; - - if (securityObject && securityObject.type === "allOf") { - securityObject.secdef.forEach((def) => { - // get scopes - let thisScopes = []; - if (tdScopes[def] !== undefined) { - thisScopes = tdScopes[def]; - } - - const supportedSchemes = Object.keys(aapSecuritySchemes); - - if (supportedSchemes.some((supportedScheme) => supportedScheme === def)) { - aapSecurity[def] = thisScopes; - } - }); - - if (Object.keys(aapSecurity).length > 0) { - aapSecurityContainer.push(aapSecurity); - } - } else if (securityObject && securityObject.type === "oneOf") { - securityObject.secdef.forEach((def) => { - aapSecurity = {}; - // get scopes - let thisScopes = []; - if (tdScopes[def] !== undefined) { - thisScopes = tdScopes[def]; - } - - const supportedSchemes = Object.keys(aapSecuritySchemes); - - if (supportedSchemes.some((supportedScheme) => supportedScheme === def)) { - aapSecurity[def] = thisScopes; - } - - if (Object.keys(aapSecurity).length > 0) { - aapSecurityContainer.push(aapSecurity); - } - }); - } else { - // get scopes - let thisScopes = []; - if (tdScopes[tdSecurityKey] !== undefined) { - thisScopes = tdScopes[tdSecurityKey]; - } - - const supportedSchemes = Object.keys(aapSecuritySchemes); - - if (supportedSchemes.some((supportedScheme) => supportedScheme === tdSecurityKey)) { - aapSecurity[tdSecurityKey] = thisScopes; - } - - if (Object.keys(aapSecurity).length > 0) { - aapSecurityContainer.push(aapSecurity); - } - } + if (Object.keys(aapSecurity).length > 0) { + aapSecurityContainer.push(aapSecurity); + } + } else if (securityObject && securityObject.type === "oneOf") { + securityObject.secdef.forEach((def) => { + aapSecurity = {}; + // get scopes + let thisScopes = []; + if (tdScopes[def] !== undefined) { + thisScopes = tdScopes[def]; + } + + const supportedSchemes = Object.keys(aapSecuritySchemes); + + if ( + supportedSchemes.some((supportedScheme) => supportedScheme === def) + ) { + aapSecurity[def] = thisScopes; + } + + if (Object.keys(aapSecurity).length > 0) { + aapSecurityContainer.push(aapSecurity); + } }); - } + } else { + // get scopes + let thisScopes = []; + if (tdScopes[tdSecurityKey] !== undefined) { + thisScopes = tdScopes[tdSecurityKey]; + } + + const supportedSchemes = Object.keys(aapSecuritySchemes); + + if ( + supportedSchemes.some( + (supportedScheme) => supportedScheme === tdSecurityKey + ) + ) { + aapSecurity[tdSecurityKey] = thisScopes; + } + + if (Object.keys(aapSecurity).length > 0) { + aapSecurityContainer.push(aapSecurity); + } + } + }); + } - return aapSecurityContainer; + return aapSecurityContainer; } /** @@ -179,24 +192,24 @@ function mapSecurityString(tdSecurity, aapSecuritySchemes, tdScopes) { * @param {object|undefined} tdDefinitions if no object is given, empty securitySchemes and scopes are returned */ function mapSecurityDefinitions(tdDefinitions) { - const securitySchemes = {}; - const scopes = {}; - - if (typeof tdDefinitions === "object") { - Object.keys(tdDefinitions).forEach((key) => { - if (typeof tdDefinitions[key].scheme === "string") { - const { aapDefinition, scope } = genaapDefinition(tdDefinitions[key]); - if (Object.keys(aapDefinition).length > 0) { - securitySchemes[key] = aapDefinition; - if (typeof scope === "object") { - scopes[key] = scope; - } - } - } - }); - } + const securitySchemes = {}; + const scopes = {}; + + if (typeof tdDefinitions === "object") { + Object.keys(tdDefinitions).forEach((key) => { + if (typeof tdDefinitions[key].scheme === "string") { + const { aapDefinition, scope } = genaapDefinition(tdDefinitions[key]); + if (Object.keys(aapDefinition).length > 0) { + securitySchemes[key] = aapDefinition; + if (typeof scope === "object") { + scopes[key] = scope; + } + } + } + }); + } - return { securitySchemes, scopes }; + return { securitySchemes, scopes }; } /** @@ -204,19 +217,23 @@ function mapSecurityDefinitions(tdDefinitions) { * @param {object} tdDefinition one security definition object */ function genaapDefinition(tdDefinition) { - const aapDefinition = {}; - const tdScheme = tdDefinition.scheme.toLowerCase(); - let addOptionals = true; - const httpSchemes = ["basic", "digest", "bearer"]; - let scope; - - if (httpSchemes.some((httpScheme) => httpScheme === tdScheme)) { - aapDefinition.type = "http"; - aapDefinition.scheme = tdScheme; - if (tdDefinition.in && tdDefinition.in !== "header") { - throw new Error("Cannot represent " + tdScheme + " authentication outside the header in AsyncAPI"); - } + const aapDefinition = {}; + const tdScheme = tdDefinition.scheme.toLowerCase(); + let addOptionals = true; + const httpSchemes = ["basic", "digest", "bearer"]; + let scope; + + if (httpSchemes.some((httpScheme) => httpScheme === tdScheme)) { + aapDefinition.type = "http"; + aapDefinition.scheme = tdScheme; + if (tdDefinition.in && tdDefinition.in !== "header") { + throw new Error( + "Cannot represent " + + tdScheme + + " authentication outside the header in AsyncAPI" + ); } + } switch (tdScheme) { case "nosec": @@ -233,88 +250,99 @@ function genaapDefinition(tdDefinition) { // do nothing? break;*/ - case "digest": - aapDefinition["x-qop"] = (tdDefinition.qop === undefined) ? "auth" : tdDefinition.qop; - if (tdDefinition.name) { - aapDefinition["x-name"] = tdDefinition.name; - } - // should the x-in parameter be added if in = header? - if (tdDefinition.in) { - aapDefinition["x-in"] = tdDefinition.in; - } - break + case "digest": + aapDefinition["x-qop"] = + tdDefinition.qop === undefined ? "auth" : tdDefinition.qop; + if (tdDefinition.name) { + aapDefinition["x-name"] = tdDefinition.name; + } + // should the x-in parameter be added if in = header? + if (tdDefinition.in) { + aapDefinition["x-in"] = tdDefinition.in; + } + break; - case "bearer": - aapDefinition.bearerFormat = (tdDefinition.format === undefined) ? "jwt" : tdDefinition.format; - aapDefinition["x-alg"] = (tdDefinition.alg === undefined) ? "ES256" : tdDefinition.alg; - if (tdDefinition.name) { - aapDefinition["x-name"] = tdDefinition.name; - } - // should the x-in parameter be added if in = header? - if (tdDefinition.in) { - aapDefinition["x-in"] = tdDefinition.in; - } - if (tdDefinition.authorization) { - aapDefinition["x-authorization"] = tdDefinition.authorization; - } - break + case "bearer": + aapDefinition.bearerFormat = + tdDefinition.format === undefined ? "jwt" : tdDefinition.format; + aapDefinition["x-alg"] = + tdDefinition.alg === undefined ? "ES256" : tdDefinition.alg; + if (tdDefinition.name) { + aapDefinition["x-name"] = tdDefinition.name; + } + // should the x-in parameter be added if in = header? + if (tdDefinition.in) { + aapDefinition["x-in"] = tdDefinition.in; + } + if (tdDefinition.authorization) { + aapDefinition["x-authorization"] = tdDefinition.authorization; + } + break; - case "apikey": - aapDefinition.type = "httpApiKey"; - aapDefinition.in = tdDefinition.in === undefined ? "query" : tdDefinition.in; - aapDefinition.name = (tdDefinition.name === undefined) ? "UNKNOWN" : tdDefinition.name; - if (aapDefinition.in != "query" && aapDefinition.in != "header" && aapDefinition.in != "cookie") { - throw new Error("Cannot represent ApiKey in" + aapDefinition.in +" with AsyncAPI"); - } - break + case "apikey": + aapDefinition.type = "httpApiKey"; + aapDefinition.in = + tdDefinition.in === undefined ? "query" : tdDefinition.in; + aapDefinition.name = + tdDefinition.name === undefined ? "UNKNOWN" : tdDefinition.name; + if ( + aapDefinition.in != "query" && + aapDefinition.in != "header" && + aapDefinition.in != "cookie" + ) { + throw new Error( + "Cannot represent ApiKey in" + aapDefinition.in + " with AsyncAPI" + ); + } + break; + + case "oauth2": + if (typeof tdDefinition.scopes === "string") { + scope = [tdDefinition.scopes]; + } else if (typeof tdDefinition.scopes === "object") { + scope = tdDefinition.scopes; + } - case "oauth2": - if (typeof tdDefinition.scopes === "string") { - scope = [tdDefinition.scopes]; - } else if (typeof tdDefinition.scopes === "object") { - scope = tdDefinition.scopes; - } - - aapDefinition.type = "oauth2"; - aapDefinition.flows = genOAuthFlows(tdDefinition); - break; - /* + aapDefinition.type = "oauth2"; + aapDefinition.flows = genOAuthFlows(tdDefinition); + break; + /* case "openidconnect": aapDefinition.type = "openIdConnect" aapDefinition.openIdConnectUrl = (tdDefinition.openIdConnectUrl === undefined) ? "UNKNOWN" : tdDefinition.openIdConnectUrl //aapDefinition.flows = genOAuthFlows(tdDefinition) break */ - case "combo": - // todo Implement combo security scheme - if (tdDefinition.allOf) { - aapDefinition.type = "allOf"; - aapDefinition.secdef = tdDefinition.allOf; - } else { - aapDefinition.type = "oneOf"; - aapDefinition.secdef = tdDefinition.oneOf; - } - break; - - default: - console.log("unknown security definition: " + tdScheme); - addOptionals = false; - } + case "combo": + // todo Implement combo security scheme + if (tdDefinition.allOf) { + aapDefinition.type = "allOf"; + aapDefinition.secdef = tdDefinition.allOf; + } else { + aapDefinition.type = "oneOf"; + aapDefinition.secdef = tdDefinition.oneOf; + } + break; - // add optional fields - if (addOptionals) { - if (tdDefinition.description) { - aapDefinition.description = tdDefinition.description; - } - if (tdDefinition.descriptions) { - aapDefinition["x-descriptions"] = tdDefinition.descriptions; - } - if (tdDefinition.proxy) { - aapDefinition["x-proxy"] = tdDefinition.proxy; - } + default: + console.log("unknown security definition: " + tdScheme); + addOptionals = false; + } + + // add optional fields + if (addOptionals) { + if (tdDefinition.description) { + aapDefinition.description = tdDefinition.description; + } + if (tdDefinition.descriptions) { + aapDefinition["x-descriptions"] = tdDefinition.descriptions; } + if (tdDefinition.proxy) { + aapDefinition["x-proxy"] = tdDefinition.proxy; + } + } - return { aapDefinition, scope }; + return { aapDefinition, scope }; } /** @@ -322,62 +350,73 @@ function genaapDefinition(tdDefinition) { * @param {object} tdDefinition The security definition object of a TD */ function genOAuthFlows(tdDefinition) { - const aapFlow = {}; - if (typeof tdDefinition.flow !== "string") { - throw new Error("the oauth2 object has no flow of type string"); - } - - const tdFlow = tdDefinition.flow.toLowerCase(); - const mapTdToAap = { - implicit: ["implicit"], - password: ["password", "ropc"], - clientCredentials: ["application", "client", "clientcredentials", "clientcredential"], - authorizationCode: ["accesscode", "code", "authorizationcode"], - "x-device": ["device"], - }; - - Object.keys(mapTdToAap).forEach((key) => { - if (mapTdToAap[key].some((arrayElement) => arrayElement === tdFlow)) { - const protoFlow = {}; - if (key === "implicit" || key === "authorizationCode" || key === "x-device") { - if (tdDefinition.authorization === undefined) { - throw new Error("the authorization URI is required for oauth2 flow: " + key); - } else { - protoFlow.authorizationUrl = tdDefinition.authorization; - } - } - if ( - key === "password" || - key === "clientCredentials" || - key === "authorizationCode" || - key === "x-device" - ) { - if (tdDefinition.token === undefined) { - throw new Error("the token URI is required for oauth2 flow: " + key); - } else { - protoFlow.tokenUrl = tdDefinition.token; - } - } - if (typeof tdDefinition.refresh === "string") { - protoFlow.refreshUrl = tdDefinition.refresh; - } - if (tdDefinition.scopes === undefined) { - protoFlow.scopes = { - /* "default": "autogenerated default scope" */ - }; // TODO: is one scope required? (I don't think so) - } else { - protoFlow.scopes = {}; - if (typeof tdDefinition.scopes === "string") { - tdDefinition.scopes = [tdDefinition.scopes]; - } - tdDefinition.scopes.forEach((scope) => { - protoFlow.scopes[scope] = ""; - }); - } - aapFlow[key] = protoFlow; + const aapFlow = {}; + if (typeof tdDefinition.flow !== "string") { + throw new Error("the oauth2 object has no flow of type string"); + } + + const tdFlow = tdDefinition.flow.toLowerCase(); + const mapTdToAap = { + implicit: ["implicit"], + password: ["password", "ropc"], + clientCredentials: [ + "application", + "client", + "clientcredentials", + "clientcredential", + ], + authorizationCode: ["accesscode", "code", "authorizationcode"], + "x-device": ["device"], + }; + + Object.keys(mapTdToAap).forEach((key) => { + if (mapTdToAap[key].some((arrayElement) => arrayElement === tdFlow)) { + const protoFlow = {}; + if ( + key === "implicit" || + key === "authorizationCode" || + key === "x-device" + ) { + if (tdDefinition.authorization === undefined) { + throw new Error( + "the authorization URI is required for oauth2 flow: " + key + ); + } else { + protoFlow.authorizationUrl = tdDefinition.authorization; } - }); - return aapFlow; + } + if ( + key === "password" || + key === "clientCredentials" || + key === "authorizationCode" || + key === "x-device" + ) { + if (tdDefinition.token === undefined) { + throw new Error("the token URI is required for oauth2 flow: " + key); + } else { + protoFlow.tokenUrl = tdDefinition.token; + } + } + if (typeof tdDefinition.refresh === "string") { + protoFlow.refreshUrl = tdDefinition.refresh; + } + if (tdDefinition.scopes === undefined) { + protoFlow.scopes = { + /* "default": "autogenerated default scope" */ + }; // TODO: is one scope required? (I don't think so) + } else { + protoFlow.scopes = {}; + if (typeof tdDefinition.scopes === "string") { + tdDefinition.scopes = [tdDefinition.scopes]; + } + tdDefinition.scopes.forEach((scope) => { + protoFlow.scopes[scope] = ""; + }); + } + aapFlow[key] = protoFlow; + } + }); + return aapFlow; } /** @@ -386,30 +425,32 @@ function genOAuthFlows(tdDefinition) { * @param {string|string[]} tdSecurity security scheme names that apply to this TD part */ function hasNoSec(tdDefinitions, tdSecurity) { - let foundNoSec = false; - - // find all noSec names - const noSecNames = []; - if (typeof tdDefinitions === "object") { - Object.keys(tdDefinitions).forEach((key) => { - const tdScheme = tdDefinitions[key].scheme; - if (typeof tdScheme === "string" && tdScheme.toLowerCase() === "nosec") { - noSecNames.push(key); - } - }); - } - - if (typeof tdSecurity === "string") { - tdSecurity = [tdSecurity]; - } - if (typeof tdSecurity === "undefined") { - tdSecurity = []; - } - - // check if all security Schemes are of type noSec - if (tdSecurity.length > 0) { - foundNoSec = tdSecurity.every((securityString) => noSecNames.some((noSecName) => noSecName === securityString)); - } - - return foundNoSec; + let foundNoSec = false; + + // find all noSec names + const noSecNames = []; + if (typeof tdDefinitions === "object") { + Object.keys(tdDefinitions).forEach((key) => { + const tdScheme = tdDefinitions[key].scheme; + if (typeof tdScheme === "string" && tdScheme.toLowerCase() === "nosec") { + noSecNames.push(key); + } + }); + } + + if (typeof tdSecurity === "string") { + tdSecurity = [tdSecurity]; + } + if (typeof tdSecurity === "undefined") { + tdSecurity = []; + } + + // check if all security Schemes are of type noSec + if (tdSecurity.length > 0) { + foundNoSec = tdSecurity.every((securityString) => + noSecNames.some((noSecName) => noSecName === securityString) + ); + } + + return foundNoSec; } From e667a8d13ae8d4f777914e98a8169221fe479f30 Mon Sep 17 00:00:00 2001 From: Monika Singh Date: Thu, 31 Aug 2023 12:05:35 +0200 Subject: [PATCH 8/9] Formatting of the Json file --- .../td_to_asyncapi/examples/asyncapi.json | 274 +++++++++--------- 1 file changed, 137 insertions(+), 137 deletions(-) diff --git a/packages/td_to_asyncapi/examples/asyncapi.json b/packages/td_to_asyncapi/examples/asyncapi.json index 8d6887d81..92ac5a099 100644 --- a/packages/td_to_asyncapi/examples/asyncapi.json +++ b/packages/td_to_asyncapi/examples/asyncapi.json @@ -1,148 +1,148 @@ { - "asyncapi": "2.0.0", - "info": { - "title": "MyCoffeeMaker-Home", - "version": "unknown", - "description": "Order your coffee remotely!", - "x-AT-context": [ - "https://www.w3.org/2022/wot/td/v1.1", - { - "cov": "http://www.example.org/coap-binding#", - "mqv": "http://www.example.org/mqtt-binding#" - }, - { - "@language": "en" - } - ], - "x-AT-type": "Thing", - "x-name": "MyCoffeeMaker" - }, - "channels": { - "/oneOfTest": { - "subscribe": { - "operationId": "observeoneOfTestproperty", - "tags": [ - { - "name": "properties" - } + "asyncapi": "2.0.0", + "info": { + "title": "MyCoffeeMaker-Home", + "version": "unknown", + "description": "Order your coffee remotely!", + "x-AT-context": [ + "https://www.w3.org/2022/wot/td/v1.1", + { + "cov": "http://www.example.org/coap-binding#", + "mqv": "http://www.example.org/mqtt-binding#" + }, + { + "@language": "en" + } ], - "message": { - "contentType": "application/json", - "payload": { - "oneOf": [ - { - "type": "string", - "readOnly": false, - "writeOnly": false - }, - { - "type": "integer", - "readOnly": false, - "writeOnly": false - }, - { - "type": "null", - "readOnly": false, - "writeOnly": false - } - ], - "readOnly": true, - "writeOnly": false - } + "x-AT-type": "Thing", + "x-name": "MyCoffeeMaker" + }, + "channels": { + "/oneOfTest": { + "subscribe": { + "operationId": "observeoneOfTestproperty", + "tags": [ + { + "name": "properties" + } + ], + "message": { + "contentType": "application/json", + "payload": { + "oneOf": [ + { + "type": "string", + "readOnly": false, + "writeOnly": false + }, + { + "type": "integer", + "readOnly": false, + "writeOnly": false + }, + { + "type": "null", + "readOnly": false, + "writeOnly": false + } + ], + "readOnly": true, + "writeOnly": false + } + }, + "bindings": { + "mqtt": { + "bindingVersion": "0.1.0", + "qos": 1 + } + } + } }, - "bindings": { - "mqtt": { - "bindingVersion": "0.1.0", - "qos": 1 - } + "/maxItemsTest": { + "subscribe": { + "operationId": "observemaxItemsTestproperty", + "tags": [ + { + "name": "properties" + } + ], + "message": { + "contentType": "application/json", + "payload": { + "type": "array", + "readOnly": true, + "writeOnly": false + } + } + } + }, + "/binfull": { + "subscribe": { + "operationId": "subscribebinFullevent", + "tags": [ + { + "name": "events" + } + ], + "message": { + "contentType": "application/json", + "payload": { + "type": "number", + "readOnly": false, + "writeOnly": false + } + } + } } - } }, - "/maxItemsTest": { - "subscribe": { - "operationId": "observemaxItemsTestproperty", - "tags": [ - { - "name": "properties" - } - ], - "message": { - "contentType": "application/json", - "payload": { - "type": "array", - "readOnly": true, - "writeOnly": false - } + "id": "urn:dev:home:coff:type123-SNR123456", + "servers": { + "0": { + "url": "mqtt://iot.eclipse.org", + "protocol": "mqtt" + }, + "1": { + "url": "mqtt://mycoffeemaker.example.com", + "protocol": "mqtt" } - } }, - "/binfull": { - "subscribe": { - "operationId": "subscribebinFullevent", - "tags": [ - { - "name": "events" - } - ], - "message": { - "contentType": "application/json", - "payload": { - "type": "number", - "readOnly": false, - "writeOnly": false - } + "tags": [ + { + "name": "properties", + "description": "A property can expose a variable of a Thing, this variable might be readable, writable and/or observable.", + "externalDocs": { + "url": "https://www.w3.org/TR/wot-thing-description/#propertyaffordance", + "description": "Find out more about Property Affordances." + } + }, + { + "name": "actions", + "description": "An action can expose something to be executed by a Thing, an action can be invoked.", + "externalDocs": { + "url": "https://www.w3.org/TR/wot-thing-description/#actionaffordance", + "description": "Find out more about Action Affordances." + } + }, + { + "name": "events", + "description": "An event can expose a notification by a Thing, this notification can be subscribed and/or unsubscribed.", + "externalDocs": { + "url": "https://www.w3.org/TR/wot-thing-description/#eventaffordance", + "description": "Find out more about Event Affordances." + } + } + ], + "components": { + "securitySchemes": { + "basic_sc": { + "type": "http", + "scheme": "basic", + "x-in": "header" + } } - } - } - }, - "id": "urn:dev:home:coff:type123-SNR123456", - "servers": { - "0": { - "url": "mqtt://iot.eclipse.org", - "protocol": "mqtt" - }, - "1": { - "url": "mqtt://mycoffeemaker.example.com", - "protocol": "mqtt" - } - }, - "tags": [ - { - "name": "properties", - "description": "A property can expose a variable of a Thing, this variable might be readable, writable and/or observable.", - "externalDocs": { - "url": "https://www.w3.org/TR/wot-thing-description/#propertyaffordance", - "description": "Find out more about Property Affordances." - } - }, - { - "name": "actions", - "description": "An action can expose something to be executed by a Thing, an action can be invoked.", - "externalDocs": { - "url": "https://www.w3.org/TR/wot-thing-description/#actionaffordance", - "description": "Find out more about Action Affordances." - } }, - { - "name": "events", - "description": "An event can expose a notification by a Thing, this notification can be subscribed and/or unsubscribed.", - "externalDocs": { - "url": "https://www.w3.org/TR/wot-thing-description/#eventaffordance", - "description": "Find out more about Event Affordances." - } - } - ], - "components": { - "securitySchemes": { - "basic_sc": { - "type": "http", - "scheme": "basic", - "x-in": "header" - } + "externalDocs": { + "url": "http://plugfest.thingweb.io/playground/", + "description": "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" } - }, - "externalDocs": { - "url": "http://plugfest.thingweb.io/playground/", - "description": "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" - } } From 5707f41eae8835b452040f36a117ee41df23e36b Mon Sep 17 00:00:00 2001 From: Ege Korkan Date: Thu, 31 Aug 2023 17:31:08 +0300 Subject: [PATCH 9/9] format via prettier --- .../assertions/assertions-csv/changelog.md | 73 +- .../assertions/assertions-csv/report.json | 9 +- .../sec-body-name-json-pointer-array.json | 2 +- .../examples/tds/invalid/emptyOpArray.json | 2 +- .../core/examples/tds/valid/semanticTD.json | 10 +- packages/core/td-schema.json | 253 ++----- packages/core/tm-schema.json | 169 +---- packages/td_to_asyncapi/examples/td.json | 305 ++++---- packages/td_to_asyncapi/index.js | 66 +- packages/td_to_asyncapi/src/genChannels.js | 7 +- packages/td_to_asyncapi/src/mapSecurity.js | 676 +++++++++--------- .../td_to_asyncapi/tests/mapSecurity.test.js | 394 +++++----- packages/web/index.html | 32 +- packages/web/script.js | 48 +- packages/web/style.css | 36 +- packages/web/util.js | 4 +- 16 files changed, 895 insertions(+), 1191 deletions(-) diff --git a/packages/assertions/assertions-csv/changelog.md b/packages/assertions/assertions-csv/changelog.md index 1068c5a3e..2aaa13dfb 100644 --- a/packages/assertions/assertions-csv/changelog.md +++ b/packages/assertions/assertions-csv/changelog.md @@ -1,48 +1,45 @@ - # CSV Changelog - 27/05/2023 [Old CSV Path](assertions-csv/oldManual.csv) [New CSV Path](assertions-csv/manual.csv) - ## REMOVED -- `privacy-immutable-id-as-property` was removed -- `security-context-secure-fetch` was removed - +- `privacy-immutable-id-as-property` was removed +- `security-context-secure-fetch` was removed ## LINE-CHANGE -- `privacy-mutable-identifiers` was moved from Line 51 to 50 -- `privacy-temp-id-metadata` was moved from Line 52 to 51 -- `sec-body-name-json-pointer-type` was moved from Line 53 to 52 -- `sec-inj-no-intl-markup` was moved from Line 54 to 53 -- `security-jsonld-expansion` was moved from Line 56 to 54 -- `security-mutual-auth-td` was moved from Line 57 to 55 -- `security-no-execution` was moved from Line 58 to 56 -- `security-oauth-limits` was moved from Line 59 to 57 -- `security-remote-context` was moved from Line 60 to 58 -- `security-server-auth-td` was moved from Line 61 to 59 -- `security-static-context` was moved from Line 62 to 60 -- `td-context-ns-td10-namespacev10` was moved from Line 63 to 61 -- `td-default-AdditionalResponseContentType` was moved from Line 64 to 62 -- `td-default-observable` was moved from Line 65 to 63 -- `td-processor-bidi-isolation` was moved from Line 66 to 64 -- `td-producer-mixed-direction` was moved from Line 67 to 65 -- `td-security-extension` was moved from Line 68 to 66 -- `td-text-direction-first-strong` was moved from Line 69 to 67 -- `td-text-direction-language-tag` was moved from Line 70 to 68 -- `thing-model-td-generation-processor-extends` was moved from Line 71 to 69 -- `thing-model-td-generation-processor-forms` was moved from Line 72 to 70 -- `thing-model-td-generation-processor-placeholder` was moved from Line 73 to 71 -- `thing-model-td-generation-processor-required` was moved from Line 74 to 72 -- `tm-derivation-validity` was moved from Line 75 to 73 -- `tm-overwrite-interaction` was moved from Line 76 to 74 -- `tm-overwrite-types` was moved from Line 77 to 75 -- `tm-placeholder-replacement` was moved from Line 78 to 76 -- `tm-ref-recursive-extensions` was moved from Line 79 to 77 -- `tm-tmRef-overwrite-possibility` was moved from Line 80 to 78 -- `tm-tmRef-overwrite-process` was moved from Line 81 to 79 -- `tm-tmRef-overwrite-semantic-meaning` was moved from Line 82 to 80 -- `tm-tmRef2` was moved from Line 83 to 81 -- `tm-versioning-1` was moved from Line 84 to 82 +- `privacy-mutable-identifiers` was moved from Line 51 to 50 +- `privacy-temp-id-metadata` was moved from Line 52 to 51 +- `sec-body-name-json-pointer-type` was moved from Line 53 to 52 +- `sec-inj-no-intl-markup` was moved from Line 54 to 53 +- `security-jsonld-expansion` was moved from Line 56 to 54 +- `security-mutual-auth-td` was moved from Line 57 to 55 +- `security-no-execution` was moved from Line 58 to 56 +- `security-oauth-limits` was moved from Line 59 to 57 +- `security-remote-context` was moved from Line 60 to 58 +- `security-server-auth-td` was moved from Line 61 to 59 +- `security-static-context` was moved from Line 62 to 60 +- `td-context-ns-td10-namespacev10` was moved from Line 63 to 61 +- `td-default-AdditionalResponseContentType` was moved from Line 64 to 62 +- `td-default-observable` was moved from Line 65 to 63 +- `td-processor-bidi-isolation` was moved from Line 66 to 64 +- `td-producer-mixed-direction` was moved from Line 67 to 65 +- `td-security-extension` was moved from Line 68 to 66 +- `td-text-direction-first-strong` was moved from Line 69 to 67 +- `td-text-direction-language-tag` was moved from Line 70 to 68 +- `thing-model-td-generation-processor-extends` was moved from Line 71 to 69 +- `thing-model-td-generation-processor-forms` was moved from Line 72 to 70 +- `thing-model-td-generation-processor-placeholder` was moved from Line 73 to 71 +- `thing-model-td-generation-processor-required` was moved from Line 74 to 72 +- `tm-derivation-validity` was moved from Line 75 to 73 +- `tm-overwrite-interaction` was moved from Line 76 to 74 +- `tm-overwrite-types` was moved from Line 77 to 75 +- `tm-placeholder-replacement` was moved from Line 78 to 76 +- `tm-ref-recursive-extensions` was moved from Line 79 to 77 +- `tm-tmRef-overwrite-possibility` was moved from Line 80 to 78 +- `tm-tmRef-overwrite-process` was moved from Line 81 to 79 +- `tm-tmRef-overwrite-semantic-meaning` was moved from Line 82 to 80 +- `tm-tmRef2` was moved from Line 83 to 81 +- `tm-versioning-1` was moved from Line 84 to 82 diff --git a/packages/assertions/assertions-csv/report.json b/packages/assertions/assertions-csv/report.json index f8132a288..0d575f136 100644 --- a/packages/assertions/assertions-csv/report.json +++ b/packages/assertions/assertions-csv/report.json @@ -1 +1,8 @@ -{"generationDate":"Sat, 27 May 2023 01:02:28 GMT","assertionsSize":451,"implementedSize":372,"manualSize":82,"oldSize":3,"needsReviewSize":0} \ No newline at end of file +{ + "generationDate": "Sat, 27 May 2023 01:02:28 GMT", + "assertionsSize": 451, + "implementedSize": 372, + "manualSize": 82, + "oldSize": 3, + "needsReviewSize": 0 +} diff --git a/packages/assertions/assertions-td/sec-body-name-json-pointer-array.json b/packages/assertions/assertions-td/sec-body-name-json-pointer-array.json index 83e93d905..beb2c6833 100644 --- a/packages/assertions/assertions-td/sec-body-name-json-pointer-array.json +++ b/packages/assertions/assertions-td/sec-body-name-json-pointer-array.json @@ -30,7 +30,7 @@ }, "name": { "type": "string", - "pattern": "\/-" + "pattern": "/-" } }, "required": ["in", "scheme", "name"] diff --git a/packages/core/examples/tds/invalid/emptyOpArray.json b/packages/core/examples/tds/invalid/emptyOpArray.json index 1d704d56b..f814e5067 100644 --- a/packages/core/examples/tds/invalid/emptyOpArray.json +++ b/packages/core/examples/tds/invalid/emptyOpArray.json @@ -10,7 +10,7 @@ "properties": { "status": { "type": "string", - "forms": [{ "href": "https://mylamp.example.com/status", "op":[] }] + "forms": [{ "href": "https://mylamp.example.com/status", "op": [] }] } }, "actions": { diff --git a/packages/core/examples/tds/valid/semanticTD.json b/packages/core/examples/tds/valid/semanticTD.json index 4b7d5d35a..8424ecf53 100644 --- a/packages/core/examples/tds/valid/semanticTD.json +++ b/packages/core/examples/tds/valid/semanticTD.json @@ -1,10 +1,10 @@ { "@context": [ - "https://www.w3.org/2022/wot/td/v1.1", - { - "s": "http://schema.org", - "iot": "http://iotschema.org" - } + "https://www.w3.org/2022/wot/td/v1.1", + { + "s": "http://schema.org", + "iot": "http://iotschema.org" + } ], "@type": "Thing", "id": "urn:semantic", diff --git a/packages/core/td-schema.json b/packages/core/td-schema.json index 4eb9236bb..4c3086006 100644 --- a/packages/core/td-schema.json +++ b/packages/core/td-schema.json @@ -55,11 +55,7 @@ }, "subprotocol": { "type": "string", - "examples": [ - "longpoll", - "websub", - "sse" - ] + "examples": ["longpoll", "websub", "sse"] }, "thing-context-td-uri-v1": { "type": "string", @@ -181,15 +177,7 @@ }, "dataSchema-type": { "type": "string", - "enum": [ - "boolean", - "integer", - "number", - "string", - "object", - "array", - "null" - ] + "enum": ["boolean", "integer", "number", "string", "object", "array", "null"] }, "dataSchema": { "type": "object", @@ -318,10 +306,7 @@ } }, "multipleOfDefinition": { - "type": [ - "integer", - "number" - ], + "type": ["integer", "number"], "exclusiveMinimum": 0 }, "expectedResponse": { @@ -331,9 +316,7 @@ "type": "string" } }, - "required": [ - "contentType" - ] + "required": ["contentType"] }, "form_element_base": { "type": "object", @@ -376,9 +359,7 @@ "$ref": "#/definitions/additionalResponsesDefinition" } }, - "required": [ - "href" - ], + "required": ["href"], "additionalProperties": true }, "form_element_property": { @@ -393,23 +374,13 @@ "oneOf": [ { "type": "string", - "enum": [ - "readproperty", - "writeproperty", - "observeproperty", - "unobserveproperty" - ] + "enum": ["readproperty", "writeproperty", "observeproperty", "unobserveproperty"] }, { "type": "array", "items": { "type": "string", - "enum": [ - "readproperty", - "writeproperty", - "observeproperty", - "unobserveproperty" - ] + "enum": ["readproperty", "writeproperty", "observeproperty", "unobserveproperty"] }, "minItems": 1 } @@ -430,21 +401,13 @@ "oneOf": [ { "type": "string", - "enum": [ - "invokeaction", - "queryaction", - "cancelaction" - ] + "enum": ["invokeaction", "queryaction", "cancelaction"] }, { "type": "array", "items": { "type": "string", - "enum": [ - "invokeaction", - "queryaction", - "cancelaction" - ] + "enum": ["invokeaction", "queryaction", "cancelaction"] }, "minItems": 1 } @@ -465,19 +428,13 @@ "oneOf": [ { "type": "string", - "enum": [ - "subscribeevent", - "unsubscribeevent" - ] + "enum": ["subscribeevent", "unsubscribeevent"] }, { "type": "array", "items": { "type": "string", - "enum": [ - "subscribeevent", - "unsubscribeevent" - ] + "enum": ["subscribeevent", "unsubscribeevent"] }, "minItems": 1 } @@ -532,9 +489,7 @@ } }, "additionalProperties": true, - "required": [ - "op" - ] + "required": ["op"] }, "form": { "$comment": "This is NOT for validation purposes but for automatic generation of TS types. For more info, please see: https://github.com/w3c/wot-thing-description/pull/1319#issuecomment-994950057", @@ -671,9 +626,7 @@ } } }, - "required": [ - "forms" - ], + "required": ["forms"], "additionalProperties": true }, "action_element": { @@ -723,9 +676,7 @@ "type": "boolean" } }, - "required": [ - "forms" - ], + "required": ["forms"], "additionalProperties": true }, "event_element": { @@ -772,9 +723,7 @@ "$ref": "#/definitions/dataSchema" } }, - "required": [ - "forms" - ], + "required": ["forms"], "additionalProperties": true }, "base_link_element": { @@ -806,9 +755,7 @@ ] } }, - "required": [ - "href" - ], + "required": ["href"], "additionalProperties": true }, "link_element": { @@ -823,9 +770,7 @@ "properties": { "sizes": {} }, - "required": [ - "sizes" - ] + "required": ["sizes"] } }, { @@ -833,15 +778,10 @@ "description": "A basic link element should not contain icon or tm:extends", "properties": { "rel": { - "enum": [ - "icon", - "tm:extends" - ] + "enum": ["icon", "tm:extends"] } }, - "required": [ - "rel" - ] + "required": ["rel"] } } ] @@ -861,9 +801,7 @@ "pattern": "[0-9]*x[0-9]+" } }, - "required": [ - "rel" - ] + "required": ["rel"] } ] }, @@ -875,10 +813,7 @@ "scheme": "ace:ACESecurityScheme", "ace:as": "coaps://as.example.com/token", "ace:audience": "coaps://rs.example.com", - "ace:scopes": [ - "limited", - "special" - ], + "ace:scopes": ["limited", "special"], "ace:cnonce": true } ], @@ -901,9 +836,7 @@ "pattern": ".+:.*" } }, - "required": [ - "scheme" - ], + "required": ["scheme"], "additionalProperties": true }, "noSecurityScheme": { @@ -923,14 +856,10 @@ }, "scheme": { "type": "string", - "enum": [ - "nosec" - ] + "enum": ["nosec"] } }, - "required": [ - "scheme" - ], + "required": ["scheme"], "additionalProperties": true }, "autoSecurityScheme": { @@ -950,19 +879,13 @@ }, "scheme": { "type": "string", - "enum": [ - "auto" - ] + "enum": ["auto"] } }, "not": { - "required": [ - "name" - ] + "required": ["name"] }, - "required": [ - "scheme" - ], + "required": ["scheme"], "additionalProperties": true }, "comboSecurityScheme": { @@ -984,9 +907,7 @@ }, "scheme": { "type": "string", - "enum": [ - "combo" - ] + "enum": ["combo"] }, "oneOf": { "type": "array", @@ -996,10 +917,7 @@ } } }, - "required": [ - "scheme", - "oneOf" - ], + "required": ["scheme", "oneOf"], "additionalProperties": true }, { @@ -1019,9 +937,7 @@ }, "scheme": { "type": "string", - "enum": [ - "combo" - ] + "enum": ["combo"] }, "allOf": { "type": "array", @@ -1031,10 +947,7 @@ } } }, - "required": [ - "scheme", - "allOf" - ], + "required": ["scheme", "allOf"], "additionalProperties": true } ] @@ -1056,27 +969,17 @@ }, "scheme": { "type": "string", - "enum": [ - "basic" - ] + "enum": ["basic"] }, "in": { "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie", - "auto" - ] + "enum": ["header", "query", "body", "cookie", "auto"] }, "name": { "type": "string" } }, - "required": [ - "scheme" - ], + "required": ["scheme"], "additionalProperties": true }, "digestSecurityScheme": { @@ -1096,34 +999,21 @@ }, "scheme": { "type": "string", - "enum": [ - "digest" - ] + "enum": ["digest"] }, "qop": { "type": "string", - "enum": [ - "auth", - "auth-int" - ] + "enum": ["auth", "auth-int"] }, "in": { "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie", - "auto" - ] + "enum": ["header", "query", "body", "cookie", "auto"] }, "name": { "type": "string" } }, - "required": [ - "scheme" - ], + "required": ["scheme"], "additionalProperties": true }, "apiKeySecurityScheme": { @@ -1143,28 +1033,17 @@ }, "scheme": { "type": "string", - "enum": [ - "apikey" - ] + "enum": ["apikey"] }, "in": { "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie", - "uri", - "auto" - ] + "enum": ["header", "query", "body", "cookie", "uri", "auto"] }, "name": { "type": "string" } }, - "required": [ - "scheme" - ], + "required": ["scheme"], "additionalProperties": true }, "bearerSecurityScheme": { @@ -1184,9 +1063,7 @@ }, "scheme": { "type": "string", - "enum": [ - "bearer" - ] + "enum": ["bearer"] }, "authorization": { "$ref": "#/definitions/anyUri" @@ -1199,21 +1076,13 @@ }, "in": { "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie", - "auto" - ] + "enum": ["header", "query", "body", "cookie", "auto"] }, "name": { "type": "string" } }, - "required": [ - "scheme" - ], + "required": ["scheme"], "additionalProperties": true }, "pskSecurityScheme": { @@ -1233,17 +1102,13 @@ }, "scheme": { "type": "string", - "enum": [ - "psk" - ] + "enum": ["psk"] }, "identity": { "type": "string" } }, - "required": [ - "scheme" - ], + "required": ["scheme"], "additionalProperties": true }, "oAuth2SecurityScheme": { @@ -1263,9 +1128,7 @@ }, "scheme": { "type": "string", - "enum": [ - "oauth2" - ] + "enum": ["oauth2"] }, "authorization": { "$ref": "#/definitions/anyUri" @@ -1296,17 +1159,12 @@ }, { "type": "string", - "enum": [ - "code", - "client" - ] + "enum": ["code", "client"] } ] } }, - "required": [ - "scheme" - ], + "required": ["scheme"], "additionalProperties": true }, "securityScheme": { @@ -1387,9 +1245,7 @@ "type": "string" } }, - "required": [ - "instance" - ] + "required": ["instance"] }, "links": { "type": "array", @@ -1480,11 +1336,6 @@ "$ref": "#/definitions/thing-context" } }, - "required": [ - "title", - "security", - "securityDefinitions", - "@context" - ], + "required": ["title", "security", "securityDefinitions", "@context"], "additionalProperties": true -} \ No newline at end of file +} diff --git a/packages/core/tm-schema.json b/packages/core/tm-schema.json index cad49ae6f..5d0cecb71 100644 --- a/packages/core/tm-schema.json +++ b/packages/core/tm-schema.json @@ -64,11 +64,7 @@ }, "subprotocol": { "type": "string", - "examples": [ - "longpoll", - "websub", - "sse" - ] + "examples": ["longpoll", "websub", "sse"] }, "thing-context-td-uri-v1": { "type": "string", @@ -186,15 +182,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "boolean", - "integer", - "number", - "string", - "object", - "array", - "null" - ] + "enum": ["boolean", "integer", "number", "string", "object", "array", "null"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -408,10 +396,7 @@ "multipleOfDefinition": { "anyOf": [ { - "type": [ - "integer", - "number" - ], + "type": ["integer", "number"], "exclusiveMinimum": 0 }, { @@ -494,12 +479,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "readproperty", - "writeproperty", - "observeproperty", - "unobserveproperty" - ] + "enum": ["readproperty", "writeproperty", "observeproperty", "unobserveproperty"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -553,11 +533,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "invokeaction", - "queryaction", - "cancelaction" - ] + "enum": ["invokeaction", "queryaction", "cancelaction"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -570,11 +546,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "invokeaction", - "queryaction", - "cancelaction" - ] + "enum": ["invokeaction", "queryaction", "cancelaction"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -610,10 +582,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "subscribeevent", - "unsubscribeevent" - ] + "enum": ["subscribeevent", "unsubscribeevent"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -626,10 +595,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "subscribeevent", - "unsubscribeevent" - ] + "enum": ["subscribeevent", "unsubscribeevent"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1140,9 +1106,7 @@ "properties": { "sizes": {} }, - "required": [ - "sizes" - ] + "required": ["sizes"] } }, { @@ -1152,9 +1116,7 @@ "rel": { "anyOf": [ { - "enum": [ - "icon" - ] + "enum": ["icon"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1162,9 +1124,7 @@ ] } }, - "required": [ - "rel" - ] + "required": ["rel"] } } ] @@ -1184,9 +1144,7 @@ "pattern": "[0-9]*x[0-9]+" } }, - "required": [ - "rel" - ] + "required": ["rel"] } ] }, @@ -1198,10 +1156,7 @@ "scheme": "ace:ACESecurityScheme", "ace:as": "coaps://as.example.com/token", "ace:audience": "coaps://rs.example.com", - "ace:scopes": [ - "limited", - "special" - ], + "ace:scopes": ["limited", "special"], "ace:cnonce": true } ], @@ -1250,9 +1205,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "nosec" - ] + "enum": ["nosec"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1289,9 +1242,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "auto" - ] + "enum": ["auto"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1300,9 +1251,7 @@ } }, "not": { - "required": [ - "name" - ] + "required": ["name"] }, "additionalProperties": true, "propertyNames": { @@ -1332,9 +1281,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "combo" - ] + "enum": ["combo"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1373,9 +1320,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "combo" - ] + "enum": ["combo"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1416,9 +1361,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "basic" - ] + "enum": ["basic"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1429,13 +1372,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "header", - "query", - "body", - "cookie", - "auto" - ] + "enum": ["header", "query", "body", "cookie", "auto"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1475,9 +1412,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "digest" - ] + "enum": ["digest"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1488,10 +1423,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "auth", - "auth-int" - ] + "enum": ["auth", "auth-int"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1502,13 +1434,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "header", - "query", - "body", - "cookie", - "auto" - ] + "enum": ["header", "query", "body", "cookie", "auto"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1548,9 +1474,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "apikey" - ] + "enum": ["apikey"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1561,14 +1485,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "header", - "query", - "body", - "cookie", - "uri", - "auto" - ] + "enum": ["header", "query", "body", "cookie", "uri", "auto"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1608,9 +1525,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "bearer" - ] + "enum": ["bearer"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1630,13 +1545,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "header", - "query", - "body", - "cookie", - "auto" - ] + "enum": ["header", "query", "body", "cookie", "auto"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1676,9 +1585,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "psk" - ] + "enum": ["psk"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1718,9 +1625,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "oauth2" - ] + "enum": ["oauth2"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1758,10 +1663,7 @@ "type": "string", "anyOf": [ { - "enum": [ - "code", - "client" - ] + "enum": ["code", "client"] }, { "$ref": "#/definitions/placeholder-pattern" @@ -1932,9 +1834,7 @@ "type": "string" } }, - "required": [ - "instance" - ] + "required": ["instance"] } }, { @@ -2053,8 +1953,5 @@ "$ref": "#/definitions/placeholder-pattern" } }, - "required": [ - "@context", - "@type" - ] -} \ No newline at end of file + "required": ["@context", "@type"] +} diff --git a/packages/td_to_asyncapi/examples/td.json b/packages/td_to_asyncapi/examples/td.json index e18358f30..b3b921bbc 100644 --- a/packages/td_to_asyncapi/examples/td.json +++ b/packages/td_to_asyncapi/examples/td.json @@ -1,163 +1,174 @@ { - "@context": [ - "https://www.w3.org/2022/wot/td/v1.1", - { - "cov": "http://www.example.org/coap-binding#", - "mqv": "http://www.example.org/mqtt-binding#" - }, - { "@language": "en" } - ], - "id": "urn:dev:home:coff:type123-SNR123456", - "name": "MyCoffeeMaker", - "@type": "Thing", - "title": "MyCoffeeMaker-Home", - "description": "Order your coffee remotely!", - "securityDefinitions": { - "basic_sc": { "scheme": "basic", "in": "header" }, - "psk_sc": { "scheme": "psk" }, - "nosec_sc": { "scheme": "nosec" } - }, - "security": ["nosec_sc"], - "properties": { - "oneOfTest": { - "readOnly": true, - "writeOnly": false, - "oneOf": [ - { - "type": "string" - }, + "@context": [ + "https://www.w3.org/2022/wot/td/v1.1", { - "type": "integer" + "cov": "http://www.example.org/coap-binding#", + "mqv": "http://www.example.org/mqtt-binding#" }, { - "type": "null" + "@language": "en" } - ], - "forms": [ - { - "href": "mqtt://iot.eclipse.org/oneOfTest", - "op": "observeproperty", - "mqv:controlPacketValue": "SUBSCRIBE", - "mqv:options": [ - { - "mqv:optionName": "qos", - "mqv:optionValue": 1 - } - ] + ], + "id": "urn:dev:home:coff:type123-SNR123456", + "name": "MyCoffeeMaker", + "@type": "Thing", + "title": "MyCoffeeMaker-Home", + "description": "Order your coffee remotely!", + "securityDefinitions": { + "basic_sc": { + "scheme": "basic", + "in": "header" }, - { - "href": "mqtt://iot.eclipse.org/oneOfTest", - "op": "unobserveproperty", - "mqv:controlPacketValue": "UNSUBSCRIBE" - } - ] - }, - "maxItemsTest": { - "readOnly": true, - "writeOnly": false, - "type": "array", - "items": { "type": "integer" }, - "maxItems": 5, - "minItems": 2, - "forms": [ - { - "href": "mqtt://iot.eclipse.org/maxItemsTest", - "op": "observeproperty" + "psk_sc": { + "scheme": "psk" }, - { - "href": "mqtt://iot.eclipse.org/maxItemsTest", - "op": "unobserveproperty" - } - ] - } - }, - "actions": { - "brewCoffee": { - "input": { - "type": "string", - "enum": ["Espresso", "EspressoDoppio", "Coffee", "HotWater"] - }, - "forms": [ - { - "href": "http://mycoffeemaker.example.com/brewcoffee", - "op": "invokeaction", - "contentType": "application/json" - } - ], - "safe": false, - "idempotent": false - }, - "stopBrewing": { - "forms": [ - { - "href": "coap://mycoffeemaker.example.com/stopbrewing", - "cov:methodName": "GET", - "op": "invokeaction", - "contentType": "application/json" + "nosec_sc": { + "scheme": "nosec" } - ], - "safe": false, - "idempotent": false }, - "switchOff": { - "forms": [ - { - "href": "http://mycoffeemaker.example.com/switchoff", - "op": "invokeaction", - "contentType": "application/json" - } - ], - "safe": false, - "idempotent": false - } - }, - "events": { - "waterEmpty": { - "description": "The water fillstate is below a certain level!", - "data": { - "type": "number", - "minimum": 0, - "maximum": 100 - }, - "forms": [ - { - "href": "http://mycoffeemaker.example.com/waterempty", - "op": "subscribeevent", - "contentType": "application/json", - "subprotocol": "longpoll" + "security": ["nosec_sc"], + "properties": { + "oneOfTest": { + "readOnly": true, + "writeOnly": false, + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "null" + } + ], + "forms": [ + { + "href": "mqtt://iot.eclipse.org/oneOfTest", + "op": "observeproperty", + "mqv:controlPacketValue": "SUBSCRIBE", + "mqv:options": [ + { + "mqv:optionName": "qos", + "mqv:optionValue": 1 + } + ] + }, + { + "href": "mqtt://iot.eclipse.org/oneOfTest", + "op": "unobserveproperty", + "mqv:controlPacketValue": "UNSUBSCRIBE" + } + ] + }, + "maxItemsTest": { + "readOnly": true, + "writeOnly": false, + "type": "array", + "items": { + "type": "integer" + }, + "maxItems": 5, + "minItems": 2, + "forms": [ + { + "href": "mqtt://iot.eclipse.org/maxItemsTest", + "op": "observeproperty" + }, + { + "href": "mqtt://iot.eclipse.org/maxItemsTest", + "op": "unobserveproperty" + } + ] } - ] }, - "coffeeEmpty": { - "description": "The coffee bean fillstate is below a certain amount!", - "data": { - "type": "number", - "minimum": 0, - "maximum": 100 - }, - "forms": [ - { - "href": "http://mycoffeemaker.example.com/coffeeempty", - "op": "subscribeevent", - "contentType": "application/json", - "subprotocol": "longpoll" + "actions": { + "brewCoffee": { + "input": { + "type": "string", + "enum": ["Espresso", "EspressoDoppio", "Coffee", "HotWater"] + }, + "forms": [ + { + "href": "http://mycoffeemaker.example.com/brewcoffee", + "op": "invokeaction", + "contentType": "application/json" + } + ], + "safe": false, + "idempotent": false + }, + "stopBrewing": { + "forms": [ + { + "href": "coap://mycoffeemaker.example.com/stopbrewing", + "cov:methodName": "GET", + "op": "invokeaction", + "contentType": "application/json" + } + ], + "safe": false, + "idempotent": false + }, + "switchOff": { + "forms": [ + { + "href": "http://mycoffeemaker.example.com/switchoff", + "op": "invokeaction", + "contentType": "application/json" + } + ], + "safe": false, + "idempotent": false } - ] }, - "binFull": { - "description": "The bin fillstate is above a certain level!", - "data": { - "type": "number", - "minimum": 0, - "maximum": 100 - }, - "forms": [ - { - "href": "mqtt://mycoffeemaker.example.com/binfull", - "op": "subscribeevent", - "contentType": "application/json" + "events": { + "waterEmpty": { + "description": "The water fillstate is below a certain level!", + "data": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "forms": [ + { + "href": "http://mycoffeemaker.example.com/waterempty", + "op": "subscribeevent", + "contentType": "application/json", + "subprotocol": "longpoll" + } + ] + }, + "coffeeEmpty": { + "description": "The coffee bean fillstate is below a certain amount!", + "data": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "forms": [ + { + "href": "http://mycoffeemaker.example.com/coffeeempty", + "op": "subscribeevent", + "contentType": "application/json", + "subprotocol": "longpoll" + } + ] + }, + "binFull": { + "description": "The bin fillstate is above a certain level!", + "data": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "forms": [ + { + "href": "mqtt://mycoffeemaker.example.com/binfull", + "op": "subscribeevent", + "contentType": "application/json" + } + ] } - ] } - } } diff --git a/packages/td_to_asyncapi/index.js b/packages/td_to_asyncapi/index.js index a68851881..d52751315 100644 --- a/packages/td_to_asyncapi/index.js +++ b/packages/td_to_asyncapi/index.js @@ -26,41 +26,41 @@ const { mapSecurity } = require("./src/mapSecurity"); * @returns {Promise<{json:object, yaml:String}>} Resolves as object containing the OAP document or rejects */ function toAsyncAPI(td) { - return new Promise((res, rej) => { - if (typeof td !== "object") { - rej("TD has wrong type, should be an object"); - } - const { securitySchemes, security } = mapSecurity(td.securityDefinitions, td.security); - const components = { - securitySchemes - }; + return new Promise((res, rej) => { + if (typeof td !== "object") { + rej("TD has wrong type, should be an object"); + } + const { securitySchemes, security } = mapSecurity(td.securityDefinitions, td.security); + const components = { + securitySchemes, + }; - defaults.addDefaults(td); - let securityPara = { - security: security, - }; - const servers = genBaseServer(td, securityPara); - const asyncApiInstance = new AsyncAPI("2.0.0", genInfo(td), genChannels(td, servers), { - id: td.id, - servers, - tags: genTags(td), - components: components, - externalDocs: new ExternalDocs( - "http://plugfest.thingweb.io/playground/", - "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" - ), - }); + defaults.addDefaults(td); + let securityPara = { + security: security, + }; + const servers = genBaseServer(td, securityPara); + const asyncApiInstance = new AsyncAPI("2.0.0", genInfo(td), genChannels(td, servers), { + id: td.id, + servers, + tags: genTags(td), + components: components, + externalDocs: new ExternalDocs( + "http://plugfest.thingweb.io/playground/", + "This AsyncAPI instance was generated from a Web of Things (WoT) - Thing Description by the WoT Playground" + ), + }); - asyncApiInstance.parse().then( - (apiExport) => { - res(apiExport); - }, - (err) => { - console.log(asyncApiInstance.asYaml()); - rej(err); - } - ); - }); + asyncApiInstance.parse().then( + (apiExport) => { + res(apiExport); + }, + (err) => { + console.log(asyncApiInstance.asYaml()); + rej(err); + } + ); + }); } module.exports = toAsyncAPI; diff --git a/packages/td_to_asyncapi/src/genChannels.js b/packages/td_to_asyncapi/src/genChannels.js index ad8bfb06b..97d26e758 100644 --- a/packages/td_to_asyncapi/src/genChannels.js +++ b/packages/td_to_asyncapi/src/genChannels.js @@ -24,7 +24,6 @@ const { Channel, Operation, Tag, Message, MqttOperationBinding, Server } = requi * @param {object} servers The AsyncAPI servers map */ function genChannels(td, servers, security) { - const channels = {}; // Scan Properties @@ -111,10 +110,10 @@ function scanPropForm(form, channels, propertyName, payload, servers, interactio ) { const { channel, server } = extractChannel(form.href); let newOptionalPara = { - "security": security - } + security: security, + }; // add server - addServer(servers, server, tryProtocol.id, newOptionalPara) + addServer(servers, server, tryProtocol.id, newOptionalPara); // add channel if (!channels[channel]) { diff --git a/packages/td_to_asyncapi/src/mapSecurity.js b/packages/td_to_asyncapi/src/mapSecurity.js index a507d5f84..27e183166 100644 --- a/packages/td_to_asyncapi/src/mapSecurity.js +++ b/packages/td_to_asyncapi/src/mapSecurity.js @@ -14,11 +14,11 @@ */ module.exports = { - mapSecurity, - mapSecurityString, - mapSecurityDefinitions, - hasNoSec, - mapFormSecurity, + mapSecurity, + mapSecurityString, + mapSecurityDefinitions, + hasNoSec, + mapFormSecurity, }; /** @@ -28,19 +28,19 @@ module.exports = { * @param {string|string[]} tdSecurity security scheme names that apply per default */ function mapSecurity(tdDefinitions, tdSecurity) { - const { securitySchemes, scopes } = mapSecurityDefinitions(tdDefinitions); - const security = mapSecurityString(tdSecurity, securitySchemes, scopes); - if (security.length === 0 && hasNoSec(tdDefinitions, tdSecurity)) { - security.push({}); - } - - for (const key in securitySchemes) { - if (key.includes("combo_sc")) { - delete securitySchemes[key]; + const { securitySchemes, scopes } = mapSecurityDefinitions(tdDefinitions); + const security = mapSecurityString(tdSecurity, securitySchemes, scopes); + if (security.length === 0 && hasNoSec(tdDefinitions, tdSecurity)) { + security.push({}); } - } - return { securitySchemes, security }; + for (const key in securitySchemes) { + if (key.includes("combo_sc")) { + delete securitySchemes[key]; + } + } + + return { securitySchemes, security }; } /** @@ -51,54 +51,52 @@ function mapSecurity(tdDefinitions, tdSecurity) { * @param {string|string[]|undefined} tdFormScopes oauth2 scopes that apply to this form */ function mapFormSecurity(tdDefinitions, tdSecurity, tdFormScopes) { - const { securitySchemes, scopes } = mapSecurityDefinitions(tdDefinitions); - let aapScopes = scopes; - if (typeof tdFormScopes === "string") { - tdFormScopes = [tdFormScopes]; - } - - if (typeof tdFormScopes === "object") { - const scopeSecurities = []; - // filter TD scopes that are not supported by the conversion - aapScopes = {}; - - Object.keys(scopes).forEach((supportedSecurity) => { - scopeSecurities.push(supportedSecurity); - const addScope = tdFormScopes.find((tdFormScope) => - scopes[supportedSecurity].some( - (supportedScope) => supportedScope === tdFormScope - ) - ); - if (addScope !== undefined) { - if (aapScopes[supportedSecurity] === undefined) { - aapScopes[supportedSecurity] = [addScope]; - } else { - aapScopes[supportedSecurity].push(addScope); - } - } - }); + const { securitySchemes, scopes } = mapSecurityDefinitions(tdDefinitions); + let aapScopes = scopes; + if (typeof tdFormScopes === "string") { + tdFormScopes = [tdFormScopes]; + } - // add security scheme names if only a scope was given in the TD - scopeSecurities.forEach((scopeSecurity) => { - if (typeof tdSecurity === "string") { - tdSecurity = [tdSecurity]; - } - if (typeof tdSecurity === "object") { - if (!tdSecurity.some((tdSecString) => tdSecString === scopeSecurity)) { - tdSecurity.push(scopeSecurity); - } - } else if (tdSecurity === undefined) { - tdSecurity = scopeSecurities; - } - }); - } - const security = mapSecurityString(tdSecurity, securitySchemes, aapScopes); + if (typeof tdFormScopes === "object") { + const scopeSecurities = []; + // filter TD scopes that are not supported by the conversion + aapScopes = {}; + + Object.keys(scopes).forEach((supportedSecurity) => { + scopeSecurities.push(supportedSecurity); + const addScope = tdFormScopes.find((tdFormScope) => + scopes[supportedSecurity].some((supportedScope) => supportedScope === tdFormScope) + ); + if (addScope !== undefined) { + if (aapScopes[supportedSecurity] === undefined) { + aapScopes[supportedSecurity] = [addScope]; + } else { + aapScopes[supportedSecurity].push(addScope); + } + } + }); - if (security.length === 0 && hasNoSec(tdDefinitions, tdSecurity)) { - security.push({}); - } + // add security scheme names if only a scope was given in the TD + scopeSecurities.forEach((scopeSecurity) => { + if (typeof tdSecurity === "string") { + tdSecurity = [tdSecurity]; + } + if (typeof tdSecurity === "object") { + if (!tdSecurity.some((tdSecString) => tdSecString === scopeSecurity)) { + tdSecurity.push(scopeSecurity); + } + } else if (tdSecurity === undefined) { + tdSecurity = scopeSecurities; + } + }); + } + const security = mapSecurityString(tdSecurity, securitySchemes, aapScopes); + + if (security.length === 0 && hasNoSec(tdDefinitions, tdSecurity)) { + security.push({}); + } - return { security }; + return { security }; } /** @@ -108,81 +106,73 @@ function mapFormSecurity(tdDefinitions, tdSecurity, tdFormScopes) { * @param {object} tdScopes the found scopes as map {string: string[]} */ function mapSecurityString(tdSecurity, aapSecuritySchemes, tdScopes) { - const aapSecurityContainer = []; - if (typeof tdSecurity === "string") { - tdSecurity = [tdSecurity]; - } - - if (typeof tdSecurity === "object") { - tdSecurity.forEach((tdSecurityKey) => { - let aapSecurity = {}; - const securityObject = aapSecuritySchemes[tdSecurityKey]; - if (securityObject && securityObject.type === "allOf") { - securityObject.secdef.forEach((def) => { - // get scopes - let thisScopes = []; - if (tdScopes[def] !== undefined) { - thisScopes = tdScopes[def]; - } - - const supportedSchemes = Object.keys(aapSecuritySchemes); - - if ( - supportedSchemes.some((supportedScheme) => supportedScheme === def) - ) { - aapSecurity[def] = thisScopes; - } - }); + const aapSecurityContainer = []; + if (typeof tdSecurity === "string") { + tdSecurity = [tdSecurity]; + } - if (Object.keys(aapSecurity).length > 0) { - aapSecurityContainer.push(aapSecurity); - } - } else if (securityObject && securityObject.type === "oneOf") { - securityObject.secdef.forEach((def) => { - aapSecurity = {}; - // get scopes - let thisScopes = []; - if (tdScopes[def] !== undefined) { - thisScopes = tdScopes[def]; - } - - const supportedSchemes = Object.keys(aapSecuritySchemes); - - if ( - supportedSchemes.some((supportedScheme) => supportedScheme === def) - ) { - aapSecurity[def] = thisScopes; - } - - if (Object.keys(aapSecurity).length > 0) { - aapSecurityContainer.push(aapSecurity); - } + if (typeof tdSecurity === "object") { + tdSecurity.forEach((tdSecurityKey) => { + let aapSecurity = {}; + const securityObject = aapSecuritySchemes[tdSecurityKey]; + if (securityObject && securityObject.type === "allOf") { + securityObject.secdef.forEach((def) => { + // get scopes + let thisScopes = []; + if (tdScopes[def] !== undefined) { + thisScopes = tdScopes[def]; + } + + const supportedSchemes = Object.keys(aapSecuritySchemes); + + if (supportedSchemes.some((supportedScheme) => supportedScheme === def)) { + aapSecurity[def] = thisScopes; + } + }); + + if (Object.keys(aapSecurity).length > 0) { + aapSecurityContainer.push(aapSecurity); + } + } else if (securityObject && securityObject.type === "oneOf") { + securityObject.secdef.forEach((def) => { + aapSecurity = {}; + // get scopes + let thisScopes = []; + if (tdScopes[def] !== undefined) { + thisScopes = tdScopes[def]; + } + + const supportedSchemes = Object.keys(aapSecuritySchemes); + + if (supportedSchemes.some((supportedScheme) => supportedScheme === def)) { + aapSecurity[def] = thisScopes; + } + + if (Object.keys(aapSecurity).length > 0) { + aapSecurityContainer.push(aapSecurity); + } + }); + } else { + // get scopes + let thisScopes = []; + if (tdScopes[tdSecurityKey] !== undefined) { + thisScopes = tdScopes[tdSecurityKey]; + } + + const supportedSchemes = Object.keys(aapSecuritySchemes); + + if (supportedSchemes.some((supportedScheme) => supportedScheme === tdSecurityKey)) { + aapSecurity[tdSecurityKey] = thisScopes; + } + + if (Object.keys(aapSecurity).length > 0) { + aapSecurityContainer.push(aapSecurity); + } + } }); - } else { - // get scopes - let thisScopes = []; - if (tdScopes[tdSecurityKey] !== undefined) { - thisScopes = tdScopes[tdSecurityKey]; - } - - const supportedSchemes = Object.keys(aapSecuritySchemes); - - if ( - supportedSchemes.some( - (supportedScheme) => supportedScheme === tdSecurityKey - ) - ) { - aapSecurity[tdSecurityKey] = thisScopes; - } - - if (Object.keys(aapSecurity).length > 0) { - aapSecurityContainer.push(aapSecurity); - } - } - }); - } + } - return aapSecurityContainer; + return aapSecurityContainer; } /** @@ -192,24 +182,24 @@ function mapSecurityString(tdSecurity, aapSecuritySchemes, tdScopes) { * @param {object|undefined} tdDefinitions if no object is given, empty securitySchemes and scopes are returned */ function mapSecurityDefinitions(tdDefinitions) { - const securitySchemes = {}; - const scopes = {}; - - if (typeof tdDefinitions === "object") { - Object.keys(tdDefinitions).forEach((key) => { - if (typeof tdDefinitions[key].scheme === "string") { - const { aapDefinition, scope } = genaapDefinition(tdDefinitions[key]); - if (Object.keys(aapDefinition).length > 0) { - securitySchemes[key] = aapDefinition; - if (typeof scope === "object") { - scopes[key] = scope; - } - } - } - }); - } + const securitySchemes = {}; + const scopes = {}; + + if (typeof tdDefinitions === "object") { + Object.keys(tdDefinitions).forEach((key) => { + if (typeof tdDefinitions[key].scheme === "string") { + const { aapDefinition, scope } = genaapDefinition(tdDefinitions[key]); + if (Object.keys(aapDefinition).length > 0) { + securitySchemes[key] = aapDefinition; + if (typeof scope === "object") { + scopes[key] = scope; + } + } + } + }); + } - return { securitySchemes, scopes }; + return { securitySchemes, scopes }; } /** @@ -217,132 +207,117 @@ function mapSecurityDefinitions(tdDefinitions) { * @param {object} tdDefinition one security definition object */ function genaapDefinition(tdDefinition) { - const aapDefinition = {}; - const tdScheme = tdDefinition.scheme.toLowerCase(); - let addOptionals = true; - const httpSchemes = ["basic", "digest", "bearer"]; - let scope; - - if (httpSchemes.some((httpScheme) => httpScheme === tdScheme)) { - aapDefinition.type = "http"; - aapDefinition.scheme = tdScheme; - if (tdDefinition.in && tdDefinition.in !== "header") { - throw new Error( - "Cannot represent " + - tdScheme + - " authentication outside the header in AsyncAPI" - ); + const aapDefinition = {}; + const tdScheme = tdDefinition.scheme.toLowerCase(); + let addOptionals = true; + const httpSchemes = ["basic", "digest", "bearer"]; + let scope; + + if (httpSchemes.some((httpScheme) => httpScheme === tdScheme)) { + aapDefinition.type = "http"; + aapDefinition.scheme = tdScheme; + if (tdDefinition.in && tdDefinition.in !== "header") { + throw new Error("Cannot represent " + tdScheme + " authentication outside the header in AsyncAPI"); + } } - } - - switch (tdScheme) { - case "nosec": - case "basic": - if (tdDefinition.name) { - aapDefinition["x-name"] = tdDefinition.name; - } - // should the x-in parameter be added if in = header? - if (tdDefinition.in) { - aapDefinition["x-in"] = tdDefinition.in; - } - break; - /*case "psk": + + switch (tdScheme) { + case "nosec": + case "basic": + if (tdDefinition.name) { + aapDefinition["x-name"] = tdDefinition.name; + } + // should the x-in parameter be added if in = header? + if (tdDefinition.in) { + aapDefinition["x-in"] = tdDefinition.in; + } + break; + /*case "psk": // do nothing? break;*/ - case "digest": - aapDefinition["x-qop"] = - tdDefinition.qop === undefined ? "auth" : tdDefinition.qop; - if (tdDefinition.name) { - aapDefinition["x-name"] = tdDefinition.name; - } - // should the x-in parameter be added if in = header? - if (tdDefinition.in) { - aapDefinition["x-in"] = tdDefinition.in; - } - break; - - case "bearer": - aapDefinition.bearerFormat = - tdDefinition.format === undefined ? "jwt" : tdDefinition.format; - aapDefinition["x-alg"] = - tdDefinition.alg === undefined ? "ES256" : tdDefinition.alg; - if (tdDefinition.name) { - aapDefinition["x-name"] = tdDefinition.name; - } - // should the x-in parameter be added if in = header? - if (tdDefinition.in) { - aapDefinition["x-in"] = tdDefinition.in; - } - if (tdDefinition.authorization) { - aapDefinition["x-authorization"] = tdDefinition.authorization; - } - break; - - case "apikey": - aapDefinition.type = "httpApiKey"; - aapDefinition.in = - tdDefinition.in === undefined ? "query" : tdDefinition.in; - aapDefinition.name = - tdDefinition.name === undefined ? "UNKNOWN" : tdDefinition.name; - if ( - aapDefinition.in != "query" && - aapDefinition.in != "header" && - aapDefinition.in != "cookie" - ) { - throw new Error( - "Cannot represent ApiKey in" + aapDefinition.in + " with AsyncAPI" - ); - } - break; - - case "oauth2": - if (typeof tdDefinition.scopes === "string") { - scope = [tdDefinition.scopes]; - } else if (typeof tdDefinition.scopes === "object") { - scope = tdDefinition.scopes; - } - - aapDefinition.type = "oauth2"; - aapDefinition.flows = genOAuthFlows(tdDefinition); - break; - /* + case "digest": + aapDefinition["x-qop"] = tdDefinition.qop === undefined ? "auth" : tdDefinition.qop; + if (tdDefinition.name) { + aapDefinition["x-name"] = tdDefinition.name; + } + // should the x-in parameter be added if in = header? + if (tdDefinition.in) { + aapDefinition["x-in"] = tdDefinition.in; + } + break; + + case "bearer": + aapDefinition.bearerFormat = tdDefinition.format === undefined ? "jwt" : tdDefinition.format; + aapDefinition["x-alg"] = tdDefinition.alg === undefined ? "ES256" : tdDefinition.alg; + if (tdDefinition.name) { + aapDefinition["x-name"] = tdDefinition.name; + } + // should the x-in parameter be added if in = header? + if (tdDefinition.in) { + aapDefinition["x-in"] = tdDefinition.in; + } + if (tdDefinition.authorization) { + aapDefinition["x-authorization"] = tdDefinition.authorization; + } + break; + + case "apikey": + aapDefinition.type = "httpApiKey"; + aapDefinition.in = tdDefinition.in === undefined ? "query" : tdDefinition.in; + aapDefinition.name = tdDefinition.name === undefined ? "UNKNOWN" : tdDefinition.name; + if (aapDefinition.in != "query" && aapDefinition.in != "header" && aapDefinition.in != "cookie") { + throw new Error("Cannot represent ApiKey in" + aapDefinition.in + " with AsyncAPI"); + } + break; + + case "oauth2": + if (typeof tdDefinition.scopes === "string") { + scope = [tdDefinition.scopes]; + } else if (typeof tdDefinition.scopes === "object") { + scope = tdDefinition.scopes; + } + + aapDefinition.type = "oauth2"; + aapDefinition.flows = genOAuthFlows(tdDefinition); + break; + /* case "openidconnect": aapDefinition.type = "openIdConnect" aapDefinition.openIdConnectUrl = (tdDefinition.openIdConnectUrl === undefined) ? "UNKNOWN" : tdDefinition.openIdConnectUrl //aapDefinition.flows = genOAuthFlows(tdDefinition) break */ - case "combo": - // todo Implement combo security scheme - if (tdDefinition.allOf) { - aapDefinition.type = "allOf"; - aapDefinition.secdef = tdDefinition.allOf; - } else { - aapDefinition.type = "oneOf"; - aapDefinition.secdef = tdDefinition.oneOf; - } - break; - - default: - console.log("unknown security definition: " + tdScheme); - addOptionals = false; - } - - // add optional fields - if (addOptionals) { - if (tdDefinition.description) { - aapDefinition.description = tdDefinition.description; - } - if (tdDefinition.descriptions) { - aapDefinition["x-descriptions"] = tdDefinition.descriptions; + case "combo": + // todo Implement combo security scheme + if (tdDefinition.allOf) { + aapDefinition.type = "allOf"; + aapDefinition.secdef = tdDefinition.allOf; + } else { + aapDefinition.type = "oneOf"; + aapDefinition.secdef = tdDefinition.oneOf; + } + break; + + default: + console.log("unknown security definition: " + tdScheme); + addOptionals = false; } - if (tdDefinition.proxy) { - aapDefinition["x-proxy"] = tdDefinition.proxy; + + // add optional fields + if (addOptionals) { + if (tdDefinition.description) { + aapDefinition.description = tdDefinition.description; + } + if (tdDefinition.descriptions) { + aapDefinition["x-descriptions"] = tdDefinition.descriptions; + } + if (tdDefinition.proxy) { + aapDefinition["x-proxy"] = tdDefinition.proxy; + } } - } - return { aapDefinition, scope }; + return { aapDefinition, scope }; } /** @@ -350,73 +325,62 @@ function genaapDefinition(tdDefinition) { * @param {object} tdDefinition The security definition object of a TD */ function genOAuthFlows(tdDefinition) { - const aapFlow = {}; - if (typeof tdDefinition.flow !== "string") { - throw new Error("the oauth2 object has no flow of type string"); - } - - const tdFlow = tdDefinition.flow.toLowerCase(); - const mapTdToAap = { - implicit: ["implicit"], - password: ["password", "ropc"], - clientCredentials: [ - "application", - "client", - "clientcredentials", - "clientcredential", - ], - authorizationCode: ["accesscode", "code", "authorizationcode"], - "x-device": ["device"], - }; - - Object.keys(mapTdToAap).forEach((key) => { - if (mapTdToAap[key].some((arrayElement) => arrayElement === tdFlow)) { - const protoFlow = {}; - if ( - key === "implicit" || - key === "authorizationCode" || - key === "x-device" - ) { - if (tdDefinition.authorization === undefined) { - throw new Error( - "the authorization URI is required for oauth2 flow: " + key - ); - } else { - protoFlow.authorizationUrl = tdDefinition.authorization; - } - } - if ( - key === "password" || - key === "clientCredentials" || - key === "authorizationCode" || - key === "x-device" - ) { - if (tdDefinition.token === undefined) { - throw new Error("the token URI is required for oauth2 flow: " + key); - } else { - protoFlow.tokenUrl = tdDefinition.token; - } - } - if (typeof tdDefinition.refresh === "string") { - protoFlow.refreshUrl = tdDefinition.refresh; - } - if (tdDefinition.scopes === undefined) { - protoFlow.scopes = { - /* "default": "autogenerated default scope" */ - }; // TODO: is one scope required? (I don't think so) - } else { - protoFlow.scopes = {}; - if (typeof tdDefinition.scopes === "string") { - tdDefinition.scopes = [tdDefinition.scopes]; - } - tdDefinition.scopes.forEach((scope) => { - protoFlow.scopes[scope] = ""; - }); - } - aapFlow[key] = protoFlow; + const aapFlow = {}; + if (typeof tdDefinition.flow !== "string") { + throw new Error("the oauth2 object has no flow of type string"); } - }); - return aapFlow; + + const tdFlow = tdDefinition.flow.toLowerCase(); + const mapTdToAap = { + implicit: ["implicit"], + password: ["password", "ropc"], + clientCredentials: ["application", "client", "clientcredentials", "clientcredential"], + authorizationCode: ["accesscode", "code", "authorizationcode"], + "x-device": ["device"], + }; + + Object.keys(mapTdToAap).forEach((key) => { + if (mapTdToAap[key].some((arrayElement) => arrayElement === tdFlow)) { + const protoFlow = {}; + if (key === "implicit" || key === "authorizationCode" || key === "x-device") { + if (tdDefinition.authorization === undefined) { + throw new Error("the authorization URI is required for oauth2 flow: " + key); + } else { + protoFlow.authorizationUrl = tdDefinition.authorization; + } + } + if ( + key === "password" || + key === "clientCredentials" || + key === "authorizationCode" || + key === "x-device" + ) { + if (tdDefinition.token === undefined) { + throw new Error("the token URI is required for oauth2 flow: " + key); + } else { + protoFlow.tokenUrl = tdDefinition.token; + } + } + if (typeof tdDefinition.refresh === "string") { + protoFlow.refreshUrl = tdDefinition.refresh; + } + if (tdDefinition.scopes === undefined) { + protoFlow.scopes = { + /* "default": "autogenerated default scope" */ + }; // TODO: is one scope required? (I don't think so) + } else { + protoFlow.scopes = {}; + if (typeof tdDefinition.scopes === "string") { + tdDefinition.scopes = [tdDefinition.scopes]; + } + tdDefinition.scopes.forEach((scope) => { + protoFlow.scopes[scope] = ""; + }); + } + aapFlow[key] = protoFlow; + } + }); + return aapFlow; } /** @@ -425,32 +389,30 @@ function genOAuthFlows(tdDefinition) { * @param {string|string[]} tdSecurity security scheme names that apply to this TD part */ function hasNoSec(tdDefinitions, tdSecurity) { - let foundNoSec = false; - - // find all noSec names - const noSecNames = []; - if (typeof tdDefinitions === "object") { - Object.keys(tdDefinitions).forEach((key) => { - const tdScheme = tdDefinitions[key].scheme; - if (typeof tdScheme === "string" && tdScheme.toLowerCase() === "nosec") { - noSecNames.push(key); - } - }); - } - - if (typeof tdSecurity === "string") { - tdSecurity = [tdSecurity]; - } - if (typeof tdSecurity === "undefined") { - tdSecurity = []; - } - - // check if all security Schemes are of type noSec - if (tdSecurity.length > 0) { - foundNoSec = tdSecurity.every((securityString) => - noSecNames.some((noSecName) => noSecName === securityString) - ); - } - - return foundNoSec; + let foundNoSec = false; + + // find all noSec names + const noSecNames = []; + if (typeof tdDefinitions === "object") { + Object.keys(tdDefinitions).forEach((key) => { + const tdScheme = tdDefinitions[key].scheme; + if (typeof tdScheme === "string" && tdScheme.toLowerCase() === "nosec") { + noSecNames.push(key); + } + }); + } + + if (typeof tdSecurity === "string") { + tdSecurity = [tdSecurity]; + } + if (typeof tdSecurity === "undefined") { + tdSecurity = []; + } + + // check if all security Schemes are of type noSec + if (tdSecurity.length > 0) { + foundNoSec = tdSecurity.every((securityString) => noSecNames.some((noSecName) => noSecName === securityString)); + } + + return foundNoSec; } diff --git a/packages/td_to_asyncapi/tests/mapSecurity.test.js b/packages/td_to_asyncapi/tests/mapSecurity.test.js index b3fa90a5c..a181ee64a 100644 --- a/packages/td_to_asyncapi/tests/mapSecurity.test.js +++ b/packages/td_to_asyncapi/tests/mapSecurity.test.js @@ -2,261 +2,231 @@ * @file Modules test for mapSecurity.js, test its functionality by hardcoded example -> expected output pairs */ -const { - mapSecurity, - mapSecurityString, - mapSecurityDefinitions, - mapFormSecurity, - } = require("../src/mapSecurity"); - - // reused definitions - const oauth2Definitions = { +const { mapSecurity, mapSecurityString, mapSecurityDefinitions, mapFormSecurity } = require("../src/mapSecurity"); + +// reused definitions +const oauth2Definitions = { oauth2_sc: { - scheme: "oauth2", - flow: "code", - scopes: ["limited", "special"], - refresh: "https://refreshServer.com/", - token: "https://tokenServer.com/", - authorization: "https://authServer.com/", + scheme: "oauth2", + flow: "code", + scopes: ["limited", "special"], + refresh: "https://refreshServer.com/", + token: "https://tokenServer.com/", + authorization: "https://authServer.com/", }, - }; - - const oauth2Aap = { +}; + +const oauth2Aap = { securitySchemes: { - oauth2_sc: { - type: "oauth2", - flows: { - authorizationCode: { - authorizationUrl: "https://authServer.com/", - tokenUrl: "https://tokenServer.com/", - refreshUrl: "https://refreshServer.com/", - scopes: { - limited: "", - special: "", + oauth2_sc: { + type: "oauth2", + flows: { + authorizationCode: { + authorizationUrl: "https://authServer.com/", + tokenUrl: "https://tokenServer.com/", + refreshUrl: "https://refreshServer.com/", + scopes: { + limited: "", + special: "", + }, + }, }, - }, }, - }, }, scopes: { - oauth2_sc: ["limited", "special"], + oauth2_sc: ["limited", "special"], }, - }; - - const basicDefinitions = { +}; + +const basicDefinitions = { basic_sc: { - scheme: "basic", - in: "header", + scheme: "basic", + in: "header", }, - }; - const basicAap = { +}; +const basicAap = { securitySchemes: { - basic_sc: { - type: "http", - scheme: "basic", - 'x-in': "header", - }, + basic_sc: { + type: "http", + scheme: "basic", + "x-in": "header", + }, }, scopes: {}, - }; +}; const digestDefinitions = { digest_sc: { scheme: "digest", in: "header", - qop: "auth" - }, -} + qop: "auth", + }, +}; const digestAap = { - "scopes": { }, -"securitySchemes": { - digest_sc: { - type: "http", + scopes: {}, + securitySchemes: { + digest_sc: { + type: "http", scheme: "digest", - "x-in": "header", - "x-qop": "auth" + "x-in": "header", + "x-qop": "auth", + }, }, -}, -} +}; const apikeyDefinitions = { apikey_sc: { scheme: "apikey", in: "header", - }, -} + }, +}; const apikeyAap = { - "scopes": {}, - "securitySchemes": { + scopes: {}, + securitySchemes: { apikey_sc: { type: "httpApiKey", in: "header", name: "UNKNOWN", }, }, -} - const comboAllOfAap = { +}; +const comboAllOfAap = { securitySchemes: { - basic_sc: { - type: "http", - scheme: "basic", - }, - basic_sc2: { - type: "http", - scheme: "basic", - }, - combo_sc: { - type: "allOf", - secdef: ["basic_sc", "basic_sc2"], - }, + basic_sc: { + type: "http", + scheme: "basic", + }, + basic_sc2: { + type: "http", + scheme: "basic", + }, + combo_sc: { + type: "allOf", + secdef: ["basic_sc", "basic_sc2"], + }, }, - }; - - const comboOneOfAap = { +}; + +const comboOneOfAap = { securitySchemes: { - basic_sc: { - type: "http", - scheme: "basic", - }, - basic_sc2: { - type: "http", - scheme: "basic", - }, - combo_sc: { - type: "oneOf", - secdef: ["basic_sc", "basic_sc2"], - }, + basic_sc: { + type: "http", + scheme: "basic", + }, + basic_sc2: { + type: "http", + scheme: "basic", + }, + combo_sc: { + type: "oneOf", + secdef: ["basic_sc", "basic_sc2"], + }, }, - }; - - const noSecDefinitions = { +}; + +const noSecDefinitions = { nosec_sc: { - scheme: "nosec", + scheme: "nosec", }, - }; - - describe("mapSecurity unit tests", () => { +}; + +describe("mapSecurity unit tests", () => { describe("mapSecurityString", () => { - test("oauth2", () => { - const result = [{ oauth2_sc: ["limited"] }]; - const computed = mapSecurityString( - ["oauth2_sc"], - oauth2Aap.securitySchemes, - { oauth2_sc: ["limited"] } - ); - const computed2 = mapSecurityString( - "oauth2_sc", - oauth2Aap.securitySchemes, - { oauth2_sc: ["limited"] } - ); - expect(computed).toEqual(result); - expect(computed2).toEqual(result); - }); - - test("comboAllOf", () => { - const result = [{ basic_sc: [], basic_sc2: [] }]; - const computed = mapSecurityString( - "combo_sc", - comboAllOfAap.securitySchemes, - {} - ); - expect(computed).toEqual(result); - }); - - test("comboOneOf", () => { - const result = [{ basic_sc: [] }, { basic_sc2: [] }]; - const computed = mapSecurityString( - "combo_sc", - comboOneOfAap.securitySchemes, - {} - ); - expect(computed).toEqual(result); - }); + test("oauth2", () => { + const result = [{ oauth2_sc: ["limited"] }]; + const computed = mapSecurityString(["oauth2_sc"], oauth2Aap.securitySchemes, { oauth2_sc: ["limited"] }); + const computed2 = mapSecurityString("oauth2_sc", oauth2Aap.securitySchemes, { oauth2_sc: ["limited"] }); + expect(computed).toEqual(result); + expect(computed2).toEqual(result); + }); + + test("comboAllOf", () => { + const result = [{ basic_sc: [], basic_sc2: [] }]; + const computed = mapSecurityString("combo_sc", comboAllOfAap.securitySchemes, {}); + expect(computed).toEqual(result); + }); + + test("comboOneOf", () => { + const result = [{ basic_sc: [] }, { basic_sc2: [] }]; + const computed = mapSecurityString("combo_sc", comboOneOfAap.securitySchemes, {}); + expect(computed).toEqual(result); + }); }); - + describe("mapSecurityDefinitions", () => { test("basic", () => { - console.log(mapSecurityDefinitions(basicDefinitions)) - expect(mapSecurityDefinitions(basicDefinitions)).toEqual(basicAap); - }); - - test("digest", () => { - expect(mapSecurityDefinitions(digestDefinitions)).toEqual(digestAap); - }); - - test("apikey", () => { - expect(mapSecurityDefinitions(apikeyDefinitions)).toEqual(apikeyAap); - }); - - test("oauth2", () => { - expect(mapSecurityDefinitions(oauth2Definitions)).toEqual(oauth2Aap); - }); + console.log(mapSecurityDefinitions(basicDefinitions)); + expect(mapSecurityDefinitions(basicDefinitions)).toEqual(basicAap); + }); + + test("digest", () => { + expect(mapSecurityDefinitions(digestDefinitions)).toEqual(digestAap); + }); + + test("apikey", () => { + expect(mapSecurityDefinitions(apikeyDefinitions)).toEqual(apikeyAap); + }); + + test("oauth2", () => { + expect(mapSecurityDefinitions(oauth2Definitions)).toEqual(oauth2Aap); + }); }); - }); - - describe("mapSecurity integration tests", () => { +}); + +describe("mapSecurity integration tests", () => { describe("mapFormSecurity", () => { - const result = { security: [{ oauth2_sc: ["limited"] }] }; - - test("oauth2 single scope no security", () => { - expect( - mapFormSecurity(oauth2Definitions, undefined, ["limited"]) - ).toEqual(result); - expect(mapFormSecurity(oauth2Definitions, undefined, "limited")).toEqual( - result - ); - }); - test("oauth2 single scope with security", () => { - const security = ["oauth2_sc"]; - - expect(mapFormSecurity(oauth2Definitions, security, ["limited"])).toEqual( - result - ); - expect(mapFormSecurity(oauth2Definitions, security, "limited")).toEqual( - result - ); - }); + const result = { security: [{ oauth2_sc: ["limited"] }] }; + + test("oauth2 single scope no security", () => { + expect(mapFormSecurity(oauth2Definitions, undefined, ["limited"])).toEqual(result); + expect(mapFormSecurity(oauth2Definitions, undefined, "limited")).toEqual(result); + }); + test("oauth2 single scope with security", () => { + const security = ["oauth2_sc"]; + + expect(mapFormSecurity(oauth2Definitions, security, ["limited"])).toEqual(result); + expect(mapFormSecurity(oauth2Definitions, security, "limited")).toEqual(result); + }); }); - + describe("mapSecurity", () => { - test("oauth2", () => { - const result = { - securitySchemes: oauth2Aap.securitySchemes, - security: [ - { - oauth2_sc: ["limited", "special"], - }, - ], - }; - expect(mapSecurity(oauth2Definitions, "oauth2_sc")).toEqual(result); - }); - - test("basic", () => { - const result = { - securitySchemes: basicAap.securitySchemes, - security: [ - { - basic_sc: [], - }, - ], - }; - expect(mapSecurity(basicDefinitions, "basic_sc")).toEqual(result); - expect(mapSecurity(basicDefinitions, ["basic_sc"])).toEqual(result); - }); - - test("nosec", () => { - const result = { - securitySchemes: {}, - security: [{}], - }; - expect(mapSecurity(noSecDefinitions, "nosec_sc")).toEqual(result); - }); - - test("empty", () => { - const result = { - securitySchemes: {}, - security: [], - }; - expect(mapSecurity(noSecDefinitions, undefined)).toEqual(result); - }); + test("oauth2", () => { + const result = { + securitySchemes: oauth2Aap.securitySchemes, + security: [ + { + oauth2_sc: ["limited", "special"], + }, + ], + }; + expect(mapSecurity(oauth2Definitions, "oauth2_sc")).toEqual(result); + }); + + test("basic", () => { + const result = { + securitySchemes: basicAap.securitySchemes, + security: [ + { + basic_sc: [], + }, + ], + }; + expect(mapSecurity(basicDefinitions, "basic_sc")).toEqual(result); + expect(mapSecurity(basicDefinitions, ["basic_sc"])).toEqual(result); + }); + + test("nosec", () => { + const result = { + securitySchemes: {}, + security: [{}], + }; + expect(mapSecurity(noSecDefinitions, "nosec_sc")).toEqual(result); + }); + + test("empty", () => { + const result = { + securitySchemes: {}, + security: [], + }; + expect(mapSecurity(noSecDefinitions, undefined)).toEqual(result); + }); }); - }); - \ No newline at end of file +}); diff --git a/packages/web/index.html b/packages/web/index.html index 7c778505d..b5c729444 100644 --- a/packages/web/index.html +++ b/packages/web/index.html @@ -143,19 +143,37 @@ diff --git a/packages/web/script.js b/packages/web/script.js index c357afdb8..e088a33e1 100644 --- a/packages/web/script.js +++ b/packages/web/script.js @@ -260,23 +260,20 @@ const urlInput = document.querySelector("#url-input"); const linkTypeText = document.querySelectorAll("#link-type"); //Open the share link pop up and populate the url field with the new link -document - .getElementById("btn_save") - .addEventListener("click", async () => { - try{ - const URL = await util.save(docType, window.editor.getModel().getLanguageId()); - if(URL !== undefined){ - linkTypeText.forEach(linkText => { - linkText.innerText = docType.toUpperCase() - }) - urlInput.value = URL - shareLinkWrapper.classList.remove("closed") - } +document.getElementById("btn_save").addEventListener("click", async () => { + try { + const URL = await util.save(docType, window.editor.getModel().getLanguageId()); + if (URL !== undefined) { + linkTypeText.forEach((linkText) => { + linkText.innerText = docType.toUpperCase(); + }); + urlInput.value = URL; + shareLinkWrapper.classList.remove("closed"); } - catch(err){ - console.log(err); - } - }); + } catch (err) { + console.log(err); + } +}); //Open the shared link in ediTDor document @@ -284,21 +281,16 @@ document .addEventListener("click", () => util.openEditdor(docType, window.editor.getModel().getLanguageId())); //Open the shared link in another playground tab -document - .getElementById("btn-open-tab") - .addEventListener("click", () => { - window.open(urlInput.value, '_blank'); - }) +document.getElementById("btn-open-tab").addEventListener("click", () => { + window.open(urlInput.value, "_blank"); +}); //Close the share link pop up -document - .getElementById("btn-close-linkpopup") - .addEventListener("click", () => { - urlInput.value = '' - shareLinkWrapper.classList.add("closed") - }); +document.getElementById("btn-close-linkpopup").addEventListener("click", () => { + urlInput.value = ""; + shareLinkWrapper.classList.add("closed"); +}); - urlAddrObject = util.getExamplesList(docType); // Fetching list of examples from the given array(in helperFunctions.js). util.populateExamples(urlAddrObject); // Loading the examples given in list from their respective URLs diff --git a/packages/web/style.css b/packages/web/style.css index 81e48fa08..9a6d36520 100644 --- a/packages/web/style.css +++ b/packages/web/style.css @@ -429,7 +429,7 @@ i { text-decoration: underline dotted gray; } -.link-popup-wrapper{ +.link-popup-wrapper { position: fixed; top: 0; left: 0; @@ -441,11 +441,11 @@ i { background-color: rgba(0, 0, 0, 0.8); } -.link-popup-wrapper.closed{ +.link-popup-wrapper.closed { display: none; } -.link-popup{ +.link-popup { background-color: rgb(255, 255, 255); padding: 1.5rem; border-radius: 10px; @@ -453,42 +453,42 @@ i { width: 33%; } -.link-popup__header{ +.link-popup__header { font-weight: bold; font-size: 1.25rem; } -.link-popup__text{ +.link-popup__text { font-size: 1rem; color: #575757; } -.link-popup__url{ +.link-popup__url { width: 100%; display: flex; align-items: center; justify-content: center; } -.link-popup__url .url-input{ +.link-popup__url .url-input { width: 100%; height: 2.25rem; margin-left: 0; border-top-left-radius: 5px; border-bottom-left-radius: 5px; border: 2px solid #2b2b2b; - padding: .25rem .5rem; + padding: 0.25rem 0.5rem; font-size: 1rem; } -.link-popup__url .url-input:focus{ +.link-popup__url .url-input:focus { outline: none; } -.link-popup__url .btn-new-tab{ +.link-popup__url .btn-new-tab { width: fit-content; height: 2.25rem; - padding: .25rem .75rem; + padding: 0.25rem 0.75rem; border: none; background-color: #2b2b2b; border-top-right-radius: 5px; @@ -496,34 +496,34 @@ i { transition: background-color 150ms ease-in-out; } -.link-popup__url .btn-new-tab:hover{ +.link-popup__url .btn-new-tab:hover { background-color: #1f1f1f; } -.link-popup__url .btn-new-tab:focus{ +.link-popup__url .btn-new-tab:focus { outline: none; background-color: #1f1f1f; } -.btn-new-tab svg{ +.btn-new-tab svg { fill: #ffffff; height: 1rem; } -.link-popup__btn-container{ +.link-popup__btn-container { text-align: right; } -.link-popup__btn-container .btn-editdor{ +.link-popup__btn-container .btn-editdor { background-color: transparent; } -.link-popup__btn-container .btn-close{ +.link-popup__btn-container .btn-close { background-color: #138496; color: #fff; transition: background-color 250ms ease-in-out; } -.link-popup__btn-container .btn-close:hover{ +.link-popup__btn-container .btn-close:hover { background-color: #106775; } diff --git a/packages/web/util.js b/packages/web/util.js index 95d21b41d..cf5deba34 100644 --- a/packages/web/util.js +++ b/packages/web/util.js @@ -575,8 +575,8 @@ export async function openEditdor(docType, format) { const data = docType + format + value; const compressed = Validators.compress(data); - const URL = `https://eclipse.github.io/editdor/?td=${compressed}` - window.open(URL, '_blank'); + const URL = `https://eclipse.github.io/editdor/?td=${compressed}`; + window.open(URL, "_blank"); } /**