Implement create data type property 36/132136/8
authorandre.schmid <andre.schmid@est.tech>
Wed, 12 Oct 2022 17:14:23 +0000 (18:14 +0100)
committerVasyl Razinkov <vasyl.razinkov@est.tech>
Fri, 25 Nov 2022 11:00:53 +0000 (11:00 +0000)
Allows to add a new data type property and visualize the properties
details.

Change-Id: Ib7bcd4b0bd8213dbe8ee8a3762a0636e22dc67eb
Issue-ID: SDC-4258
Signed-off-by: André Schmid <andre.schmid@est.tech>
32 files changed:
asdctool/src/main/resources/config/error-configuration.yaml
catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml
catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java
catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/DefaultExceptionMapper.java
catalog-be/src/main/resources/config/error-configuration.yaml
catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java
catalog-model/src/main/java/org/openecomp/sdc/be/exception/OperationException.java [deleted file]
catalog-model/src/main/java/org/openecomp/sdc/be/exception/supplier/DataTypeOperationExceptionSupplier.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/dto/PropertyDefinitionDto.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/mapper/PropertyDefinitionDtoMapper.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java
catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperationTest.java
catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html
catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts
catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.less [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.spec.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts
catalog-ui/src/app/ng2/services/data-type.service.ts
catalog-ui/src/app/utils/constants.ts
catalog-ui/src/app/utils/tosca-type-helper.ts [new file with mode: 0644]
catalog-ui/src/assets/languages/en_US.json
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceOperationInputListComponent.java

index e201f89..f8efb0b 100644 (file)
@@ -8,6 +8,10 @@ errors:
     code: 201,
     message: "OK"
   }
+  ACCEPTED: {
+    code: 202,
+    message: "Accepted"
+  }
   NO_CONTENT: {
     code: 204,
     message: "No Content"
@@ -42,6 +46,18 @@ errors:
     message: "Error: Not authorized to use the API.",
     messageId: "POL5003"
   }
+  #---------POL5004------------------------------
+  MISSING_USER_ID: {
+    code: 400  ,
+    message: "Error: Missing 'USER_ID' HTTP header.",
+    messageId: "POL5004"
+  }
+  #---------POL5005------------------------------
+  SESSION_EXPIRED: {
+    code: 403  ,
+    message: "Your session has expired. Please close the SDC tab and re-enter the SDC application.",
+    messageId: "POL5005"
+  }
   #---------SVC4000-----------------------------
   INVALID_CONTENT: {
     code: 400,
@@ -55,7 +71,7 @@ errors:
     messageId: "SVC4002"
   }
   #---------SVC4003------------------------------
-  # %1 - Users's USER_ID
+  # %1 - Users's ID
   USER_NOT_FOUND: {
     code: 404,
     message: "Error: User '%1' was not found.",
@@ -89,17 +105,23 @@ errors:
     messageId: "SVC4007"
   }
   #---------SVC4008-----------------------------
-  # %1 - Users's USER_ID
+  # %1 - Users's userId
   INVALID_USER_ID: {
     code: 400,
-    message: "Error: Invalid USER_ID '%1'.",
+    message: "Error: Invalid userId '%1'.",
     messageId: "SVC4008"
   }
+  #---------SVC4009-----------------------------
+  USER_DEFINED: {
+    code: 400,
+    message: "Error: User Defined '%1'.",
+    messageId: "SVC4009"
+  }
   #---------SVC4049------------------------------
   # %1 - service/resource
   COMPONENT_MISSING_CONTACT: {
     code: 400,
-    message: "Error: Invalid Content. Missing %1 Contact Id.",
+    message: "Error: Invalid Content. Missing %1 contact.",
     messageId: "SVC4049"
   }
   #---------SVC4050-----------------------------
@@ -202,14 +224,14 @@ errors:
   }
 
   #---------SVC4064------------------------------
-  # %1 - Service/Resource
+  # %1 - Service/Resource/Property
   COMPONENT_INVALID_DESCRIPTION: {
     code: 400,
     message: "Error: Invalid Content. %1 description contains non-english characters.",
     messageId: "SVC4064"
   }
   #---------SVC4065------------------------------
-  # %1 - Service/Resource
+  # %1 - Service/Resource/Property
   # %2 - max resource/service name length
   COMPONENT_DESCRIPTION_EXCEEDS_LIMIT: {
     code: 400,
@@ -242,7 +264,7 @@ errors:
   # %1 - Service/Resource/Product
   COMPONENT_INVALID_CONTACT: {
     code: 400,
-    message: "Error: Invalid Content. %1 Contact Id should be in format 'mnnnnnn' or 'aannna' or 'aannnn', where m=m ,a=a-zA-Z and n=0-9",
+    message: "Error: Invalid Content. %1 Contact Id should be in format 'mnnnnnn' or 'aannna' or 'aannnn', where m=m ,a=a-zA-Z and n=0-9. Max length: 50",
     messageId: "SVC4069"
   }
   #---------SVC4070------------------------------
@@ -340,7 +362,7 @@ errors:
   # %2  - resource/service
   COMPONENT_IN_USE: {
     code: 403,
-    message: "Error: Requested '%1' %2 is in use by another user.",
+    message: "Error: '%1' %2 is still held by previous action. Please try again later.",
     messageId: "SVC4086"
   }
   #-----------SVC4087---------------------------
@@ -441,7 +463,7 @@ errors:
   # %1 - User's USER_ID
   USER_INACTIVE: {
     code: 404,
-    message: "Error: User %1 was not found.",
+    message: "Error: User %1 inactive.",
     messageId: "SVC4120"
   }
   #-----------SVC4121---------------------------
@@ -506,7 +528,7 @@ errors:
   #-----------SVC4130---------------------------
   INVALID_PROJECT_CODE: {
     code: 400,
-    message: "Error: Invalid Content. PROJECT_CODE must be from 3 up to 50 characters.",
+    message: "Error: Invalid Content. PROJECT_CODE must be from 5 up to 50 characters.",
     messageId: "SVC4130"
   }
   #-----------SVC4131---------------------------
@@ -565,13 +587,6 @@ errors:
     message: "Error: Invalid distribution environment.",
     messageId: "SVC4138"
   }
-  #---------SVC4139------------------------------
-  # %1 - service name
-  DISTRIBUTION_ARTIFACT_NOT_FOUND: {
-    code: 409,
-    message: "Error: Service '%1' cannot be distributed due to missing deployment artifacts.",
-    messageId: "SVC4139"
-  }
   #---------SVC4200------------------------------
   # %1 - Service/Resource
   # %2 - max icon name length
@@ -907,38 +922,19 @@ errors:
     message: "Error: Invalid resource checksum.",
     messageId: "SVC4549"
   }
-  #---------SVC4550------------------------------
-  #%1  -  Consumer salt
-  INVALID_LENGTH: {
-    code: 400,
-    message: "Error: Invalid %1 length.",
-    messageId: "SVC4550"
-  }
   #---------SVC4551------------------------------
-  #%1  -  ECOMP User name
+  # %1  -  ECOMP User name
   ECOMP_USER_NOT_FOUND: {
     code: 404,
     message: "Error: ECOMP User '%1' was not found.",
     messageId: "SVC4551"
   }
-  #---------SVC4552------------------------------
-  CONSUMER_ALREADY_EXISTS: {
-    code: 409,
-    message: "Error: ECOMP User already exists.",
-    messageId: "SVC4552"
-  }
-  #---------SVC4553-----------------------------
-  #%1  -  Consumer name / Consumer password/ Consumer salt
-  INVALID_CONTENT_PARAM: {
-    code: 400,
-    message: "Error: %1 is invalid.",
-    messageId: "SVC4553"
-  }
   #---------SVC4554------------------------------
-  # %1 - "Resource"/"Service"
+  # %1 - Artifact Id
+  # %2 - "Resource"/"Service"
   COMPONENT_ARTIFACT_NOT_FOUND: {
     code: 404,
-    message: "Error: Requested artifact doesn't belong to specified %1.",
+    message: "Error: Requested artifact %1 doesn't belong to specified %2.",
     messageId: "SVC4554"
   }
   #---------SVC4554------------------------------
@@ -949,24 +945,24 @@ errors:
     messageId: "SVC4554"
   }
   #---------SVC4555------------------------------
-  #%1 - "Resource"/"Service"/"Product"
-  #%2 - "category"
+  # %1 - "Resource"/"Service"/"Product"
+  # %2 - "category"
   COMPONENT_ELEMENT_INVALID_NAME_LENGTH: {
     code: 400,
     message: "Error: Invalid %1 %2 name length.",
     messageId: "SVC4555"
   }
   #---------SVC4556------------------------------
-  #%1 - "Resource"/"Service"/"Product"
-  #%2 - "category"
+  # %1 - "Resource"/"Service"/"Product"
+  # %2 - "category"
   COMPONENT_ELEMENT_INVALID_NAME_FORMAT: {
     code: 400,
     message: "Error: Invalid %1 %2 name format.",
     messageId: "SVC4556"
   }
   #---------SVC4557------------------------------
-  #%1 - "Resource"/"Service"/"Product"
-  #%2 - "category name"
+  # %1 - "Resource"/"Service"/"Product"
+  # %2 - "category name"
   COMPONENT_CATEGORY_ALREADY_EXISTS: {
     code: 409,
     message: "Error: %1 category name '%2' already exists.",
@@ -1054,16 +1050,16 @@ errors:
     message: "Error: Uploaded XML file for %1 artifact is invalid.",
     messageId: "SVC4567"
   }
-  #---------SVC4567------------------------------
-  # %1 - "User Name and USER_ID"
-  # %2  -"checked-out"/"in-certification"
+  #---------SVC4569------------------------------
+  # %1 - "User Name and UserId"
+  # %2  -"component names ot IDs"
   CANNOT_DELETE_USER_WITH_ACTIVE_ELEMENTS: {
-    code: 409,
-    message: "Error: User cannot be deleted. User '%1' has %2 projects.",
-    messageId: "SVC4567"
+    code: 412,
+    message: "Error: User cannot be deleted. User '%1' has projects that cannot be committed: %2.",
+    messageId: "SVC4569"
   }
   #---------SVC4568------------------------------
-  # %1 - "User Name and USER_ID"
+  # %1 - "User Name and UserId"
   # %2  -"checked-out"/"in-certification"
   CANNOT_UPDATE_USER_WITH_ACTIVE_ELEMENTS: {
     code: 409,
@@ -1139,7 +1135,7 @@ errors:
     messageId: "SVC4580"
   }
   #---------SVC4581------------------------------
-  # %1 - USER_ID
+  # %1 - UserId
   INVALID_PRODUCT_CONTACT: {
     code: 400,
     message: "Error: Invalid content. User '%1' cannot be set as Product Contact.",
@@ -1317,7 +1313,7 @@ errors:
   # %3 - state
   ILLEGAL_COMPONENT_STATE: {
     code: 403,
-    message: "Error: Component instance of %1 can not be created because the component '%2' is in an illegal state %3.",
+    message: "Error: Action is not allowed on %1 '%2' because it is in an illegal state %3.",
     messageId: "SVC4603"
   }
   #---------SVC4604------------------------------
@@ -1420,7 +1416,7 @@ errors:
   # %4 - component type [VF ]
   GROUP_INVALID_COMPONENT_INSTANCE: {
     code: 400,
-    message: "Error: member %1 listed in group %2 is not part of %3 %4.",
+    message: "Error: Member '%1' listed in group '%2' is not part of '%3' %4.",
     messageId: "SVC4616"
   }
   #---------SVC4617------------------------------
@@ -1652,7 +1648,7 @@ errors:
   # %1 - "groupName"
   INVALID_VF_MODULE_NAME: {
     code: 400,
-    message: "Error: Invalid Content. VF Module name '%1' contains invalid characters",
+    message: "Error: Invalid Content. Group name '%1' contains invalid characters",
     messageId: "SVC4646"
   }
 
@@ -1663,132 +1659,1185 @@ errors:
     message: "Error: Invalid VF Module name modification, can not modify '%1'",
     messageId: "SVC4647"
   }
+  #---------SVC4648------------------------------
+  # %1 - "inputId"
+  # %2 - "componentId"
+  INPUT_IS_NOT_CHILD_OF_COMPONENT: {
+    code: 400,
+    message: "Error: Input id: '%1' is not child of component id: '%2'",
+    messageId: "SVC4648"
+  }
+  #---------SVC4649------------------------------
+  # %1 - "groupName"
+  GROUP_HAS_CYCLIC_DEPENDENCY: {
+    code: 400,
+    message: "Error: The group '%1' has cyclic dependency",
+    messageId: "SVC4649"
+  }
+  #---------SVC4650------------------------------
+  # %1 - "Component Type"
+  # %2 - <ServiceName>
+  # %3 - error description
+  AAI_ARTIFACT_GENERATION_FAILED: {
+    code: 500,
+    message: "Error: %1 %2 automatic generation of artifacts failed. Description: %3",
+    messageId: "SVC4650"
+  }
+  #---------SVC4651------------------------------
+  PARENT_RESOURCE_DOES_NOT_EXTEND: {
+    code: 400,
+    message: "Error: Once resource is certified, derived_from can be changed only to a sibling",
+    messageId: "SVC4651"
+  }
+  #---------SVC4652------------------------------
+  # %1 - resource/service
+  COMPONENT_INVALID_SUBCATEGORY: {
+    code: 400,
+    message: "Error: Invalid Content. Invalid %1 sub category.",
+    messageId: "SVC4652"
+  }
+  #---------SVC4653------------------------------
+  # %1 - group instance uniqueId
+  # %2 - service uniqueId
+  GROUP_INSTANCE_NOT_FOUND_ON_COMPONENT_INSTANCE: {
+    code: 404,
+    message: "Error: Requested group instance %1 was not found on component %2.",
+    messageId: "SVC4653"
+  }
+  #---------SVC4654------------------------------
+  # %1 - group property name
+  # %2 - valid min limit value
+  # %3 - valid max limit value
+  INVALID_GROUP_MIN_MAX_INSTANCES_PROPERTY_VALUE: {
+    code: 400,
+    message: "Error: Value of %1 must be not higher than %2, and not lower than %3.",
+    messageId: "SVC4654"
+  }
+  #---------SVC4655------------------------------
+  # %1 - group property name
+  # %2 - valid min limit value
+  # %3 - valid max limit value
+  INVALID_GROUP_INITIAL_COUNT_PROPERTY_VALUE: {
+    code: 400,
+    message: "Error: Value of %1 must be between %2 and %3.",
+    messageId: "SVC4655"
+  }
+  #---------SVC4656------------------------------
+  # %1 - group property name
+  # %2 - lower/higher
+  # %3 - valid max/min value
+  INVALID_GROUP_PROPERTY_VALUE_LOWER_HIGHER: {
+    code: 400,
+    message: "Error: Value of %1 must be %2 or equals to %3.",
+    messageId: "SVC4656"
+  }
+  #---------SVC4657------------------------------
+  # %1 - certificationRequest / startTesting
+  RESOURCE_VFCMT_LIFECYCLE_STATE_NOT_VALID: {
+    code: 400,
+    message: "Error - Lifecycle state %1 is not valid for resource of type VFCMT",
+    messageId: "SVC4657"
+  }
+  #---------SVC4658------------------------------
+  # %1 – asset type [service / resource ]
+  # %2 – main asset uuid
+  # %3 – not found asset type [service / resource]
+  # %4 – not found asset name
+  ASSET_NOT_FOUND_DURING_CSAR_CREATION: {
+    code: 400,
+    message: "Error: CSAR packaging failed for %1 %2. %3 %4 was not found",
+    messageId: "SVC4658"
+  }
+  #---------SVC4659------------------------------
+  # %1 – asset type [service / resource ]
+  # %2 – main asset uuid
+  # %3 – Artifact name
+  # %4 – Artifact uuid
+  ARTIFACT_PAYLOAD_NOT_FOUND_DURING_CSAR_CREATION: {
+    code: 400,
+    message: "Error: CSAR packaging failed for %1 %2. Artifact %3 [%4] was not found",
+    messageId: "SVC4659"
+  }
+  #---------SVC4660------------------------------
+  # %1 - assetType
+  # %2 - matching generic node type name
+  GENERIC_TYPE_NOT_FOUND: {
+    code: 404,
+    message: "Creation of %1 failed. Generic type %2 was not found",
+    messageId: "SVC4660"
+  }
+  #---------SVC4661------------------------------
+  # %1 - assetType
+  # %2 - matching generic node type name
+  TOSCA_SCHEMA_FILES_NOT_FOUND: {
+    code: 400,
+    message: "Error: CSAR packaging failed. TOSCA schema files for SDC-Version: %1 and Conformance-Level %2 were not found",
+    messageId: "SVC4661"
+  }
+  #---------SVC4662------------------------------
+  # %1 - file name
+  # %2 - parser error
+  TOSCA_PARSE_ERROR: {
+    code: 400,
+    message: "Error: Invalid TOSCA template in file %1. %2",
+    messageId: "SVC4662"
+  }
+  #---------SVC4663------------------------------
+  # %1 - max length
+  RESOURCE_VENDOR_MODEL_NUMBER_EXCEEDS_LIMIT: {
+    code: 400,
+    message: "Error: Invalid Content. Resource vendor model number exceeds limit of %1 characters.",
+    messageId: "SVC4663"
+  }
+  #---------SVC4664------------------------------
+  INVALID_RESOURCE_VENDOR_MODEL_NUMBER: {
+    code: 400,
+    message: 'Error: Invalid Content. Resource vendor model number is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+    messageId: "SVC4664"
+  }
 
