Support a custom yaml value in tosca function 73/129973/5
authorandre.schmid <andre.schmid@est.tech>
Mon, 18 Jul 2022 16:25:41 +0000 (17:25 +0100)
committerMichael Morris <michael.morris@est.tech>
Fri, 29 Jul 2022 20:08:14 +0000 (20:08 +0000)
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 <andre.schmid@est.tech>
26 files changed:
catalog-be/src/main/java/org/openecomp/sdc/be/tosca/PropertyConvertor.java
catalog-ui/src/app/models/properties-inputs/input-fe-model.ts
catalog-ui/src/app/models/properties-inputs/property-be-model.ts
catalog-ui/src/app/models/properties-inputs/property-fe-model.ts
catalog-ui/src/app/models/yaml-function.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html
catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts
catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.spec.ts
catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.html
catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.ts
catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.module.ts
catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.less [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.spec.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/yaml-function/yaml-function.component.ts [new file with mode: 0644]
catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts
catalog-ui/src/assets/languages/en_US.json
common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CustomYamlFunction.java
common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ToscaFunctionJsonDeserializer.java
common-be/src/test/java/org/openecomp/sdc/be/datatypes/elements/CustomYamlFunctionTest.java [new file with mode: 0644]
common-be/src/test/java/org/openecomp/sdc/be/datatypes/elements/ToscaFunctionJsonDeserializerTest.java
common-be/src/test/resources/toscaFunctionJsonDeserializer/concatFunction.json [new file with mode: 0644]
common-be/src/test/resources/toscaFunctionJsonDeserializer/getFunctionMissingType.json [new file with mode: 0644]
common-be/src/test/resources/toscaFunctionJsonDeserializer/getInput.json [new file with mode: 0644]
common-be/src/test/resources/toscaFunctionJsonDeserializer/getInputLegacy.json [new file with mode: 0644]
common-be/src/test/resources/toscaFunctionJsonDeserializer/yamlFunction.json [new file with mode: 0644]

index 5bb3d11..148e699 100644 (file)
@@ -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;
index 347b8a1..bcdc95c 100644 (file)
@@ -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() {
index ae71413..9429036 100644 (file)
@@ -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;
     }
 }
