From c6cb16f234b8ae9de4aede3ca09a57e6ca177abe Mon Sep 17 00:00:00 2001 From: franciscovila Date: Tue, 31 Jan 2023 14:24:02 +0000 Subject: [PATCH 1/1] Constraints in data type view Develop all necessary changes in the UI to allow managing data type constraints Issue-ID: SDC-4331 Signed-off-by: franciscovila Change-Id: I337438ba088e4f2f4978a1aff2408eda8157b892 --- .../openecomp/sdc/be/servlets/DataTypeServlet.java | 4 + .../sdc/be/model/dto/PropertyDefinitionDto.java | 3 +- .../model/mapper/PropertyDefinitionDtoMapper.java | 31 +++++- .../model/operations/impl/DataTypeOperation.java | 1 + .../constraints/constraints.component.ts | 10 +- .../add-property/add-property.component.html | 13 ++- .../add-property/add-property.component.spec.ts | 3 +- .../add-property/add-property.component.ts | 27 ++++- .../type-workspace-properties.component.ts | 38 ++++++- .../pages/type-workspace/type-workspace.module.ts | 4 +- .../src/app/ng2/services/data-type.service.ts | 2 +- .../src/app/utils/service-data-type-reader.ts | 116 +++++++++++++++++++++ 12 files changed, 235 insertions(+), 17 deletions(-) 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 2e67a9bb47..0f6257157c 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 @@ -62,6 +62,7 @@ import org.openecomp.sdc.common.api.Constants; import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode; import org.openecomp.sdc.common.log.wrappers.Logger; import org.springframework.stereotype.Controller; +import org.apache.commons.lang3.StringUtils; @Loggable(prepend = true, value = Loggable.DEBUG, trim = false) @Path("/v1/catalog/data-types") @@ -159,6 +160,9 @@ public class DataTypeServlet extends BeGenericServlet { throw new OperationException(ActionStatus.INVALID_MODEL, String.format("Property model is not the same as the data type model. Must be '%s'", model)); } + if (StringUtils.isEmpty(dataType.getModel())) { + dataType.setModel("SDC AID"); + } final PropertyDefinitionDto property = dataTypeOperation.createProperty(id, propertyDefinitionDto); dataTypeOperation.addPropertyToAdditionalTypeDataType(dataType, property); dataTypeBusinessLogic.updateApplicationDataTypeCache(id); 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 4144e1f5b6..79643aa560 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 @@ -23,7 +23,6 @@ package org.openecomp.sdc.be.model.dto; import java.util.List; import lombok.Data; -import org.openecomp.sdc.be.model.PropertyConstraint; @Data public class PropertyDefinitionDto { @@ -36,6 +35,6 @@ public class PropertyDefinitionDto { private Boolean required; private Object value; private Object defaultValue; - private List constraints; + private List constraints; } 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 67600a5dbe..6e8134c9a5 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 @@ -21,9 +21,14 @@ package org.openecomp.sdc.be.model.mapper; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.google.gson.Gson; import java.util.ArrayList; import java.util.List; +import java.util.Map; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.apache.commons.collections4.CollectionUtils; @@ -33,10 +38,15 @@ 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; +import org.openecomp.sdc.be.model.operations.impl.PropertyOperation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class PropertyDefinitionDtoMapper { + private static final Logger LOGGER = LoggerFactory.getLogger(PropertyDefinitionDtoMapper.class); + public static PropertyDefinition mapTo(final PropertyDefinitionDto propertyDefinitionDto) { final var propertyDefinition = new PropertyDefinition(); propertyDefinition.setUniqueId(propertyDefinitionDto.getUniqueId()); @@ -52,8 +62,23 @@ public class PropertyDefinitionDtoMapper { } if (CollectionUtils.isNotEmpty(propertyDefinitionDto.getConstraints())) { final List propertyConstraints = new ArrayList<>(); + + propertyDefinitionDto.getConstraints().forEach(rawConstraint -> { + ObjectMapper mapper = new ObjectMapper(); + + SimpleModule module = new SimpleModule("customDeserializationModule"); + module.addDeserializer(PropertyConstraint.class, new PropertyOperation.PropertyConstraintJacksonDeserializer()); + mapper.registerModule(module); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + try { + PropertyConstraint constraint = + mapper.readValue(new Gson().toJson(rawConstraint, Map.class), PropertyConstraint.class); + propertyConstraints.add(constraint); + } catch (JsonProcessingException e) { + LOGGER.error("Could not parse constraint '{}' for property '{}'", rawConstraint, propertyDefinitionDto.getName()); + } + }); propertyDefinition.setConstraints(propertyConstraints); - propertyConstraints.addAll(propertyDefinitionDto.getConstraints()); } propertyDefinition.setDescription(propertyDefinitionDto.getDescription()); propertyDefinition.setValue(new Gson().toJson(propertyDefinitionDto.getValue())); @@ -71,9 +96,7 @@ public class PropertyDefinitionDtoMapper { propertyDefinitionDto.setRequired(propertyDefinition.getRequired()); propertyDefinitionDto.setSchemaType(propertyDefinition.getSchemaType()); if (CollectionUtils.isNotEmpty(propertyDefinition.getConstraints())) { - final List propertyConstraints = new ArrayList<>(); - propertyDefinitionDto.setConstraints(propertyConstraints); - propertyConstraints.addAll(propertyDefinition.getConstraints()); + propertyDefinitionDto.setConstraints(new ArrayList<>(propertyDefinition.getConstraints())); } propertyDefinitionDto.setValue(new Gson().fromJson(propertyDataDefinition.getValue(), Object.class)); propertyDefinitionDto.setDefaultValue(new Gson().fromJson(propertyDataDefinition.getDefaultValue(), Object.class)); 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 103221cb76..bbc70f6c3f 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 @@ -277,6 +277,7 @@ public class DataTypeOperation extends AbstractOperation { final PropertyData propertyData = resultEither.left().value(); final PropertyDataDefinition propertyDataDefinition = propertyData.getPropertyDataDefinition(); propertyDataDefinition.setName(propertyName); + propertyDataDefinition.setPropertyConstraints(propertyData.getConstraints()); return PropertyDefinitionDtoMapper.mapFrom(propertyDataDefinition); } diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/constraints/constraints.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/constraints/constraints.component.ts index 525bf884f0..2fb8b64e54 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/constraints/constraints.component.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/constraints/constraints.component.ts @@ -52,10 +52,12 @@ export class ConstraintsComponent implements OnInit { } } this.constraints = new Array(); - if (changes.propertyConstraints && changes.propertyConstraints.currentValue) { - changes.propertyConstraints.currentValue.forEach((constraint: any) => { - this.constraints.push(this.getConstraintFromPropertyBEModel(constraint)); - }); + if(changes.propertyConstraints) { + if (changes.propertyConstraints.currentValue) { + changes.propertyConstraints.currentValue.forEach((constraint: any) => { + this.constraints.push(this.getConstraintFromPropertyBEModel(constraint)); + }); + } } } 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 index af72e6d6d6..a2e4f48531 100644 --- 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 @@ -40,7 +40,18 @@ [readOnly]="readOnly"> -
+
+ + + + + +
+ +
{{'PROPERTY_SET_DEFAULT_VALUE_MSG' | translate}} 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 index 7e2c312792..fdb6eefcbf 100644 --- 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 @@ -25,7 +25,7 @@ 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 {Component, Input, NO_ERRORS_SCHEMA} from "@angular/core"; import {DataTypeModel} from "../../../../../models/data-types"; import {SchemaPropertyGroupModel} from "../../../../../models/schema-property"; import {DataTypeService} from "../../../../services/data-type.service"; @@ -68,6 +68,7 @@ describe('AddPropertyComponent', () => { TranslateModule, UiElementsModule ], + schemas: [NO_ERRORS_SCHEMA], providers: [ {provide: DataTypeService, useValue: dataTypeServiceMock}, {provide: TranslateService, useValue: translateServiceMock} 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 index d4d88b5deb..56db2cefeb 100644 --- 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 @@ -42,6 +42,7 @@ export class AddPropertyComponent implements OnInit, OnDestroy { @Output() onValidityChange: EventEmitter = new EventEmitter(); + private validConstraints: boolean = true; private valueChangesSub: Subscription; private descriptionForm: FormControl = new FormControl(undefined); private requiredForm: FormControl = new FormControl(false, Validators.required); @@ -53,8 +54,10 @@ export class AddPropertyComponent implements OnInit, OnDestroy { } return null; }); + hasDefaultValueForm: FormControl = new FormControl(false, Validators.required); defaultValueForm: FormControl = new FormControl(undefined); + constraintsForm: FormControl = new FormControl(undefined); formGroup: FormGroup = new FormGroup({ 'name': this.nameForm, 'description': this.descriptionForm, @@ -63,6 +66,7 @@ export class AddPropertyComponent implements OnInit, OnDestroy { 'schema': this.schemaForm, 'defaultValue': this.defaultValueForm, 'hasDefaultValue': this.hasDefaultValueForm, + 'constraints': this.constraintsForm, }); isLoading: boolean = false; @@ -99,6 +103,9 @@ export class AddPropertyComponent implements OnInit, OnDestroy { this.showSchema = this.typeNeedsSchema(); this.updateDataType(); this.resetDefaultValue(); + if (this.property) { + this.property.constraints = []; + } } private updateDataType(): void { @@ -116,6 +123,7 @@ export class AddPropertyComponent implements OnInit, OnDestroy { this.showSchema = this.typeNeedsSchema(); this.requiredForm.setValue(this.property.required); this.schemaForm.setValue(this.property.schemaType); + this.constraintsForm.setValue(this.property.constraints); this.initDefaultValueForm(); } @@ -156,7 +164,7 @@ export class AddPropertyComponent implements OnInit, OnDestroy { private emitValidityChange(): void { const isValid: boolean = this.formGroup.valid; this.onValidityChange.emit({ - isValid: isValid, + isValid: isValid && this.validConstraints, property: isValid ? this.buildPropertyFromForm() : undefined }); } @@ -165,6 +173,7 @@ export class AddPropertyComponent implements OnInit, OnDestroy { const property = new PropertyBEModel(); property.name = this.nameForm.value; property.type = this.typeForm.value; + property.constraints = this.constraintsForm.value; if (this.schemaForm.value) { property.schemaType = this.schemaForm.value; } @@ -225,6 +234,22 @@ export class AddPropertyComponent implements OnInit, OnDestroy { return new SchemaPropertyGroupModel(schemaProperty); } + onConstraintChange = (constraints: any): void => { + if (this.property) { + if (!this.property.constraints) { + this.property.constraints = []; + } + this.property.constraints = constraints.constraints; + } + else { + this.constraintsForm.setValue(constraints.constraints); + } + this.validConstraints = constraints.valid; + this.onValidityChange.emit({ + isValid: constraints.valid, + property: constraints.valid ? this.buildPropertyFromForm() : undefined + }); + } } export class PropertyValidationEvent { 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 bcc5fe9c28..83651fc73c 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 @@ -32,6 +32,7 @@ import {TranslateService} from "../../../shared/translator/translate.service"; import {AddPropertyComponent, PropertyValidationEvent} from "./add-property/add-property.component"; import {IWorkspaceViewModelScope} from "../../../../view-models/workspace/workspace-view-model"; import {SdcUiServices} from "onap-ui-angular/dist"; +import {ToscaTypeHelper} from "../../../../utils/tosca-type-helper"; @Component({ selector: 'app-type-workspace-properties', @@ -192,14 +193,47 @@ export class TypeWorkspacePropertiesComponent implements OnInit { this.properties = properties.map(value => { const property = new PropertyBEModel(value); if (property.defaultValue) { - property.defaultValue = JSON.parse(property.defaultValue); + if (!this.isTypeSimple(property.type) && typeof property.defaultValue === 'string') { + property.defaultValue = JSON.parse(property.defaultValue); + } else { + property.defaultValue = property.defaultValue; + } } - return property; }); this.filteredProperties = Array.from(this.properties); this.sort(); } + + public isTypeSimple(value:any): boolean { + return ToscaTypeHelper.isTypeSimple(value); + } + + onConstraintChange = (constraints: any): void => { + if (!this.$scope.invalidMandatoryFields) { + this.$scope.footerButtons[0].disabled = !constraints.valid; + } else { + this.$scope.footerButtons[0].disabled = this.$scope.invalidMandatoryFields; + } + if (!constraints.constraints || constraints.constraints.length == 0) { + this.$scope.editPropertyModel.property.propertyConstraints = null; + this.$scope.editPropertyModel.property.constraints = null; + return; + } + this.$scope.editPropertyModel.property.propertyConstraints = this.serializePropertyConstraints(constraints.constraints); + this.$scope.editPropertyModel.property.constraints = constraints.constraints; + } + + private serializePropertyConstraints(constraints: any[]): string[] { + if (constraints) { + let stringConstraints = new Array(); + constraints.forEach((constraint) => { + stringConstraints.push(JSON.stringify(constraint)); + }) + return stringConstraints; + } + return null; + } } interface TableHeader { 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 e7ddb46602..c517dd22c8 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 @@ -39,6 +39,7 @@ 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"; import {AutoCompleteModule} from "onap-ui-angular/dist/autocomplete/autocomplete.module"; +import {ConstraintsModule} from "../properties-assignment/constraints/constraints.module"; @NgModule({ imports: [ @@ -52,7 +53,8 @@ import {AutoCompleteModule} from "onap-ui-angular/dist/autocomplete/autocomplete InterfaceOperationHandlerModule, NgxDatatableModule, SvgIconModule, - AutoCompleteModule + AutoCompleteModule, + ConstraintsModule ], declarations: [ TypeWorkspaceComponent, 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 3a27801315..7e18d0a4ba 100644 --- a/catalog-ui/src/app/ng2/services/data-type.service.ts +++ b/catalog-ui/src/app/ng2/services/data-type.service.ts @@ -88,7 +88,7 @@ export class DataTypeService { } public createProperty(id: string, property: PropertyBEModel): Observable { - const url = `${this.dataTypeUrl}/${id}/properties` + const url = `${this.dataTypeUrl}/${id}/properties`; return this.httpClient.post(url, property); } diff --git a/catalog-ui/src/app/utils/service-data-type-reader.ts b/catalog-ui/src/app/utils/service-data-type-reader.ts index 99b3d3ae63..9686f3d40e 100644 --- a/catalog-ui/src/app/utils/service-data-type-reader.ts +++ b/catalog-ui/src/app/utils/service-data-type-reader.ts @@ -19,6 +19,7 @@ */ import {DataTypeModel, PropertyBEModel} from "../models"; +import {Constraint, ConstraintTypes} from "../ng2/pages/properties-assignment/constraints/constraints.component"; import {load} from 'js-yaml'; export class ServiceDataTypeReader { @@ -81,8 +82,123 @@ export class ServiceDataTypeReader { property.type = properties[key]["type"]; property.schemaType = properties[key]["schema"]; property.required = properties[key]["required"]; + const constraints = properties[key]["constraints"]; + + if (constraints) { + property.constraints = new Array(); + let constraintArray = new Array(); + Object.keys(constraints).forEach((constrainKey) => { + Object.keys(constraints[constrainKey]).forEach((kc) => { + let newConstraint = this.mapValuesToConstraint(kc, constraints[constrainKey][kc]); + let jsonObject = this.getConstraintFormat(newConstraint); + constraintArray.push(jsonObject); + + }); + }); + property.constraints.push(constraintArray); + } this.serviceDataType.properties.push(property); } ); } + + private getConstraintFormat(constraint: Constraint): any { + switch (constraint.type) { + case ConstraintTypes.equal: + return { + [ConstraintTypes.equal]: constraint.value + } + case ConstraintTypes.less_or_equal: + return { + [ConstraintTypes.less_or_equal]: constraint.value + } + case ConstraintTypes.less_than: + return { + [ConstraintTypes.less_than]: constraint.value + } + case ConstraintTypes.greater_or_equal: + return { + [ConstraintTypes.greater_or_equal]: constraint.value + } + case ConstraintTypes.greater_than: + return { + [ConstraintTypes.greater_than]: constraint.value + } + case ConstraintTypes.in_range: + return { + [ConstraintTypes.in_range]: constraint.value + } + case ConstraintTypes.length: + return { + [ConstraintTypes.length]: constraint.value + } + case ConstraintTypes.max_length: + return { + [ConstraintTypes.max_length]: constraint.value + } + case ConstraintTypes.min_length: + return { + [ConstraintTypes.min_length]: constraint.value + } + case ConstraintTypes.pattern: + return { + [ConstraintTypes.pattern]: constraint.value + } + case ConstraintTypes.valid_values: + return { + [ConstraintTypes.valid_values]: constraint.value + } + default: + return; + } + } + + private mapValuesToConstraint(type: string, value: any):Constraint { + let constraintType: ConstraintTypes; + let constraintValue: any; + if (!type) { + constraintType = ConstraintTypes.null; + constraintValue = ""; + } else if(type === "valid_values"){ + constraintType = ConstraintTypes.valid_values; + constraintValue = value; + } else if(type === "equal") { + constraintType = ConstraintTypes.equal; + constraintValue = value; + } else if(type === "greater_than") { + constraintType = ConstraintTypes.greater_than; + constraintValue = value; + } else if(type === "greater_or_equal") { + constraintType = ConstraintTypes.greater_or_equal; + constraintValue = value; + } else if(type === "less_than") { + constraintType = ConstraintTypes.less_than; + constraintValue = value; + } else if(type === "less_or_equal") { + constraintType = ConstraintTypes.less_or_equal; + constraintValue = value; + } else if(type === "in_range") { + constraintType = ConstraintTypes.in_range; + constraintValue = value; + } else if(type === "range_max_value" || type === "range_min_value") { + constraintType = ConstraintTypes.in_range; + constraintValue = value; + } else if(type === "length") { + constraintType = ConstraintTypes.length; + constraintValue = value; + } else if(type === "min_length") { + constraintType = ConstraintTypes.min_length; + constraintValue = value; + } else if(type === "max_length") { + constraintType = ConstraintTypes.max_length; + constraintValue = value; + } else if(type === "pattern") { + constraintType = ConstraintTypes.pattern; + constraintValue = value; + } + return { + type:constraintType, + value:constraintValue + } + } } \ No newline at end of file -- 2.16.6