-
+  #---------SVC4669-----------------------------
+  INVALID_RESOURCE_TYPE: {
+    code: 400,
+    message: "Error: Invalid resource type.",
+    messageId: "SVC4669"
+  }
+  #---------SVC4670------------------------------
+  # %1 - artifactname
+  # %2 - validname
+  ARTIFACT_NAME_INVALID: {
+    code: 400,
+    message: "Error: Artifact name '%1' is invalid. Only the following characters are allowed in the Artifact Name: '%2'.",
+    messageId: "SVC4670"
+  }
+  #---------SVC4671------------------------------
+  # %1 - VSP name
+  # %2 - VFC name
+  CFVC_LOOP_DETECTED: {
+    code: 400,
+    message: 'Error: VSP %1 cannot be imported. The VSP contains internal loop in VFC %2',
+    messageId: "SVC4671"
+  }
+  #---------SVC4672------------------------------
+  # %1 - capability uniqueId
+  # %2 - instance uniqueId
+  # %3 - container uniqueId
+  CAPABILITY_OF_INSTANCE_NOT_FOUND_ON_CONTAINER: {
+    code: 404,
+    message: "Error: Requested capability %1 of instance %2 was not found on the container %3.",
+    messageId: "SVC4672"
+  }
   #---------SVC4673------------------------------
