Constraints in data type view 93/133093/7
authorfranciscovila <javier.paradela.vila@est.tech>
Tue, 31 Jan 2023 14:24:02 +0000 (14:24 +0000)
committerVasyl Razinkov <vasyl.razinkov@est.tech>
Wed, 8 Feb 2023 22:19:53 +0000 (22:19 +0000)
Develop all necessary changes in the UI to allow managing data type constraints

Issue-ID: SDC-4331
Signed-off-by: franciscovila <javier.paradela.vila@est.tech>
Change-Id: I337438ba088e4f2f4978a1aff2408eda8157b892

12 files changed:
catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/dto/PropertyDefinitionDto.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/mapper/PropertyDefinitionDtoMapper.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java
catalog-ui/src/app/ng2/pages/properties-assignment/constraints/constraints.component.ts
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.html
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.spec.ts
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/add-property/add-property.component.ts
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts
catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts
catalog-ui/src/app/ng2/services/data-type.service.ts
catalog-ui/src/app/utils/service-data-type-reader.ts

index 2e67a9b..0f62571 100644 (file)
@@ -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);
index 4144e1f..79643aa 100644 (file)
@@ -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<PropertyConstraint> constraints;
+    private List<Object> constraints;
 
 }
index 67600a5..6e8134c 100644 (file)
 
 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<PropertyConstraint> 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<PropertyConstraint> 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));
index 103221c..bbc70f6 100644 (file)
@@ -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);
     }
 
index 525bf88..2fb8b64 100644 (file)
@@ -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));
+        });
+      }
     }
   }
 
index af72e6d..a2e4f48 100644 (file)
                 [readOnly]="readOnly">
       </textarea>
     </div>
-    <div class="default-value-container i-sdc-form-item" *ngIf="showDefaultValue()">
+      <div class="constraints-section i-sdc-form-item">
+          <label class="i-sdc-form-label" *ngIf="property ? property.constraints: false">Constraints</label>
+          <ng-container>
+              <app-constraints [propertyConstraints]="property ? property.constraints ? property.constraints : null : null"
+                               [isViewOnly]="readOnly"
+                               [propertyType]="property ? property.type : typeForm.value"
+                               (onConstraintChange)="onConstraintChange($event)">
+              </app-constraints>
+          </ng-container>
+      </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}}
index 7e2c312..fdb6eef 100644 (file)
@@ -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}
index d4d88b5..56db2ce 100644 (file)
@@ -42,6 +42,7 @@ export class AddPropertyComponent implements OnInit, OnDestroy {
 
     @Output() onValidityChange: EventEmitter<PropertyValidationEvent> = new EventEmitter<PropertyValidationEvent>();
 
+    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 {
index bcc5fe9..83651fc 100644 (file)
@@ -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 {
index e7ddb46..c517dd2 100644 (file)
@@ -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,
index 3a27801..7e18d0a 100644 (file)
@@ -88,7 +88,7 @@ export class DataTypeService {
     }
 
     public createProperty(id: string, property: PropertyBEModel): Observable<PropertyBEModel> {
-        const url = `${this.dataTypeUrl}/${id}/properties`
+        const url = `${this.dataTypeUrl}/${id}/properties`;
         return this.httpClient.post<PropertyBEModel>(url, property);
     }
 
index 99b3d3a..9686f3d 100644 (file)
@@ -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(<ConstraintTypes>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