From 3f9669fdae5f7c6cb1bfe34742df35dfe3a14aa7 Mon Sep 17 00:00:00 2001 From: "andre.schmid" Date: Mon, 18 Jul 2022 17:25:41 +0100 Subject: [PATCH] Support a custom yaml value in tosca function MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Allows to add a custom YAML value to properties in the TOSCA function feature. Change-Id: I15e65088a18537d9832428717be826ac0ef6049a Issue-ID: SDC-4099 Signed-off-by: André Schmid --- .../openecomp/sdc/be/tosca/PropertyConvertor.java | 4 + .../app/models/properties-inputs/input-fe-model.ts | 2 +- .../models/properties-inputs/property-be-model.ts | 4 +- .../models/properties-inputs/property-fe-model.ts | 30 ++-- catalog-ui/src/app/models/yaml-function.ts | 37 +++++ .../dynamic-property.component.html | 20 +-- .../services/properties.utils.ts | 2 +- .../tosca-concat-function.component.spec.ts | 3 +- .../tosca-function/tosca-function.component.html | 3 + .../tosca-function/tosca-function.component.ts | 21 ++- .../tosca-function/tosca-function.module.ts | 2 + .../yaml-function/yaml-function.component.html | 35 +++++ .../yaml-function/yaml-function.component.less | 40 ++++++ .../yaml-function/yaml-function.component.spec.ts | 53 ++++++++ .../yaml-function/yaml-function.component.ts | 80 +++++++++++ .../property-form-view-model.ts | 6 +- catalog-ui/src/assets/languages/en_US.json | 1 + .../be/datatypes/elements/CustomYamlFunction.java | 5 +- .../elements/ToscaFunctionJsonDeserializer.java | 22 ++- .../datatypes/elements/CustomYamlFunctionTest.java | 50 +++++++ .../ToscaFunctionJsonDeserializerTest.java | 151 ++++++++++----------- .../concatFunction.json | 37 +++++ .../getFunctionMissingType.json | 11 ++ .../toscaFunctionJsonDeserializer/getInput.json | 12 ++ .../getInputLegacy.json | 12 ++ .../yamlFunction.json | 4 + 26 files changed, 527 insertions(+), 120 deletions(-) create mode 100644 catalog-ui/src/app/models/yaml-function.ts create mode 100644 catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.html create mode 100644 catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.less create mode 100644 catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.spec.ts create mode 100644 catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.ts create mode 100644 common-be/src/test/java/org/openecomp/sdc/be/datatypes/elements/CustomYamlFunctionTest.java create mode 100644 common-be/src/test/resources/toscaFunctionJsonDeserializer/concatFunction.json create mode 100644 common-be/src/test/resources/toscaFunctionJsonDeserializer/getFunctionMissingType.json create mode 100644 common-be/src/test/resources/toscaFunctionJsonDeserializer/getInput.json create mode 100644 common-be/src/test/resources/toscaFunctionJsonDeserializer/getInputLegacy.json create mode 100644 common-be/src/test/resources/toscaFunctionJsonDeserializer/yamlFunction.json diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/PropertyConvertor.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/PropertyConvertor.java index 5bb3d116f8..148e69907c 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/PropertyConvertor.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/PropertyConvertor.java @@ -34,6 +34,7 @@ import java.util.function.Supplier; 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.datatypes.elements.ToscaFunctionType; import org.openecomp.sdc.be.model.Component; import org.openecomp.sdc.be.model.DataTypeDefinition; import org.openecomp.sdc.be.model.PropertyDefinition; @@ -115,6 +116,9 @@ public class PropertyConvertor { return null; } } + if (property.isToscaFunction() && property.getToscaFunction().getType() == ToscaFunctionType.YAML) { + return new Yaml().load(property.getValue()); + } try { ToscaMapValueConverter mapConverterInst = ToscaMapValueConverter.getInstance(); ToscaValueConverter innerConverter = null; diff --git a/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts b/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts index 347b8a1665..bcdc95c18e 100644 --- a/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts +++ b/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts @@ -83,7 +83,7 @@ export class InputFEModel extends InputBEModel { } public getDefaultValueObj(): any { - return PropertyFEModel.parseValueObj(this.defaultValue, this.type, this.derivedDataType); + return PropertyFEModel.parseValueObj(this.defaultValue, this.type, this.derivedDataType, this.isToscaFunction()); } public resetDefaultValueObjValidation() { diff --git a/catalog-ui/src/app/models/properties-inputs/property-be-model.ts b/catalog-ui/src/app/models/properties-inputs/property-be-model.ts index ae7141353b..9429036f6e 100644 --- a/catalog-ui/src/app/models/properties-inputs/property-be-model.ts +++ b/catalog-ui/src/app/models/properties-inputs/property-be-model.ts @@ -189,9 +189,9 @@ export class PropertyBEModel { } /** - * Checks whether the property value is a tosca get function (e.g. get_input, get_property, get_attribute) + * Checks whether the property value is a TOSCA function (e.g. get_input, get_property, get_attribute, concat, etc.) */ - public isToscaGetFunction(): boolean { + public isToscaFunction(): boolean { return this.toscaFunction != null; } } diff --git a/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts b/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts index f231ec8aa8..eb18c4e4f9 100644 --- a/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts +++ b/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts @@ -21,9 +21,9 @@ */ import * as _ from "lodash"; -import {SchemaPropertyGroupModel, SchemaProperty} from '../schema-property'; -import { PROPERTY_DATA, PROPERTY_TYPES } from 'app/utils'; -import { FilterPropertiesAssignmentData, PropertyBEModel, DerivedPropertyType, DerivedFEPropertyMap, DerivedFEProperty } from 'app/models'; +import {PROPERTY_DATA, PROPERTY_TYPES} from 'app/utils'; +import {DerivedFEProperty, DerivedPropertyType, PropertyBEModel} from 'app/models'; +import * as jsYaml from 'js-yaml'; export class PropertyFEModel extends PropertyBEModel { @@ -96,7 +96,7 @@ export class PropertyFEModel extends PropertyBEModel { } public getValueObj = (): any => { - return PropertyFEModel.parseValueObj(this.value, this.type, this.derivedDataType, this.defaultValue); + return PropertyFEModel.parseValueObj(this.value, this.type, this.derivedDataType, this.isToscaFunction(), this.defaultValue); } public setNonDeclared = (childPath?: string): void => { @@ -278,23 +278,27 @@ export class PropertyFEModel extends PropertyBEModel { return valueObj.trim(); } - static parseValueObj(value: string, propertyType: PROPERTY_TYPES, propertyDerivedType: DerivedPropertyType, defaultValue?: string): any { - let valueObj; + static parseValueObj(value: string, propertyType: PROPERTY_TYPES, propertyDerivedType: DerivedPropertyType, isToscaFunction: boolean, + defaultValue?: string): any { + if (isToscaFunction) { + return jsYaml.load(value); + } if (propertyDerivedType === DerivedPropertyType.SIMPLE) { - valueObj = value || defaultValue || null; // use null for empty value object + const valueObj = value || defaultValue || null; // use null for empty value object if (valueObj && propertyType !== PROPERTY_TYPES.STRING && propertyType !== PROPERTY_TYPES.TIMESTAMP && propertyType !== PROPERTY_TYPES.JSON && PROPERTY_DATA.SCALAR_TYPES.indexOf(propertyType) == -1) { - valueObj = JSON.parse(value); // the value object contains the real value ans not the value as string + return JSON.parse(value); // the value object contains the real value ans not the value as string } - } else if (propertyDerivedType == DerivedPropertyType.LIST) { - valueObj = _.merge([], JSON.parse(defaultValue || '[]'), JSON.parse(value || '[]')); // value object should be merged value and default value. Value takes higher precedence. Set value object to empty obj if undefined. - } else { - valueObj = _.merge({}, JSON.parse(defaultValue || '{}'), JSON.parse(value || '{}')); // value object should be merged value and default value. Value takes higher precedence. Set value object to empty obj if undefined. + return valueObj; } - return valueObj; + if (propertyDerivedType == DerivedPropertyType.LIST) { + return _.merge([], JSON.parse(defaultValue || '[]'), JSON.parse(value || '[]')); // value object should be merged value and default value. Value takes higher precedence. Set value object to empty obj if undefined. + } + + return _.merge({}, JSON.parse(defaultValue || '{}'), JSON.parse(value || '{}')); // value object should be merged value and default value. Value takes higher precedence. Set value object to empty obj if undefined. }; static cleanValueObj(valueObj: any, unsetEmpty?: boolean): any { diff --git a/catalog-ui/src/app/models/yaml-function.ts b/catalog-ui/src/app/models/yaml-function.ts new file mode 100644 index 0000000000..e80d783424 --- /dev/null +++ b/catalog-ui/src/app/models/yaml-function.ts @@ -0,0 +1,37 @@ +/* + * - + * ============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 {ToscaFunction} from "./tosca-function"; +import {ToscaFunctionType} from "./tosca-function-type.enum"; +import {ToscaFunctionParameter} from "./tosca-function-parameter"; + +export class YamlFunction implements ToscaFunction, ToscaFunctionParameter { + type = ToscaFunctionType.YAML; + value: any; + + constructor(yamlFunction?: YamlFunction) { + if (!yamlFunction) { + return; + } + this.value = yamlFunction.value; + } + +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html index 81f0d533b4..f855265157 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html @@ -15,13 +15,13 @@
- +
{{property.name}}
@@ -41,31 +41,31 @@
- +
- +
{{property.type | contentAfterLastDot }}
{{property.schema.property.type | contentAfterLastDot }}
- +
- + Add value to list Add value to map @@ -74,7 +74,7 @@
-
+
{ let component: ToscaConcatFunctionComponent; @@ -34,7 +35,7 @@ describe('ToscaConcatFunctionComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ToscaConcatFunctionComponent, ToscaFunctionComponent, ToscaGetFunctionComponent], + declarations: [ToscaConcatFunctionComponent, ToscaFunctionComponent, ToscaGetFunctionComponent, YamlFunctionComponent], imports: [ FormsModule, ReactiveFormsModule, diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.html b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.html index e98f688eef..f93973cb16 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.html +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.html @@ -36,6 +36,9 @@ [functionType]="toscaFunctionTypeForm.value" (onValidityChange)="onGetFunctionValidityChange($event)">
+
+ +
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.ts index 076e1182ad..ae778006ce 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.ts @@ -30,6 +30,7 @@ import {ToscaGetFunctionValidationEvent} from "./tosca-get-function/tosca-get-fu import {ToscaFunction} from "../../../../models/tosca-function"; import {ToscaConcatFunctionValidationEvent} from "./tosca-concat-function/tosca-concat-function.component"; import {PROPERTY_TYPES} from "../../../../utils/constants"; +import {YamlFunctionValidationEvent} from "./yaml-function/yaml-function.component"; @Component({ selector: 'tosca-function', @@ -75,7 +76,7 @@ export class ToscaFunctionComponent implements OnInit { this.onValidFunction.emit(this.toscaFunctionForm.value); } }); - this.initToscaGetFunction(); + this.initToscaFunction(); this.emitValidityChange(); this.isInitialized = true; } @@ -84,8 +85,8 @@ export class ToscaFunctionComponent implements OnInit { return (!this.toscaFunctionForm.value && !this.toscaFunctionTypeForm.value) || this.formGroup.valid; } - private initToscaGetFunction() { - if (!this.property.isToscaGetFunction()) { + private initToscaFunction() { + if (!this.property.isToscaFunction()) { return; } this.toscaFunctionForm.setValue(this.property.toscaFunction); @@ -99,6 +100,7 @@ export class ToscaFunctionComponent implements OnInit { if (this.property.type === PROPERTY_TYPES.STRING) { this.toscaFunctions.push(ToscaFunctionType.CONCAT); } + this.toscaFunctions.push(ToscaFunctionType.YAML); } private resetForm(): void { @@ -126,6 +128,10 @@ export class ToscaFunctionComponent implements OnInit { return this.isGetInputSelected() || this.isGetPropertySelected() || this.isGetAttributeSelected(); } + isYamlFunctionSelected(): boolean { + return this.formGroup.get('toscaFunctionType').value === ToscaFunctionType.YAML; + } + onClearValues() { this.resetForm(); } @@ -150,6 +156,14 @@ export class ToscaFunctionComponent implements OnInit { } } + onYamlFunctionValidityChange(validationEvent: YamlFunctionValidationEvent) { + if (validationEvent.isValid) { + this.toscaFunctionForm.setValue(validationEvent.value); + } else { + this.toscaFunctionForm.setValue(undefined); + } + } + private emitValidityChange() { const isValid = this.validate(); this.onValidityChange.emit({ @@ -157,6 +171,7 @@ export class ToscaFunctionComponent implements OnInit { toscaFunction: isValid ? this.toscaFunctionForm.value : undefined }); } + } export class ToscaFunctionValidationEvent { diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.module.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.module.ts index 2db76cf544..19cf2fb260 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.module.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.module.ts @@ -27,12 +27,14 @@ import { ToscaFunctionComponent } from './tosca-function.component'; import { SdcUiComponentsModule } from 'onap-ui-angular'; import { ToscaGetFunctionComponent } from './tosca-get-function/tosca-get-function.component'; import { ToscaConcatFunctionComponent } from './tosca-concat-function/tosca-concat-function.component'; +import { YamlFunctionComponent } from './yaml-function/yaml-function.component'; @NgModule({ declarations: [ ToscaFunctionComponent, ToscaGetFunctionComponent, ToscaConcatFunctionComponent, + YamlFunctionComponent, ], imports: [ CommonModule, diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.html b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.html new file mode 100644 index 0000000000..ac51d413be --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.html @@ -0,0 +1,35 @@ + + +
+
+
+ +
+
+ {{'YAML_FUNCTION_INVALID_YAML_ERROR' | translate}} +
+
+
+
+
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.less b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.less new file mode 100644 index 0000000000..7cbcbc4985 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.less @@ -0,0 +1,40 @@ +/* + * - + * ============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 "../../../../../../assets/styles/mixins.less"; + +.yaml-text-area { + width: 100%; + min-height: 250px; + max-height: 400px; + font-size: 12px; +} + +.error { + color: @func_color_q; +} + +.input-error { + .error(); + border: solid 1px @func_color_q; + outline: none; + box-sizing: border-box; +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.spec.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.spec.ts new file mode 100644 index 0000000000..500fe643c8 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.spec.ts @@ -0,0 +1,53 @@ +/* + * - + * ============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 {YamlFunctionComponent} from './yaml-function.component'; +import {FormsModule, ReactiveFormsModule} from "@angular/forms"; +import {TranslateModule} from "../../../../shared/translator/translate.module"; + +describe('YamlFunctionComponent', () => { + let component: YamlFunctionComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [YamlFunctionComponent], + imports: [ + FormsModule, + ReactiveFormsModule, + TranslateModule + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(YamlFunctionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.ts new file mode 100644 index 0000000000..e0272270d1 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.ts @@ -0,0 +1,80 @@ +/* + * - + * ============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, OnInit, Output} from '@angular/core'; +import {YamlFunction} from "../../../../../models/yaml-function"; +import {FormControl, FormGroup, Validators, AbstractControl, ValidationErrors, ValidatorFn} from "@angular/forms"; +import * as jsYaml from 'js-yaml'; + +@Component({ + selector: 'app-yaml-function', + templateUrl: './yaml-function.component.html', + styleUrls: ['./yaml-function.component.less'] +}) +export class YamlFunctionComponent implements OnInit { + + @Input() yamlFunction: YamlFunction; + @Output() onValidityChange: EventEmitter = new EventEmitter(); + + yamlValueForm: FormControl = new FormControl('', [Validators.minLength(1), YamlValidator()]); + formGroup: FormGroup = new FormGroup( + { + 'value': this.yamlValueForm + } + ); + + ngOnInit() { + this.formGroup.valueChanges.subscribe(() => { + this.onValidityChange.emit({ + isValid: this.formGroup.valid, + value: this.formGroup.valid ? this.buildYamlFunctionFromForm() : undefined + }); + }); + if (this.yamlFunction) { + this.yamlValueForm.setValue(this.yamlFunction.value); + } + } + + private buildYamlFunctionFromForm(): YamlFunction { + const yamlFunction = new YamlFunction(); + yamlFunction.value = this.yamlValueForm.value; + return yamlFunction; + } +} + +export class YamlFunctionValidationEvent { + isValid: boolean; + value: YamlFunction; +} + +export function YamlValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if (!control.value) { + return {invalidYaml: {value: control.value}}; + } + try { + jsYaml.load(control.value); + return null; + } catch (e) { + return {invalidYaml: {value: control.value}}; + } + }; +} \ No newline at end of file diff --git a/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts index 1419f3a217..5ea1106a8f 100644 --- a/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts +++ b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts @@ -145,14 +145,14 @@ export class PropertyFormViewModel { }; private initToscaGetFunction() { - this.$scope.editPropertyModel.hasGetFunctionValue = this.$scope.editPropertyModel.property.isToscaGetFunction(); + this.$scope.editPropertyModel.hasGetFunctionValue = this.$scope.editPropertyModel.property.isToscaFunction(); this.$scope.editPropertyModel.isGetFunctionValid = true; } private initForNotSimpleType = ():void => { const property = this.$scope.editPropertyModel.property; this.$scope.isTypeDataType = this.DataTypesService.isDataTypeForPropertyType(this.$scope.editPropertyModel.property); - if (property.isToscaGetFunction()) { + if (property.isToscaFunction()) { this.initValueForGetFunction(); return; } @@ -249,7 +249,7 @@ export class PropertyFormViewModel { 'property': property, types: PROPERTY_DATA.TYPES, simpleTypes: PROPERTY_DATA.SIMPLE_TYPES, - hasGetFunctionValue: property.isToscaGetFunction(), + hasGetFunctionValue: property.isToscaFunction(), isGetFunctionValid: true, }; this.$scope.isPropertyValueOwner = this.isPropertyValueOwner; diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json index 96e01dbaf9..2bb8d75b21 100644 --- a/catalog-ui/src/assets/languages/en_US.json +++ b/catalog-ui/src/assets/languages/en_US.json @@ -521,6 +521,7 @@ "TOSCA_FUNCTION_NO_INPUT_FOUND": "No input found with type {{type}}", "TOSCA_FUNCTION_NO_PROPERTY_FOUND": "No property found with type {{type}}", "TOSCA_FUNCTION_NO_ATTRIBUTE_FOUND": "No attribute found with type {{type}}", + "YAML_FUNCTION_INVALID_YAML_ERROR": "Invalid YAML", "=========== AUTOMATED UPGRADE ===========": "", "RESOURCE_UPGRADE_TITLE": "Upgrade Services", "SERVICE_UPGRADE_TITLE": "Update Service References", diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CustomYamlFunction.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CustomYamlFunction.java index 84543e29f7..e9d584486d 100644 --- a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CustomYamlFunction.java +++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CustomYamlFunction.java @@ -36,7 +36,10 @@ public class CustomYamlFunction implements ToscaFunction, ToscaFunctionParameter @Override public String getValue() { - return new Yaml().dump(yamlValue); + if (yamlValue == null) { + return null; + } + return yamlValue instanceof String ? (String) yamlValue : new Yaml().dump(yamlValue); } @Override diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ToscaFunctionJsonDeserializer.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ToscaFunctionJsonDeserializer.java index 74aed12471..fda832b98e 100644 --- a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ToscaFunctionJsonDeserializer.java +++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ToscaFunctionJsonDeserializer.java @@ -32,10 +32,14 @@ import java.util.List; import org.apache.commons.lang3.StringUtils; import org.openecomp.sdc.be.datatypes.enums.PropertySource; import org.openecomp.sdc.be.datatypes.tosca.ToscaGetFunctionType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; public class ToscaFunctionJsonDeserializer extends StdDeserializer { + private static final Logger LOGGER = LoggerFactory.getLogger(ToscaFunctionJsonDeserializer.class); + public ToscaFunctionJsonDeserializer() { this(null); } @@ -74,16 +78,26 @@ public class ToscaFunctionJsonDeserializer extends StdDeserializer> yamlValue2 = Map.of("entry", Map.of("property1", "value1")); + yamlFunction.setYamlValue(yamlValue2); + assertEquals(new Yaml().dump(yamlValue2), yamlFunction.getValue()); + } +} \ No newline at end of file diff --git a/common-be/src/test/java/org/openecomp/sdc/be/datatypes/elements/ToscaFunctionJsonDeserializerTest.java b/common-be/src/test/java/org/openecomp/sdc/be/datatypes/elements/ToscaFunctionJsonDeserializerTest.java index e11b661098..b17a3dc3a3 100644 --- a/common-be/src/test/java/org/openecomp/sdc/be/datatypes/elements/ToscaFunctionJsonDeserializerTest.java +++ b/common-be/src/test/java/org/openecomp/sdc/be/datatypes/elements/ToscaFunctionJsonDeserializerTest.java @@ -21,35 +21,36 @@ package org.openecomp.sdc.be.datatypes.elements; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 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 com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.exc.ValueInstantiationException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; +import java.util.Map; import org.junit.jupiter.api.Test; import org.openecomp.sdc.be.datatypes.enums.PropertySource; import org.openecomp.sdc.be.datatypes.tosca.ToscaGetFunctionType; +import org.yaml.snakeyaml.Yaml; class ToscaFunctionJsonDeserializerTest { + private static final Path TEST_RESOURCES_PATH = Path.of("src/test/resources/toscaFunctionJsonDeserializer"); + @Test - void testGetInputToscaFunction() throws JsonProcessingException { - final String toscaGetInputFunction = "{\n" - + " \"propertyUniqueId\": \"e57525d7-2115-4934-9ba4-9cebfa22bad2.nf_naming\",\n" - + " \"type\": \"GET_INPUT\",\n" - + " \"propertySource\": \"SELF\",\n" - + " \"propertyName\": \"instance_name\",\n" - + " \"sourceName\": \"ciResVFc26a0b30ec20\",\n" - + " \"sourceUniqueId\": \"aee643c9-6c8e-4a24-af7a-a9aff5c072c0\",\n" - + " \"propertyPathFromSource\": [\n" - + " \"nf_naming\",\n" - + " \"instance_name\"\n" - + " ]\n" - + " }"; - ToscaFunction toscaFunction = parseToscaFunction(toscaGetInputFunction); + void testGetInputToscaFunction() throws IOException { + //given + final String toscaGetInputFunction = Files.readString(TEST_RESOURCES_PATH.resolve("getInput.json")); + //when + final ToscaFunction toscaFunction = parseToscaFunction(toscaGetInputFunction); + //then assertTrue(toscaFunction instanceof ToscaGetFunctionDataDefinition); final ToscaGetFunctionDataDefinition toscaGetFunction = (ToscaGetFunctionDataDefinition) toscaFunction; assertEquals(ToscaFunctionType.GET_INPUT, toscaGetFunction.getType()); @@ -63,20 +64,12 @@ class ToscaFunctionJsonDeserializerTest { } @Test - void testGetInputToscaFunctionLegacyConversion() throws JsonProcessingException { - final String toscaGetInputFunction = "{\n" - + " \"propertyUniqueId\": \"e57525d7-2115-4934-9ba4-9cebfa22bad2.nf_naming\",\n" - + " \"functionType\": \"GET_INPUT\",\n" - + " \"propertySource\": \"SELF\",\n" - + " \"propertyName\": \"instance_name\",\n" - + " \"sourceName\": \"ciResVFc26a0b30ec20\",\n" - + " \"sourceUniqueId\": \"aee643c9-6c8e-4a24-af7a-a9aff5c072c0\",\n" - + " \"propertyPathFromSource\": [\n" - + " \"nf_naming\",\n" - + " \"instance_name\"\n" - + " ]\n" - + " }"; - ToscaFunction toscaFunction = parseToscaFunction(toscaGetInputFunction); + void testGetInputToscaFunctionLegacyConversion() throws IOException { + //given + final String toscaGetInputFunction = Files.readString(TEST_RESOURCES_PATH.resolve("getInputLegacy.json")); + //when + final ToscaFunction toscaFunction = parseToscaFunction(toscaGetInputFunction); + //then assertTrue(toscaFunction instanceof ToscaGetFunctionDataDefinition); final ToscaGetFunctionDataDefinition toscaGetFunction = (ToscaGetFunctionDataDefinition) toscaFunction; assertEquals(ToscaFunctionType.GET_INPUT, toscaGetFunction.getType()); @@ -84,70 +77,66 @@ class ToscaFunctionJsonDeserializerTest { } @Test - void testNoFunctionTypeProvided() { - final String toscaGetInputFunction = "{\n" - + " \"propertyUniqueId\": \"e57525d7-2115-4934-9ba4-9cebfa22bad2.nf_naming\",\n" - + " \"propertySource\": \"SELF\",\n" - + " \"propertyName\": \"instance_name\",\n" - + " \"sourceName\": \"ciResVFc26a0b30ec20\",\n" - + " \"sourceUniqueId\": \"aee643c9-6c8e-4a24-af7a-a9aff5c072c0\",\n" - + " \"propertyPathFromSource\": [\n" - + " \"nf_naming\",\n" - + " \"instance_name\"\n" - + " ]\n" - + " }"; + void testNoFunctionTypeProvided() throws IOException { + //given + final String toscaGetInputFunction = Files.readString(TEST_RESOURCES_PATH.resolve("getFunctionMissingType.json")); + //when/then final ValueInstantiationException actualException = assertThrows(ValueInstantiationException.class, () -> parseToscaFunction(toscaGetInputFunction)); assertTrue(actualException.getMessage().contains("Attribute type not provided")); } @Test - void testConcatToscaFunction() throws JsonProcessingException { - final String toscaGetInputFunction = "{\n" - + " \"type\": \"CONCAT\",\n" - + " \"parameters\": [\n" - + " {\n" - + " \"propertyUniqueId\": \"e57525d7-2115-4934-9ba4-9cebfa22bad2.nf_naming\",\n" - + " \"type\": \"GET_INPUT\",\n" - + " \"propertySource\": \"SELF\",\n" - + " \"propertyName\": \"instance_name\",\n" - + " \"sourceName\": \"ciResVFc26a0b30ec20\",\n" - + " \"sourceUniqueId\": \"aee643c9-6c8e-4a24-af7a-a9aff5c072c0\",\n" - + " \"propertyPathFromSource\": [\n" - + " \"nf_naming\",\n" - + " \"instance_name\"\n" - + " ]\n" - + " }, {\n" - + " \"type\": \"STRING\",\n" - + " \"value\": \"my string\"\n" - + " },\n" - + " {\n" - + " \"type\": \"CONCAT\",\n" - + " \"parameters\": [\n" - + " {\n" - + " \"type\": \"STRING\",\n" - + " \"value\": \"string1\"\n" - + " },\n" - + " {\n" - + " \"type\": \"STRING\",\n" - + " \"value\": \"string2\"\n" - + " }\n" - + " ]\n" - + " }\n" - + " ]\n" - + "}"; - ToscaFunction toscaFunction = parseToscaFunction(toscaGetInputFunction); + void testConcatToscaFunction() throws IOException { + //given + final String toscaConcatFunction = Files.readString(TEST_RESOURCES_PATH.resolve("concatFunction.json")); + //when + final ToscaFunction toscaFunction = parseToscaFunction(toscaConcatFunction); + //then assertTrue(toscaFunction instanceof ToscaConcatFunction); + final Object yamlObject = new Yaml().load(toscaFunction.getValue()); + assertTrue(yamlObject instanceof Map); + final Map yamlMap = (Map) yamlObject; + final Object concatFunctionObj = yamlMap.get(ToscaFunctionType.CONCAT.getName()); + assertNotNull(concatFunctionObj); + assertTrue(concatFunctionObj instanceof List); + final List concatFunctionParameters = (List) concatFunctionObj; + assertEquals(3, concatFunctionParameters.size(), "Expecting three parameters"); + assertTrue(concatFunctionParameters.get(0) instanceof Map); + final Map parameter1Map = (Map) concatFunctionParameters.get(0); + assertNotNull(parameter1Map.get(ToscaFunctionType.GET_INPUT.getName())); + assertTrue(parameter1Map.get(ToscaFunctionType.GET_INPUT.getName()) instanceof List); + List getInputParameters = (List) parameter1Map.get(ToscaFunctionType.GET_INPUT.getName()); + assertEquals(2, getInputParameters.size(), "Expecting two parameters in the get_input function"); + assertEquals("nf_naming", getInputParameters.get(0)); + assertEquals("instance_name", getInputParameters.get(1)); + + assertEquals("my string", concatFunctionParameters.get(1)); + + assertTrue(concatFunctionParameters.get(2) instanceof Map); + final Map parameter2Map = (Map) concatFunctionParameters.get(2); + assertNotNull(parameter2Map.get(ToscaFunctionType.CONCAT.getName())); + assertTrue(parameter2Map.get(ToscaFunctionType.CONCAT.getName()) instanceof List); + List concatParameters = (List) parameter2Map.get(ToscaFunctionType.CONCAT.getName()); + assertEquals(3, concatParameters.size(), "Expecting two parameters in the sub concat function"); + assertEquals("string1", concatParameters.get(0)); + assertEquals("string2", concatParameters.get(1)); + assertTrue(concatParameters.get(2) instanceof Map); + Map yamlFunctionValueMap = (Map) concatParameters.get(2); + assertTrue(yamlFunctionValueMap.get("myList") instanceof List); + assertTrue(yamlFunctionValueMap.get("get_something") instanceof List); + assertTrue(yamlFunctionValueMap.get("string") instanceof String); } @Test - void testYamlFunction() throws JsonProcessingException { - String yamlFunction = "{\n" - + " \"type\": \"YAML\",\n" - + " \"value\": \"tosca_definitions_version: tosca_simple_yaml_1_0_0\\nnode_types: \\n tosca.nodes.Compute:\\n derived_from: tosca.nodes.Root\\n attributes:\\n private_address:\\n type: string\\n public_address:\\n type: string\\n networks:\\n type: map\\n entry_schema:\\n type: tosca.datatypes.network.NetworkInfo\\n ports:\\n type: map\\n entry_schema:\\n type: tosca.datatypes.network.PortInfo\\n requirements:\\n - local_storage: \\n capability: tosca.capabilities.Attachment\\n node: tosca.nodes.BlockStorage\\n relationship: tosca.relationships.AttachesTo\\n occurrences: [0, UNBOUNDED] \\n capabilities:\\n host: \\n type: tosca.capabilities.Container\\n valid_source_types: [tosca.nodes.SoftwareComponent] \\n endpoint :\\n type: tosca.capabilities.Endpoint.Admin \\n os: \\n type: tosca.capabilities.OperatingSystem\\n scalable:\\n type: tosca.capabilities.Scalable\\n binding:\\n type: tosca.capabilities.network.Bindable\\n\"\n" - + "}"; - ToscaFunction toscaFunction = parseToscaFunction(yamlFunction); + void testYamlFunction() throws IOException { + //given + final String yamlFunction = Files.readString(TEST_RESOURCES_PATH.resolve("yamlFunction.json")); + //when + final ToscaFunction toscaFunction = parseToscaFunction(yamlFunction); + //then assertTrue(toscaFunction instanceof CustomYamlFunction); + assertDoesNotThrow(() -> new Yaml().load(toscaFunction.getValue())); } private ToscaFunction parseToscaFunction(final String toscaFunctionJson) throws JsonProcessingException { diff --git a/common-be/src/test/resources/toscaFunctionJsonDeserializer/concatFunction.json b/common-be/src/test/resources/toscaFunctionJsonDeserializer/concatFunction.json new file mode 100644 index 0000000000..808d96f3e6 --- /dev/null +++ b/common-be/src/test/resources/toscaFunctionJsonDeserializer/concatFunction.json @@ -0,0 +1,37 @@ +{ + "type": "CONCAT", + "parameters": [ + { + "propertyUniqueId": "e57525d7-2115-4934-9ba4-9cebfa22bad2.nf_naming", + "type": "GET_INPUT", + "propertySource": "SELF", + "propertyName": "instance_name", + "sourceName": "ciResVFc26a0b30ec20", + "sourceUniqueId": "aee643c9-6c8e-4a24-af7a-a9aff5c072c0", + "propertyPathFromSource": [ + "nf_naming", + "instance_name" + ] + }, { + "type": "STRING", + "value": "my string" + }, + { + "type": "CONCAT", + "parameters": [ + { + "type": "STRING", + "value": "string1" + }, + { + "type": "STRING", + "value": "string2" + }, + { + "type": "YAML", + "value": "myList: [1, two, three three]\nget_something: [SELF, something]\nstring: this is my string\n" + } + ] + } + ] +} \ No newline at end of file diff --git a/common-be/src/test/resources/toscaFunctionJsonDeserializer/getFunctionMissingType.json b/common-be/src/test/resources/toscaFunctionJsonDeserializer/getFunctionMissingType.json new file mode 100644 index 0000000000..23a2678587 --- /dev/null +++ b/common-be/src/test/resources/toscaFunctionJsonDeserializer/getFunctionMissingType.json @@ -0,0 +1,11 @@ +{ + "propertyUniqueId": "e57525d7-2115-4934-9ba4-9cebfa22bad2.nf_naming", + "propertySource": "SELF", + "propertyName": "instance_name", + "sourceName": "ciResVFc26a0b30ec20", + "sourceUniqueId": "aee643c9-6c8e-4a24-af7a-a9aff5c072c0", + "propertyPathFromSource": [ + "nf_naming", + "instance_name" + ] +} \ No newline at end of file diff --git a/common-be/src/test/resources/toscaFunctionJsonDeserializer/getInput.json b/common-be/src/test/resources/toscaFunctionJsonDeserializer/getInput.json new file mode 100644 index 0000000000..a992f22a75 --- /dev/null +++ b/common-be/src/test/resources/toscaFunctionJsonDeserializer/getInput.json @@ -0,0 +1,12 @@ +{ + "propertyUniqueId": "e57525d7-2115-4934-9ba4-9cebfa22bad2.nf_naming", + "type": "GET_INPUT", + "propertySource": "SELF", + "propertyName": "instance_name", + "sourceName": "ciResVFc26a0b30ec20", + "sourceUniqueId": "aee643c9-6c8e-4a24-af7a-a9aff5c072c0", + "propertyPathFromSource": [ + "nf_naming", + "instance_name" + ] +} \ No newline at end of file diff --git a/common-be/src/test/resources/toscaFunctionJsonDeserializer/getInputLegacy.json b/common-be/src/test/resources/toscaFunctionJsonDeserializer/getInputLegacy.json new file mode 100644 index 0000000000..c09571bb3e --- /dev/null +++ b/common-be/src/test/resources/toscaFunctionJsonDeserializer/getInputLegacy.json @@ -0,0 +1,12 @@ +{ + "propertyUniqueId": "e57525d7-2115-4934-9ba4-9cebfa22bad2.nf_naming", + "functionType": "GET_INPUT", + "propertySource": "SELF", + "propertyName": "instance_name", + "sourceName": "ciResVFc26a0b30ec20", + "sourceUniqueId": "aee643c9-6c8e-4a24-af7a-a9aff5c072c0", + "propertyPathFromSource": [ + "nf_naming", + "instance_name" + ] +} \ No newline at end of file diff --git a/common-be/src/test/resources/toscaFunctionJsonDeserializer/yamlFunction.json b/common-be/src/test/resources/toscaFunctionJsonDeserializer/yamlFunction.json new file mode 100644 index 0000000000..dfe63f00f4 --- /dev/null +++ b/common-be/src/test/resources/toscaFunctionJsonDeserializer/yamlFunction.json @@ -0,0 +1,4 @@ +{ + "type": "YAML", + "value": "tosca_definitions_version: tosca_simple_yaml_1_0_0\nnode_types: \n tosca.nodes.Compute:\n derived_from: tosca.nodes.Root\n attributes:\n private_address:\n type: string\n public_address:\n type: string\n networks:\n type: map\n entry_schema:\n type: tosca.datatypes.network.NetworkInfo\n ports:\n type: map\n entry_schema:\n type: tosca.datatypes.network.PortInfo\n requirements:\n - local_storage: \n capability: tosca.capabilities.Attachment\n node: tosca.nodes.BlockStorage\n relationship: tosca.relationships.AttachesTo\n occurrences: [0, UNBOUNDED] \n capabilities:\n host: \n type: tosca.capabilities.Container\n valid_source_types: [tosca.nodes.SoftwareComponent] \n endpoint :\n type: tosca.capabilities.Endpoint.Admin \n os: \n type: tosca.capabilities.OperatingSystem\n scalable:\n type: tosca.capabilities.Scalable\n binding:\n type: tosca.capabilities.network.Bindable\n" +} \ No newline at end of file -- 2.16.6