From 52d2debda046c88db11c85ce7e528a6c279cb5b6 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 13 Aug 2024 11:20:23 +0200 Subject: [PATCH] fix transformation to TOSCA so that properties are splitted correctly between node and relationship; added endpoint properties --- package.json | 3 +- src/core/entities/endpoint.ts | 16 ++-- src/core/entities/externalEndpoint.ts | 41 ++++++++++- .../tosca-adapter/EntitiesToToscaConverter.ts | 16 ++-- .../v2dot0-profiles/cna_modeling_profile.ts | 73 ++++++++++++++++++- .../cna-modeling-profile/node_types.yaml | 12 ++- 6 files changed, 142 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index c1fea5b2..0c43aaf6 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "build": "vite build", "ghpages": "vite build --base=/", "preview": "vite preview", - "test": "vitest run" + "test": "vitest run", + "parseProfiles": "cd ./src/totypa/parsers/v2dot0-parsers && npx tsx profileParser.ts && cd /../../.." }, "dependencies": { "@joint/core": "^4.0.2", diff --git a/src/core/entities/endpoint.ts b/src/core/entities/endpoint.ts index ae823530..38e37b7f 100644 --- a/src/core/entities/endpoint.ts +++ b/src/core/entities/endpoint.ts @@ -21,9 +21,9 @@ function getEndpointProperties(): EntityProperty[] { for (const prop of parsed) { switch (prop.getKey) { - case "protocol": - prop.setName = "Endpoint Type:"; - prop.setExample = "e.g. HTTP GET"; + case "method_name": + prop.setName = "Method name"; + prop.setExample = "e.g. GET if protocol is http"; (prop as TextEntityProperty).setOptions = [{ value: "GET", text: "GET" @@ -33,12 +33,12 @@ function getEndpointProperties(): EntityProperty[] { text: "POST" }, { - value: "Topic send-to", - text: "Topic send-to" + value: "publish", + text: "publish" }, { - value: "Topic receive-from", - text: "Topic receive-from" + value: "subscribe", + text: "subscribe" } ]; break; @@ -158,4 +158,4 @@ class Endpoint { } } -export { Endpoint, ENDPOINT_TOSCA_KEY, getEndpointProperties }; \ No newline at end of file +export { Endpoint, ENDPOINT_TOSCA_KEY, ENDPOINT_TOSCA_EQUIVALENT, ENDPOINT_CAPABILITY_EQUIVALENT, getEndpointProperties }; \ No newline at end of file diff --git a/src/core/entities/externalEndpoint.ts b/src/core/entities/externalEndpoint.ts index fa3cae0a..ffeeb890 100644 --- a/src/core/entities/externalEndpoint.ts +++ b/src/core/entities/externalEndpoint.ts @@ -1,4 +1,4 @@ -import { EntityProperty, parseProperties } from "../common/entityProperty.js" +import { EntityProperty, parseProperties, TextEntityProperty } from "../common/entityProperty.js" import { Endpoint } from "./endpoint.js"; import { tosca_simple_2_0 } from '../../totypa/parsedProfiles/v2dot0-profiles/tosca_simple_2_0.js' import { MetaData } from "../common/entityDataTypes.js"; @@ -16,7 +16,42 @@ const EXTERNAL_ENDPOINT_CAPABILITY_EQUIVALENT = tosca_simple_2_0.capability_type function getExternalEndpointProperties(): EntityProperty[] { - let parsed = parseProperties(EXTERNAL_ENDPOINT_CAPABILITY_EQUIVALENT.properties); + let parsed = parseProperties(EXTERNAL_ENDPOINT_CAPABILITY_EQUIVALENT.properties).concat(parseProperties(EXTERNAL_ENDPOINT_TOSCA_EQUIVALENT.properties)); + + /* + for (const prop of parsed) { + switch (prop.getKey) { + case "method_name": + prop.setName = "Method name"; + prop.setExample = "e.g. GET if protocol is http"; + (prop as TextEntityProperty).setOptions = [{ + value: "GET", + text: "GET" + }, + { + value: "POST", + text: "POST" + }, + { + value: "publish", + text: "publish" + }, + { + value: "subscribe", + text: "subscribe" + } + ]; + break; + case "url_path": + prop.setName = "Endpoint Path:"; + prop.setExample = "e.g. /orders"; + break; + case "port": // TODO transform to Number type? + prop.setName = "Port: "; + prop.setExample = "e.g. 3306" + break; + } + */ return parsed; } @@ -49,4 +84,4 @@ class ExternalEndpoint extends Endpoint { } } -export { ExternalEndpoint, EXTERNAL_ENDPOINT_TOSCA_KEY, getExternalEndpointProperties }; \ No newline at end of file +export { ExternalEndpoint, EXTERNAL_ENDPOINT_TOSCA_KEY, EXTERNAL_ENDPOINT_TOSCA_EQUIVALENT, EXTERNAL_ENDPOINT_CAPABILITY_EQUIVALENT, getExternalEndpointProperties }; \ No newline at end of file diff --git a/src/core/tosca-adapter/EntitiesToToscaConverter.ts b/src/core/tosca-adapter/EntitiesToToscaConverter.ts index 527229a9..45ab0e78 100644 --- a/src/core/tosca-adapter/EntitiesToToscaConverter.ts +++ b/src/core/tosca-adapter/EntitiesToToscaConverter.ts @@ -2,11 +2,11 @@ import * as Entities from '../entities' import { TwoWayKeyIdMap } from "./TwoWayKeyIdMap"; import { UniqueKeyManager } from "./UniqueKeyManager"; import { flatMetaData } from '../common/entityDataTypes'; -import { ENDPOINT_TOSCA_KEY } from '../entities/endpoint'; +import { ENDPOINT_CAPABILITY_EQUIVALENT, ENDPOINT_TOSCA_EQUIVALENT, ENDPOINT_TOSCA_KEY } from '../entities/endpoint'; import { REQUEST_TRACE_TOSCA_KEY } from '../entities/requestTrace'; import { DEPLOYMENT_MAPPING_TOSCA_KEY } from '../entities/deploymentMapping'; import { LINK_TOSCA_KEY } from '../entities/link'; -import { EntityProperty } from '../common/entityProperty'; +import { EntityProperty, parseProperties } from '../common/entityProperty'; import { DATA_AGGREGATE_TOSCA_KEY } from '../entities/dataAggregate'; import { BACKING_DATA_TOSCA_KEY } from '../entities/backingData'; import { INFRASTRUCTURE_TOSCA_KEY } from '../entities/infrastructure'; @@ -14,7 +14,7 @@ import { SERVICE_TOSCA_KEY } from '../entities/service'; import { BACKING_SERVICE_TOSCA_KEY } from '../entities/backingService'; import { STORAGE_BACKING_SERVICE_TOSCA_KEY } from '../entities/storageBackingService'; import { COMPONENT_TOSCA_KEY } from '../entities/component'; -import { EXTERNAL_ENDPOINT_TOSCA_KEY } from '../entities/externalEndpoint'; +import { EXTERNAL_ENDPOINT_CAPABILITY_EQUIVALENT, EXTERNAL_ENDPOINT_TOSCA_EQUIVALENT, EXTERNAL_ENDPOINT_TOSCA_KEY } from '../entities/externalEndpoint'; import { TOSCA_File } from '@/totypa/tosca-types/v2dot0-types/definition-types'; import { TOSCA_Node_Template, TOSCA_Relationship_Template, TOSCA_Requirement_Assignment, TOSCA_Service_Template } from '@/totypa/tosca-types/v2dot0-types/template-types'; import { TOSCA_Property_Assignment } from '@/totypa/tosca-types/v2dot0-types/alias-types'; @@ -433,27 +433,33 @@ class EntitiesToToscaConverter { #createEndpointTemplate(endpoint: Entities.Endpoint): TOSCA_Node_Template { + let endpointNodePropertyKeys = parseProperties(ENDPOINT_TOSCA_EQUIVALENT.properties).map(property => property.getKey); let template: TOSCA_Node_Template = { type: ENDPOINT_TOSCA_KEY, metadata: flatMetaData(endpoint.getMetaData), + properties: this.#parsePropertiesForYaml(endpoint.getProperties().filter(property => endpointNodePropertyKeys.includes(property.getKey))), capabilities: {} }; + let endpointCapabilityPropertyKeys = parseProperties(ENDPOINT_CAPABILITY_EQUIVALENT.properties).map(property => property.getKey); template.capabilities.endpoint = { - properties: this.#parsePropertiesForYaml(endpoint.getProperties()) + properties: this.#parsePropertiesForYaml(endpoint.getProperties().filter(property => endpointCapabilityPropertyKeys.includes(property.getKey))) } return template; } #createExternalEndpointTemplate(endpoint: Entities.ExternalEndpoint): TOSCA_Node_Template { + let externalEndpointNodePropertyKeys = parseProperties(EXTERNAL_ENDPOINT_TOSCA_EQUIVALENT.properties).map(property => property.getKey); let template: TOSCA_Node_Template = { type: EXTERNAL_ENDPOINT_TOSCA_KEY, metadata: flatMetaData(endpoint.getMetaData), + properties: this.#parsePropertiesForYaml(endpoint.getProperties().filter(property => externalEndpointNodePropertyKeys.includes(property.getKey))), capabilities: {} }; + let externalEndpointCapabilityPropertyKeys = parseProperties(EXTERNAL_ENDPOINT_CAPABILITY_EQUIVALENT.properties).map(property => property.getKey); template.capabilities.external_endpoint = { - properties: this.#parsePropertiesForYaml(endpoint.getProperties()) + properties: this.#parsePropertiesForYaml(endpoint.getProperties().filter(property => externalEndpointCapabilityPropertyKeys.includes(property.getKey))) } return template; diff --git a/src/totypa/parsedProfiles/v2dot0-profiles/cna_modeling_profile.ts b/src/totypa/parsedProfiles/v2dot0-profiles/cna_modeling_profile.ts index 9eaae1b1..bf78b90c 100644 --- a/src/totypa/parsedProfiles/v2dot0-profiles/cna_modeling_profile.ts +++ b/src/totypa/parsedProfiles/v2dot0-profiles/cna_modeling_profile.ts @@ -1233,6 +1233,27 @@ export const cna_modeling_profile: TOSCA_File = { }, "derived_from": "Root", "properties": { + "kind": { + "type": "string", + "required": true, + "description": "The kind of endpoint which can be either \"query\", \"command\", or \"event\". A \"query\" is a synchronous request for data which the client needs for further processing. A \"command\" is a synchronous send of data for which the client needs a corresponding answer for further processing. An \"event\" is an asynchronous send of data for which the client does not expect data to be returned for further processing.", + "validation": { + "$valid_values": [ + "$value", + [ + "query", + "command", + "event" + ] + ] + }, + "default": "query" + }, + "method_name": { + "type": "string", + "required": false, + "description": "An optional name of the method/action used. For REST APIs it can for example be specified whether it is a GET or POST method. For message brokers it can be specified whether it is a publish or subscribe action." + }, "rate_limiting": { "type": "string", "required": true, @@ -1270,6 +1291,9 @@ export const cna_modeling_profile: TOSCA_File = { "feature": { "type": "Node" }, + "endpoint": { + "type": "Endpoint" + }, "external_endpoint": { "type": "Endpoint.Public" } @@ -1303,7 +1327,54 @@ export const cna_modeling_profile: TOSCA_File = { "type": "Lifecycle.Standard" } }, - "derived_from": "Root" + "derived_from": "cna-modeling.entities.Endpoint", + "properties": { + "kind": { + "type": "string", + "required": true, + "description": "The kind of endpoint which can be either \"query\", \"command\", or \"event\". A \"query\" is a synchronous request for data which the client needs for further processing. A \"command\" is a synchronous send of data for which the client needs a corresponding answer for further processing. An \"event\" is an asynchronous send of data for which the client does not expect data to be returned for further processing.", + "validation": { + "$valid_values": [ + "$value", + [ + "query", + "command", + "event" + ] + ] + }, + "default": "query" + }, + "method_name": { + "type": "string", + "required": false, + "description": "An optional name of the method/action used. For REST APIs it can for example be specified whether it is a GET or POST method. For message brokers it can be specified whether it is a publish or subscribe action." + }, + "rate_limiting": { + "type": "string", + "required": true, + "description": "If for this endpoint rate limiting is enforced, the limit can be stated here, otherwise it is \"none\".", + "default": "none" + }, + "idempotent": { + "type": "boolean", + "required": true, + "description": "Flag to specify whether this endpoint is idempotent, meaning that the effect of a successful invocation is independent of the number of times it is invoked.", + "default": false + }, + "readiness_check": { + "type": "boolean", + "required": true, + "description": "Flag to specify whether this endpoint is used as a readiness check", + "default": false + }, + "health_check": { + "type": "boolean", + "required": true, + "description": "Flag to specify whether this endpoint is used as a health check", + "default": false + } + } }, "cna-modeling.entities.BackingData": { "description": "Node Type to model Backing Data entities", diff --git a/tosca-profiles/v2dot0-profiles/cna-modeling-profile/node_types.yaml b/tosca-profiles/v2dot0-profiles/cna-modeling-profile/node_types.yaml index 6bc03ebc..045ea5bc 100644 --- a/tosca-profiles/v2dot0-profiles/cna-modeling-profile/node_types.yaml +++ b/tosca-profiles/v2dot0-profiles/cna-modeling-profile/node_types.yaml @@ -209,6 +209,16 @@ node_types: derived_from: Root description: Endpoint type to explicitly model endpoints as entities properties: + kind: + type: string + required: true + description: The kind of endpoint which can be either "query", "command", or "event". A "query" is a synchronous request for data which the client needs for further processing. A "command" is a synchronous send of data for which the client needs a corresponding answer for further processing. An "event" is an asynchronous send of data for which the client does not expect data to be returned for further processing. + validation: { $valid_values: [ $value, ["query", "command", "event"]]} + default: "query" + method_name: + type: string + required: false + description: An optional name of the method/action used. For REST APIs it can for example be specified whether it is a GET or POST method. For message brokers it can be specified whether it is a publish or subscribe action. rate_limiting: type: string required: true @@ -243,7 +253,7 @@ node_types: count_range: [0, UNBOUNDED] cna-modeling.entities.Endpoint.External: - derived_from: Root + derived_from: cna-modeling.entities.Endpoint description: Endpoint type to explicitly model external endpoints as entities capabilities: # Allow assigning exactly one External Endpoint Capability