diff options
author | franciscovila <javier.paradela.vila@est.tech> | 2023-02-07 17:00:03 +0000 |
---|---|---|
committer | Michael Morris <michael.morris@est.tech> | 2023-02-09 19:17:04 +0000 |
commit | abbc25ad32db4f4c898bdaaea0b66c0a1d5fd8da (patch) | |
tree | a760d6e5495b797a8ee28acc42a83439e9b1a68f | |
parent | be850f6e83cbaa5c16f79c2c8732583f42e55429 (diff) |
Edit properties of non-normative data types
Develop all necessary changes in the UI to allow editing of non-normative data types
Issue-ID: SDC-4373
Signed-off-by: franciscovila <javier.paradela.vila@est.tech>
Change-Id: I37749fd3d2992f3134a09c07bb43c0208ce12a23
9 files changed, 133 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 0f6257157c..05c1fb7ad3 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 @@ -38,6 +38,7 @@ import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -169,6 +170,45 @@ public class DataTypeServlet extends BeGenericServlet { return Response.status(Status.CREATED).entity(property).build(); } + @PUT + @Path("{id}/properties") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Update a property in the given data type", method = "POST", description = "Update a property in the given data type", + responses = { + @ApiResponse(content = @Content(schema = @Schema(implementation = PropertyDefinitionDto.class))), + @ApiResponse(responseCode = "201", description = "Property updated in the data type"), + @ApiResponse(responseCode = "400", description = "Invalid payload"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "404", description = "Data type not found") + }) + @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) + public Response updateProperty(@Parameter(in = ParameterIn.PATH, required = true, description = "The data type id") + @PathParam("id") final String id, + @RequestBody(description = "Property to update", required = true) final PropertyDefinitionDto propertyDefinitionDto) { + Optional<DataTypeDataDefinition> dataTypeOptional = dataTypeOperation.getDataTypeByUid(id); + dataTypeOptional.orElseThrow(() -> { + throw new OperationException(ActionStatus.DATA_TYPE_NOT_FOUND, String.format("Failed to find data type '%s'", id)); + }); + DataTypeDataDefinition dataType = dataTypeOptional.get(); + String model = dataType.getModel(); + Optional<DataTypeDataDefinition> propertyDataType = dataTypeOperation.getDataTypeByNameAndModel(propertyDefinitionDto.getType(), model); + if (propertyDataType.isEmpty()) { + if (StringUtils.isEmpty(model)) { + model = Constants.DEFAULT_MODEL_NAME; + } + 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(Constants.DEFAULT_MODEL_NAME); + } + final PropertyDefinitionDto property = dataTypeOperation.updateProperty(id, propertyDefinitionDto); + dataTypeOperation.addPropertyToAdditionalTypeDataType(dataType, property); + dataTypeBusinessLogic.updateApplicationDataTypeCache(id); + return Response.status(Status.CREATED).entity(property).build(); + } + @GET @Path("{dataTypeName}/models") @Consumes(MediaType.APPLICATION_JSON) 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 bbc70f6c3f..391add9a94 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 @@ -281,6 +281,28 @@ public class DataTypeOperation extends AbstractOperation { return PropertyDefinitionDtoMapper.mapFrom(propertyDataDefinition); } + public PropertyDefinitionDto updateProperty(final String dataTypeId, final PropertyDefinitionDto propertyDefinitionDto) { + final String propertyName = propertyDefinitionDto.getName(); + LOGGER.debug("Updating property '{}' to data type '{}'.", propertyName, dataTypeId); + + getDataTypeByUid(dataTypeId).orElseThrow(DataTypeOperationExceptionSupplier.dataTypeNotFound(dataTypeId)); + + final Either<PropertyDefinition, JanusGraphOperationStatus> resultEither = + propertyOperation.updatePropertyAssociatedToNode(NodeTypeEnum.DataType, dataTypeId, PropertyDefinitionDtoMapper.mapTo(propertyDefinitionDto)); + if (resultEither.isRight()) { + final JanusGraphOperationStatus status = resultEither.right().value(); + LOGGER.debug("Could not update 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 update property '{}' on data type '{}'. JanusGraph status is '{}'", propertyName, dataTypeId, status); + throw DataTypeOperationExceptionSupplier.unexpectedErrorWhileCreatingProperty(dataTypeId, propertyName).get(); + } + LOGGER.debug("Property '{}' was updated in data type '{}'.", propertyName, dataTypeId); + final PropertyDefinition propertyData = resultEither.left().value(); + return PropertyDefinitionDtoMapper.mapFrom(propertyData); + } + public void addPropertyToAdditionalTypeDataType(DataTypeDataDefinition dataTypeDataDefinition, PropertyDefinitionDto property) { modelOperation.addPropertyToAdditionalType(ElementTypeEnum.DATA_TYPE, property, dataTypeDataDefinition.getModel(), dataTypeDataDefinition.getName()); } 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 0b8d2c8cb1..516474331b 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 @@ -382,6 +382,23 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe return Either.left(createNodeResult.left().value()); } + public Either<PropertyDefinition, JanusGraphOperationStatus> updatePropertyAssociatedToNode(NodeTypeEnum nodeType, String uniqueId, + PropertyDefinition newProperty) { + Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> oldPropertiesRes = findPropertiesOfNode(nodeType, uniqueId); + + if (oldPropertiesRes.isRight()) { + return Either.right(oldPropertiesRes.right().value()); + } else { + Map<String, PropertyDefinition> oldProperties = oldPropertiesRes.left().value(); + PropertyDefinition oldPropDef = oldProperties.get(newProperty.getName()); + JanusGraphOperationStatus status = updateOldProperty(newProperty, oldPropDef); + if (status != JanusGraphOperationStatus.OK) { + return Either.right(status); + } + } + return Either.left(newProperty); + } + public Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode(NodeTypeEnum nodeType, String uniqueId) { Map<String, PropertyDefinition> resourceProps = new HashMap<>(); Either<List<ImmutablePair<PropertyData, GraphEdge>>, JanusGraphOperationStatus> childrenNodes = janusGraphGenericDao @@ -501,6 +518,7 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe oldPropDef.setDefaultValue(newPropDef.getDefaultValue()); oldPropDef.setDescription(newPropDef.getDescription()); oldPropDef.setRequired(newPropDef.isRequired()); + oldPropDef.setConstraints(newPropDef.getConstraints()); // Type is updated to fix possible null type issue in janusGraph DB oldPropDef.setType(newPropDef.getType()); } 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 a2e4f48531..3ac4f7a31d 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 @@ -6,6 +6,7 @@ <div class="i-sdc-form-item"> <label class="i-sdc-form-label required">{{'PROPERTY_NAME_LABEL' | translate}}</label> <input class="i-sdc-form-input" + [ngClass]="{ 'disabled': property ? true : false }" type="text" data-tests-id="property-name" formControlName="name" @@ -13,7 +14,7 @@ </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"> + <select formControlName="type" (change)="onTypeChange()" [ngClass]="{ 'disabled': property ? true : false }"> <option [ngValue]="null">{{'GENERAL_LABEL_SELECT' | translate}}</option> <option *ngFor="let type of typeList" [ngValue]="type">{{type}}</option> 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 56db2cefeb..8eb04c0cbb 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 @@ -45,7 +45,7 @@ export class AddPropertyComponent implements OnInit, OnDestroy { private validConstraints: boolean = true; private valueChangesSub: Subscription; private descriptionForm: FormControl = new FormControl(undefined); - private requiredForm: FormControl = new FormControl(false, Validators.required); + private requiredForm: FormControl = new FormControl(false); nameForm: FormControl = new FormControl(undefined, [Validators.required]); typeForm: FormControl = new FormControl(undefined, Validators.required); schemaForm: FormControl = new FormControl(undefined, (control: AbstractControl): ValidationErrors | null => { @@ -163,9 +163,10 @@ export class AddPropertyComponent implements OnInit, OnDestroy { private emitValidityChange(): void { const isValid: boolean = this.formGroup.valid; + this.findInvalidControls().forEach(name => console.error("Validation error in field: " + name)); this.onValidityChange.emit({ isValid: isValid && this.validConstraints, - property: isValid ? this.buildPropertyFromForm() : undefined + property: isValid ? this.buildPropertyFromForm() : this.nameForm.value }); } @@ -173,6 +174,7 @@ export class AddPropertyComponent implements OnInit, OnDestroy { const property = new PropertyBEModel(); property.name = this.nameForm.value; property.type = this.typeForm.value; + property.required = this.requiredForm.value; property.constraints = this.constraintsForm.value; if (this.schemaForm.value) { property.schemaType = this.schemaForm.value; @@ -241,15 +243,27 @@ export class AddPropertyComponent implements OnInit, OnDestroy { } this.property.constraints = constraints.constraints; } - else { - this.constraintsForm.setValue(constraints.constraints); - } + + this.constraintsForm.setValue(constraints.constraints); + this.validConstraints = constraints.valid; + let formValid = constraints.valid && this.findInvalidControls().length === 0; this.onValidityChange.emit({ - isValid: constraints.valid, - property: constraints.valid ? this.buildPropertyFromForm() : undefined + isValid: formValid, + property: formValid ? this.buildPropertyFromForm() : undefined }); } + + findInvalidControls() { + const invalid = []; + const controls = this.formGroup.controls; + for (const name in controls) { + if (controls[name].invalid) { + invalid.push(name); + } + } + return invalid; + } } export class PropertyValidationEvent { 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 ec67a02a1b..e657520ee4 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 @@ -38,9 +38,9 @@ <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 *ngFor="let property of filteredProperties" [attr.data-tests-id]="'property-row-' + property.name" class="flex-container data-row" (click)="onNameClick(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> + <a [attr.data-tests-id]="'property-name-' + property.name" [ngClass]="{'disabled': false}">{{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> 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 83651fc73c..60edd13c2d 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 @@ -132,12 +132,20 @@ export class TypeWorkspacePropertiesComponent implements OnInit { this.filter(); } + private updateProperty(oldProperty: PropertyBEModel, newProperty: PropertyBEModel) { + this.properties.forEach((value,index)=>{ + if(value.name == oldProperty.name) this.properties.splice(index,1); + }); + this.properties.push(newProperty); + this.filter(); + } + onClickAddProperty() { - this.openAddPropertyModal(); + this.openAddPropertyModal(null, false); } private openAddPropertyModal(property?: PropertyBEModel, readOnly: boolean = false) { - const modalTitle = this.translateService.translate('PROPERTY_ADD_MODAL_TITLE'); + const modalTitle = this.translateService.translate(property ? 'PROPERTY_EDIT_MODAL_TITLE' : 'PROPERTY_ADD_MODAL_TITLE'); const modalButtons = []; let disableSaveButtonFlag = true; let propertyFromModal: PropertyBEModel = undefined; @@ -156,9 +164,16 @@ export class TypeWorkspacePropertiesComponent implements OnInit { 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)); - }); + if (property) { + this.dataTypeService.updateProperty(this.dataType.uniqueId, propertyFromModal).subscribe(property => { + this.updateProperty(propertyFromModal, new PropertyBEModel(property)); + }); + } + else { + this.dataTypeService.createProperty(this.dataType.uniqueId, propertyFromModal).subscribe(property => { + this.addProperty(new PropertyBEModel(property)); + }); + } this.modalService.closeCurrentModal(); }, (): boolean => { @@ -185,8 +200,8 @@ export class TypeWorkspacePropertiesComponent implements OnInit { modal.instance.open(); } - onRowClick(property: PropertyBEModel) { - this.openAddPropertyModal(property, true); + onNameClick(property: PropertyBEModel) { + this.openAddPropertyModal(property, this.isViewOnly); } private showPropertiesMap(properties: Array<PropertyBEModel>): void { 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 7e18d0a4ba..38714c96da 100644 --- a/catalog-ui/src/app/ng2/services/data-type.service.ts +++ b/catalog-ui/src/app/ng2/services/data-type.service.ts @@ -92,6 +92,11 @@ export class DataTypeService { return this.httpClient.post<PropertyBEModel>(url, property); } + public updateProperty(id: string, property: PropertyBEModel): Observable<PropertyBEModel> { + const url = `${this.dataTypeUrl}/${id}/properties`; + return this.httpClient.put<PropertyBEModel>(url, property); + } + public createImportedType(model: string, importingFile: File): Observable<any> { const url = `${this.dataTypeUploadUrl}/datatypesyaml`; const formData = new FormData(); diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json index bb2ebd596d..a98bf4c8c1 100644 --- a/catalog-ui/src/assets/languages/en_US.json +++ b/catalog-ui/src/assets/languages/en_US.json @@ -594,6 +594,7 @@ "PROPERTY_LIST_EMPTY_MESSAGE": "There are no properties to display", "PROPERTY_SHOWING_LABEL": "Showing Properties", "PROPERTY_ADD_MODAL_TITLE": "Add Property", + "PROPERTY_EDIT_MODAL_TITLE": "Update Property", "PROPERTY_VIEW_MODAL_TITLE": "View Property", "PROPERTY_DESCRIPTION_LABEL": "Description", "PROPERTY_DEFAULT_VALUE_LABEL": "Default Value", |