index f231ec8..eb18c4e 100644 (file)
@@ -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(<string>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 (file)
index 0000000..e80d783
--- /dev/null
@@ -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
index 81f0d53..f855265 100644 (file)
  
  
 <div *ngIf="!property.hidden" class="dynamic-property-row nested-level-{{nestedLevel}}" [@fadeIn]
-    [ngClass]="{'selected': selectedPropertyId && selectedPropertyId === property.propertiesName, 'readonly':  property.isDisabled || property.isDeclared || property.isToscaGetFunction()}"
+    [ngClass]="{'selected': selectedPropertyId && selectedPropertyId === property.propertiesName, 'readonly':  property.isDisabled || property.isDeclared || property.isToscaFunction()}"
     [class.with-top-border]="property.isChildOfListOrMap"
     (click)="onClickPropertyRow(property, $event)">
     <!-- LEFT CELL -->
     <ng-container *ngIf="!isPropertyFEModel">
         <div class="table-cell" *ngIf="canBeDeclared" [ngClass]="{'filtered':property.name === propertyNameSearchText}" [class.round-checkbox]="property.isDeclared"> <!-- simple children of complex type [@checkEffect]="property.isDeclared"-->
-            <checkbox *ngIf="hasDeclareOption" [(checked)]="property.isSelected" [disabled]="property.isDisabled || property.isDeclared || readonly || property.isToscaGetFunction()" (checkedChange)="checkProperty.emit(property.propertiesName)" ></checkbox>
+            <checkbox *ngIf="hasDeclareOption" [(checked)]="property.isSelected" [disabled]="property.isDisabled || property.isDeclared || readonly || property.isToscaFunction()" (checkedChange)="checkProperty.emit(property.propertiesName)" ></checkbox>
             <div class="inner-cell-div" tooltip="{{property.name}}"><span>{{property.name}}</span></div>
         </div>
         <div class="table-cell" *ngIf="!canBeDeclared && !property.isChildOfListOrMap">
         </div>
     </ng-container>
     <!-- RIGHT CELL OR FULL WIDTH CELL-->
-    <ng-container *ngIf="propType == derivedPropertyTypes.SIMPLE || property.isDeclared || property.isToscaGetFunction() || (property.isChildOfListOrMap && propType == derivedPropertyTypes.MAP && property.schema.property.isSimpleType)">
+    <ng-container *ngIf="propType == derivedPropertyTypes.SIMPLE || property.isDeclared || property.isToscaFunction() || (property.isChildOfListOrMap && propType == derivedPropertyTypes.MAP && property.schema.property.isSimpleType)">
         <div class="table-cell">
             <dynamic-element class="value-input"
                 pattern="validationUtils.getValidationPattern(property.type)"
-                [value]="property.isDeclared || property.isToscaGetFunction() ? property.value : property.valueObj"
-                [type]="property.isDeclared || property.isToscaGetFunction() ? 'string' : property.type"
+                [value]="property.isDeclared || property.isToscaFunction() ? property.value : property.valueObj"
+                [type]="property.isDeclared || property.isToscaFunction() ? 'string' : property.type"
                 [name]="property.name"
                 [path]="property.propertiesName"
                 (elementChanged)="onElementChanged($event)"
-                [readonly]="readonly || property.isDeclared || property.isDisabled || property.isToscaGetFunction()"
+                [readonly]="readonly || property.isDeclared || property.isDisabled || property.isToscaFunction()"
                 [testId]="'prop-' + propertyTestsId"
                 [declared] = "property.isDeclared"
                 [constraints] = "constraints"
             ></dynamic-element>
         </div>
     </ng-container>
-    <ng-container *ngIf="!isPropertyFEModel && propType != derivedPropertyTypes.SIMPLE && !property.isDeclared && !property.isToscaGetFunction()"> <!-- right cell for complex elements, or list complex -->
+    <ng-container *ngIf="!isPropertyFEModel && propType != derivedPropertyTypes.SIMPLE && !property.isDeclared && !property.isToscaFunction()"> <!-- right cell for complex elements, or list complex -->
         <div class="table-cell" *ngIf="propType == derivedPropertyTypes.COMPLEX">{{property.type | contentAfterLastDot }}</div>
         <div class="table-cell" *ngIf="propType == derivedPropertyTypes.MAP && !property.schema.property.isSimpleType">{{property.schema.property.type | contentAfterLastDot }}</div>
     </ng-container>
-    <ng-container *ngIf="isPropertyFEModel && (propType == derivedPropertyTypes.LIST || propType == derivedPropertyTypes.MAP) && !property.isDeclared && !property.isToscaGetFunction()"><!-- empty, full-width table cell - for PropertyFEModel of type list or map -->
+    <ng-container *ngIf="isPropertyFEModel && (propType == derivedPropertyTypes.LIST || propType == derivedPropertyTypes.MAP) && !property.isDeclared && !property.isToscaFunction()"><!-- empty, full-width table cell - for PropertyFEModel of type list or map -->
         <div class="table-cell empty"></div>
     </ng-container>
     <!-- ICONS: add, delete, and expand -->
-    <ng-container *ngIf="!property.isDeclared && !property.isToscaGetFunction()">
+    <ng-container *ngIf="!property.isDeclared && !property.isToscaFunction()">
             <a *ngIf="(propType == derivedPropertyTypes.LIST) && (!property.isChildOfListOrMap || property.mapInlist)" class="property-icon add-item" (click)="createNewChildProperty();" [ngClass]="{'disabled':readonly || preventInsertItem(property)}" [attr.data-tests-id]="'add-to-list-' + propertyTestsId">Add value to list</a>
             <a *ngIf="(propType == derivedPropertyTypes.MAP) && (!property.isChildOfListOrMap || property.mapInlist)" class="property-icon add-item" (click)="createNewChildProperty();" [ngClass]="{'disabled':readonly || preventInsertItem(property)}" [attr.data-tests-id]="'add-to-list-' + propertyTestsId">Add value to map</a>
             <span *ngIf="property.isChildOfListOrMap" (click)="deleteItem.emit(property);" class="property-icon sprite-new delete-item-icon" [ngClass]="{'disabled':readonly}" [attr.data-tests-id]="'delete-from-list-' + propertyTestsId"></span>
@@ -74,7 +74,7 @@
 
 </div>
 <!-- FLAT CHILDREN -->
-<div class="flat-children-container" *ngIf="isPropertyFEModel && !property.isDeclared && !property.isToscaGetFunction()">
+<div class="flat-children-container" *ngIf="isPropertyFEModel && !property.isDeclared && !property.isToscaFunction()">
     <ng-container *ngFor="let prop of property.flattenedChildren | filterChildProperties: expandedChildId; trackBy:prop?.propertiesName">
         <dynamic-property
             [selectedPropertyId]="selectedPropertyId"
index 70c9303..8e9be8b 100644 (file)
@@ -55,7 +55,7 @@ export class PropertiesUtils {
                     newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children
                     this.dataTypeService.checkForCustomBehavior(newFEProp);
 
-                    if (newFEProp.isToscaGetFunction()) {
+                    if (newFEProp.isToscaFunction()) {
                         return;
                     }
                     //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children
index 5c9af47..199f733 100644 (file)
@@ -27,6 +27,7 @@ import {ToscaFunctionComponent} from "../tosca-function.component";
 import {TranslateModule} from "../../../../shared/translator/translate.module";
 import {ToscaGetFunctionComponent} from "../tosca-get-function/tosca-get-function.component";
 import {UiElementsModule} from "../../../../components/ui/ui-elements.module";
+import {YamlFunctionComponent} from "../yaml-function/yaml-function.component";
 
 describe('ToscaConcatFunctionComponent', () => {
     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,
index e98f688..f93973c 100644 (file)
@@ -36,6 +36,9 @@
                               [functionType]="toscaFunctionTypeForm.value"
                               (onValidityChange)="onGetFunctionValidityChange($event)"></app-tosca-get-function>
     </div>
+    <div *ngIf="isYamlFunctionSelected()">
+      <app-yaml-function [yamlFunction]="toscaFunction" (onValidityChange)="onYamlFunctionValidityChange($event)"></app-yaml-function>
+    </div>
     <div *ngIf="showClearButton()" class="button-container">
       <button (click)="onClearValues()" class="tlv-btn red ng-star-inserted">{{'TOSCA_FUNCTION_CLEAR_VALUE_BUTTON' | translate}}</button>
     </div>
index 076e118..ae77800 100644 (file)
@@ -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 {
index 2db76cf..19cf2fb 100644 (file)
@@ -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 (file)
index 0000000..ac51d41
--- /dev/null
@@ -0,0 +1,35 @@
+<!--
+  ~ -
+  ~  ============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=========================================================
+  -->
+
+<div>
+  <div [formGroup]="formGroup">
+    <div>
+      <textarea class="yaml-text-area"
+                [formControl]="yamlValueForm"
+                [class.input-error]="yamlValueForm.invalid && (yamlValueForm.dirty || yamlValueForm.touched)"></textarea>
+      <div *ngIf="yamlValueForm.invalid && (yamlValueForm.dirty || yamlValueForm.touched)">
+        <div *ngIf="yamlValueForm.errors && yamlValueForm.errors['invalidYaml']" class="error">
+          {{'YAML_FUNCTION_INVALID_YAML_ERROR' | translate}}
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
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 (file)
index 0000000..7cbcbc4
--- /dev/null
@@ -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 (file)
index 0000000..500fe64
--- /dev/null
@@ -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<YamlFunctionComponent>;
+
+    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 (file)
index 0000000..e027227
--- /dev/null
@@ -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<YamlFunctionValidationEvent> = new EventEmitter<YamlFunctionValidationEvent>();
+
+    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
index 1419f3a..5ea1106 100644 (file)
@@ -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;
index 96e01db..2bb8d75 100644 (file)
   "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",
index 84543e2..e9d5844 100644 (file)
@@ -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
index 74aed12..fda832b 100644 (file)
@@ -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<ToscaFunction> {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(ToscaFunctionJsonDeserializer.class);
+
     public ToscaFunctionJsonDeserializer() {
         this(null);
     }
@@ -74,16 +78,26 @@ public class ToscaFunctionJsonDeserializer extends StdDeserializer<ToscaFunction
         }
 
         if (toscaFunctionType == ToscaFunctionType.YAML) {
-            return this.deserializeYamlFunction(node);
+            return this.deserializeYamlFunction(node, context);
         }
 
         return null;
     }
 
-    private ToscaFunction deserializeYamlFunction(JsonNode node) {
+    private ToscaFunction deserializeYamlFunction(final JsonNode node, final DeserializationContext context) throws JsonMappingException {
         var yamlFunction = new CustomYamlFunction();
-        final Object value = new Yaml().load(node.get("value").asText());
-        yamlFunction.setYamlValue(value);
+        final JsonNode valueJsonNode = node.get("value");
+        if (valueJsonNode == null) {
+            return yamlFunction;
+        }
+        final String valueAsText = valueJsonNode.asText();
+        try {
+            yamlFunction.setYamlValue(new Yaml().load(valueAsText));
+        } catch (final Exception e) {
+            final String errorMsg = String.format("Could not parse YAML expression: '%s'", valueAsText);
+            LOGGER.debug(errorMsg, e);
+            throw context.instantiationException(ToscaFunction.class, errorMsg);
+        }
         return yamlFunction;
     }
 
diff --git a/common-be/src/test/java/org/openecomp/sdc/be/datatypes/elements/CustomYamlFunctionTest.java b/common-be/src/test/java/org/openecomp/sdc/be/datatypes/elements/CustomYamlFunctionTest.java
new file mode 100644 (file)
index 0000000..033fd10
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * -
+ *  ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation.
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+import org.yaml.snakeyaml.Yaml;
+
+class CustomYamlFunctionTest {
+
+    @Test
+    void getTypeTest() {
+        final var yamlFunction = new CustomYamlFunction();
+        assertEquals(ToscaFunctionType.YAML, yamlFunction.getType());
+    }
+
+    @Test
+    void getValue() {
+        final var yamlFunction = new CustomYamlFunction();
+        assertNull(yamlFunction.getValue());
+        final String yamlValue1 = "my value";
+        yamlFunction.setYamlValue(yamlValue1);
+        assertEquals(yamlValue1, yamlFunction.getValue());
+        final Map<String, Map<String, String>> yamlValue2 = Map.of("entry", Map.of("property1", "value1"));
+        yamlFunction.setYamlValue(yamlValue2);
+        assertEquals(new Yaml().dump(yamlValue2), yamlFunction.getValue());
+    }
+}
\ No newline at end of file
index e11b661..b17a3dc 100644 (file)
 
 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<String, Object> yamlMap = (Map<String, Object>) yamlObject;
+        final Object concatFunctionObj = yamlMap.get(ToscaFunctionType.CONCAT.getName());
+        assertNotNull(concatFunctionObj);
+        assertTrue(concatFunctionObj instanceof List);
+        final List<Object> concatFunctionParameters = (List<Object>) concatFunctionObj;
+        assertEquals(3, concatFunctionParameters.size(), "Expecting three parameters");
+        assertTrue(concatFunctionParameters.get(0) instanceof Map);
+        final Map<String, Object> parameter1Map = (Map<String, Object>) concatFunctionParameters.get(0);
+        assertNotNull(parameter1Map.get(ToscaFunctionType.GET_INPUT.getName()));
+        assertTrue(parameter1Map.get(ToscaFunctionType.GET_INPUT.getName()) instanceof List);
+        List<String> getInputParameters = (List<String>) 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<String, Object> parameter2Map = (Map<String, Object>) concatFunctionParameters.get(2);
+        assertNotNull(parameter2Map.get(ToscaFunctionType.CONCAT.getName()));
+        assertTrue(parameter2Map.get(ToscaFunctionType.CONCAT.getName()) instanceof List);
+        List<Object> concatParameters = (List<Object>) 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<String, Object> yamlFunctionValueMap = (Map<String, Object>) 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 (file)
index 0000000..808d96f
--- /dev/null
@@ -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 (file)
index 0000000..23a2678
--- /dev/null
@@ -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 (file)
index 0000000..a992f22
--- /dev/null
@@ -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 (file)
index 0000000..c09571b
--- /dev/null
@@ -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 (file)
index 0000000..dfe63f0
--- /dev/null
@@ -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