-  INVALID_SERVICE_STATE: {
-    code: 409,
-    message: "Error: Invalid service state. Expected state: %1, actual state: %2",
+  # %1 - requirement uniqueId
+  # %2 - instance uniqueId
+  # %3 - container uniqueId
+  REQUIREMENT_OF_INSTANCE_NOT_FOUND_ON_CONTAINER: {
+    code: 404,
+    message: "Error: Requested requirement %1 of instance %2 was not found on the container %3.",
     messageId: "SVC4673"
   }
-
-  #---------SVC4674------------------------------
-  INVALID_RESPONSE_FROM_PROXY: {
-    code: 502,
-    message: "Error: The server was acting as a gateway or proxy and received an invalid response from the upstream server",
+  #---------SVC4674-----------------------------
+  # %1 - relation Id
+  # %2 - container uniqueId
+  RELATION_NOT_FOUND: {
+    code: 404,
+    message: "Error: Requested relation %1 was not found on the container %2.",
     messageId: "SVC4674"
   }
+
+
   #---------SVC4675------------------------------
-  # %1 - resource Id
-  CAPABILITY_NOT_FOUND: {
-    code: 400,
-    message: "Error: Capability not found in the resource '%1'.",
+  INVALID_SERVICE_STATE: {
+    code: 409,
+    message: "Service state is invalid for this action",
     messageId: "SVC4675"
   }
 
   #---------SVC4676------------------------------
-  # %1 - resource Id
-  REQUIREMENT_NOT_FOUND: {
-    code: 400,
-    message: "Error: Requirement not found in the resource '%1'.",
+  INVALID_RESPONSE_FROM_PROXY: {
+    code: 502,
+    message: "Error: The server was acting as a gateway or proxy and received an invalid response from the upstream server",
     messageId: "SVC4676"
   }
-  #---------SVC4677-----------------------------
-  CAPABILITY_NAME_MANDATORY: {
+
+  #---------SVC4677------------------------------
+  API_RESOURCE_NOT_FOUND: {
     code: 404,
-    message: "Error: Capability name is mandatory, Capability name can't be empty.",
+    message: "Error: Requested '%1' was not found.",
     messageId: "SVC4677"
   }
-  #---------SVC4678-----------------------------
-  CAPABILITY_TYPE_MANDATORY: {
-    code: 404,
-    message: "Error: Capability type is mandatory, Capability type can't be empty.",
+
+  #---------SVC4678------------------------------
+  BAD_REQUEST_MISSING_RESOURCE: {
+    code: 400,
+    message: "Error: The required resource name/id  is missing in the request",
     messageId: "SVC4678"
   }
-  #---------SVC4679-----------------------------
-  # %1 - Capability Name
-  CAPABILITY_NAME_ALREADY_IN_USE: {
+  #---------SVC4679------------------------------
+  # %1 forwarding path name maximum length
+  FORWARDING_PATH_NAME_MAXIMUM_LENGTH: {
     code: 400,
-    message: "Error: Capability name '%1' already in use, Your current changes will not be saved.",
+    message: "Forwarding path name too long, , maximum allowed 200 characters : '%1'.",
     messageId: "SVC4679"
   }
-
-  #---------SVC4680-----------------------------
-  REQUIREMENT_NAME_MANDATORY: {
-    code: 404,
-    message: "Error: Requirement name is mandatory, Requirement name can't be empty.",
+  #---------SVC4680------------------------------
+  # %1 Forwarding path name already in use
+  FORWARDING_PATH_NAME_ALREADY_IN_USE: {
+    code: 400,
+    message: "Forwarding path name already in use : '%1'.",
     messageId: "SVC4680"
   }
-  #---------SVC4681-----------------------------
-  REQUIREMENT_CAPABILITY_MANDATORY: {
-    code: 404,
-    message: "Error: Requirement capability is mandatory, Requirement capability can't be empty.",
+  #---------SVC4681------------------------------
+  # %1 Forwarding path name empty
+  FORWARDING_PATH_NAME_EMPTY: {
+    code: 400,
+    message: "Forwarding Path Name can't be empty .",
     messageId: "SVC4681"
   }
-  #---------SVC4682-----------------------------
-  # %1 - Requirement Name
-  REQUIREMENT_NAME_ALREADY_IN_USE: {
+  #---------SVC4682------------------------------
+  # %1 - resource uniqueId
+  # %2 - resource component type
+  RESOURCE_CANNOT_CONTAIN_POLICIES: {
     code: 400,
-    message: "Error: Requirement name '%1' already in use, Your current changes will not be saved.",
+    message: "Error: The resource %1 type of %2 cannot contain policies.",
     messageId: "SVC4682"
   }
-  #---------SVC4683-----------------------------
-  MAX_OCCURRENCES_SHOULD_BE_GREATER_THAN_MIN_OCCURRENCES: {
-    code: 400,
-    message: "Error: maxOccurrences should be greater than minOccurrences",
+  #---------SVC4683------------------------------
+  # %1 - policy uniqueId
+  # %2 - component uniqueId
+  POLICY_NOT_FOUND_ON_CONTAINER: {
+    code: 404,
+    message: "Error: Requested policy %1 was not found on the container %2.",
     messageId: "SVC4683"
   }
-  #---------SVC4684-----------------------------
-  # %1 - Capability name
-  CAPABILITY_DELETION_NOT_ALLOWED_USED_IN_COMPOSITION: {
-    code: 409,
-    message: "Error: Capability '%1' can't be deleted,  since it is used in service composition",
+  #---------SVC4684------------------------------
+  # %1 - policy name
+  INVALID_POLICY_NAME: {
+    code: 400,
+    message: "Error: Invalid policy name %1 received.",
     messageId: "SVC4684"
   }
-  #---------SVC4685-----------------------------
-  # %1 - Requirement name
-  REQUIREMENT_DELETION_NOT_ALLOWED_USED_IN_COMPOSITION: {
+  #---------SVC4801------------------------------
+  # %1 - policy type
+  POLICY_TYPE_IS_INVALID: {
+    code: 400,
+    message: "Error: Invalid content. Policy type %1 does not exist",
+    messageId: "SVC4801"
+  }
+
+  #---------SVC4802------------------------------
+  # %1 - policy name
+  POLICY_MISSING_POLICY_TYPE: {
+    code: 400,
+    message: "Error: Invalid content. Type name is not defined for policy %1",
+    messageId: "SVC4802"
+  }
+
+  #---------SVC4685------------------------------
+  # %1 - policy name
+  POLICY_NAME_ALREADY_EXIST: {
     code: 409,
-    message: "Error: Requirement '%1' can't be deleted,  since it is used in service composition",
+    message: "Error: The policy with the name %1 already exists.",
     messageId: "SVC4685"
   }
-  #---------SVC4686-----------------------------
-  CAPABILITY_UPDATE_NOT_ALLOWED_USED_IN_COMPOSITION: {
-    code: 409,
-    message: "Error: Capability can't be updated,  since it is used in service composition",
+  #---------SVC4686------------------------------
+  # %1 - policy name
+  POLICY_TARGET_DOES_NOT_EXIST: {
+    code: 400,
+    message: "Error: The targets %1 are not valid, all targets have to be on the topologyTemplate.",
     messageId: "SVC4686"
   }
-
-  #---------SVC4687-----------------------------
-  REQUIREMENT_UPDATE_NOT_ALLOWED_USED_IN_COMPOSITION: {
-    code: 409,
-    message: "Error: Requirement can't be updated,  since it is used in service composition",
+  #---------SVC4687------------------------------
+  # %1 - policy type
+  # %2 - component type
+  EXCLUDED_POLICY_TYPE: {
+    code: 400,
+    message: "Error: The policy of the type %1 excluded to add to a component of the type %2.",
     messageId: "SVC4687"
   }
-  #---------SVC4688-----------------------------
-  CAPABILITY_TYPE_CANNOT_BE_EMPTY: {
-    code: 500,
-    message: "Error: Capability types are empty. Please import the capability types.",
+  #---------SVC4688------------------------------
+  # %1 - group type
+  # %2 - component type
+  GROUP_TYPE_ILLEGAL_PER_COMPONENT: {
+    code: 400,
+    message: "Error: group type %1 not permitted in component of type %2",
     messageId: "SVC4688"
   }
-  #---------SVC4689-----------------------------
-  RELATIONSHIP_TYPE_ALREADY_EXIST: {
-    code: 409,
-    message: "Error: Relationship Type with name '%1' already exists.",
+  #---------SVC4689------------------------------
+  # %1 - group type
+  # %2 - component type
+  POLICY_TARGET_TYPE_DOES_NOT_EXIST: {
+    code: 400,
+    message: "Error: The target types %1 are not valid.",
     messageId: "SVC4689"
   }
-  #---------SVC4690-----------------------------
-  # %1 - Requirement name
-  INVALID_REQUIREMENT_NAME: {
+
+  #---------SVC4690------------------------------
+  # %1 forwarding path protocol maximum length
+  FORWARDING_PATH_PROTOCOL_MAXIMUM_LENGTH: {
     code: 400,
-    message: "Requirement name '%1' is invalid, Only alphanumeric chars, underscore and dot allowed",
+    message: "Forwarding path protocol too long, , maximum allowed 200 characters : '%1'.",
     messageId: "SVC4690"
   }
 
-  #---------SVC4691-----------------------------
-  # %1 - Capability name
-  INVALID_CAPABILITY_NAME: {
+  #---------SVC4691------------------------------
+  # %1 forwarding path destination port maximum length
+  FORWARDING_PATH_DESTINATION_PORT_MAXIMUM_LENGTH: {
     code: 400,
-    message: "Capability name '%1' is invalid, Only alphanumeric chars, underscore and dot allowed",
+    message: "Forwarding path destination port too long, , maximum allowed 200 characters : '%1'.",
     messageId: "SVC4691"
   }
+
+  #---------POL4692------------------------------
+  MISSING_OLD_COMPONENT_INSTANCE: {
+    code: 400  ,
+    message: "Error: Missing 'componentInstanceId' HTTP param.",
+    messageId: "POL4692"
+  }
+
+  #---------POL4693------------------------------
+  MISSING_NEW_COMPONENT_INSTANCE: {
+    code: 400  ,
+    message: "Error: Missing 'newComponentInstanceId' HTTP param.",
+    messageId: "POL4693"
+  }
+
+  #---------SVC4694------------------------------
+  # %1 External Reference Value
+  EXT_REF_NOT_FOUND: {
+    code: 404,
+    message: "Error: External Reference '%1' was not found.",
+    messageId: "SVC4694"
+  }
+  #---------SVC4695-----------------------------
+  # %1 - Interface Operation Name
+  INTERFACE_OPERATION_NAME_ALREADY_IN_USE: {
+    code: 409,
+    message: "Error: Interface Operation name '%1' already in use, Your current changes will not be saved.",
+    messageId: "SVC4695"
+  }
+  #---------SVC4696-----------------------------
+  # %1 - Interface Operation Name
+  INTERFACE_OPERATION_NAME_INVALID: {
+    code: 400,
+    message: "Error: Interface Operation name '%1' is Invalid, Operation name should not contain special character, space and should not be greater than 200 characters.",
+    messageId: "SVC4696"
+  }
+  #---------SVC4697-----------------------------
+  INTERFACE_OPERATION_NAME_MANDATORY: {
+    code: 400,
+    message: "Error: Interface Operation name is mandatory, Operation name can't be empty.",
+    messageId: "SVC4697"
+  }
+  #---------SVC4698-----------------------------
+  # %1 - Interface type
+  INTERFACE_OPERATION_INVALID_FOR_LOCAL_TYPE: {
+    code: 400,
+    message: "Error: Invalid input, only one operation is allowed in local interface type '%1'.",
+    messageId: "SVC4698"
+  }
+  #---------SVC4699-----------------------------
+  # %1 - Interface Operation input parameter name
+  INTERFACE_OPERATION_INPUT_NAME_ALREADY_IN_USE: {
+    code: 400,
+    message: "Error: Interface Operation input parameter name '%1' already in use, Your current changes will not be saved.",
+    messageId: "SVC4699"
+  }
+  #---------SVC4700-----------------------------
+  INTERFACE_OPERATION_INPUT_NAME_MANDATORY: {
+    code: 400,
+    message: "Error: Interface operation input parameter name should not be empty.",
+    messageId: "SVC4700"
+  }
+  #---------SVC4701-----------------------------
+  # %1 - component Id
+  INTERFACE_OPERATION_NOT_FOUND: {
+    code: 404,
+    message: "Error: Interface operation not found in the component '%1'.",
+    messageId: "SVC4701"
+  }
+  #---------SVC4702-----------------------------
+  INTERFACE_OPERATION_NOT_DELETED: {
+    code: 400,
+    message: "Error: Failed to delete interface operation.",
+    messageId: "SVC4702"
+  }
+  #SVC4732
+  INTERFACE_UNKNOWN: {
+    code: 400,
+    message: "Error: The interface '%1' does not exists in the database.",
+    messageId: "SVC4732"
+  }
+  #SVC4733
+  INTERFACE_OPERATION_NOT_DEFINED: {
+    code: 400,
+    message: "Error: The operation '%1' does not exists in the interface '%2'.",
+    messageId: "SVC4733"
+  }
+  #-----------SVC4692---------------------------
+  RESOURCE_LIFECYCLE_STATE_NOT_VALID: {
+    code: 400,
+    message: "Error: Lifecycle state %1 is not valid for resource",
+    messageId: "SVC4692"
+  }
+  #-----------SVC4693---------------------------
+  #%1 - component name
+  COMPONENT_IS_ARCHIVED: {
+    code: 400,
+    message: "Error: Component %1 is archived",
+    messageId: "SVC4693"
+  }
+  #-----------SVC4703---------------------------
+  #%1 - component name
+  COMPONENT_IS_NOT_HIHGEST_CERTIFIED: {
+    code: 400,
+    message: "Error: Component %1 is not highest certified",
+    messageId: "SVC4703"
+  }
+  #---------SVC4704------------------------------
+  # %1 - "service"/"VF"
+  # %2 - "Resource name"
+  ARCHIVED_ORIGINS_FOUND: {
+    code: 403,
+    message: "Error: Action is not permitted as your '%1' '%2' includes archived resources",
+    messageId: "SVC4704"
+  }
+  #---------SVC4705------------------------------
+  # %1-artifact name
+  ARTIFACT_PAYLOAD_EMPTY: {
+    code: 400,
+    message: "Error: Invalid content. Uploaded file %1 is empty. Please retry with the correct file.",
+    messageId: "SVC4705"
+  }
+  #---------SVC4800------------------------------
+  # %1 - "component id"
+  UPDATE_CATALOG_FAILED: {
+    code: 403,
+    message: "Error: update catalog for component '%1' failed.",
+    messageId: "SVC4800"
+  }
+  #---------SVC4706------------------------------
+  # %1-input(s) name(s) string
+  INPUTS_NOT_FOUND: {
+    code: 400,
+    message: "Error: missing input value(s) %1.",
+    messageId: "SVC4706"
+  }
+  #---------SVC4707-----------------------------
+  # %1 – asset type [service / resource ]
+  # %2 – main asset uuid
+  ERROR_DURING_CSAR_CREATION: {
+    code: 404,
+    message: "Error: CSAR packaging failed for %1 %2.",
+    messageId: "SVC4706"
+  }
+  #---------SVC4708-----------------------------
+  # %1 - Interface Operation input property name, component type
+  INTERFACE_OPERATION_INPUT_PROPERTY_NOT_FOUND_IN_COMPONENT: {
+    code: 404,
+    message: "Error: Interface operation input parameter property '%1' not found in '%2' input properties, capability properties or outputs of other operations.",
+    messageId: "SVC4708"
+  }
+  #---------SVC4709-----------------------------
+  # %1 - Interface Operation output parameter name
+  INTERFACE_OPERATION_OUTPUT_NAME_ALREADY_IN_USE: {
+    code: 400,
+    message: "Error: Interface Operation output parameter name '%1' already in use, Your current changes will not be saved.",
+    messageId: "SVC4708"
+  }
+  #---------SVC4710-----------------------------
+  INTERFACE_OPERATION_OUTPUT_NAME_MANDATORY: {
+    code: 400,
+    message: "Error: Interface operation output parameter name should not be empty.",
+    messageId: "SVC4710"
+  }
+  #---------SVC4711-----------------------------
+  # %1 - interface Id
+  INTERFACE_NOT_FOUND_IN_COMPONENT: {
+    code: 404,
+    message: "Error: Interface not found in the component '%1'.",
+    messageId: "SVC4711"
+  }
+  #---------SVC4709-----------------------------
+  INVALID_PROPERTY_CONSTRAINTS: {
+    # %1 – property constraints type
+    # %2 – received property constraints value
+    # %3 – property type
+    code: 400,
+    message: "Error: Invalid %1 %2 for the type %3 have been received.",
+    messageId: "SVC4709"
+  }
+  #---------SVC4710-----------------------------
+  INVALID_PROPERTY_CONSTRAINTS_FORMAT: {
+    # %1 – received property constraints json section
+    code: 400,
+    message: "Error: Invalid format of the received property constraints section: %1. The property constraints section should be a list.",
+    messageId: "SVC4710"
+  }
+  #---------SVC4711-----------------------------
+  CANNOT_DELETE_VALID_VALUES: {
+    # %1 – property constraints type
+    # %2 – missing valid values
+    code: 400,
+    message: "Error: Deletion of existing %1 is not permitted on an update. Missing values: %2",
+    messageId: "SVC4711"
+  }
+  #---------SVC4712------------------------------
+  MISSING_PROPERTY_NAME: {
+    code: 400  ,
+    message: "Error: Invalid Content. Missing mandatory parameter 'name'." ,
+    messageId: "SVC4712"
+  }
+  #---------SVC4713------------------------------
+  MISSING_PROPERTY_VALUE: {
+    code: 400  ,
+    message: "Error: Invalid Content. Missing mandatory parameter 'value'." ,
+    messageId: "SVC4713"
+  }
+
+  #---------SVC4712---------------------------
+  INVALID_INSTANTIATION_TYPE: {
+    code: 400,
+    message: "Invalid instantiationType: %1",
+    messageId: "SVC4712"
+  }
+
+  #----------SVC4713---------------------------
+  MISSING_ECOMP_GENERATED_NAMING: {
+    code: 400,
+    message: "Missing ecompGeneratedNaming property",
+    messageId: "SVC4713"
+  }
+
+  #-----------SVC4714--------------------------
+  NAMING_POLICY_EXCEEDS_LIMIT: {
+    code: 400,
+    message: "Error: Invalid Content. Naming policy exceeds limit of %1 characters.",
+    messageId: "SVC4714"
+  }
+
+  #---------SVC4715------------------------------
+  INVALID_NAMING_POLICY: {
+    code: 400,
+    message: 'Error: Invalid Content. Naming policy is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+    messageId: "SVC4715"
+  }
+
+  #---------SVC4716------------------------------
+  INVALID_ENVIRONMENT_CONTEXT: {
+    code: 400,
+    message: 'Error: Invalid Environment context: %1',
+    messageId: "SVC4716"
+  }
+
+  #---------SVC4717------------------------------
+  UNSUPPORTED_DISTRIBUTION_STATUS: {
+    code: 400,
+    message: 'Error: Unsupported distribution action: %1',
+    messageId: "SVC4717"
+  }
+  #---------SVC4718------------------------------
+  CONTAINER_CANNOT_CONTAIN_INSTANCE: {
+    # %1 - "container type"
+    # %2- “component type”
+    code: 400  ,
+    message: "Error : %1 can’t contain component instance %2" ,
+    messageId: "SVC4718"
+  }
+  #---------SVC4719------------------------------
+  CONTAINER_CANNOT_CONTAIN_COMPONENT_IN_STATE: {
+    # %1 - "container type"
+    # %2- "lifecycle state"
+    code: 400  ,
+    message: "Error: Container %1 can’t contain component in state %2" ,
+    messageId: "SVC4719"
+  }
+
+  #---------SVC4720------------------------------
+  MISSING_MANDATORY_PROPERTY: {
+    # %1 - "property name"
+    code: 400  ,
+    message: "Error: Missing mandatory %1 property" ,
+    messageId: "SVC4720"
+  }
+
+  #---------SVC4721------------------------------
+  MANDATORY_PROPERTY_MISSING_VALUE: {
+    # %1 - "property name"
+    code: 400  ,
+    message: "Error: Missing value for the mandatory %1 property" ,
+    messageId: "SVC4721"
+  }
+  #---------SVC4712-----------------------------
+  INTERFACE_LIFECYCLE_TYPES_NOT_FOUND: {
+    code: 404,
+    message: "Error: Interface Lifecycle types not found.",
+    messageId: "SVC4712"
+  }
+  #---------SVC4713-----------------------------
+  # %1 - Interface Operation Name
+  INTERFACE_OPERATION_INVALID_FOR_GLOBAL_TYPE: {
+    code: 400,
+    message: "Error: Invalid input, only pre-defined operation names are allowed in global interface type '%1'",
+    messageId: "SVC4713"
+  }
+
+  #---------SVC4714-----------------------------
+  NODE_FILTER_NOT_FOUND: {
+    code: 400,
+    message: "Error: Node Filter was not found",
+    messageId: "SVC4714"
+  }
+  #---------SVC4715----------------------------
+  UNSUPPORTED_VALUE_PROVIDED: {
+    code: 400,
+    message: "Error: Supported value type is %1 for %2 property. Provided Value: %3",
+    messageId: "SVC4715"
+  }
+  #---------SVC4716----------------------------
+  # %1 - Property Name
+  SELECTED_PROPERTY_NOT_PRESENT: {
+    code: 400,
+    message: "Error: %1 property does not exists in Service anymore.",
+    messageId: "SVC4716"
+  }
+
+  #---------SVC4184----------------------------
+  # %1 - Source type
+  # %2 - Property Type
+  # %3 - Property Name
+  FILTER_PROPERTY_NOT_FOUND:
+    code: 400
+    message: "%1 %2 %3 does not exist."
+    messageId: "SVC4184"
+
+  #---------SVC4718----------------------------
+  # %1 - Property Name
+  # %2 - Operator Name
+  UNSUPPORTED_OPERATOR_PROVIDED: {
+    code: 400,
+    message: "Error: %1 property does not support %2 operator.",
+    messageId: "SVC4718"
+  }
+
+  #---------SVC4719----------------------------
+  CONSTRAINT_FORMAT_INCORRECT: {
+    code: 400,
+    message: "Error: Constraint provided does not contains expected values.",
+    messageId: "SVC4719"
+  }
+
+  #---------SVC4182----------------------------
+  # %1 - Property Name
+  # %2 - Operator Type
+  SOURCE_TARGET_PROPERTY_TYPE_MISMATCH: {
+    code: 400,
+    message: "Error: Property '%1' type '%2' does not match with property '%3' type '%4'.",
+    messageId: "SVC4182"
+  }
+
+  #---------SVC4721----------------------------
+  # %1 - Property Type
+  # %2 - Operator Type
+  UNSUPPORTED_PROPERTY_TYPE: {
+    code: 400,
+    message: "Error: Property type %1 provided against %2 is not supported for static value.",
+    messageId: "SVC4721"
+  }
+
+  #---------SVC4722------------------------------
+  # %1 Directive value set
+  DIRECTIVES_INVALID_VALUE: {
+    code: 404,
+    message: "Error: Invalid directive value : '%1' .",
+    messageId: "SVC4722"
+  }
+  #---------SVC4723-----------------------------
+  # %1 - Interface Operation output name
+  INTERFACE_OPERATION_MAPPED_OUTPUT_MODIFIED: {
+    code: 400,
+    message: "Error: Cannot update or delete interface operation output(s) '%1' mapped to an operation input",
+    messageId: "SVC4723"
+  }
+  #---------SVC4724-----------------------------
+  # %1 - Interface Operation output name
+  INTERFACE_OPERATION_DELETE_WITH_MAPPED_OUTPUT: {
+    code: 400,
+    message: "Error: Cannot delete interface operation with output(s) '%1' mapped to another operation input",
+    messageId: "SVC4724"
+  }
+  #---------SVC4725-----------------------------
+  INVALID_CONSUMPTION_TYPE: {
+    code: 400,
+    message: "Error: Given value is different than input type. Needs to be %1",
+    messageId: "SVC4725"
+  }
+  #---------SVC4726-----------------------------
+  INVALID_PROPERTY_VALUES: {
+    code: 400,
+    message: "Error: Invalid property values provided:\n %1",
+    messageId: "SVC4726"
+  }
+  #---------SVC4727------------------------------
+  INVALID_PROPERTY_NAME: {
+    code: 400,
+    message: "Error: Property name contains invalid characters. It should have only letters, numbers and underscores.",
+    messageId: "SVC4727"
+  }
+
+  #---------SVC4728------------------------------
+  FAILED_TO_CREATE_OR_UPDATE_CAPABILITY_PROPERTIES: {
+    code: 500,
+    message: "Error: Failed to create or update capabilities properties",
+    messageId: "SVC4728"
+  }
+
+  #---------SVC4729------------------------------
+  # %1 - resource Id
+  CAPABILITY_PROPERTIES_NOT_FOUND: {
+    code: 400,
+    message: "Error: Capability properties not found in the resource '%1'.",
+    messageId: "SVC4729"
+  }
+  #---------SVC4730------------------------------
+  # %1 - property name
+  PROPERTY_EXCEEDS_LIMIT: {
+    code: 400,
+    message: "Error: Invalid Content. %1 exceeds limit.",
+    messageId: "SVC4722"
+  }
+  #---------SVC4731------------------------------
+  INVALID_PROPERY: {
+    # %1 - property name
+    code: 400,
+    message: 'Error: Invalid Content. %1 has invalid format.',
+    messageId: "SVC4723"
+  }
+  #---------SVC4734------------------------------
+  # %1 - list of validation errors
+  INVALID_PM_DICTIONARY_FILE: {
+    code: 400,
+    message: 'Error: Invalid PM Dictionary File. %1',
+    messageId: "SVC4734"
+  }
+  #-----------SVC4735---------------------------
+  #%1 - input name
+  INPUT_ALREADY_EXIST: {
+    code: 409,
+    message: "Error: Input with '%1' name already exists.",
+    messageId: "SVC4735"
+  }
+  #---------SVC4736------------------------------
+  INVALID_INPUT_NAME: {
+    code: 400,
+    message: "Error: Input name contains invalid characters. It should have only letters, numbers and underscores.",
+    messageId: "SVC4736"
+  }
+  #---------SVC4139------------------------------
+  # %1 - The action that is not supported
+  NOT_SUPPORTED: {
+    code: 400,
+    message: '%1 is not yet supported',
+    messageId: "SVC4139"
+  }
+  #---------SVC4140------------------------------
+  # %1 - Component uid
+  COMPONENT_FIND_ERROR: {
+    code: 500,
+    message: "An unexpected error occurred while retrieving the component '%1'.",
+    messageId: "SVC4140"
+  }
+  #---------SVC4141------------------------------
+  # %1 - Component uid
+  COMPONENT_CAPABILITIES_FIND_ERROR: {
+    code: 500,
+    message: "An unexpected error occurred while retrieving the component '%1' capabilities.",
+    messageId: "SVC4141"
+  }
+  #---------SVC4142------------------------------
+  # %1 - Component uid or name
+  COMPONENT_NOT_FOUND: {
+    code: 404,
+    message: "Component '%1' was not found.",
+    messageId: "SVC4142"
+  }
+  #---------SVC4143------------------------------
+  # %1 - Capability name
+  COMPONENT_INSTANCE_CAPABILITY_UPDATE_ERROR: {
+    code: 500,
+    message: "An unexpected error occurred while updating the capability '%1'.",
+    messageId: "SVC4143"
+  }
+
+  #---------SVC4144------------------------------
+  # %1 - "Model name"
+  MODEL_ALREADY_EXISTS: {
+    code: 409,
+    message: "Error: Model name '%1' already exists.",
+    messageId: "SVC4144"
+  }
+
+  #---------SVC4145------------------------------
+  # %1 - "Model name"
+  INVALID_MODEL: {
+    code: 400,
+    message: "Invalid model '%1'.",
+    messageId: "SVC4145"
+  }
+
+  #---------SVC4146------------------------------
+  MODEL_IMPORTS_IS_EMPTY: {
+    code: 400,
+    message: "Given model imports zip is empty.",
+    messageId: "SVC4146"
+  }
+
+  #---------SVC4147------------------------------
+  COULD_NOT_READ_MODEL_IMPORTS: {
+    code: 400,
+    message: "Could not read imports zip.",
+    messageId: "SVC4147"
+  }
+
+  #---------SVC4148------------------------------
+  # %1 - "Model name"
+  MODEL_NOT_FOUND: {
+    code: 404,
+    message: "Error: Model name '%1' not found. Please, make sure the model is created.",
+    messageId: "SVC4148"
+  }
+
+  #---------SVC4149------------------------------
+  MODEL_NAME_CANNOT_BE_EMPTY: {
+    code: 409,
+    message: "Error: Model name cannot be empty.",
+    messageId: "SVC4149"
+  }
+
+  #-----------SVC4150---------------------------
+  # %1 - "Component name"
+  # %2 - "Model name"
+  COMPONENT_WITH_MODEL_ALREADY_EXIST: {
+    code: 409,
+    message: "Error: Component %1 with Model %2 already exist.",
+    messageId: "SVC4150"
+  }
+  #-----------SVC4151---------------------------
+  # %1 - "Component name"
+  # %2 - "Vendor release"
+  # %3 - "Model name"
+  COMPONENT_WITH_VENDOR_RELEASE_ALREADY_EXISTS_IN_MODEL: {
+    code: 409,
+    message: "Error: Component '%1' with Vendor Release '%2' already exists in Model '%3'.",
+    messageId: "SVC4151"
+  }
+  #-----------SVC4152---------------------------
+  # %1 - "Component name"
+  # %2 - "Vendor release"
+  COMPONENT_WITH_VENDOR_RELEASE_ALREADY_EXISTS: {
+    code: 409,
+    message: "Error: Component '%1' with Vendor Release '%2' already exists.",
+    messageId: "SVC4152"
+  }
+
+  #-----------SVC4153---------------------------
+  # %1 - "Model name"
+  DATA_TYPES_NOT_LOADED: {
+    code: 500,
+    message: "Could not fetch data types from data base with model %1",
+    messageId: "SVC4153"
+  }
+
+  #-----------SVC4154---------------------------
+  # %1 - "Model name"
+  UNKNOWN_MODEL_TYPE: {
+    code: 400,
+    message: "Error: Model type  %1 not known in the system",
+    messageId: "SVC4154"
+  }
+
+  #-----------SVC4154---------------------------
+  CSAR_TOSCA_IMPORTS_ERROR: {
+    code: 500,
+    message: "Error: An error has occurred while including the default TOSCA imports in the CSAR",
+    messageId: "SVC4154"
+  }
+
+  #-----------SVC4155---------------------------
+  # %1 - "VSP id"
+  # %2 - "VSP version id"
+  VSP_FIND_ERROR: {
+    code: 500,
+    message: "An error has occurred while retrieving the Vendor Software Product of id '%1', version id '%2'",
+    messageId: "SVC4155"
+  }
+
+  #-----------SVC4156---------------------------
+  # %1 - "VSP id"
+  # %2 - "VSP version id"
+  VSP_NOT_FOUND: {
+    code: 404,
+    message: "Could not find Vendor Software Product of id '%1', version id '%2'",
+    messageId: "SVC4156"
+  }
+
+  #-----------SVC4157---------------------------
+  # %1 - "The model name"
+  # %2 - "List of allowed models"
+  VSP_MODEL_NOT_ALLOWED: {
+    code: 400,
+    message: "The Model '%1' is not allowed for the imported Vendor Software Product. Allowed Models: '%2'",
+    messageId: "SVC4157"
+  }
+
+  #---------SVC4158-----------------------------
+  # %1 - Valid artifact label name
+  INVALID_ARTIFACT_LABEL_NAME: {
+    code: 400,
+    message: "Invalid label name. Only the following characters are allowed in label name: '%1'",
+    messageId: "SVC4158"
+  }
+
+  #---------SVC4159-----------------------------
+  # %1 - The model name
+  COULD_NOT_DELETE_MODEL: {
+    code: 500,
+    message: "Could not delete the model '%1'.",
+    messageId: "SVC4159"
+  }
+
+  #---------SVC4160-----------------------------
+  # %1 - The model name
+  COULD_NOT_DELETE_MODEL_ELEMENTS: {
+    code: 500,
+    message: "Could not delete the model '%1' elements.",
+    messageId: "SVC4160"
+  }
+  #---------SVC4161-----------------------------
+  INVALID_NODE_TYPES_YAML: {
+    code: 400,
+    message: "Invalid node_types TOSCA yaml",
+    messageId: "SVC4161"
+  }
+
+  #---------SVC4162-----------------------------
+  # %1 - The janusgraph status
+  FAILED_CREATE_ARTIFACTS_TYPES: {
+    code: 500,
+    message: "Failed to create artifact types with status '%1'.",
+    messageId: "SVC4162"
+  }
+
+  #---------SVC4163-----------------------------
+  # %1 - The janusgraph status
+  ARTIFACT_TYPE_ALREADY_EXIST: {
+    code: 409,
+    message: "Artifact type '%1' already exist.",
+    messageId: "SVC4163"
+  }
+
+  #---------SVC4692-----------------------------
+  # %1 - Component name
+  COMPONENT_NOT_ARCHIVED: {
+    code: 403,
+    message: "Component '%1' is not archived",
+    messageId: "SVC4692"
+  }
+
+  #---------SVC4693-----------------------------
+  # %1 - List of services
+  COMPONENT_IN_USE_BY_ANOTHER_COMPONENT: {
+    code: 403,
+    message: "Component is in use by '%1'",
+    messageId: "SVC4693"
+  }
+
+  #---------SVC4164-----------------------------
+  # %1 - componentType
+  # %2 - component name
+  CANNOT_DELETE_SYSTEM_DEPLOYED_RESOURCES: {
+    code: 409,
+    message: "System deployed '%1' cannot be deleted '%2'",
+    messageId: "SVC4164"
+  }
+
+  #---------SVC4165-----------------------------
+  # %1 - input origin
+  TOSCA_GET_FUNCTION_INPUTS_ONLY_SELF_ERROR: {
+    code: 400,
+    message: "Invalid get_input definition. Inputs can only be get from SELF, the given origin was '%1'",
+    messageId: "SVC4165"
+  }
+
+  #---------SVC4166-----------------------------
+  # %1 - input name
+  # %2 - input origin
+  TOSCA_GET_FUNCTION_INPUTS_NOT_FOUND: {
+    code: 404,
+    message: "The given input '%1' was not found on '%2'.",
+    messageId: "SVC4166"
+  }
+
+  #---------SVC4167-----------------------------
+  # %1 - Tosca function
+  # %2 - Referred input type
+  # %3 - Selected property type
+  TOSCA_GET_FUNCTION_TYPE_DIVERGE: {
+    code: 400,
+    message: "Could not set %1. Referred input type '%2' diverges from the selected property type '%3'.",
+    messageId: "SVC4167"
+  }
+
+  #---------SVC4168-----------------------------
+  # %1 - Tosca function
+  # %2 - Referred input schema
+  # %3 - Selected property schema
+  TOSCA_GET_FUNCTION_SCHEMA_DIVERGE: {
+    code: 400,
+    message: "Could not set %1. Referred input schema '%2' diverges from the selected property schema '%3'.",
+    messageId: "SVC4168"
+  }
+
+  #---------SVC4169-----------------------------
+  # %1 - Property type (property|input|attribute)
+  # %1 - Property name
+  # %2 - Property origin
+  TOSCA_GET_FUNCTION_PROPERTY_NOT_FOUND: {
+    code: 404,
+    message: "The given %1 '%2' was not found on '%3'.",
+    messageId: "SVC4169"
+  }
+
+  #---------SVC4170-----------------------------
+  # %1 - Property type (property|input|attribute)
+  # %2 - Property name/path
+  # %3 - Property data type
+  TOSCA_GET_FUNCTION_PROPERTY_DATA_TYPE_NOT_FOUND: {
+    code: 404,
+    message: "The %1 '%2' type '%3' was not found.",
+    messageId: "SVC4170"
+  }
+
+  #---------SVC4171-----------------------------
+  # %1 - Instance name
+  TOSCA_GET_FUNCTION_INSTANCE_NOT_FOUND: {
+    code: 404,
+    message: "The instance '%1' was not found.",
+    messageId: "SVC4171"
+  }
+
+  #-----------SVC4172---------------------------
+  #%1 - TOSCA function attribute
+  TOSCA_FUNCTION_MISSING_ATTRIBUTE: {
+    code: 400,
+    message: "Missing TOSCA function '%1'.",
+    messageId: "SVC4172"
+  }
+
+  #-----------SVC4173---------------------------
+  RELATIONSHIP_TEMPLATE_NOT_FOUND: {
+    code: 404,
+    message: "Relationship_templates entry not found in TOSCA CSAR.",
+    messageId: "SVC4173"
+  }
+
+  #-----------SVC4174---------------------------
+  RELATIONSHIP_TEMPLATE_DEFINITION_NOT_FOUND: {
+    code: 404,
+    message: "Relationship_templates definition not found in TOSCA CSAR.",
+    messageId: "SVC4174"
+  }
+
+  #-----------SVC4175---------------------------
+  TOSCA_FUNCTION_EXPECTED_ERROR: {
+    code: 400,
+    message: "Expecting a Tosca Function value.",
+    messageId: "SVC4175"
+  }
+
+  #-----------SVC4176---------------------------
+  FILTER_CONSTRAINT_MISSING: {
+    code: 400,
+    message: "The filter constraint was not provided.",
+    messageId: "SVC4176"
+  }
+
+  #-----------SVC4177---------------------------
+  #%1 - The missing field
+  FILTER_CONSTRAINT_MISSING_FIELD: {
+    code: 400,
+    message: "Required field '%1' is missing in the filter constraint.",
+    messageId: "SVC4177"
+  }
+
+  #%1 - the component id
+  SUBSTITUTION_FILTER_NOT_FOUND: {
+    code: 404,
+    message: 'Substitution filter not found in the component "%1"',
+    messageId: "SVC4178"
+  }
+
+  # %1 - Component name
+  COMPONENT_DOES_NOT_HAVE_INPUTS: {
+    code: 400,
+    message: "Component '%1' does not have inputs.",
+    messageId: "SVC4179"
+  }
+
+  # %1 - Input name
+  # %2 - Component name
+  COMPONENT_INPUT_NOT_FOUND: {
+    code: 400,
+    message: "Input '%1' does not exist in '%2'.",
+    messageId: "SVC4180"
+  }
+
+  # %1 - Target property name
+  # %2 - Source property name
+  SOURCE_TARGET_SCHEMA_MISMATCH: {
+    code: 400,
+    message: "Target property '%1' schema '%2' does not match with source property '%3' schema '%4'.",
+    messageId: "SVC4181"
+  }
+
+  # %1 - Property name
+  # %2 - Component name
+  COMPONENT_PROPERTY_NOT_FOUND:
+    code: 400
+    message: "Property '%1' does not exist in '%2'."
+    messageId: "SVC4183"
+
+  # %1 - The component type
+  # %2 - The expected component types
+  INVALID_COMPONENT_TYPE:
+    code: 400
+    message: "Invalid component type '%1'. Expected types are: %2"
+    messageId: "SVC4185"
+
+  # %1 - The capability name
+  # %2 - The component type
+  # %3 - The component name
+  CAPABILITY_NOT_FOUND_IN_COMPONENT:
+    code: 400
+    message: "Capability '%1' not found in '%2' '%3'."
+    messageId: "SVC4186"
+
+  # %1 - The data type Uid
+  DATA_TYPE_NOT_FOUND:
+    code: 404
+    message: "Data type '%1' was not found."
+    messageId: "SVC4011"
+
+  # %1 - The data type Uid
+  # %2 - The property name
+  DATA_TYPE_PROPERTY_ALREADY_EXISTS:
+    code: 409
+    message: "Data type '%1' property '%2' already exists."
+    messageId: "SVC4012"
+
+  # %1 - The operation that the error occurred
+  UNEXPECTED_ERROR:
+    code: 500
+    message: "An unexpected error occurred while %1."
+    messageId: "SVC4013"
+
index 75f8904..4be1cba 100644 (file)
@@ -2833,4 +2833,17 @@ errors:
     DATA_TYPE_NOT_FOUND:
         code: 404
         message: "Data type '%1' was not found."
-        messageId: "SVC4011"
\ No newline at end of file
+        messageId: "SVC4011"
+
+    # %1 - The data type Uid
+    # %2 - The property name
+    DATA_TYPE_PROPERTY_ALREADY_EXISTS:
+        code: 409
+        message: "Data type '%1' property '%2' already exists."
+        messageId: "SVC4012"
+
+    # %1 - The operation that the error occurred
+    UNEXPECTED_ERROR:
+        code: 500
+        message: "An unexpected error occurred while %1."
+        messageId: "SVC4013"
index e39e0d2..711ebcb 100644 (file)
@@ -22,9 +22,12 @@ package org.openecomp.sdc.be.servlets;
 
 import com.jcabi.aspects.Loggable;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
 import io.swagger.v3.oas.annotations.media.ArraySchema;
 import io.swagger.v3.oas.annotations.media.Content;
 import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.servers.Server;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -41,6 +44,7 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
 import org.openecomp.sdc.be.components.impl.aaf.AafPermission;
 import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed;
 import org.openecomp.sdc.be.config.BeEcompErrorManager;
@@ -114,9 +118,31 @@ public class DataTypeServlet extends BeGenericServlet {
         @ApiResponse(responseCode = "403", description = "Restricted operation"),
         @ApiResponse(responseCode = "404", description = "Data type not found")})
     @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
-    public Response fetchProperties(@PathParam("id") final String id) {
+    public Response fetchProperties(@Parameter(in = ParameterIn.PATH, required = true, description = "The data type id")
+                                    @PathParam("id") final String id) {
         final List<PropertyDefinition> allProperties = dataTypeOperation.findAllProperties(id);
         return buildOkResponse(allProperties);
     }
 
+    @POST
+    @Path("{id}/properties")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(summary = "Create a property in the given data type", method = "POST", description = "Create a property in the given data type",
+        responses = {
+            @ApiResponse(content = @Content(schema = @Schema(implementation = PropertyDefinitionDto.class))),
+            @ApiResponse(responseCode = "201", description = "Property created in the data type"),
+            @ApiResponse(responseCode = "400", description = "Invalid payload"),
+            @ApiResponse(responseCode = "409", description = "Property already exists in the data type"),
+            @ApiResponse(responseCode = "403", description = "Restricted operation"),
+            @ApiResponse(responseCode = "404", description = "Data type not found")
+        })
+    @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
+    public Response createProperty(@Parameter(in = ParameterIn.PATH, required = true, description = "The data type id")
+                                       @PathParam("id") final String id,
+                                   @RequestBody(description = "Property to add", required = true) final PropertyDefinitionDto propertyDefinitionDto) {
+        final PropertyDefinitionDto property = dataTypeOperation.createProperty(id, propertyDefinitionDto);
+        return Response.status(Status.CREATED).entity(property).build();
+    }
+
 }
index 6b161f9..a2a8659 100644 (file)
@@ -23,18 +23,19 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.ext.ExceptionMapper;
 import javax.ws.rs.ext.Provider;
 import org.eclipse.jetty.http.HttpStatus;
-import org.openecomp.sdc.common.log.wrappers.Logger;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
 @Component
 @Provider
 public class DefaultExceptionMapper implements ExceptionMapper<Exception> {
 
-    private static final Logger log = Logger.getLogger(DefaultExceptionMapper.class);
+    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExceptionMapper.class);
 
     @Override
-    public Response toResponse(Exception exception) {
-        log.debug("#toResponse - An error occurred: ", exception);
+    public Response toResponse(final Exception exception) {
+        LOGGER.error("An unhandled error has occurred", exception);
         return Response.status(HttpStatus.INTERNAL_SERVER_ERROR_500).entity(exception.getMessage()).build();
     }
 }
index 0830dda..ee5f7fa 100644 (file)
@@ -2834,3 +2834,17 @@ errors:
         code: 404
         message: "Data type '%1' was not found."
         messageId: "SVC4011"
+
+    # %1 - The data type Uid
+    # %2 - The property name
+    DATA_TYPE_PROPERTY_ALREADY_EXISTS:
+        code: 409
+        message: "Data type '%1' property '%2' already exists."
+        messageId: "SVC4012"
+
+    # %1 - The operation that the error occurred
+    UNEXPECTED_ERROR:
+        code: 500
+        message: "An unexpected error occurred while %1."
+        messageId: "SVC4013"
+
index d31d4a6..ad40131 100644 (file)
@@ -2820,4 +2820,24 @@ errors:
     CAPABILITY_NOT_FOUND_IN_COMPONENT:
         code: 400
         message: "Capability '%1' not found in '%2' '%3'."
-        messageId: "SVC4186"
\ No newline at end of file
+        messageId: "SVC4186"
+
+    # %1 - The data type Uid
+    DATA_TYPE_NOT_FOUND:
+        code: 404
+        message: "Data type '%1' was not found."
+        messageId: "SVC4011"
+
+    # %1 - The data type Uid
+    # %2 - The property name
+    DATA_TYPE_PROPERTY_ALREADY_EXISTS:
+        code: 409
+        message: "Data type '%1' property '%2' already exists."
+        messageId: "SVC4012"
+
+    # %1 - The operation that the error occurred
+    UNEXPECTED_ERROR:
+        code: 500
+        message: "An unexpected error occurred while %1."
+        messageId: "SVC4013"
+
index 55297f9..cd5a6e5 100644 (file)
@@ -148,5 +148,7 @@ public enum ActionStatus {
     COMPONENT_DOES_NOT_HAVE_INPUTS,
     COMPONENT_INPUT_NOT_FOUND,
     COMPONENT_PROPERTY_NOT_FOUND,
-    INVALID_COMPONENT_TYPE
+    INVALID_COMPONENT_TYPE,
+    DATA_TYPE_PROPERTY_ALREADY_EXISTS,
+    UNEXPECTED_ERROR
 }
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/exception/OperationException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/exception/OperationException.java
deleted file mode 100644 (file)
index 6a647b1..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * -
- *  ============LICENSE_START=======================================================
- *  Copyright (C) 2022 Nordix Foundation.
- *  ================================================================================
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- *  SPDX-License-Identifier: Apache-2.0
- *  ============LICENSE_END=========================================================
- */
-
-package org.openecomp.sdc.be.exception;
-
-public class OperationException extends BusinessException {
-
-    public OperationException(final String message) {
-        super(message);
-    }
-
-    public OperationException(final Throwable cause) {
-        super(cause);
-    }
-
-    public OperationException(final String message, final Throwable cause) {
-        super(message, cause);
-    }
-
-}
index 75cacdd..6b444b2 100644 (file)
@@ -24,14 +24,28 @@ package org.openecomp.sdc.be.exception.supplier;
 import java.util.function.Supplier;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
-import org.openecomp.sdc.be.exception.OperationException;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
 
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class DataTypeOperationExceptionSupplier {
 
     public static Supplier<OperationException> unexpectedErrorWhileFetchingProperties(final String uniqueId) {
-        final String errorMessage = String.format("An unexpected error has occurred while retrieving the data type '%s' properties", uniqueId);
-        return () -> new OperationException(errorMessage);
+        return () -> new OperationException(ActionStatus.UNEXPECTED_ERROR, String.format("retrieving the data type '%s' properties", uniqueId));
+    }
+
+    public static Supplier<OperationException> dataTypeNotFound(final String dataTypeId) {
+        return () -> new OperationException(ActionStatus.DATA_TYPE_NOT_FOUND, dataTypeId);
+    }
+
+    public static Supplier<OperationException> dataTypePropertyAlreadyExists(final String dataTypeId, final String propertyName) {
+        return () -> new OperationException(ActionStatus.DATA_TYPE_PROPERTY_ALREADY_EXISTS, dataTypeId, propertyName);
+    }
+
+    public static Supplier<OperationException> unexpectedErrorWhileCreatingProperty(String dataTypeId, String propertyName) {
+        return () -> new OperationException(ActionStatus.UNEXPECTED_ERROR,
+            String.format("creating the property '%s' in the data type '%s'", propertyName, dataTypeId)
+        );
     }
 
 }
index bac73d9..4144e1f 100644 (file)
@@ -30,7 +30,9 @@ public class PropertyDefinitionDto {
 
     private String uniqueId;
     private String type;
+    private String schemaType;
     private String name;
+    private String description;
     private Boolean required;
     private Object value;
     private Object defaultValue;
index 8566f55..67600a5 100644 (file)
@@ -24,12 +24,17 @@ package org.openecomp.sdc.be.model.mapper;
 import com.google.gson.Gson;
 import java.util.ArrayList;
 import java.util.List;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
 import org.openecomp.sdc.be.model.PropertyConstraint;
 import org.openecomp.sdc.be.model.PropertyDefinition;
 import org.openecomp.sdc.be.model.dto.PropertyDefinitionDto;
 
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
 public class PropertyDefinitionDtoMapper {
 
     public static PropertyDefinition mapTo(final PropertyDefinitionDto propertyDefinitionDto) {
@@ -38,11 +43,19 @@ public class PropertyDefinitionDtoMapper {
         propertyDefinition.setType(propertyDefinitionDto.getType());
         propertyDefinition.setRequired(propertyDefinitionDto.getRequired());
         propertyDefinition.setName(propertyDefinitionDto.getName());
+        if (StringUtils.isNotBlank(propertyDefinitionDto.getSchemaType())) {
+            final PropertyDefinition schemaProperty = new PropertyDefinition();
+            schemaProperty.setType(propertyDefinitionDto.getSchemaType());
+            final SchemaDefinition schema = new SchemaDefinition();
+            schema.setProperty(schemaProperty);
+            propertyDefinition.setSchema(schema);
+        }
         if (CollectionUtils.isNotEmpty(propertyDefinitionDto.getConstraints())) {
             final List<PropertyConstraint> propertyConstraints = new ArrayList<>();
             propertyDefinition.setConstraints(propertyConstraints);
             propertyConstraints.addAll(propertyDefinitionDto.getConstraints());
         }
+        propertyDefinition.setDescription(propertyDefinitionDto.getDescription());
         propertyDefinition.setValue(new Gson().toJson(propertyDefinitionDto.getValue()));
         propertyDefinition.setDefaultValue(new Gson().toJson(propertyDefinitionDto.getDefaultValue()));
         return propertyDefinition;
@@ -52,9 +65,11 @@ public class PropertyDefinitionDtoMapper {
         final var propertyDefinition = new PropertyDefinition(propertyDataDefinition);
         final var propertyDefinitionDto = new PropertyDefinitionDto();
         propertyDefinitionDto.setUniqueId(propertyDefinition.getUniqueId());
+        propertyDefinitionDto.setName(propertyDefinition.getName());
         propertyDefinitionDto.setType(propertyDefinition.getType());
+        propertyDefinitionDto.setDescription(propertyDefinition.getDescription());
         propertyDefinitionDto.setRequired(propertyDefinition.getRequired());
-        propertyDefinitionDto.setName(propertyDefinition.getName());
+        propertyDefinitionDto.setSchemaType(propertyDefinition.getSchemaType());
         if (CollectionUtils.isNotEmpty(propertyDefinition.getConstraints())) {
             final List<PropertyConstraint> propertyConstraints = new ArrayList<>();
             propertyDefinitionDto.setConstraints(propertyConstraints);
index 4194ab7..7cd042a 100644 (file)
@@ -39,12 +39,16 @@ import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
 import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.exception.supplier.DataTypeOperationExceptionSupplier;
 import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.dto.PropertyDefinitionDto;
 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
+import org.openecomp.sdc.be.model.mapper.PropertyDefinitionDtoMapper;
 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
 import org.openecomp.sdc.be.resources.data.DataTypeData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -204,4 +208,29 @@ public class DataTypeOperation extends AbstractOperation {
         return propertyDefinitions;
     }
 
+    public PropertyDefinitionDto createProperty(final String dataTypeId, final PropertyDefinitionDto propertyDefinitionDto) {
+        final String propertyName = propertyDefinitionDto.getName();
+        LOGGER.debug("Adding property '{}' to data type '{}'.", propertyName, dataTypeId);
+
+        getDataTypeByUid(dataTypeId).orElseThrow(DataTypeOperationExceptionSupplier.dataTypeNotFound(dataTypeId));
+
+        final Either<PropertyData, JanusGraphOperationStatus> resultEither =
+            propertyOperation.addPropertyToNodeType(propertyName, PropertyDefinitionDtoMapper.mapTo(propertyDefinitionDto),
+                NodeTypeEnum.DataType, dataTypeId, false);
+        if (resultEither.isRight()) {
+            final JanusGraphOperationStatus status = resultEither.right().value();
+            LOGGER.debug("Could not create property '{}' on data type '{}'. JanusGraph status is '{}'", propertyName, dataTypeId, status);
+            if (status == JanusGraphOperationStatus.JANUSGRAPH_SCHEMA_VIOLATION) {
+                throw DataTypeOperationExceptionSupplier.dataTypePropertyAlreadyExists(dataTypeId, propertyName).get();
+            }
+            LOGGER.error("Could not create property '{}' on data type '{}'. JanusGraph status is '{}'", propertyName, dataTypeId, status);
+            throw DataTypeOperationExceptionSupplier.unexpectedErrorWhileCreatingProperty(dataTypeId, propertyName).get();
+        }
+        LOGGER.debug("Property '{}' was added to data type '{}'.", propertyName, dataTypeId);
+        final PropertyData propertyData = resultEither.left().value();
+        final PropertyDataDefinition propertyDataDefinition = propertyData.getPropertyDataDefinition();
+        propertyDataDefinition.setName(propertyName);
+        return PropertyDefinitionDtoMapper.mapFrom(propertyDataDefinition);
+    }
+
 }
index 1068e31..cd87dd4 100644 (file)
@@ -332,6 +332,13 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
 
     public Either<PropertyData, JanusGraphOperationStatus> addPropertyToNodeType(String propertyName, PropertyDefinition propertyDefinition,
                                                                                  NodeTypeEnum nodeType, String uniqueId) {
+        return addPropertyToNodeType(propertyName, propertyDefinition, nodeType, uniqueId, true);
+    }
+
+    public Either<PropertyData, JanusGraphOperationStatus> addPropertyToNodeType(final String propertyName,
+                                                                                 final PropertyDefinition propertyDefinition,
+                                                                                 final NodeTypeEnum nodeType, final String uniqueId,
+                                                                                 final boolean inTransaction) {
         List<PropertyConstraint> constraints = propertyDefinition.getConstraints();
         propertyDefinition.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(uniqueId, propertyName));
         PropertyData propertyData = new PropertyData(propertyDefinition, convertConstraintsToString(constraints));
@@ -339,6 +346,9 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
         Either<PropertyData, JanusGraphOperationStatus> createNodeResult = janusGraphGenericDao.createNode(propertyData, PropertyData.class);
         log.debug(AFTER_ADDING_PROPERTY_TO_GRAPH, propertyData);
         if (createNodeResult.isRight()) {
+            if (!inTransaction) {
+                janusGraphGenericDao.rollback();
+            }
             JanusGraphOperationStatus operationStatus = createNodeResult.right().value();
             log.error("Failed to add property {} to graph. status is {}", propertyName, operationStatus);
             return Either.right(operationStatus);
@@ -350,10 +360,16 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
         Either<GraphRelation, JanusGraphOperationStatus> createRelResult =
             janusGraphGenericDao.createRelation(uniqueIdData, propertyData, GraphEdgeLabels.PROPERTY, props);
         if (createRelResult.isRight()) {
+            if (!inTransaction) {
+                janusGraphGenericDao.rollback();
+            }
             JanusGraphOperationStatus operationStatus = createNodeResult.right().value();
             log.error(FAILED_TO_ASSOCIATE_RESOURCE_TO_PROPERTY_IN_GRAPH_STATUS_IS, uniqueId, propertyName, operationStatus);
             return Either.right(operationStatus);
         }
+        if (!inTransaction) {
+            janusGraphGenericDao.commit();
+        }
         return Either.left(createNodeResult.left().value());
     }
 
index 1f448fd..26fb765 100644 (file)
@@ -19,7 +19,9 @@
 package org.openecomp.sdc.be.model.operations.impl;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -42,14 +44,18 @@ import org.mockito.MockitoAnnotations;
 import org.openecomp.sdc.be.dao.janusgraph.HealingJanusGraphGenericDao;
 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
 import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
 import org.openecomp.sdc.be.datatypes.enums.ModelTypeEnum;
 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
-import org.openecomp.sdc.be.exception.OperationException;
 import org.openecomp.sdc.be.exception.supplier.DataTypeOperationExceptionSupplier;
 import org.openecomp.sdc.be.model.DataTypeDefinition;
 import org.openecomp.sdc.be.model.Model;
 import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.dto.PropertyDefinitionDto;
+import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
+import org.openecomp.sdc.be.model.mapper.PropertyDefinitionDtoMapper;
 import org.openecomp.sdc.be.resources.data.DataTypeData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
 import org.springframework.test.context.ContextConfiguration;
 
 @ContextConfiguration("classpath:application-context-test.xml")
@@ -87,9 +93,10 @@ class DataTypeOperationTest {
         when(janusGraphGenericDao.getByCriteriaForModel(NodeTypeEnum.DataType, null, modelName, DataTypeData.class))
             .thenReturn(Either.left(dataTypesWithModel));
         final var dataTypesFound = dataTypeOperation.getAllDataTypeNodes();
-        assertThat(dataTypesFound.size()).isEqualTo(4);
-        assertThat(dataTypesFound.containsAll(dataTypesWithoutModel)).isTrue();
-        assertThat(dataTypesFound.containsAll(dataTypesWithModel)).isTrue();
+        assertThat(dataTypesFound)
+            .hasSize(4)
+            .containsAll(dataTypesWithoutModel)
+            .containsAll(dataTypesWithModel);
     }
 
     @Test
@@ -100,8 +107,9 @@ class DataTypeOperationTest {
         when(janusGraphGenericDao.getByCriteriaForModel(NodeTypeEnum.DataType, null, modelName, DataTypeData.class))
             .thenReturn(Either.left(dataTypesWithModel));
         final var dataTypesFound = dataTypeOperation.getAllDataTypeNodes();
-        assertThat(dataTypesFound.size()).isEqualTo(2);
-        assertThat(dataTypesFound.containsAll(dataTypesWithModel)).isTrue();
+        assertThat(dataTypesFound)
+            .hasSize(2)
+            .containsAll(dataTypesWithModel);
         assertThat(dataTypesFound.containsAll(dataTypesWithoutModel)).isFalse();
     }
 
@@ -110,7 +118,7 @@ class DataTypeOperationTest {
         when(janusGraphGenericDao.getByCriteria(NodeTypeEnum.DataType, null, DataTypeData.class))
             .thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
         final var dataTypesFound = dataTypeOperation.getAllDataTypeNodes();
-        assertThat(dataTypesFound.isEmpty()).isTrue();
+        assertThat(dataTypesFound).isEmpty();
     }
 
     @Test
@@ -187,6 +195,79 @@ class DataTypeOperationTest {
         assertEquals(expectedException.getMessage(), actualException.getMessage());
     }
 
+    @Test
+    void createPropertyTest_Success() {
+        final String dataTypeId = "uniqueId";
+        final var property1 = new PropertyDefinitionDto();
+        property1.setName("property1");
+        final var propertyData = new PropertyData();
+        final var propertyDataDefinition = new PropertyDataDefinition();
+        propertyDataDefinition.setName("property1");
+        propertyData.setPropertyDataDefinition(propertyDataDefinition);
+        when(janusGraphGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), dataTypeId, DataTypeData.class))
+            .thenReturn(Either.left(new DataTypeData()));
+        when(propertyOperation.addPropertyToNodeType(eq(property1.getName()), any(PropertyDefinition.class),
+            eq(NodeTypeEnum.DataType), eq(dataTypeId), eq(false)))
+            .thenReturn(Either.left(propertyData));
+        final PropertyDefinitionDto propertyDefinitionDto = dataTypeOperation.createProperty(dataTypeId, property1);
+        assertNotNull(propertyDefinitionDto);
+        assertEquals(property1.getName(), propertyDefinitionDto.getName());
+    }
+
+    @Test
+    void createPropertyTest_DataTypePropertyAlreadyExists() {
+        final String dataTypeId = "uniqueId";
+        final var property1 = new PropertyDefinitionDto();
+        property1.setName("property1");
+        when(janusGraphGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), dataTypeId, DataTypeData.class))
+            .thenReturn(Either.left(new DataTypeData()));
+        when(propertyOperation.addPropertyToNodeType(eq(property1.getName()), any(PropertyDefinition.class),
+            eq(NodeTypeEnum.DataType), eq(dataTypeId), eq(false)))
+            .thenReturn(Either.right(JanusGraphOperationStatus.JANUSGRAPH_SCHEMA_VIOLATION));
+        final OperationException actualException = assertThrows(OperationException.class,
+            () -> dataTypeOperation.createProperty(dataTypeId, property1));
+        final OperationException expectedException =
+            DataTypeOperationExceptionSupplier.dataTypePropertyAlreadyExists(dataTypeId, property1.getName()).get();
+
+        assertEquals(expectedException.getActionStatus(), actualException.getActionStatus());
+        assertArrayEquals(expectedException.getParams(), actualException.getParams());
+    }
+
+    @Test
+    void createPropertyTest_DataTypeNotFound() {
+        final String dataTypeId = "uniqueId";
+        final var property1 = new PropertyDefinitionDto();
+        property1.setName("property1");
+        when(janusGraphGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), dataTypeId, DataTypeData.class))
+            .thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
+        final OperationException actualException = assertThrows(OperationException.class,
+            () -> dataTypeOperation.createProperty(dataTypeId, property1));
+        final OperationException expectedException =
+            DataTypeOperationExceptionSupplier.dataTypeNotFound(dataTypeId).get();
+
+        assertEquals(expectedException.getActionStatus(), actualException.getActionStatus());
+        assertArrayEquals(expectedException.getParams(), actualException.getParams());
+    }
+
+    @Test
+    void createPropertyTest_UnexpectedError() {
+        final String dataTypeId = "uniqueId";
+        final var property1 = new PropertyDefinitionDto();
+        property1.setName("property1");
+        when(janusGraphGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), dataTypeId, DataTypeData.class))
+            .thenReturn(Either.left(new DataTypeData()));
+        when(propertyOperation.addPropertyToNodeType(eq(property1.getName()), any(PropertyDefinition.class),
+            eq(NodeTypeEnum.DataType), eq(dataTypeId), eq(false)))
+            .thenReturn(Either.right(JanusGraphOperationStatus.GENERAL_ERROR));
+        final OperationException actualException = assertThrows(OperationException.class,
+            () -> dataTypeOperation.createProperty(dataTypeId, property1));
+        final OperationException expectedException =
+            DataTypeOperationExceptionSupplier.unexpectedErrorWhileCreatingProperty(dataTypeId, property1.getName()).get();
+
+        assertEquals(expectedException.getActionStatus(), actualException.getActionStatus());
+        assertArrayEquals(expectedException.getParams(), actualException.getParams());
+    }
+
     private void initTestData() {
         model = new Model(modelName, ModelTypeEnum.NORMATIVE);
         final String TEST_DATA_TYPE_001 = "test.data.type001";
index 1e0804e..6a5bd5a 100644 (file)
       <ul *ngIf="isExpanded">
         <li class="input-value">
           <ng-container *ngIf="isViewOnly">
-            {{valueObjRef}}<em class="empty-value" *ngIf="valueObjRef !== false && !valueObjRef">empty</em>
+            {{valueObjRef}}<em class="empty-value" *ngIf="valueObjRef !== false && !valueObjRef">{{'GENERAL_LABEL_EMPTY' | translate | lowercase}}</em>
           </ng-container>
-          <input *ngIf="!isViewOnly" [type]="getSimpleValueInputType()" name="value"
-                [(ngModel)]="valueObjRef"
-                (ngModelChange)="onValueChange($event)"
-          />
+          <input *ngIf="!isViewOnly && isTypeLiteral(type.name)" type="text" name="value"
+                 [(ngModel)]="valueObjRef" (ngModelChange)="onValueChange($event)"/>
+          <input *ngIf="!isViewOnly && isTypeNumber(type.name)" type="number"
+                 [(ngModel)]="valueObjRef" (ngModelChange)="onValueChange($event)"/>
+          <select *ngIf="!isViewOnly && isTypeBoolean(type.name)" [(ngModel)]="valueObjRef" (ngModelChange)="onValueChange($event)">
+            <option [value]="true">{{'GENERAL_LABEL_TRUE' | translate}}</option>
+            <option [value]="false">{{'GENERAL_LABEL_FALSE' | translate}}</option>
+          </select>
         </li>
       </ul>
     </ng-container>
               [type]="getDataType(schema.property.type)"
               [dataTypeMap]="dataTypeMap"
               [valueObjRef]="valueObjRef[i]"
-              [schema]="schema"
+              [schema]="buildSchemaGroupProperty()"
               [nestingLevel]="nestingLevel + 1"
               [listIndex]="i"
               [isListChild]="true"
               [type]="getDataType(schema.property.type)"
               [dataTypeMap]="dataTypeMap"
               [valueObjRef]="valueObjRef[key]"
-              [schema]="schema"
+              [schema]="buildSchemaGroupProperty()"
               [isMapChild]="true"
               [nestingLevel]="nestingLevel + 1"
               [isViewOnly]="isViewOnly"
index 88ff8de..145aad6 100644 (file)
 
 import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
 import {DataTypeModel} from '../../../../../../../models/data-types';
-import {SchemaPropertyGroupModel} from '../../../../../../../models/schema-property';
-import {DerivedPropertyType, PropertyBEModel} from '../../../../../../../models/properties-inputs/property-be-model';
+import {SchemaProperty, SchemaPropertyGroupModel} from '../../../../../../../models/schema-property';
+import {PropertyBEModel} from '../../../../../../../models/properties-inputs/property-be-model';
 import {PROPERTY_DATA, PROPERTY_TYPES} from '../../../../../../../utils/constants';
 import {ToscaFunction} from '../../../../../../../models/tosca-function';
-import {ToscaFunctionValidationEvent} from "../../../../../../../ng2/pages/properties-assignment/tosca-function/tosca-function.component";
+import {ToscaFunctionValidationEvent} from "../../../../../properties-assignment/tosca-function/tosca-function.component";
 import {InstanceFeDetails} from "../../../../../../../models/instance-fe-details";
+import {ToscaTypeHelper} from "app/utils/tosca-type-helper";
 
 @Component({
   selector: 'app-input-list-item',
@@ -41,6 +42,7 @@ export class InputListItemComponent implements OnInit {
   @Input() type: DataTypeModel;
   @Input() schema: SchemaPropertyGroupModel;
   @Input() nestingLevel: number;
+  @Input() isExpanded: boolean = false;
   @Input() isListChild: boolean = false;
   @Input() isMapChild: boolean = false;
   @Input() showToscaFunctionOption: boolean = false;
@@ -53,12 +55,11 @@ export class InputListItemComponent implements OnInit {
   @Output('onDelete') onDeleteEvent: EventEmitter<string> = new EventEmitter<string>();
   @Output('onChildListItemDelete') onChildListItemDeleteEvent: EventEmitter<number> = new EventEmitter<number>();
 
-  isExpanded: boolean = false;
   mapEntryName: string;
   isToscaFunction: boolean = false;
   property: PropertyBEModel;
 
-  ngOnInit() {
+  ngOnInit(): void {
     if (!this.nestingLevel) {
       this.nestingLevel = 0;
     }
@@ -93,26 +94,20 @@ export class InputListItemComponent implements OnInit {
     }
   }
 
-  getType(typeName: string): DerivedPropertyType {
-    if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(typeName) > -1) {
-      return DerivedPropertyType.SIMPLE;
-    } else if (typeName === PROPERTY_TYPES.LIST) {
-      return DerivedPropertyType.LIST;
-    } else if (typeName === PROPERTY_TYPES.MAP) {
-      return DerivedPropertyType.MAP;
-    } else if (typeName === PROPERTY_TYPES.RANGE) {
-      return DerivedPropertyType.RANGE;
-    } else {
-      return DerivedPropertyType.COMPLEX;
-    }
+  isTypeSimple(typeName: string): boolean {
+    return ToscaTypeHelper.isTypeSimple(typeName) || this.isTypeDerivedFromSimple(typeName) && (this.isTypeWithoutProperties(typeName));
+  }
+
+  isTypeRange(typeName: string): boolean {
+    return ToscaTypeHelper.isTypeRange(typeName);
   }
 
   isTypeWithoutProperties(typeName: string): boolean {
-      if (this.dataTypeMap.get(typeName) === undefined) {
-          return true;
-      }
-      return this.dataTypeMap.get(typeName).properties === undefined ||
-          this.dataTypeMap.get(typeName).properties.length == 0;
+    if (this.dataTypeMap.get(typeName) === undefined) {
+      return true;
+    }
+    return this.dataTypeMap.get(typeName).properties === undefined ||
+        this.dataTypeMap.get(typeName).properties.length == 0;
   }
 
   isTypeDerivedFromSimple(typeName: string): boolean {
@@ -134,27 +129,28 @@ export class InputListItemComponent implements OnInit {
     return true;
   }
 
-  isTypeSimple(typeName: string): boolean {
-    if (this.getType(typeName) == DerivedPropertyType.SIMPLE) {
-      return true;
-    }
-    return this.isTypeDerivedFromSimple(typeName) && (this.isTypeWithoutProperties(typeName));
+  isTypeList(typeName: string): boolean {
+    return ToscaTypeHelper.isTypeList(typeName);
   }
 
-  isTypeRange(typeName: string): boolean {
-    return this.getType(typeName) == DerivedPropertyType.RANGE;
+  isTypeMap(typeName: string): boolean {
+    return ToscaTypeHelper.isTypeMap(typeName);
   }
 
-  isTypeList(typeName: string): boolean {
-    return this.getType(typeName) == DerivedPropertyType.LIST;
+  isTypeComplex(typeName: string): boolean {
+    return ToscaTypeHelper.isTypeComplex(typeName);
   }
 
-  isTypeMap(typeName: string): boolean {
-    return this.getType(typeName) == DerivedPropertyType.MAP;
+  isTypeNumber(type: string): boolean {
+    return ToscaTypeHelper.isTypeNumber(type);
   }
 
-  isTypeComplex(typeName: string): boolean {
-    return !this.isTypeSimple(typeName) && !this.isTypeList(typeName) && !this.isTypeMap(typeName) && !this.isTypeRange(typeName);
+  isTypeBoolean(type: string): boolean {
+    return ToscaTypeHelper.isTypeBoolean(type);
+  }
+
+  isTypeLiteral(type: string): boolean {
+    return ToscaTypeHelper.isTypeLiteral(type);
   }
 
   expandAndCollapse() {
@@ -180,7 +176,7 @@ export class InputListItemComponent implements OnInit {
   }
 
   onValueChange(value: any): void {
-    if (this.isNumber(this.type.name)) {
+    if (this.isTypeNumber(this.type.name)) {
       this.emitValueChangeEvent(this.parseNumber(value));
       return;
     }
@@ -297,14 +293,20 @@ export class InputListItemComponent implements OnInit {
   }
 
   getSimpleValueInputType() {
-    if (this.isNumber(this.type.name)){
+    if (this.isTypeNumber(this.type.name)){
       return 'number';
     }
     return 'text';
   }
 
-  isNumber(type: string): boolean {
-    return type === PROPERTY_TYPES.INTEGER || type === PROPERTY_TYPES.FLOAT;
+  buildSchemaGroupProperty(): SchemaPropertyGroupModel {
+    const schemaProperty = new SchemaProperty();
+    if (this.schema.property.type === PROPERTY_TYPES.MAP || this.schema.property.type === PROPERTY_TYPES.LIST) {
+      schemaProperty.type = PROPERTY_TYPES.STRING;
+    } else {
+      schemaProperty.type = this.schema.property.type
+    }
+    return new SchemaPropertyGroupModel(schemaProperty);
   }
 
   private parseBoolean(value: any) {
index c829ff9..b4fc9d1 100644 (file)
@@ -36,31 +36,32 @@ import {PropertyTableModule} from "app/ng2/components/logic/properties-table/pro
 import {ToscaFunctionModule} from '../../../properties-assignment/tosca-function/tosca-function.module';
 
 @NgModule({
-  declarations: [
-    InterfaceOperationHandlerComponent,
-    PropertyParamRowComponent,
-    AddInputComponent,
-    InputListComponent,
-    InputListItemComponent
-  ],
-  imports: [
-    CommonModule,
-    SdcUiComponentsModule,
-    FormsModule,
-    FormElementsModule,
-    TranslateModule,
-    UiElementsModule,
-    PropertyTableModule,
-    ReactiveFormsModule,
-    ToscaFunctionModule
-  ],
-  exports: [
-    PropertyParamRowComponent
-  ],
-  entryComponents: [
-    InterfaceOperationHandlerComponent
-  ],
-  providers: []
+    declarations: [
+        InterfaceOperationHandlerComponent,
+        PropertyParamRowComponent,
+        AddInputComponent,
+        InputListComponent,
+        InputListItemComponent
+    ],
+    imports: [
+        CommonModule,
+        SdcUiComponentsModule,
+        FormsModule,
+        FormElementsModule,
+        TranslateModule,
+        UiElementsModule,
+        PropertyTableModule,
+        ReactiveFormsModule,
+        ToscaFunctionModule
+    ],
+    exports: [
+        PropertyParamRowComponent,
+        InputListItemComponent
+    ],
+    entryComponents: [
+        InterfaceOperationHandlerComponent
+    ],
+    providers: []
 })
 
 export class InterfaceOperationHandlerModule {
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.html
new file mode 100644 (file)
index 0000000..af72e6d
--- /dev/null
@@ -0,0 +1,66 @@
+<div class="add-property-container">
+  <loader [display]="isLoading" [size]="'large'" [relative]="true" [loaderDelay]="500"></loader>
+  <form class="w-sdc-form" [formGroup]="formGroup">
+
+    <div class="side-by-side">
+      <div class="i-sdc-form-item">
+        <label class="i-sdc-form-label required">{{'PROPERTY_NAME_LABEL' | translate}}</label>
+        <input class="i-sdc-form-input"
+               type="text"
+               data-tests-id="property-name"
+               formControlName="name"
+               [readOnly]="readOnly"/>
+      </div>
+      <div class="i-sdc-form-item">
+        <label class="i-sdc-form-label required">{{'PROPERTY_TYPE_LABEL' | translate}}</label>
+        <select formControlName="type" (change)="onTypeChange()" [attr.disabled]="readOnly ? readOnly : null">
+          <option [ngValue]="null">{{'GENERAL_LABEL_SELECT' | translate}}</option>
+          <option *ngFor="let type of typeList"
+                  [ngValue]="type">{{type}}</option>
+        </select>
+      </div>
+      <div class="i-sdc-form-item">
+        <input type="checkbox" formControlName="required" [attr.disabled]="readOnly ? readOnly : null"/> {{'PROPERTY_REQUIRED_LABEL' | translate}}
+      </div>
+      <div class="i-sdc-form-item propertySchemaType" *ngIf="showSchema">
+        <label class="i-sdc-form-label required">{{'PROPERTY_SCHEMA_LABEL' | translate}}</label>
+        <select formControlName="schema" (change)="onSchemaChange()" [attr.disabled]="readOnly ? readOnly : null">
+          <option [ngValue]="null">{{'GENERAL_LABEL_SELECT' | translate}}</option>
+          <option *ngFor="let type of schemaTypeList"
+                  [ngValue]="type">{{type}}</option>
+        </select>
+      </div>
+    </div>
+
+    <div class="i-sdc-form-item">
+      <label class="i-sdc-form-label">{{'PROPERTY_DESCRIPTION_LABEL' | translate}}</label>
+      <textarea class="i-sdc-form-textarea"
+                formControlName="description"
+                data-tests-id="property-description"
+                [readOnly]="readOnly">
+      </textarea>
+    </div>
+    <div class="default-value-container i-sdc-form-item" *ngIf="showDefaultValue()">
+      <label class="i-sdc-form-label">{{'PROPERTY_DEFAULT_VALUE_LABEL' | translate}}</label>
+      <ng-container *ngIf="!readOnly">
+        <input type="checkbox" formControlName="hasDefaultValue" [attr.disabled]="readOnly ? readOnly : null"/> {{'PROPERTY_SET_DEFAULT_VALUE_MSG' | translate}}
+      </ng-container>
+      <ng-container *ngIf="hasDefaultValueForm.value">
+        <ul>
+          <app-input-list-item
+              [name]="nameForm.value"
+              [type]="getDataType(typeForm.value)"
+              [dataTypeMap]="dataTypeMap"
+              [valueObjRef]="defaultValueForm.value"
+              [schema]="buildSchemaGroupProperty()"
+              [nestingLevel]="0"
+              [isViewOnly]="readOnly"
+              [allowDeletion]="false"
+              [isExpanded]="true"
+              (onValueChange)="onPropertyValueChange($event)">
+          </app-input-list-item>
+        </ul>
+      </ng-container>
+    </div>
+  </form>
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.less b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.less
new file mode 100644 (file)
index 0000000..2c33002
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * -
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation.
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+.add-property-container {
+    max-width: 100%;
+}
+
+.default-value-container {
+    overflow: scroll;
+    max-height: 300px;
+    max-width: 100%;
+
+    ul {
+        margin: 0 0 0 20px;
+        list-style: none;
+        line-height: 2em;
+    }
+
+    &::-webkit-scrollbar-track {
+        border: 0;
+    }
+}
+
+li {
+    position: relative;
+
+    &:before {
+        position: absolute;
+        left: -15px;
+        top: 0;
+        content: '';
+        display: block;
+        border-left: 1px solid #ddd;
+        height: 1em;
+        border-bottom: 1px solid #ddd;
+        width: 10px;
+    }
+
+    &:after {
+        position: absolute;
+        left: -15px;
+        bottom: -7px;
+        content: '';
+        display: block;
+        border-left: 1px solid #ddd;
+        height: 100%;
+    }
+
+    &.root {
+        margin: 0 0 0 -20px;
+
+        &:before {
+            display: none;
+        }
+
+        &:after {
+            display: none;
+        }
+    }
+
+    &:last-child {
+        &:after {
+            display: none
+        }
+    }
+}
+
+.input-label {
+    margin: 0;
+    font-weight: bold;
+}
+
+.input-value {
+    display: flex;
+    flex-flow: row nowrap;
+    gap: 7px;
+
+    input {
+        min-width: 150px;
+        max-width: 250px;
+    }
+}
+
+.empty-value {
+    color: #aaaaaa;
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.spec.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.spec.ts
new file mode 100644 (file)
index 0000000..7e2c312
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * -
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation.
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {AddPropertyComponent} from './add-property.component';
+import {FormsModule, ReactiveFormsModule} from "@angular/forms";
+import {TranslateModule} from "../../../../shared/translator/translate.module";
+import {UiElementsModule} from "../../../../components/ui/ui-elements.module";
+import {Component, Input} from "@angular/core";
+import {DataTypeModel} from "../../../../../models/data-types";
+import {SchemaPropertyGroupModel} from "../../../../../models/schema-property";
+import {DataTypeService} from "../../../../services/data-type.service";
+import {TranslateService} from "../../../../shared/translator/translate.service";
+
+@Component({selector: 'app-input-list-item', template: ''})
+class InputListItemStubComponent {
+    @Input() valueObjRef: any;
+    @Input() name: string;
+    @Input() dataTypeMap: Map<string, DataTypeModel>;
+    @Input() type: DataTypeModel;
+    @Input() schema: SchemaPropertyGroupModel;
+    @Input() nestingLevel: number;
+    @Input() isExpanded: boolean = false;
+    @Input() isListChild: boolean = false;
+    @Input() isMapChild: boolean = false;
+    @Input() listIndex: number;
+    @Input() isViewOnly: boolean;
+    @Input() allowDeletion: boolean = false;
+}
+
+describe('AddPropertyComponent', () => {
+    let dataTypeServiceMock: Partial<DataTypeService> = {
+        findAllDataTypesByModel: jest.fn(args => {
+            return Promise.resolve(new Map());
+        })
+    };
+    let translateServiceMock: Partial<TranslateService> = {
+        translate: jest.fn((str: string) => {})
+    };
+    let component: AddPropertyComponent;
+    let fixture: ComponentFixture<AddPropertyComponent>;
+
+    beforeEach(async(() => {
+        TestBed.configureTestingModule({
+            declarations: [AddPropertyComponent, InputListItemStubComponent],
+            imports: [
+                FormsModule,
+                ReactiveFormsModule,
+                TranslateModule,
+                UiElementsModule
+            ],
+            providers: [
+                {provide: DataTypeService, useValue: dataTypeServiceMock},
+                {provide: TranslateService, useValue: translateServiceMock}
+            ]
+        })
+        .compileComponents();
+    }));
+
+    beforeEach(() => {
+        fixture = TestBed.createComponent(AddPropertyComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+});
diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.ts
new file mode 100644 (file)
index 0000000..dc1a032
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * -
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation.
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
+import {PropertyBEModel} from "../../../../../models/properties-inputs/property-be-model";
+import {AbstractControl, FormControl, FormGroup, ValidationErrors, Validators} from "@angular/forms";
+import {PROPERTY_DATA} from "../../../../../utils/constants";
+import {DataTypeService} from "../../../../services/data-type.service";
+import {DataTypeModel} from "../../../../../models/data-types";
+import {Subscription} from "rxjs";
+import {ToscaTypeHelper} from "../../../../../utils/tosca-type-helper";
+import {SchemaProperty, SchemaPropertyGroupModel} from "../../../../../models/schema-property";
+
+@Component({
+    selector: 'app-add-property',
+    templateUrl: './add-property.component.html',
+    styleUrls: ['./add-property.component.less']
+})
+export class AddPropertyComponent implements OnInit, OnDestroy {
+
+    @Input() property: PropertyBEModel;
+    @Input() readOnly: boolean = true;
+    @Input() model: string;
+
+    @Output() onValidityChange: EventEmitter<PropertyValidationEvent> = new EventEmitter<PropertyValidationEvent>();
+
+    private valueChangesSub: Subscription;
+    private descriptionForm: FormControl = new FormControl(undefined);
+    private requiredForm: FormControl = new FormControl(false, Validators.required);
+    nameForm: FormControl = new FormControl(undefined, [Validators.required]);
+    typeForm: FormControl = new FormControl(undefined, Validators.required);
+    schemaForm: FormControl = new FormControl(undefined, (control: AbstractControl): ValidationErrors | null => {
+        if (this.typeNeedsSchema() && !control.value) {
+            return {required: true};
+        }
+        return null;
+    });
+    hasDefaultValueForm: FormControl = new FormControl(false, Validators.required);
+    defaultValueForm: FormControl = new FormControl(undefined);
+    formGroup: FormGroup = new FormGroup({
+        'name': this.nameForm,
+        'description': this.descriptionForm,
+        'type': this.typeForm,
+        'required': this.requiredForm,
+        'schema': this.schemaForm,
+        'defaultValue': this.defaultValueForm,
+        'hasDefaultValue': this.hasDefaultValueForm,
+    });
+
+    isLoading: boolean = false;
+    showSchema: boolean = false;
+    typeList: string[];
+    dataTypeMap: Map<string, DataTypeModel>;
+    dataType: DataTypeModel;
+    schemaTypeList: string[];
+
+    constructor(private dataTypeService: DataTypeService) {
+    }
+
+    ngOnInit(): void {
+        this.isLoading = true;
+        this.initTypeAndSchemaDropdown().then(() => this.updateDataType());
+        this.initForm();
+        this.valueChangesSub = this.formGroup.valueChanges.subscribe(() => {
+            this.emitValidityChange();
+        });
+    }
+
+    ngOnDestroy(): void {
+        if (this.valueChangesSub) {
+            this.valueChangesSub.unsubscribe();
+        }
+    }
+
+    onSchemaChange(): void {
+        this.resetDefaultValue();
+    }
+
+    onTypeChange(): void {
+        this.schemaForm.setValue(null);
+        this.showSchema = this.typeNeedsSchema();
+        this.updateDataType();
+        this.resetDefaultValue();
+    }
+
+    private updateDataType(): void {
+        this.dataType = this.dataTypeMap.get(this.typeForm.value);
+    }
+
+    private initForm(): void {
+        if (!this.property) {
+            return;
+        }
+
+        this.nameForm.setValue(this.property.name);
+        this.descriptionForm.setValue(this.property.description);
+        this.typeForm.setValue(this.property.type);
+        this.showSchema = this.typeNeedsSchema();
+        this.requiredForm.setValue(this.property.required);
+        this.schemaForm.setValue(this.property.schemaType);
+        this.initDefaultValueForm();
+    }
+
+    private initDefaultValueForm() {
+        if (this.property.defaultValue == undefined) {
+            return;
+        }
+        let defaultValue;
+        if (!this.isTypeSimple() && typeof this.property.defaultValue === 'string') {
+            defaultValue = JSON.parse(this.property.defaultValue);
+        } else {
+            defaultValue = this.property.defaultValue;
+        }
+        this.defaultValueForm.setValue(defaultValue);
+        this.hasDefaultValueForm.setValue(true);
+    }
+
+    private typeNeedsSchema() {
+        return PROPERTY_DATA.SCHEMA_TYPES.indexOf(this.typeForm.value) > -1;
+    }
+
+    private initTypeAndSchemaDropdown(): Promise<Map<string, DataTypeModel>> {
+        const primitiveTypes: string[] = Array.from(PROPERTY_DATA.TYPES).sort((a, b) => a.localeCompare(b));
+        const promise = this.dataTypeService.findAllDataTypesByModel(this.model);
+        promise.then((dataTypeMap: Map<string, DataTypeModel>) => {
+            this.dataTypeMap = dataTypeMap;
+            const nonPrimitiveTypes: string[] = Array.from(dataTypeMap.keys()).filter(type => {
+                return primitiveTypes.indexOf(type) === -1;
+            });
+            nonPrimitiveTypes.sort((a, b) => a.localeCompare(b));
+            this.typeList = [...primitiveTypes, ...nonPrimitiveTypes];
+            this.schemaTypeList = Array.from(this.typeList);
+            this.isLoading = false;
+        });
+        return promise;
+    }
+
+    private emitValidityChange(): void {
+        const isValid: boolean = this.formGroup.valid;
+        this.onValidityChange.emit({
+            isValid: isValid,
+            property: isValid ? this.buildPropertyFromForm() : undefined
+        });
+    }
+
+    private buildPropertyFromForm(): PropertyBEModel {
+        const property = new PropertyBEModel();
+        property.name = this.nameForm.value;
+        property.type = this.typeForm.value;
+        if (this.schemaForm.value) {
+            property.schemaType = this.schemaForm.value;
+        }
+        property.description = this.descriptionForm.value;
+        if (this.hasDefaultValueForm.value === true) {
+            property.defaultValue = this.defaultValueForm.value;
+        }
+        return property;
+    }
+
+    public isTypeSimple(): boolean {
+        return ToscaTypeHelper.isTypeSimple(this.typeForm.value);
+    }
+
+    public isTypeList(): boolean {
+        return ToscaTypeHelper.isTypeList(this.typeForm.value);
+    }
+
+    public isTypeMap(): boolean {
+        return ToscaTypeHelper.isTypeMap(this.typeForm.value);
+    }
+
+    public isTypeComplex(): boolean {
+        return ToscaTypeHelper.isTypeComplex(this.typeForm.value);
+    }
+
+    private isTypeRange() {
+        return ToscaTypeHelper.isTypeRange(this.typeForm.value);
+    }
+
+    onPropertyValueChange($event: any): void {
+        this.defaultValueForm.setValue($event.value);
+    }
+
+    showDefaultValue(): boolean {
+        if (this.readOnly) {
+            return this.defaultValueForm.value != undefined && this.dataTypeMap && this.typeForm.valid && this.schemaForm.valid;
+        }
+        return this.dataTypeMap && this.typeForm.valid && this.schemaForm.valid;
+    }
+
+    getDataType(type: string): DataTypeModel {
+        return this.dataTypeMap.get(type);
+    }
+
+    private resetDefaultValue(): void {
+        this.defaultValueForm.reset();
+        if (this.isTypeComplex() || this.isTypeMap()) {
+            this.defaultValueForm.setValue({});
+        } else if (this.isTypeList() || this.isTypeRange()) {
+            this.defaultValueForm.setValue([]);
+        }
+    }
+
+    buildSchemaGroupProperty(): SchemaPropertyGroupModel {
+        const schemaProperty = new SchemaProperty();
+        schemaProperty.type = this.schemaForm.value
+        return new SchemaPropertyGroupModel(schemaProperty);
+    }
+
+}
+
+export class PropertyValidationEvent {
+    isValid: boolean;
+    property: PropertyBEModel;
+}
\ No newline at end of file
index 61c319e..ec67a02 100644 (file)
     <input id="search-by-name" type="search" [placeholder]="'SEARCH_LABEL' | translate" [ngModel]="tableFilterTerm" (ngModelChange)="this.tableSearchTermUpdate.next($event)"/>
     <span class="sprite magnification-glass search-button"></span>
   </div>
+  <div class="add-btn" data-tests-id="add-link" *ngIf="!isViewOnly" (click)="onClickAddProperty()">Add Property</div>
   <div class="table-container-flex">
     <div class="table" [ngClass]="{'view-mode': isViewOnly}">
       <div class="head flex-container">
         <div class="table-header head-row hand flex-item" *ngFor="let header of tableHeadersList" (click)="onUpdateSort(header.property)">{{header.title}}
           <span *ngIf="tableSortBy === header.property" class="table-header-sort-arrow" [ngClass]="{'down': tableColumnReverse, 'up': !tableColumnReverse}"></span>
         </div>
-        <div class="table-no-text-header head-row flex-item" *ngIf="!isViewOnly"><span class="delete-col-header"></span></div>
       </div>
 
       <div class="body">
-          <div *ngIf="filteredProperties.length === 0" class="no-row-text">
-            {{'PROPERTY_LIST_EMPTY_MESSAGE' | translate}}
+        <div *ngIf="filteredProperties.length === 0" class="no-row-text">
+          {{'PROPERTY_LIST_EMPTY_MESSAGE' | translate}}
+        </div>
+        <div *ngFor="let property of filteredProperties" [attr.data-tests-id]="'property-row-' + property.name" class="flex-container data-row" (click)="onRowClick(property)">
+          <div class="table-col-general flex-item text" [title]="property.name">
+            <a [attr.data-tests-id]="'property-name-' + property.name" [ngClass]="{'disabled': isViewOnly}">{{property.name}}</a>
           </div>
-            <div *ngFor="let property of filteredProperties" [attr.data-tests-id]="'property-row-' + property.name" class="flex-container data-row">
-            <div class="table-col-general flex-item text" [title]="property.name">
-              <a [attr.data-tests-id]="'property-name-' + property.name" [ngClass]="{'disabled': isViewOnly}">{{property.name}}</a>
-            </div>
-
-            <div class="table-col-general flex-item text" [title]="property.type">
-              <span [attr.data-tests-id]="'property-type-' + property.name">{{property.type}}</span>
-            </div>
-            <div class="table-col-general flex-item text" [title]="property.getSchemaType() || ''">
-              <span [attr.data-tests-id]="'property-schema-' + property.name">{{property.getSchemaType() || ''}}</span>
-            </div>
-            <div class="table-col-general flex-item text" [title]="property.description">
-              <span [attr.data-tests-id]="'property-description-' + property.name" [title]="property.description">{{property.description}}</span>
-            </div>
-            <div class="table-btn-col flex-item" *ngIf="!isViewOnly"></div>
+          <div class="table-col-general flex-item text" [title]="property.type">
+            <span [attr.data-tests-id]="'property-type-' + property.name">{{property.type}}</span>
+          </div>
+          <div class="table-col-general flex-item text" [title]="property.schemaType || ''">
+            <span [attr.data-tests-id]="'property-schema-' + property.name">{{property.schemaType || ''}}</span>
           </div>
+          <div class="table-col-general flex-item text" [title]="property.required ? 'Yes' : 'No'">
+            <span [attr.data-tests-id]="'property-required-' + property.name">{{property.required ? 'Yes' : 'No'}}</span>
+          </div>
+          <div class="table-col-general flex-item text" [title]="property.description || ''">
+            <span [attr.data-tests-id]="'property-description-' + property.name" [title]="property.description">{{property.description}}</span>
+          </div>
+        </div>
       </div>
 
     </div>
index 9c101e8..fd43bd9 100644 (file)
@@ -29,7 +29,7 @@
 
     #left-top-bar {
         float: left;
-        width: 186px;
+        width: 200px;
 
         ::-webkit-input-placeholder {
             font-style: italic;
@@ -56,7 +56,7 @@
             -webkit-border-radius: 2px;
             -moz-border-radius: 2px;
             border-radius: 2px;
-            width: 270px;
+            width: 290px;
             height: 32px;
             line-height: 32px;
             border: 1px solid @main_color_o;
         }
 
         .flex-item:nth-child(4) {
+            flex-grow: 3;
+        }
+
+        .flex-item:nth-child(5) {
             flex-grow: 20;
             white-space: normal;
         }
 
-        .flex-item:nth-child(5) {
+        .flex-item:nth-child(6) {
             flex-grow: 3;
             padding-top: 10px;
         }
index 6be572d..e6e9c12 100644 (file)
@@ -30,9 +30,11 @@ import {Observable} from "rxjs/Observable";
 import {DataTypeModel} from "../../../../models/data-types";
 import {Component, ViewChild} from "@angular/core";
 import {PropertyBEModel} from "../../../../models/properties-inputs/property-be-model";
+import {ModalService} from "../../../services/modal.service";
 
 describe('TypeWorkspacePropertiesComponent', () => {
     const messages = require("../../../../../assets/languages/en_US.json");
+    let modalService: Partial<ModalService> = {};
     let testHostComponent: TestHostComponent;
     let testHostFixture: ComponentFixture<TestHostComponent>;
     let dataTypeServiceMock: Partial<DataTypeService> = {
@@ -62,7 +64,8 @@ describe('TypeWorkspacePropertiesComponent', () => {
             ],
             providers: [
                 {provide: DataTypeService, useValue: dataTypeServiceMock},
-                {provide: TranslateService, useValue: translateServiceMock}
+                {provide: TranslateService, useValue: translateServiceMock},
+                {provide: ModalService, useValue: modalService}
             ]
         })
         .compileComponents();
index 931fd3d..90bc89a 100644 (file)
@@ -23,93 +23,173 @@ import {Component, Input, OnInit} from '@angular/core';
 import {DataTypeModel} from "../../../../models/data-types";
 import {DataTypeService} from "../../../services/data-type.service";
 import {PropertyBEModel} from "../../../../models/properties-inputs/property-be-model";
-import { Subject } from "rxjs";
+import {Subject} from "rxjs";
 import {debounceTime, distinctUntilChanged} from "rxjs/operators";
+import {ModalService} from "../../../services/modal.service";
+import {ModalModel} from "../../../../models/modal";
+import {ButtonModel} from "../../../../models/button";
+import {TranslateService} from "../../../shared/translator/translate.service";
+import {AddPropertyComponent, PropertyValidationEvent} from "./add-property/add-property.component";
 
 @Component({
-  selector: 'app-type-workspace-properties',
-  templateUrl: './type-workspace-properties.component.html',
-  styleUrls: ['./type-workspace-properties.component.less']
+    selector: 'app-type-workspace-properties',
+    templateUrl: './type-workspace-properties.component.html',
+    styleUrls: ['./type-workspace-properties.component.less']
 })
 export class TypeWorkspacePropertiesComponent implements OnInit {
-  @Input() isViewOnly = true;
-  @Input() dataType: DataTypeModel = new DataTypeModel();
-
-  properties: Array<PropertyBEModel> = [];
-  filteredProperties: Array<PropertyBEModel> = [];
-  tableHeadersList: Array<TableHeader> = [];
-  tableSortBy: string = 'name';
-  tableColumnReverse: boolean = false;
-  tableFilterTerm: string = undefined;
-  tableSearchTermUpdate = new Subject<string>();
-
-  constructor(private dataTypeService: DataTypeService) { }
-
-  ngOnInit(): void {
-    this.initTable();
-    this.initProperties();
-    this.tableSearchTermUpdate.pipe(
-        debounceTime(400),
-        distinctUntilChanged())
-    .subscribe(searchTerm => {
-      this.filter(searchTerm);
-    });
-  }
-
-  private initTable(): void {
-    this.tableHeadersList = [
-      {title: 'Name', property: 'name'},
-      {title: 'Type', property: 'type'},
-      {title: 'Schema', property: 'schema.property.type'},
-      {title: 'Description', property: 'description'},
-    ];
-
-    this.tableSortBy = this.tableHeadersList[0].property;
-  }
-
-  private initProperties(): void {
-    this.dataTypeService.findAllProperties(this.dataType.uniqueId).subscribe(properties => {
-      this.properties = properties.map(value => new PropertyBEModel(value));
-      this.filteredProperties = Array.from(this.properties);
-      this.sort();
-    });
-  }
-
-  onUpdateSort(property: string): void {
-    if (this.tableSortBy === property) {
-      this.tableColumnReverse = !this.tableColumnReverse;
-    } else {
-      this.tableColumnReverse = false;
-      this.tableSortBy = property;
+    @Input() isViewOnly = true;
+    @Input() dataType: DataTypeModel = new DataTypeModel();
+
+    properties: Array<PropertyBEModel> = [];
+    filteredProperties: Array<PropertyBEModel> = [];
+    tableHeadersList: Array<TableHeader> = [];
+    tableSortBy: string = 'name';
+    tableColumnReverse: boolean = false;
+    tableFilterTerm: string = undefined;
+    tableSearchTermUpdate = new Subject<string>();
+
+    constructor(private dataTypeService: DataTypeService, private modalService: ModalService, private translateService: TranslateService) {
+    }
+
+    ngOnInit(): void {
+        this.initTable();
+        this.initProperties();
+        this.tableSearchTermUpdate.pipe(
+            debounceTime(400),
+            distinctUntilChanged())
+        .subscribe(searchTerm => {
+            this.filter(searchTerm);
+        });
+    }
+
+    private initTable(): void {
+        this.tableHeadersList = [
+            {title: 'Name', property: 'name'},
+            {title: 'Type', property: 'type'},
+            {title: 'Schema', property: 'schema.property.type'},
+            {title: 'Required', property: 'required'},
+            {title: 'Description', property: 'description'},
+        ];
+
+        this.tableSortBy = this.tableHeadersList[0].property;
     }
-    this.sort();
-  }
-
-  private sort(): void {
-    const field = this.tableSortBy;
-    this.filteredProperties = this.filteredProperties.sort((property1, property2) => {
-      const result = property1[field] > property2[field] ? 1 : property1[field] < property2[field] ? -1 : 0;
-      return this.tableColumnReverse ? result * -1 : result;
-    });
-  }
-
-  private filter(searchTerm: string): void {
-    if (searchTerm) {
-      searchTerm = searchTerm.toLowerCase();
-      this.filteredProperties = this.properties.filter(property =>
-          property.name.toLowerCase().includes(searchTerm)
-          || property.type.toLowerCase().includes(searchTerm)
-          || (property.getSchemaType() && property.getSchemaType().toLowerCase().includes(searchTerm))
-          || (property.description && property.description.toLowerCase().includes(searchTerm))
-      );
-    } else {
-      this.filteredProperties = Array.from(this.properties);
+
+    private initProperties(): void {
+        this.dataTypeService.findAllProperties(this.dataType.uniqueId).subscribe(properties => {
+            this.properties = properties.map(value => {
+                const property = new PropertyBEModel(value);
+                if (property.defaultValue) {
+                    property.defaultValue = JSON.parse(property.defaultValue);
+                }
+
+                return property;
+            });
+            this.filteredProperties = Array.from(this.properties);
+            this.sort();
+        });
+    }
+
+    onUpdateSort(property: string): void {
+        if (this.tableSortBy === property) {
+            this.tableColumnReverse = !this.tableColumnReverse;
+        } else {
+            this.tableColumnReverse = false;
+            this.tableSortBy = property;
+        }
+        this.sort();
+    }
+
+    private sort(): void {
+        const field = this.tableSortBy;
+        this.filteredProperties.sort((property1, property2) => {
+            let result = 0;
+            if (property1[field] > property2[field]) {
+                result = 1;
+            } else if (property1[field] < property2[field]) {
+                result = -1;
+            }
+            return this.tableColumnReverse ? result * -1 : result;
+        });
+    }
+
+    private filter(searchTerm?: string): void {
+        if (searchTerm) {
+            searchTerm = searchTerm.toLowerCase();
+            this.filteredProperties = this.properties.filter(property =>
+                property.name.toLowerCase().includes(searchTerm)
+                || property.type.toLowerCase().includes(searchTerm)
+                || (property.getSchemaType() && property.getSchemaType().toLowerCase().includes(searchTerm))
+                || (property.description && property.description.toLowerCase().includes(searchTerm))
+            );
+        } else {
+            this.filteredProperties = Array.from(this.properties);
+        }
+        this.sort();
+    }
+
+    private addProperty(property: PropertyBEModel) {
+        this.properties.push(property);
+        this.filter();
+    }
+
+    onClickAddProperty() {
+        this.openAddPropertyModal();
+    }
+
+    private openAddPropertyModal(property?: PropertyBEModel, readOnly: boolean = false) {
+        const modalTitle = this.translateService.translate('PROPERTY_ADD_MODAL_TITLE');
+        const modalButtons = [];
+        let disableSaveButtonFlag = true;
+        let propertyFromModal: PropertyBEModel = undefined;
+        const modal = this.modalService.createCustomModal(new ModalModel(
+            'md',
+            modalTitle,
+            null,
+            modalButtons,
+            null
+        ));
+        if (readOnly) {
+            modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_CLOSE'), 'outline grey', () => {
+                this.modalService.closeCurrentModal();
+            }));
+        } else {
+            modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_SAVE'), 'blue',
+                () => {
+                    disableSaveButtonFlag = true;
+                    this.dataTypeService.createProperty(this.dataType.uniqueId, propertyFromModal).subscribe(property => {
+                        this.addProperty(new PropertyBEModel(property));
+                    });
+                    this.modalService.closeCurrentModal();
+                },
+                (): boolean => {
+                    return disableSaveButtonFlag
+                }
+            ));
+
+            modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_CANCEL'), 'outline grey', () => {
+                this.modalService.closeCurrentModal();
+            }));
+        }
+
+        this.modalService.addDynamicContentToModalAndBindInputs(modal, AddPropertyComponent, {
+            'readOnly': readOnly,
+            'property': property
+        });
+        modal.instance.dynamicContent.instance.onValidityChange.subscribe((validationEvent: PropertyValidationEvent) => {
+            disableSaveButtonFlag = !validationEvent.isValid;
+            if (validationEvent.isValid) {
+                propertyFromModal = validationEvent.property;
+            }
+        });
+        modal.instance.open();
+    }
+
+    onRowClick(property: PropertyBEModel) {
+        this.openAddPropertyModal(property, true);
     }
-    this.sort();
-  }
 }
 
 interface TableHeader {
-  title: string;
-  property: string;
+    title: string;
+    property: string;
 }
\ No newline at end of file
index 193c94e..4d29e86 100644 (file)
@@ -44,7 +44,7 @@
         </div>
         <div class="w-sdc-main-container-body-content" *ngIf="dataType">
           <app-type-workspace-general *ngIf="currentMenu.state === 'general'" [dataType]="dataType"></app-type-workspace-general>
-          <app-type-workspace-properties *ngIf="currentMenu.state === 'properties'" [dataType]="dataType"></app-type-workspace-properties>
+          <app-type-workspace-properties *ngIf="currentMenu.state === 'properties'" [dataType]="dataType" [isViewOnly]="false"></app-type-workspace-properties>
         </div>
 
       </div>
index 021f84a..5b2d3bf 100644 (file)
@@ -31,7 +31,10 @@ import {UpgradeModule} from "@angular/upgrade/static";
 import {FormsModule, ReactiveFormsModule} from "@angular/forms";
 import {TranslateModule} from "../../shared/translator/translate.module";
 import {DataTypeService} from "../../services/data-type.service";
-import { TypeWorkspacePropertiesComponent } from './type-workspace-properties/type-workspace-properties.component';
+import {TypeWorkspacePropertiesComponent} from './type-workspace-properties/type-workspace-properties.component';
+import {ModalService} from "../../services/modal.service";
+import {AddPropertyComponent} from './type-workspace-properties/add-property/add-property.component';
+import {InterfaceOperationHandlerModule} from "../composition/interface-operatons/operation-creator/interface-operation-handler.module";
 
 @NgModule({
     imports: [
@@ -42,19 +45,22 @@ import { TypeWorkspacePropertiesComponent } from './type-workspace-properties/ty
         ReactiveFormsModule,
         TranslateModule,
         FormsModule,
+        InterfaceOperationHandlerModule,
     ],
     declarations: [
         TypeWorkspaceComponent,
         WorkspaceMenuComponent,
         TypeWorkspaceGeneralComponent,
-        TypeWorkspacePropertiesComponent
+        TypeWorkspacePropertiesComponent,
+        AddPropertyComponent,
     ],
     providers: [
         CacheService,
         WorkspaceMenuComponent,
-        DataTypeService
+        DataTypeService,
+        ModalService
     ],
-    entryComponents: [TypeWorkspaceComponent],
+    entryComponents: [TypeWorkspaceComponent, AddPropertyComponent],
     exports: [TypeWorkspaceComponent]
 })
 export class TypeWorkspaceModule {
index 74d48d3..298ba90 100644 (file)
@@ -83,6 +83,11 @@ export class DataTypeService {
         return this.httpClient.get<Array<PropertyBEModel>>(url);
     }
 
+    public createProperty(id: string, property: PropertyBEModel): Observable<PropertyBEModel> {
+        const url = `${this.dataTypeUrl}/${id}/properties`
+        return this.httpClient.post<PropertyBEModel>(url, property);
+    }
+
     public getConstraintsByParentTypeAndUniqueID(rootPropertyType, propertyName){
         // const property = this.dataTypes[rootPropertyType].properties.filter(property =>
         //     property.name == propertyName);
index 9d11f54..c8fb196 100644 (file)
@@ -156,6 +156,7 @@ export class PROPERTY_DATA {
   public static TYPES = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.TIMESTAMP, PROPERTY_TYPES.FLOAT, PROPERTY_TYPES.BOOLEAN, PROPERTY_TYPES.JSON, PROPERTY_TYPES.SCALAR, PROPERTY_TYPES.SCALAR_FREQUENCY, PROPERTY_TYPES.SCALAR_SIZE, PROPERTY_TYPES.SCALAR_TIME, PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP, PROPERTY_TYPES.RANGE];
   public static SIMPLE_TYPES = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.TIMESTAMP, PROPERTY_TYPES.FLOAT, PROPERTY_TYPES.BOOLEAN, PROPERTY_TYPES.JSON, PROPERTY_TYPES.SCALAR, PROPERTY_TYPES.SCALAR_FREQUENCY, PROPERTY_TYPES.SCALAR_SIZE, PROPERTY_TYPES.SCALAR_TIME];
   public static SIMPLE_TYPES_COMPARABLE = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.FLOAT];
+  public static SCHEMA_TYPES = [PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP];
   public static SCALAR_TYPES = [PROPERTY_TYPES.SCALAR, PROPERTY_TYPES.SCALAR_FREQUENCY, PROPERTY_TYPES.SCALAR_SIZE, PROPERTY_TYPES.SCALAR_TIME];
   public static ROOT_DATA_TYPE = "tosca.datatypes.Root";
   public static OPENECOMP_ROOT = "org.openecomp.datatypes.Root";
diff --git a/catalog-ui/src/app/utils/tosca-type-helper.ts b/catalog-ui/src/app/utils/tosca-type-helper.ts
new file mode 100644 (file)
index 0000000..7faa90a
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * -
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation.
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+import {DerivedPropertyType} from "../models/properties-inputs/property-be-model";
+import {PROPERTY_DATA, PROPERTY_TYPES} from "./constants";
+
+export class ToscaTypeHelper {
+
+    private ToscaTypeHelper() {
+        //not designed to be instantiated
+    }
+
+    public static getType(typeName: string): DerivedPropertyType {
+        if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(typeName) > -1) {
+            return DerivedPropertyType.SIMPLE;
+        } else if (typeName === PROPERTY_TYPES.LIST) {
+            return DerivedPropertyType.LIST;
+        } else if (typeName === PROPERTY_TYPES.MAP) {
+            return DerivedPropertyType.MAP;
+        } else if (typeName === PROPERTY_TYPES.RANGE) {
+            return DerivedPropertyType.RANGE;
+        } else {
+            return DerivedPropertyType.COMPLEX;
+        }
+    }
+
+    public static isTypeSimple(typeName: string): boolean {
+        return this.getType(typeName) == DerivedPropertyType.SIMPLE;
+    }
+
+    public static isTypeList(typeName: string): boolean {
+        return this.getType(typeName) == DerivedPropertyType.LIST;
+    }
+
+    public static isTypeMap(typeName: string): boolean {
+        return this.getType(typeName) == DerivedPropertyType.MAP;
+    }
+
+    public static isTypeComplex(typeName: string): boolean {
+        return !this.isTypeSimple(typeName) && !this.isTypeList(typeName) && !this.isTypeMap(typeName);
+    }
+
+    public static isTypeNumber(typeName: string): boolean {
+        return typeName === PROPERTY_TYPES.INTEGER || typeName === PROPERTY_TYPES.FLOAT;
+    }
+
+    public static isTypeBoolean(typeName: string): boolean {
+        return typeName === PROPERTY_TYPES.BOOLEAN;
+    }
+
+    public static isTypeLiteral(typeName: string): boolean {
+        return !this.isTypeNumber(typeName) && !this.isTypeBoolean(typeName) && !this.isTypeList(typeName) && !this.isTypeMap(typeName)
+            && !this.isTypeComplex(typeName) && !this.isTypeRange(typeName);
+    }
+
+    public static isTypeRange(typeName: string): boolean {
+        return typeName === PROPERTY_TYPES.RANGE;
+    }
+
+}
index fc5445a..5dec922 100644 (file)
   "GENERAL_LABEL_SYSTEM_NAME": "System Name:",
   "GENERAL_LABEL_SOURCE_SERVICE_NAME": "Source Service Name:",
   "GENERAL_LABEL_RESOURCE_CUSTOMIZATION_UUID": "Resource Customization UUID:",
+  "GENERAL_LABEL_EMPTY": "Empty",
+  "GENERAL_LABEL_TRUE": "True",
+  "GENERAL_LABEL_FALSE": "False",
+  "GENERAL_LABEL_SELECT": "Select",
   "SEARCH_LABEL": "Search",
   "=========== GENERAL_TAB ===========": "",
   "GENERAL_TAB_LABEL_RESOURCE_MODEL_NUMBER": "Vendor Model Number",
   "MODAL_SAVE": "Save",
   "MODAL_CREATE": "Create",
   "MODAL_CANCEL": "Cancel",
+  "MODAL_CLOSE": "Close",
   "MODAL_DELETE": "Delete",
   "=========== POLICY AND GROUPS ===========": "",
   "ADD_TARGETS": "Add Targets",
   "IMPORT_FAILURE_MESSAGE_TEXT": "Import Failure - error reading CSAR",
   "=========== PROPERTIES ===========": "",
   "PROPERTY_LIST_EMPTY_MESSAGE": "There are no properties to display",
-  "PROPERTY_SHOWING_LABEL": "Showing Properties"
+  "PROPERTY_SHOWING_LABEL": "Showing Properties",
+  "PROPERTY_ADD_MODAL_TITLE": "Add Property",
+  "PROPERTY_VIEW_MODAL_TITLE": "View Property",
+  "PROPERTY_DESCRIPTION_LABEL": "Description",
+  "PROPERTY_DEFAULT_VALUE_LABEL": "Default Value",
+  "PROPERTY_SCHEMA_LABEL": "Schema",
+  "PROPERTY_REQUIRED_LABEL": "Required",
+  "PROPERTY_TYPE_LABEL": "Type",
+  "PROPERTY_SET_DEFAULT_VALUE_MSG": "Set Default Value?",
+  "PROPERTY_NAME_LABEL": "Name"
 }
index c023401..3fcbc0f 100644 (file)
@@ -33,6 +33,7 @@ import org.onap.sdc.frontend.ci.tests.pages.component.workspace.InterfaceDefinit
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.Select;
 
 /**
  * Handles the input list inside the interface operation modal.
@@ -71,10 +72,14 @@ public class InterfaceOperationInputListComponent extends AbstractPageObject {
         if (value == null) {
             return;
         }
-        if (value instanceof String || value instanceof Integer || value instanceof Boolean) {
+        if (value instanceof String || value instanceof Integer) {
             fillSimpleValue(inputName, String.valueOf(value));
             return;
         }
+        if (value instanceof Boolean) {
+            fillBooleanValue(inputName, String.valueOf(value));
+            return;
+        }
         throw new UnsupportedOperationException("Set input value not yet implemented for value type: " + value.getClass().getName());
     }
 
@@ -141,6 +146,15 @@ public class InterfaceOperationInputListComponent extends AbstractPageObject {
         inputOpt.ifPresent(webElement -> webElement.findElement(simpleInputValueSelector).sendKeys(inputValue));
     }
 
+    private void fillBooleanValue(final String inputName, final String inputValue) {
+        toggleInputExpansion(inputName);
+        final Optional<WebElement> inputOpt = findInput(inputName);
+        assertTrue(inputOpt.isPresent(), String.format("Could not set value for input '%s'. The input was not found.", inputName));
+        final By simpleInputValueSelector = By.xpath(XpathSelector.BOOLEAN_VALUE_INPUT_RELATIVE_FROM_INPUT_INFO.getXPath());
+        final WebElement booleanDropdownWebElement = inputOpt.get().findElement(simpleInputValueSelector);
+        new Select(booleanDropdownWebElement).selectByValue(inputValue);
+    }
+
     @AllArgsConstructor
     private enum XpathSelector {
         WRAPPING_ELEMENT("//div[@class='input-tree']"),
@@ -148,6 +162,7 @@ public class InterfaceOperationInputListComponent extends AbstractPageObject {
         INPUT_LABEL("label[@class='input-label']"),
         INPUT_TYPE("em[@data-tests-id='input-type']"),
         SIMPLE_VALUE_INPUT_RELATIVE_FROM_INPUT_INFO("..//li[@class='input-value']/input"),
+        BOOLEAN_VALUE_INPUT_RELATIVE_FROM_INPUT_INFO("..//li[@class='input-value']/select"),
         EXPAND_ICON("em[contains(concat(' ',normalize-space(@class),' '),' round-expand-icon ')]"),
         DELETE_ICON("span[contains(concat(' ',normalize-space(@class),' '),' delete-btn ')]");