diff options
32 files changed, 2321 insertions, 372 deletions
diff --git a/asdctool/src/main/resources/config/error-configuration.yaml b/asdctool/src/main/resources/config/error-configuration.yaml index e201f8979a..f8efb0b4e0 100644 --- a/asdctool/src/main/resources/config/error-configuration.yaml +++ b/asdctool/src/main/resources/config/error-configuration.yaml @@ -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" + diff --git a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml index 75f8904519..4be1cbab66 100644 --- a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml +++ b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml @@ -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" diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java index e39e0d2c0d..711ebcbd9c 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java @@ -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(); + } + } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/DefaultExceptionMapper.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/DefaultExceptionMapper.java index 6b161f9d4e..a2a8659b71 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/DefaultExceptionMapper.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/DefaultExceptionMapper.java @@ -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(); } } diff --git a/catalog-be/src/main/resources/config/error-configuration.yaml b/catalog-be/src/main/resources/config/error-configuration.yaml index 0830dda7b4..ee5f7fa836 100644 --- a/catalog-be/src/main/resources/config/error-configuration.yaml +++ b/catalog-be/src/main/resources/config/error-configuration.yaml @@ -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" + diff --git a/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml b/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml index d31d4a6203..ad40131f28 100644 --- a/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml +++ b/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml @@ -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" + diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java index 55297f9e13..cd5a6e5d7a 100644 --- a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java +++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java @@ -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 index 6a647b1d44..0000000000 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/exception/OperationException.java +++ /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); - } - -} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/exception/supplier/DataTypeOperationExceptionSupplier.java b/catalog-model/src/main/java/org/openecomp/sdc/be/exception/supplier/DataTypeOperationExceptionSupplier.java index 75cacdd7a8..6b444b26ad 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/exception/supplier/DataTypeOperationExceptionSupplier.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/exception/supplier/DataTypeOperationExceptionSupplier.java @@ -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) + ); } } diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/dto/PropertyDefinitionDto.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/dto/PropertyDefinitionDto.java index bac73d9e1d..4144e1f5b6 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/dto/PropertyDefinitionDto.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/dto/PropertyDefinitionDto.java @@ -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; diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/mapper/PropertyDefinitionDtoMapper.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/mapper/PropertyDefinitionDtoMapper.java index 8566f55339..67600a5dbe 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/mapper/PropertyDefinitionDtoMapper.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/mapper/PropertyDefinitionDtoMapper.java @@ -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); diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java index 4194ab70db..7cd042a27d 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java @@ -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); + } + } diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java index 1068e31db6..cd87dd4fe7 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java @@ -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()); } diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperationTest.java index 1f448fd875..26fb7658f1 100644 --- a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperationTest.java +++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperationTest.java @@ -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"; diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html index 1e0804eb04..6a5bd5a007 100644 --- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html +++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html @@ -67,12 +67,16 @@ <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> @@ -123,7 +127,7 @@ [type]="getDataType(schema.property.type)" [dataTypeMap]="dataTypeMap" [valueObjRef]="valueObjRef[i]" - [schema]="schema" + [schema]="buildSchemaGroupProperty()" [nestingLevel]="nestingLevel + 1" [listIndex]="i" [isListChild]="true" @@ -155,7 +159,7 @@ [type]="getDataType(schema.property.type)" [dataTypeMap]="dataTypeMap" [valueObjRef]="valueObjRef[key]" - [schema]="schema" + [schema]="buildSchemaGroupProperty()" [isMapChild]="true" [nestingLevel]="nestingLevel + 1" [isViewOnly]="isViewOnly" diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts index 88ff8deec6..145aad687c 100644 --- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts +++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts @@ -21,12 +21,13 @@ 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) { diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts index c829ff9e5a..b4fc9d1bdb 100644 --- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts +++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts @@ -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 index 0000000000..af72e6d6d6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.html @@ -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 index 0000000000..2c3300237c --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.less @@ -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 index 0000000000..7e2c312792 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.spec.ts @@ -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 index 0000000000..dc1a0329ed --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.ts @@ -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 diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html index 61c319eb6f..ec67a02a1b 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.html @@ -25,35 +25,36 @@ <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> diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less index 9c101e8ce3..fd43bd94a1 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.less @@ -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; @@ -127,11 +127,15 @@ } .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; } diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts index 6be572d16a..e6e9c12d14 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts @@ -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(); diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts index 931fd3d97c..90bc89ae08 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts @@ -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 diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html index 193c94e6ad..4d29e8673a 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html @@ -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> diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts index 021f84af09..5b2d3bf030 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts @@ -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 { diff --git a/catalog-ui/src/app/ng2/services/data-type.service.ts b/catalog-ui/src/app/ng2/services/data-type.service.ts index 74d48d35de..298ba90b31 100644 --- a/catalog-ui/src/app/ng2/services/data-type.service.ts +++ b/catalog-ui/src/app/ng2/services/data-type.service.ts @@ -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); diff --git a/catalog-ui/src/app/utils/constants.ts b/catalog-ui/src/app/utils/constants.ts index 9d11f54db9..c8fb1966b4 100644 --- a/catalog-ui/src/app/utils/constants.ts +++ b/catalog-ui/src/app/utils/constants.ts @@ -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 index 0000000000..7faa90a7fe --- /dev/null +++ b/catalog-ui/src/app/utils/tosca-type-helper.ts @@ -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; + } + +} diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json index fc5445a6c3..5dec922980 100644 --- a/catalog-ui/src/assets/languages/en_US.json +++ b/catalog-ui/src/assets/languages/en_US.json @@ -41,6 +41,10 @@ "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", @@ -506,6 +510,7 @@ "MODAL_SAVE": "Save", "MODAL_CREATE": "Create", "MODAL_CANCEL": "Cancel", + "MODAL_CLOSE": "Close", "MODAL_DELETE": "Delete", "=========== POLICY AND GROUPS ===========": "", "ADD_TARGETS": "Add Targets", @@ -582,5 +587,14 @@ "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" } diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceOperationInputListComponent.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceOperationInputListComponent.java index c023401deb..3fcbc0fbe8 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceOperationInputListComponent.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceOperationInputListComponent.java @@ -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 ')]"); |