Support complex types in interface operation inputs
Issue-ID: SDC-3897
Change-Id: Ieac2d74ad340de1d9f6e4cd3ac830e2ec8c35d5b
Signed-off-by: andre.schmid <andre.schmid@est.tech>
Signed-off-by: vasraz <vasyl.razinkov@est.tech>
Signed-off-by: MichaelMorris <michael.morris@est.tech>
diff --git a/catalog-ui/src/app/app.ts b/catalog-ui/src/app/app.ts
index ffa4389..e7e2828 100644
--- a/catalog-ui/src/app/app.ts
+++ b/catalog-ui/src/app/app.ts
@@ -675,7 +675,7 @@
// $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
$http.defaults.headers.common[cookieService.getUserIdSuffix()] = cookieService.getUserId();
- DataTypesService.fetchDataTypesByModel(null);
+ DataTypesService.loadDataTypesCache(null);
//handle stateChangeStart
let internalDeregisterStateChangeStartWatcher: Function = (): void => {
diff --git a/catalog-ui/src/app/models/data-types-map.ts b/catalog-ui/src/app/models/data-types-map.ts
index 3591bc2..e7b1c69 100644
--- a/catalog-ui/src/app/models/data-types-map.ts
+++ b/catalog-ui/src/app/models/data-types-map.ts
@@ -25,13 +25,13 @@
import {DataTypeModel} from "./data-types";
export class DataTypesMapData {
- [dataTypeId:string]:Array<DataTypeModel>;
+ [dataTypeId: string]: Array<DataTypeModel>;
}
export class DataTypesMap {
- dataTypesMap:DataTypesMapData;
+ dataTypesMap: DataTypesMapData;
- constructor(dataTypesMap:DataTypesMapData) {
- this.dataTypesMap = dataTypesMap;
- }
+ constructor(dataTypesMap: DataTypesMapData) {
+ this.dataTypesMap = dataTypesMap;
+ }
}
diff --git a/catalog-ui/src/app/models/data-types.ts b/catalog-ui/src/app/models/data-types.ts
index 7004b43..7fc788b 100644
--- a/catalog-ui/src/app/models/data-types.ts
+++ b/catalog-ui/src/app/models/data-types.ts
@@ -25,6 +25,7 @@
import {PropertyBEModel} from "./properties-inputs/property-be-model";
import {AttributeBEModel} from "./attributes-outputs/attribute-be-model";
import {Model} from "./model";
+import {PROPERTY_DATA} from "../utils/constants";
export class DataTypeModel {
@@ -39,16 +40,24 @@
attributes: Array<AttributeBEModel>;
model: Model;
- constructor(dataType:DataTypeModel) {
+ constructor(dataType: DataTypeModel) {
if (dataType) {
this.uniqueId = dataType.uniqueId;
this.name = dataType.name;
this.derivedFromName = dataType.derivedFromName;
+ if (dataType.derivedFrom) {
+ this.derivedFrom = new DataTypeModel(dataType.derivedFrom);
+ }
this.creationTime = dataType.creationTime;
this.modificationTime = dataType.modificationTime;
- this.properties = dataType.properties;
+ if (dataType.properties) {
+ this.properties = [];
+ dataType.properties.forEach(property => {
+ this.properties.push(new PropertyBEModel(property));
+ });
+ }
this.attributes = dataType.attributes;
- this.model = this.model;
+ this.model = dataType.model;
}
}
@@ -56,5 +65,25 @@
return this;
};
+
+ /**
+ * Parses the default value to JSON.
+ */
+ public parseDefaultValueToJson(): any {
+ if (PROPERTY_DATA.TYPES.indexOf(this.name) > -1) {
+ return undefined;
+ }
+ const defaultValue = {};
+ if (this.properties) {
+ this.properties.forEach(property => {
+ const propertyDefaultValue = property.parseDefaultValueToJson();
+ if (propertyDefaultValue != undefined) {
+ defaultValue[property.name] = propertyDefaultValue;
+ }
+ });
+ }
+
+ return defaultValue === {} ? undefined : defaultValue;
+ }
}
diff --git a/catalog-ui/src/app/models/interfaceOperation.ts b/catalog-ui/src/app/models/interfaceOperation.ts
index 109babb..9d8ab36 100644
--- a/catalog-ui/src/app/models/interfaceOperation.ts
+++ b/catalog-ui/src/app/models/interfaceOperation.ts
@@ -20,21 +20,47 @@
'use strict';
import {ArtifactModel} from "./artifacts";
+import {SchemaPropertyGroupModel} from "./schema-property";
+import {PROPERTY_DATA, PROPERTY_TYPES} from "../utils/constants";
export class InputOperationParameter {
name: string;
type: string;
+ schema: SchemaPropertyGroupModel;
inputId: string;
toscaDefaultValue?: string;
+ value?: any;
constructor(param?: any) {
if (param) {
this.name = param.name;
this.type = param.type;
+ this.schema = param.schema;
this.inputId = param.inputId;
this.toscaDefaultValue = param.toscaDefaultValue;
+ this.value = param.value;
}
- console.info("InputOperationParameter Constructor: ", param)
+ }
+
+ public getDefaultValue(): any {
+ if (this.isTypeNotSimple()) {
+ if (this.toscaDefaultValue) {
+ this.toscaDefaultValue = JSON.parse(this.toscaDefaultValue);
+ return JSON.parse(this.toscaDefaultValue);
+ }
+ switch (this.type) {
+ case PROPERTY_TYPES.LIST:
+ return [];
+ default:
+ return {};
+ }
+ }
+
+ return this.toscaDefaultValue;
+ }
+
+ private isTypeNotSimple() {
+ return PROPERTY_DATA.SIMPLE_TYPES.indexOf(this.type) == -1;
}
}
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 bd65c3a..267a2ad 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
@@ -109,15 +109,61 @@
return temp;
}
- public getDerivedPropertyType = () => {
+ public getDerivedPropertyType = (): DerivedPropertyType => {
if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(this.type) > -1) {
return DerivedPropertyType.SIMPLE;
- } else if (this.type === PROPERTY_TYPES.LIST) {
+ }
+ if (this.type === PROPERTY_TYPES.LIST) {
return DerivedPropertyType.LIST;
- } else if (this.type === PROPERTY_TYPES.MAP) {
+ }
+ if (this.type === PROPERTY_TYPES.MAP) {
return DerivedPropertyType.MAP;
- } else {
- return DerivedPropertyType.COMPLEX;
+ }
+ return DerivedPropertyType.COMPLEX;
+ }
+
+ /**
+ * Parses default value to JSON.
+ */
+ public parseDefaultValueToJson(): any {
+ if (this.defaultValue == undefined) {
+ return undefined;
+ }
+
+ const propertyType: DerivedPropertyType = this.getDerivedPropertyType();
+ if (propertyType == DerivedPropertyType.SIMPLE) {
+ return this.parseDefaultSimpleValue();
+ }
+
+ try {
+ return JSON.parse(this.defaultValue);
+ } catch (e) {
+ console.error(`Could not parse the property of type '${this.type}' default value to JSON '${this.defaultValue}'`, e);
+ }
+
+ return undefined;
+ }
+
+ private parseDefaultSimpleValue() {
+ switch (this.type) {
+ case PROPERTY_TYPES.INTEGER:
+ try {
+ return parseInt(this.defaultValue);
+ } catch (e) {
+ console.error(`Could not parse the property of type '${this.type}' default value to int '${this.defaultValue}'`, e);
+ }
+ return undefined;
+ case PROPERTY_TYPES.FLOAT:
+ try {
+ return parseFloat(this.defaultValue);
+ } catch (e) {
+ console.error(`Could not parse the property of type '${this.type}' default value to float '${this.defaultValue}'`, e);
+ }
+ return undefined;
+ case PROPERTY_TYPES.BOOLEAN:
+ return this.defaultValue === 'true';
+ default:
+ return this.defaultValue;
}
}
diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html
index e1638fb..7c83c55 100644
--- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html
+++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html
@@ -117,7 +117,7 @@
<!-- Value -->
<div class="table-cell valueCol" [class.inner-table-container]="input.childrenProperties || !input.isSimpleType">
<dynamic-element class="value-input"
- *ngIf="checkInstanceFePropertiesMapIsFilled() && input.isSimpleType"
+
pattern="validationUtils.getValidationPattern(input.type)"
[value]="input.defaultValueObj"
[type]="input.type"
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts
index 60d6678..b14d0dd 100644
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts
@@ -22,31 +22,17 @@
import {Component, ComponentRef, Inject, Input} from '@angular/core';
import {TopologyTemplateService} from '../../../services/component-services/topology-template.service';
import {TranslateService} from "../../../shared/translator/translate.service";
-import {ModalService } from 'app/ng2/services/modal.service';
-import { ModalComponent } from 'app/ng2/components/ui/modal/modal.component';
-import {
- Component as TopologyTemplate
-} from "../../../../models/components/component";
+import {ModalService} from 'app/ng2/services/modal.service';
+import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
+import {Component as TopologyTemplate} from "../../../../models/components/component";
import {PluginsService} from "app/ng2/services/plugins.service";
import {SelectedComponentType} from "../common/store/graph.actions";
import {WorkspaceService} from "../../workspace/workspace.service";
-import {
- ComponentInstanceInterfaceModel,
- InterfaceOperationModel
-} from "../../../../models/interfaceOperation";
-import {
- InterfaceOperationHandlerComponent
-} from "./operation-creator/interface-operation-handler.component";
+import {ComponentInstanceInterfaceModel, InterfaceOperationModel} from "../../../../models/interfaceOperation";
+import {InterfaceOperationHandlerComponent} from "./operation-creator/interface-operation-handler.component";
-import {
- ButtonModel,
- ComponentMetadata,
- InterfaceModel,
- InputBEModel,
- ModalModel,
- ComponentInstance, ArtifactModel
-} from 'app/models';
+import {ArtifactModel, ButtonModel, ComponentInstance, ComponentMetadata, InputBEModel, InterfaceModel, ModalModel} from 'app/models';
import {ArtifactGroupType} from "../../../../utils/constants";
import {DropdownValue} from "../../../components/ui/form-components/dropdown/ui-element-dropdown.component";
import {ToscaArtifactService} from "../../../services/tosca-artifact.service";
@@ -86,12 +72,14 @@
class ModalTranslation {
EDIT_TITLE: string;
CANCEL_BUTTON: string;
+ CLOSE_BUTTON: string;
SAVE_BUTTON: string;
constructor(private TranslateService: TranslateService) {
this.TranslateService.languageChangedObservable.subscribe(lang => {
this.EDIT_TITLE = this.TranslateService.translate('INTERFACE_EDIT_TITLE');
this.CANCEL_BUTTON = this.TranslateService.translate("INTERFACE_CANCEL_BUTTON");
+ this.CLOSE_BUTTON = this.TranslateService.translate("INTERFACE_CLOSE_BUTTON");
this.SAVE_BUTTON = this.TranslateService.translate("INTERFACE_SAVE_BUTTON");
});
}
@@ -135,7 +123,7 @@
toscaArtifactTypes: Array<DropdownValue> = [];
@Input() component: ComponentInstance;
- @Input() readonly: boolean;
+ @Input() isViewOnly: boolean;
@Input() enableMenuItems: Function;
@Input() disableMenuItems: Function;
@Input() componentType: SelectedComponentType;
@@ -209,14 +197,23 @@
}
private enableOrDisableSaveButton = (): boolean => {
- return this.modalInstance.instance.dynamicContent.instance.readonly;
+ return this.isViewOnly;
}
onSelectInterfaceOperation(interfaceModel: UIInterfaceModel, operation: InterfaceOperationModel) {
- const cancelButton: ButtonModel = new ButtonModel(this.modalTranslation.CANCEL_BUTTON, 'outline white', this.cancelAndCloseModal);
- const saveButton: ButtonModel = new ButtonModel(this.modalTranslation.SAVE_BUTTON, 'blue', () =>
- this.updateInterfaceOperation(), this.enableOrDisableSaveButton);
- const modalModel: ModalModel = new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', [saveButton, cancelButton], 'custom');
+
+ const buttonList = [];
+ if (this.isViewOnly) {
+ const closeButton: ButtonModel = new ButtonModel(this.modalTranslation.CLOSE_BUTTON, 'outline white', this.cancelAndCloseModal);
+ buttonList.push(closeButton);
+ } else {
+ const saveButton: ButtonModel = new ButtonModel(this.modalTranslation.SAVE_BUTTON, 'blue', () =>
+ this.updateInterfaceOperation(), this.enableOrDisableSaveButton);
+ const cancelButton: ButtonModel = new ButtonModel(this.modalTranslation.CANCEL_BUTTON, 'outline white', this.cancelAndCloseModal);
+ buttonList.push(saveButton);
+ buttonList.push(cancelButton);
+ }
+ const modalModel: ModalModel = new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', buttonList, 'custom');
this.modalInstance = this.modalServiceNg2.createCustomModal(modalModel);
this.modalServiceNg2.addDynamicContentToModal(
@@ -228,7 +225,7 @@
selectedInterface: interfaceModel,
selectedInterfaceOperation: operation,
validityChangedCallback: this.enableOrDisableSaveButton,
- isViewOnly: false
+ isViewOnly: this.isViewOnly
}
);
this.modalInstance.instance.open();
@@ -241,7 +238,7 @@
private updateInterfaceOperation() {
this.isLoading = true;
- let operationUpdated = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
+ const operationUpdated: InterfaceOperationModel = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
this.topologyTemplateService.updateComponentInstanceInterfaceOperation(
this.componentMetaData.uniqueId,
this.componentMetaData.componentType,
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.html
new file mode 100644
index 0000000..6753b82
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.html
@@ -0,0 +1,69 @@
+<!--
+ ~ -
+ ~ ============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 *ngIf="!isView && showAddLink" class="add-button-container">
+ <a class="add-btn" data-tests-id="add-input.add-input-link"
+ (click)="showAddInput()">{{'OPERATION_ADD_INPUT' | translate}}
+ </a>
+ </div>
+ <form *ngIf="showForm" [formGroup]="inputForm" (ngSubmit)="onSubmit()">
+ <label class="occurrences-label">{{'ADD_INPUT_TITLE' | translate}}</label>
+ <div>
+ <label class="sdc-input-label" for="input-name">{{'OPERATION_INPUT_NAME' | translate}}</label>
+ <input id="input-name" class="sdc-input" type="text" formControlName="name" data-tests-id="add-input.input-name"/>
+ </div>
+ <div>
+ <label class="sdc-input-label" for="input-type">{{'OPERATION_INPUT_TYPE' | translate}}</label>
+ <sdc-combo-box
+ id="input-type"
+ [placeHolder]="inputToAdd.type != undefined ? inputToAdd.type : 'Select...'"
+ [data]="inputTypeOptions"
+ [selectedValue]="inputToAdd.type"
+ (itemSelected)="onChangeInputType($event)"
+ [testId]="'add-input.input-type'"
+ [disabled]="isView"
+ >
+ </sdc-combo-box>
+ </div>
+ <div *ngIf="showInputSchema">
+ <label class="sdc-input-label" for="input-schema">{{'OPERATION_INPUT_SCHEMA' | translate}}</label>
+ <sdc-combo-box
+ id="input-schema"
+ [placeHolder]="getSchemaPlaceholder()"
+ [data]="inputSchemaOptions"
+ [selectedValue]="getSchemaType()"
+ (itemSelected)="onChangeInputSchema($event)"
+ [testId]="'add-input.input-schema'"
+ [disabled]="isView"
+ >
+ </sdc-combo-box>
+ </div>
+ <div class="confirmation-button-container" *ngIf="!isView">
+ <button type="submit" class="tlv-btn blue" [disabled]="!inputForm.valid" data-tests-id="add-input.add-input-btn">
+ {{'OPERATION_ADD_INPUT' | translate}}
+ </button>
+ <button type="button" class="tlv-btn outline white" (click)="onCancel()" data-tests-id="add-input.cancel-btn">
+ {{'OPERATION_CANCEL' | translate}}
+ </button>
+ </div>
+ </form>
+</div>
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.less b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.less
new file mode 100644
index 0000000..5eb4a7f
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.less
@@ -0,0 +1,64 @@
+/*
+ * -
+ * ============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=========================================================
+ */
+
+.add-button-container {
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: flex-end;
+ margin: 7px 5px 7px 0;
+}
+
+.confirmation-button-container {
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: flex-end;
+ margin: 7px 5px 7px 0;
+ button {
+ margin: 0 5px;
+ }
+}
+
+.sdc-input-label {
+ margin-bottom: 5px;
+ display: block;
+ font-family: OpenSans-Semibold, Arial, sans-serif;
+ font-style: normal;
+ font-weight: 600;
+ font-size: 12px;
+}
+
+.sdc-input {
+ box-sizing: border-box;
+ padding: 0 10px;
+ height: 38px;
+ width: 100%;
+ border: solid 1px #d2d2d2;
+ border-radius: 2px;
+ color: #5a5a5a;
+}
+
+.add-param-link {
+
+}
+
+.add-btn {
+
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.spec.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.spec.ts
new file mode 100644
index 0000000..2c6c1e6
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.spec.ts
@@ -0,0 +1,67 @@
+/*
+ * -
+ * ============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 {AddInputComponent} from './add-input.component';
+import {TranslateModule} from '../../../../../shared/translator/translate.module';
+import {ReactiveFormsModule} from '@angular/forms';
+import {SdcUiComponentsModule} from 'onap-ui-angular/dist';
+import {Observable} from 'rxjs/Observable';
+import {DataTypesMap} from '../../../../../../models/data-types-map';
+import {TranslateService} from '../../../../../shared/translator/translate.service';
+
+const translateServiceMock: Partial<TranslateService> = {
+ translate: jest.fn((str: string) => {
+ })
+};
+
+describe('AddInputComponent', () => {
+ let component: AddInputComponent;
+ let fixture: ComponentFixture<AddInputComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ AddInputComponent ],
+ imports: [
+ TranslateModule,
+ SdcUiComponentsModule,
+ ReactiveFormsModule
+ ],
+ providers: [
+ { provide: TranslateService, useValue: translateServiceMock }
+ ]
+ })
+ .compileComponents();
+
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AddInputComponent);
+ component = fixture.componentInstance;
+ component.dataTypeMap$ = new Observable<DataTypesMap>();
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.ts
new file mode 100644
index 0000000..6632d1a
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/add-input/add-input.component.ts
@@ -0,0 +1,210 @@
+/*
+ * -
+ * ============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 {InputOperationParameter} from '../../../../../../models/interfaceOperation';
+import {IDropDownOption} from 'onap-ui-angular/dist/form-elements/dropdown/dropdown-models';
+import {Observable} from 'rxjs/Observable';
+import {AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from '@angular/forms';
+import {PROPERTY_TYPES} from '../../../../../../utils/constants';
+import {SchemaProperty, SchemaPropertyGroupModel} from '../../../../../../models/schema-property';
+import {DataTypeModel} from "../../../../../../models/data-types";
+
+@Component({
+ selector: 'app-add-input',
+ templateUrl: './add-input.component.html',
+ styleUrls: ['./add-input.component.less']
+})
+export class AddInputComponent implements OnInit {
+
+ @Input('dataTypeMap') dataTypeMap$: Observable<Map<string, DataTypeModel>>;
+ @Input('isView') isView: boolean;
+ @Input() existingInputNames: Array<string> = [];
+ @Output('onAddInput') onAddInputEvent: EventEmitter<InputOperationParameter>;
+
+ dataTypeMap: Map<string, DataTypeModel>;
+ inputToAdd: InputOperationParameter;
+ inputTypeOptions: Array<IDropDownOption>;
+ inputSchemaOptions: Array<IDropDownOption>;
+ showForm: boolean = false;
+ showAddLink: boolean = true;
+ showInputSchema: boolean = false;
+
+ inputForm: FormGroup;
+
+ constructor() {
+ this.onAddInputEvent = new EventEmitter<InputOperationParameter>();
+ this.inputTypeOptions = [];
+ this.inputSchemaOptions = [];
+ this.inputToAdd = new InputOperationParameter();
+ }
+
+ schemaValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
+ const type = control.get('type');
+ const schema = control.get('schema');
+ return (type.value === 'list' || type.value === 'map') && !schema.value ? { schemaRequired: true } : null;
+ };
+
+ uniqueNameValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
+ const name = control.get('name');
+ return this.existingInputNames.indexOf(name.value) === -1 ? null : { nameIsNotUnique: true };
+ };
+
+ ngOnInit() {
+ this.initForm();
+ this.initInputType();
+ }
+
+ private initForm() {
+ this.inputForm = new FormGroup({
+ name: new FormControl({value: '', disabled: this.isView}, [Validators.required, Validators.minLength(1)]),
+ type: new FormControl({value: '', disabled: this.isView}, [Validators.required, Validators.minLength(1)]),
+ schema: new FormControl({value: '', disabled: this.isView})
+ }, { validators: [this.schemaValidator, this.uniqueNameValidator] });
+ }
+
+ private initInputType() {
+ this.dataTypeMap$.subscribe((dataTypesMap: Map<string, DataTypeModel>) => {
+ this.dataTypeMap = dataTypesMap;
+ this.inputTypeOptions = [];
+ this.inputSchemaOptions = [];
+ dataTypesMap.forEach((value, key) => {
+ const entry = {label: key, value: key};
+ this.inputTypeOptions.push(entry);
+ if (key != PROPERTY_TYPES.LIST && key != PROPERTY_TYPES.MAP) {
+ this.inputSchemaOptions.push(entry);
+ }
+ });
+ });
+ }
+
+ onChangeInputType(inputType) {
+ const typeForm = this.inputForm.get('type');
+ if (!inputType) {
+ this.inputToAdd.type = undefined;
+ typeForm.setValue(undefined);
+ this.toggleInputSchema();
+ return;
+ }
+ typeForm.setValue(inputType);
+ this.inputToAdd.type = inputType;
+ this.toggleInputSchema();
+ }
+
+ onChangeInputSchema(inputSchema: string) {
+ const schemaForm = this.inputForm.get('schema');
+ if (!inputSchema) {
+ this.inputToAdd.schema = undefined;
+ schemaForm.setValue(undefined);
+ return;
+ }
+ schemaForm.setValue(inputSchema);
+ this.inputToAdd.schema = new SchemaPropertyGroupModel();
+ this.inputToAdd.schema.property = new SchemaProperty();
+ this.inputToAdd.schema.property.type = inputSchema;
+ }
+
+ onSubmit() {
+ this.trimForm();
+ if (this.inputForm.valid) {
+ const nameForm = this.inputForm.get('name');
+ const typeForm = this.inputForm.get('type');
+ const schemaForm = this.inputForm.get('schema');
+ const input = new InputOperationParameter();
+ input.name = nameForm.value;
+ input.type = typeForm.value;
+ if (this.typeHasSchema()) {
+ input.schema = new SchemaPropertyGroupModel();
+ input.schema.property = new SchemaProperty();
+ input.schema.property.type = schemaForm.value;
+ }
+ input.inputId = this.generateUniqueId();
+ this.onAddInputEvent.emit(input);
+ this.hideAddInput();
+ this.resetForm();
+ }
+ }
+
+ showAddInput() {
+ this.showForm = true;
+ this.showAddLink = false;
+ }
+
+ hideAddInput() {
+ this.showForm = false;
+ this.showAddLink = true;
+ }
+
+ onCancel() {
+ this.hideAddInput();
+ this.resetForm();
+ }
+
+ private generateUniqueId(): string {
+ let result = '';
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+ const charactersLength = characters.length;
+ for (let i = 0; i < 36; i++ ) {
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
+ }
+ return result;
+ }
+
+ private resetForm() {
+ this.inputForm.reset();
+ this.showInputSchema = false;
+ this.inputToAdd = new InputOperationParameter();
+ }
+
+ getSchemaType() {
+ return this.inputToAdd.schema == undefined ? undefined : this.inputToAdd.schema.property.type;
+ }
+
+ getSchemaPlaceholder() {
+ const schemaType = this.getSchemaType();
+ return schemaType === undefined ? 'Select...' : schemaType;
+ }
+
+ private toggleInputSchema() {
+ this.showInputSchema = this.typeHasSchema();
+ }
+
+ private typeHasSchema() {
+ const typeForm = this.inputForm.get('type');
+ return typeForm.value == PROPERTY_TYPES.LIST || typeForm.value == PROPERTY_TYPES.MAP;
+ }
+
+ private trimForm() {
+ const nameForm = this.inputForm.get('name');
+ if (nameForm.value) {
+ nameForm.setValue(nameForm.value.trim());
+ }
+ const typeForm = this.inputForm.get('type');
+ if (typeForm.value) {
+ typeForm.setValue(typeForm.value.trim());
+ }
+ const schemaForm = this.inputForm.get('schema');
+ if (schemaForm.value) {
+ schemaForm.setValue(schemaForm.value.trim());
+ }
+ }
+
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html
new file mode 100644
index 0000000..0449da7
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.html
@@ -0,0 +1,119 @@
+<!--
+ ~ -
+ ~ ============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=========================================================
+ -->
+
+<li [class.root]="isRoot()">
+ <span class="input-info">
+ <em class="sprite-new round-expand-icon" [class.open]="isExpanded" (click)="expandAndCollapse()"></em>
+ <label class="input-label">{{name}}:</label> <em data-tests-id="input-type">{{resolveType()}}</em>
+ <span class="sprite-new delete-btn" *ngIf="showInputDelete()" (click)="onInputDelete()"></span>
+ <span class="sprite-new delete-btn" *ngIf="showListItemDelete()" (click)="onChildListItemDelete()"></span>
+ </span>
+ <ng-container *ngIf="isTypeSimple(type.name)">
+ <ul *ngIf="isExpanded">
+ <li class="input-value">
+ <ng-container *ngIf="isViewOnly">
+ {{valueObjRef}}<em class="empty-value" *ngIf="!valueObjRef">empty</em>
+ </ng-container>
+ <input *ngIf="!isViewOnly" [type]="getSimpleValueInputType()" name="value"
+ [(ngModel)]="valueObjRef"
+ (ngModelChange)="onValueChange($event)"
+ />
+ </li>
+ </ul>
+ </ng-container>
+ <ng-container *ngIf="isTypeComplex(type.name)" >
+ <ul *ngIf="isExpanded">
+ <ng-container *ngFor="let property of this.type.properties">
+ <app-input-list-item
+ [name]="property.name"
+ [type]="getDataType(property.type)"
+ [dataTypeMap]="dataTypeMap"
+ [valueObjRef]="valueObjRef[property.name]"
+ [schema]="property.schema"
+ [nestingLevel]="nestingLevel + 1"
+ [isViewOnly]="isViewOnly"
+ (onValueChange)="onPropertyValueChange($event)">
+ </app-input-list-item>
+ </ng-container>
+ </ul>
+ </ng-container>
+ <ng-container *ngIf="isTypeList(type.name)">
+ <ul *ngIf="isExpanded">
+ <ng-container *ngFor="let value1 of valueObjRef; index as i; trackBy: trackByIndex">
+ <li class="input-value" *ngIf="isTypeSimple(schema.property.type)">
+ <ng-container *ngIf="isViewOnly">
+ {{valueObjRef[i]}}
+ </ng-container>
+ <input type="text" *ngIf="!isViewOnly"
+ [(ngModel)]="valueObjRef[i]" (ngModelChange)="onListValueChange()" />
+ <span class="sprite-new delete-btn" *ngIf="!isViewOnly" (click)="onListItemDelete(i)"></span>
+ </li>
+ <app-input-list-item *ngIf="!isTypeSimple(schema.property.type)"
+ [name]="i+''"
+ [type]="getDataType(schema.property.type)"
+ [dataTypeMap]="dataTypeMap"
+ [valueObjRef]="valueObjRef[i]"
+ [schema]="schema"
+ [nestingLevel]="nestingLevel + 1"
+ [listIndex]="i"
+ [isListChild]="true"
+ [isViewOnly]="isViewOnly"
+ (onValueChange)="onPropertyValueChange($event)"
+ (onChildListItemDelete)="onListItemDelete($event)">
+ </app-input-list-item>
+ </ng-container>
+ <li class="input-value" *ngIf="!isViewOnly">
+ <a class="add-btn" (click)="addListElement()">{{'INPUT_LIST_ADD_LIST_ENTRY' | translate}}</a>
+ </li>
+ </ul>
+ </ng-container>
+ <ng-container *ngIf="isTypeMap(type.name)">
+ <ul *ngIf="isExpanded">
+ <ng-container *ngFor="let key of getObjectEntries(valueObjRef); index as i">
+ <li class="input-value" *ngIf="isTypeSimple(schema.property.type)">
+ <label class="input-label">{{key}}:</label>
+ <ng-container *ngIf="isViewOnly">
+ {{valueObjRef[key]}}
+ </ng-container>
+ <input type="text" *ngIf="!isViewOnly" [(ngModel)]="valueObjRef[key]" (ngModelChange)="onMapValueChange()"/>
+ <span class="sprite-new delete-btn" *ngIf="!isViewOnly" (click)="onMapKeyDelete(key)"></span>
+ </li>
+ <app-input-list-item
+ *ngIf="!isTypeSimple(schema.property.type)"
+ [name]="key"
+ [type]="getDataType(schema.property.type)"
+ [dataTypeMap]="dataTypeMap"
+ [valueObjRef]="valueObjRef[key]"
+ [schema]="schema"
+ [isMapChild]="true"
+ [nestingLevel]="nestingLevel + 1"
+ [isViewOnly]="isViewOnly"
+ (onValueChange)="onPropertyValueChange($event)"
+ (onDelete)="onMapKeyDelete($event)">
+ </app-input-list-item>
+ </ng-container>
+ <li class="input-value" *ngIf="!isViewOnly">
+ <input type="text" [(ngModel)]="mapEntryName" placeholder="{{ 'INPUT_LIST_MAP_KEY_PLACEHOLDER' | translate }}"/>
+ <a class="add-btn" (click)="addMapEntry()">{{ 'INPUT_LIST_ADD_MAP_ENTRY' | translate }}</a>
+ </li>
+ </ul>
+ </ng-container>
+</li>
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.less b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.less
new file mode 100644
index 0000000..cb7346e
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.less
@@ -0,0 +1,123 @@
+/*
+ * -
+ * ============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=========================================================
+ */
+
+* {
+ font-size: 0.875rem;
+}
+
+.input-info {
+ display: flex;
+ flex-flow: row nowrap;
+ align-items: center;
+ gap: 10px;
+ font-family: OpenSans-Regular, sans-serif;
+ font-weight: 400;
+}
+
+.input-label {
+ margin: 0;
+ font-weight: bold;
+}
+
+.input-value {
+ display: flex;
+ flex-flow: row nowrap;
+ gap: 7px;
+
+ input {
+ min-width: 150px;
+ max-width: 250px;
+ }
+}
+
+.input-map-key {
+ input {
+ max-width: 150px;
+ }
+}
+
+.input-action-container {
+ flex-grow: 1;
+ margin-left: auto;
+}
+
+.input-action {
+ flex-grow: 2
+}
+
+ul {
+ margin: 0 0 0 20px;
+ list-style: none;
+ line-height: 2em;
+}
+
+li {
+ position: relative;
+
+ &:before {
+ position: absolute;
+ left: -15px;
+ top: 0;
+ content: '';
+ display: block;
+ border-left: 1px solid #ddd;
+ height: 1em;
+ border-bottom: 1px solid #ddd;
+ width: 10px;
+ }
+
+ &:after {
+ position: absolute;
+ left: -15px;
+ bottom: -7px;
+ content: '';
+ display: block;
+ border-left: 1px solid #ddd;
+ height: 100%;
+ }
+
+ &.root {
+ margin: 0 0 0 -20px;
+
+ &:before {
+ display: none;
+ }
+
+ &:after {
+ display: none;
+ }
+ }
+
+ &:last-child {
+ &:after {
+ display: none
+ }
+ }
+}
+
+label {
+ margin: 0;
+ font-weight: normal;
+}
+
+.empty-value {
+ color: #aaaaaa;
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.spec.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.spec.ts
new file mode 100644
index 0000000..b7e34e5
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.spec.ts
@@ -0,0 +1,55 @@
+/*
+ * -
+ * ============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 {InputListItemComponent} from './input-list-item.component';
+import {TranslateModule} from '../../../../../../shared/translator/translate.module';
+import {FormsModule} from '@angular/forms';
+import {DataTypeModel} from '../../../../../../../models/data-types';
+
+describe('InputListItemComponent', () => {
+ let component: InputListItemComponent;
+ let fixture: ComponentFixture<InputListItemComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ InputListItemComponent ],
+ imports: [
+ TranslateModule,
+ FormsModule
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(InputListItemComponent);
+ component = fixture.componentInstance;
+ component.valueObjRef = "";
+ component.type = new DataTypeModel(undefined);
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeDefined();
+ });
+});
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts
new file mode 100644
index 0000000..cd75fe8
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list-item/input-list-item.component.ts
@@ -0,0 +1,245 @@
+/*
+ * -
+ * ============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 {DataTypeModel} from '../../../../../../../models/data-types';
+import {SchemaPropertyGroupModel} from '../../../../../../../models/schema-property';
+import {DerivedPropertyType, PropertyBEModel} from '../../../../../../../models/properties-inputs/property-be-model';
+import {PROPERTY_DATA, PROPERTY_TYPES} from '../../../../../../../utils/constants';
+
+@Component({
+ selector: 'app-input-list-item',
+ templateUrl: './input-list-item.component.html',
+ styleUrls: ['./input-list-item.component.less']
+})
+export class InputListItemComponent implements OnInit {
+
+ @Input() valueObjRef: any;
+ @Input() name: string;
+ @Input() dataTypeMap: Map<string, DataTypeModel>;
+ @Input() type: DataTypeModel;
+ @Input() schema: SchemaPropertyGroupModel;
+ @Input() nestingLevel: number;
+ @Input() isListChild: boolean = false;
+ @Input() isMapChild: boolean = false;
+ @Input() listIndex: number;
+ @Input() isViewOnly: boolean;
+ @Output('onValueChange') onValueChangeEvent: EventEmitter<any> = new EventEmitter<any>();
+ @Output('onDelete') onDeleteEvent: EventEmitter<string> = new EventEmitter<string>();
+ @Output('onChildListItemDelete') onChildListItemDeleteEvent: EventEmitter<number> = new EventEmitter<number>();
+
+ isExpanded: boolean = false;
+ mapEntryName: string;
+
+ ngOnInit() {
+ if (!this.nestingLevel) {
+ this.nestingLevel = 0;
+ }
+ if (this.type.properties) {
+ this.type.properties.forEach(property => {
+ this.initEmptyPropertyInValueObjRef(property);
+ });
+ }
+ }
+
+ private initEmptyPropertyInValueObjRef(property: PropertyBEModel) {
+ if (this.valueObjRef[property.name] == undefined) {
+ if (this.isTypeComplex(property.type) || this.isTypeMap(property.type)) {
+ this.valueObjRef[property.name] = {};
+ } else if (this.isTypeList(property.type)) {
+ this.valueObjRef[property.name] = [];
+ } else {
+ this.valueObjRef[property.name] = null;
+ }
+ }
+ }
+
+ getType(typeName: string): DerivedPropertyType {
+ if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(typeName) > -1) {
+ return DerivedPropertyType.SIMPLE;
+ } else if (typeName === PROPERTY_TYPES.LIST) {
+ return DerivedPropertyType.LIST;
+ } else if (typeName === PROPERTY_TYPES.MAP) {
+ return DerivedPropertyType.MAP;
+ } else {
+ return DerivedPropertyType.COMPLEX;
+ }
+ }
+
+ isTypeSimple(typeName: string): boolean {
+ return this.getType(typeName) == DerivedPropertyType.SIMPLE;
+ }
+
+ isTypeList(typeName: string): boolean {
+ return this.getType(typeName) == DerivedPropertyType.LIST;
+ }
+
+ isTypeMap(typeName: string): boolean {
+ return this.getType(typeName) == DerivedPropertyType.MAP;
+ }
+
+ isTypeComplex(typeName: string): boolean {
+ return !this.isTypeSimple(typeName) && !this.isTypeList(typeName) && !this.isTypeMap(typeName);
+ }
+
+ expandAndCollapse() {
+ this.isExpanded = !this.isExpanded;
+ }
+
+ getDataType(type: string) {
+ return this.dataTypeMap.get(type);
+ }
+
+ onValueChange(value: any): void {
+ if (this.type.name == PROPERTY_TYPES.INTEGER || this.type.name == PROPERTY_TYPES.FLOAT) {
+ this.emitValueChangeEvent(this.parseNumber(value));
+ return;
+ }
+ if (this.type.name == PROPERTY_TYPES.BOOLEAN) {
+ this.emitValueChangeEvent(this.parseBoolean(value));
+ return;
+ }
+ this.emitValueChangeEvent(value);
+ }
+
+ onListValueChange(): void {
+ this.emitValueChangeEvent(this.valueObjRef);
+ }
+
+ onPropertyValueChange($event: any) {
+ this.valueObjRef[$event.name] = $event.value;
+ this.emitValueChangeEvent(this.valueObjRef);
+ }
+
+ private emitValueChangeEvent(value: any) {
+ this.onValueChangeEvent.emit({
+ name: this.name,
+ value: value
+ });
+ }
+
+ isRoot(): boolean {
+ return this.nestingLevel === 0;
+ }
+
+ showListItemDelete(): boolean {
+ return !this.isViewOnly && (this.isListChild && this.nestingLevel > 0);
+ }
+
+ showInputDelete(): boolean {
+ return !this.isViewOnly && (this.isRoot() || this.isMapChild);
+ }
+
+ resolveType(): string {
+ if (this.isTypeList(this.type.name)) {
+ return `list of value type ${this.schema.property.type}`
+ }
+ if (this.isTypeMap(this.type.name)) {
+ return `map of 'string' keys and '${this.schema.property.type}' values`
+ }
+ return this.type.name;
+ }
+
+ onInputDelete() {
+ this.onDeleteEvent.emit(this.name);
+ }
+
+ onListItemDelete(index: number): void {
+ this.valueObjRef.splice(index, 1);
+ this.emitValueChangeEvent(this.valueObjRef);
+ }
+
+ addListElement() {
+ if (this.isTypeSimple(this.schema.property.type)) {
+ this.valueObjRef.push('');
+ } else if (this.isTypeComplex(this.schema.property.type) || this.isTypeMap(this.schema.property.type)) {
+ this.valueObjRef.push({});
+ } else if (this.isTypeList(this.schema.property.type)) {
+ this.valueObjRef.push([]);
+ }
+ }
+
+ trackByIndex(index: number, value: string): number {
+ return index;
+ }
+
+ onChildListItemDelete() {
+ this.onChildListItemDeleteEvent.emit(this.listIndex);
+ }
+
+ getObjectEntries(valueObjRef: object) {
+ return Object.keys(valueObjRef);
+ }
+
+ onMapValueChange() {
+ this.emitValueChangeEvent(this.valueObjRef);
+ }
+
+ onMapKeyDelete(key: string) {
+ delete this.valueObjRef[key]
+ this.emitValueChangeEvent(this.valueObjRef);
+ }
+
+ addMapEntry() {
+ let newKey;
+ if (this.mapEntryName) {
+ newKey = this.mapEntryName.trim();
+ }
+ if (!newKey) {
+ return;
+ }
+ if (Object.keys(this.valueObjRef).indexOf(newKey) !== -1) {
+ return;
+ }
+ this.mapEntryName = '';
+ if (this.isTypeSimple(this.schema.property.type)) {
+ this.valueObjRef[newKey] = '';
+ } else if (this.isTypeComplex(this.schema.property.type) || this.isTypeMap(this.schema.property.type)) {
+ this.valueObjRef[newKey] = {};
+ } else if (this.isTypeList(this.schema.property.type)) {
+ this.valueObjRef[newKey] = [];
+ }
+ this.emitValueChangeEvent(this.valueObjRef);
+ }
+
+ getSimpleValueInputType() {
+ if (this.type.name == PROPERTY_TYPES.INTEGER || this.type.name == PROPERTY_TYPES.FLOAT) {
+ return 'number';
+ }
+ return 'text';
+ }
+
+ private parseBoolean(value: any) {
+ if (value === 'true') {
+ return true;
+ }
+ if (value === 'false') {
+ return false;
+ }
+ return null;
+ }
+
+ private parseNumber(value: any) {
+ const number = parseInt(value);
+ return isNaN(number) ? null : number;
+ }
+
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.html
new file mode 100644
index 0000000..802bd63
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.html
@@ -0,0 +1,36 @@
+<!--
+ ~ -
+ ~ ============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=========================================================
+ -->
+
+<label>{{ 'INPUT_LIST_TITLE' | translate }}</label>
+<div class="input-tree">
+ <ul *ngFor="let input of _inputs">
+ <app-input-list-item
+ [name]="input.name"
+ [type]="getDataType(input.type)"
+ [dataTypeMap]="dataTypeMap"
+ [valueObjRef]="input.value"
+ [schema]="input.schema"
+ [isViewOnly]="isViewOnly"
+ (onValueChange)="onValueChange($event)"
+ (onDelete)="onDelete($event)">
+ </app-input-list-item>
+ </ul>
+</div>
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.less b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.less
new file mode 100644
index 0000000..b9784f9
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.less
@@ -0,0 +1,36 @@
+/*
+ * -
+ * ============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=========================================================
+ */
+
+.input-tree {
+ overflow: scroll;
+ max-height: 300px;
+ max-width: 100%;
+
+ ul {
+ margin: 0 0 0 20px;
+ list-style: none;
+ line-height: 2em;
+ }
+}
+
+.input-tree::-webkit-scrollbar-track {
+ border: 0;
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.spec.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.spec.ts
new file mode 100644
index 0000000..b07a4bb
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.spec.ts
@@ -0,0 +1,69 @@
+/*
+ * -
+ * ============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 {InputListComponent} from './input-list.component';
+import {TranslateModule} from '../../../../../shared/translator/translate.module';
+import {Component, Input} from '@angular/core';
+import {DataTypeModel} from '../../../../../../models/data-types';
+import {TranslateService} from '../../../../../shared/translator/translate.service';
+
+@Component({selector: 'app-input-list-item', template: ''})
+class InputListItemStubComponent {
+ @Input() name: string;
+ @Input() type: DataTypeModel;
+ @Input() dataTypeMap: any;
+ @Input() valueObjRef: any;
+ @Input() schema: any;
+ @Input() isViewOnly: boolean;
+}
+
+const translateServiceMock: Partial<TranslateService> = {
+ translate: jest.fn((str: string) => {
+ })
+};
+
+describe('InputListComponent', () => {
+ let component: InputListComponent;
+ let fixture: ComponentFixture<InputListComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ InputListComponent, InputListItemStubComponent ],
+ imports: [ TranslateModule ],
+ providers: [
+ { provide: TranslateService, useValue: translateServiceMock }
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(InputListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeDefined();
+ });
+});
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.ts
new file mode 100644
index 0000000..72812d8
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-list/input-list.component.ts
@@ -0,0 +1,125 @@
+/*
+ * -
+ * ============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, Output} from '@angular/core';
+import {InputOperationParameter} from "../../../../../../models/interfaceOperation";
+import {DataTypeModel} from "../../../../../../models/data-types";
+import {DerivedPropertyType} from "../../../../../../models/properties-inputs/property-be-model";
+import {PROPERTY_DATA, PROPERTY_TYPES} from "../../../../../../utils/constants";
+
+@Component({
+ selector: 'input-list',
+ templateUrl: './input-list.component.html',
+ styleUrls: ['./input-list.component.less']
+})
+export class InputListComponent {
+
+ @Input() set inputs(inputs: Array<InputOperationParameter>) {
+ this._inputs = new Array<InputOperationParameter>();
+ if (inputs) {
+ inputs.forEach(input => {
+ const inputCopy = new InputOperationParameter(input);
+ this.initValue(inputCopy);
+
+ this._inputs.push(inputCopy);
+ });
+ }
+ }
+ @Input() dataTypeMap: Map<string, DataTypeModel>;
+ @Input() isViewOnly: boolean;
+ @Output('onValueChange') inputValueChangeEvent: EventEmitter<InputOperationParameter> = new EventEmitter<InputOperationParameter>();
+ @Output('onDelete') inputDeleteEvent: EventEmitter<string> = new EventEmitter<string>();
+
+ _inputs: Array<InputOperationParameter>;
+
+ getDataType(type: string): DataTypeModel {
+ return this.dataTypeMap.get(type);
+ }
+
+ private initValue(input: InputOperationParameter): void {
+ if (input.value) {
+ try {
+ input.value = JSON.parse(input.value);
+ } catch (e) {
+ console.debug('Could not parse value', input.value, e);
+ }
+ return;
+ }
+
+ if (input.toscaDefaultValue) {
+ try {
+ input.value = JSON.parse(input.toscaDefaultValue);
+ return;
+ } catch (e) {
+ console.debug('Could not parse value', input.value, e);
+ }
+ }
+
+ if (this.isTypeComplex(input.type) || this.isTypeMap(input.type)) {
+ input.value = {};
+ } else if (this.isTypeList(input.type)) {
+ input.value = [];
+ } else {
+ input.value = undefined;
+ }
+ }
+
+ getType(typeName: string): DerivedPropertyType {
+ if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(typeName) > -1) {
+ return DerivedPropertyType.SIMPLE;
+ } else if (typeName === PROPERTY_TYPES.LIST) {
+ return DerivedPropertyType.LIST;
+ } else if (typeName === PROPERTY_TYPES.MAP) {
+ return DerivedPropertyType.MAP;
+ } else {
+ return DerivedPropertyType.COMPLEX;
+ }
+ }
+
+ isTypeSimple(typeName: string): boolean {
+ return this.getType(typeName) == DerivedPropertyType.SIMPLE;
+ }
+
+ isTypeList(typeName: string): boolean {
+ return this.getType(typeName) == DerivedPropertyType.LIST;
+ }
+
+ isTypeMap(typeName: string): boolean {
+ return this.getType(typeName) == DerivedPropertyType.MAP;
+ }
+
+ isTypeComplex(typeName: string): boolean {
+ return !this.isTypeSimple(typeName) && !this.isTypeList(typeName) && !this.isTypeMap(typeName);
+ }
+
+ onValueChange($event: any) {
+ const inputOperationParameter = this._inputs.find(input => input.name == $event.name);
+ if (inputOperationParameter) {
+ inputOperationParameter.value = $event.value;
+ this.inputValueChangeEvent.emit(new InputOperationParameter(inputOperationParameter));
+ }
+ }
+
+ onDelete(inputName: string) {
+ this.inputDeleteEvent.emit(inputName);
+ }
+
+}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.html
deleted file mode 100644
index 156b657..0000000
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!--
- * ============LICENSE_START=======================================================
- * SDC
- * ================================================================================
- * Copyright (C) 2021 Nordix Foundation. All rights reserved.
- * ================================================================================
- * 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.
- * ============LICENSE_END=========================================================
- -->
-
-<div class="cell field-input-name">
- <sdc-input
- [(value)]="input.name"
- testId="interface-operation-input-name"
- (valueChange)="onChange()">
- </sdc-input>
-</div>
-
-<div class="cell field-input-value">
- <sdc-input
- [(value)]="input.toscaDefaultValue"
- testId="interface-operation-input-value"
- (valueChange)="onChange()">
- </sdc-input>
-</div>
-
-<div class="cell remove" *ngIf="!readonly">
- <svg-icon
- name="trash-o"
- mode="info"
- size="small"
- (click)="onRemoveInput(input)"
- [clickable]="true">
- </svg-icon>
-</div>
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.less b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.less
deleted file mode 100644
index 12eacc6..0000000
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.less
+++ /dev/null
@@ -1,72 +0,0 @@
-/*-
- * ============LICENSE_START=======================================================
- * SDC
- * ================================================================================
- * Copyright (C) 2021 Nordix Foundation. All rights reserved.
- * ================================================================================
- * 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.
- * ============LICENSE_END=========================================================
- */
-
-@import '../../../../../../../assets/styles/variables.less';
-
-.remove {
- display: flex;
- align-items: center;
- justify-content: center;
-
- svg-icon {
- position: relative;
- right: -3px;
-
- &:hover {
- cursor: pointer;
- }
- }
-}
-
-.cell {
- min-height: 50px;
- padding: 10px;
- display: flex;
- align-items: center;
-
- > * {
- flex-basis: 100%;
- }
-
- /deep/ select {
- height: 30px;
- }
-
- input {
- height: 30px;
- border: none;
- padding-left: 10px;
- }
-
- select {
- width: 100%;
- }
-
- &.field-property {
- &:last-child {
- flex: 1;
- }
-
- .no-properties-error {
- color: @func_color_q;
- font-style: italic;
- }
- }
-}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.ts
deleted file mode 100644
index 48bb804..0000000
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/input-param-row/input-param-row.component.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-* ============LICENSE_START=======================================================
-* SDC
-* ================================================================================
-* Copyright (C) 2021 Nordix Foundation. All rights reserved.
-* ================================================================================
-* 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, Input} from '@angular/core';
-import {InputOperationParameter} from "../../../../../../models/interfaceOperation";
-
-@Component({
- selector: 'input-param-row',
- templateUrl: './input-param-row.component.html',
- styleUrls: ['./input-param-row.component.less']
-})
-
-export class InputParamRowComponent {
- @Input() input: InputOperationParameter;
- @Input() onRemoveInput: Function;
- @Input() readonly: boolean;
- @Input() validityChanged: Function;
-
- constructor() {
- }
-
- ngOnInit() {
- this.validityChanged();
- }
-
- onChange() {
- this.validityChanged();
- }
-
-}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html
index 6dec416..46db3b9 100644
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html
@@ -124,32 +124,23 @@
</div>
</div>
</div>
-
- <div class="separator-buttons">
- <tab tabTitle="Inputs"></tab>
- <a class="add-param-link add-btn"
- [ngClass]="{'disabled': readonly || isViewOnly}"
- (click)="onAddInput()">{{'OPERATION_ADD_INPUT' | translate}}
- </a>
+ <div class="group-with-border content-row" *ngIf="dataTypeMap">
+ <input-list
+ [inputs]="inputs" [dataTypeMap]="dataTypeMap"
+ [isViewOnly]="isViewOnly"
+ (onValueChange)="onInputValueChange($event)"
+ (onDelete)="onInputDelete($event)"
+ >
+ </input-list>
</div>
-
- <div class="generic-table">
- <div class="header-row table-row">
- <span class="cell header-cell field-input-name">{{ 'OPERATION_PARAM_NAME' | translate }}</span>
- <span class="cell header-cell field-input-value">{{ 'OPERATION_INPUT_VALUE' | translate }}</span>
- <span class="cell header-cell remove">●●●</span>
- </div>
- <div class="empty-msg data-row" *ngIf="!inputs.length">
- <div>{{ 'OPERATION_INPUT_EMPTY' | translate }}</div>
- </div>
- <input-param-row
- *ngFor="let inputParameter of inputs"
- class="data-row"
- [input]="inputParameter"
- [onRemoveInput]="onRemoveInput"
- [validityChanged]="validityChanged">
- </input-param-row>
+ <div class="group-with-border content-row">
+ <app-add-input
+ [dataTypeMap]="dataTypeMap$"
+ [isView]="isViewOnly"
+ [existingInputNames]="collectInputNames()"
+ (onAddInput)="onAddInput($event)"
+ >
+ </app-add-input>
</div>
-
</form>
</div>
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.less b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.less
index 955720c..cb47c8d 100644
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.less
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.less
@@ -28,10 +28,9 @@
padding-bottom: 20px;
.group-with-border {
- margin: 25px 0;
- padding: 15px 0;
+ margin: 10px 0;
+ padding: 10px 0;
border-top: 1px solid @tlv_color_u;
- border-bottom: 1px solid @tlv_color_u;
.content-row:not(:last-of-type) {
padding-bottom: 13px;
}
@@ -148,6 +147,14 @@
}
}
+ .input-param-component {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 14px;
+ }
+
.generic-table {
max-height: 244px;
min-height: 91px;
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts
index 1099391..ed295e8 100644
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts
@@ -19,13 +19,9 @@
* ============LICENSE_END=========================================================
*/
-import {Component, EventEmitter, Output} from '@angular/core';
+import {Component, EventEmitter, Input, Output} from '@angular/core';
import {UIInterfaceModel} from "../interface-operations.component";
-import {
- InputOperationParameter,
- InterfaceOperationModel,
- IOperationParamsList
-} from "../../../../../models/interfaceOperation";
+import {InputOperationParameter, InterfaceOperationModel, IOperationParamsList} from "../../../../../models/interfaceOperation";
import {TranslateService} from "../../../../shared/translator/translate.service";
import {IDropDownOption} from "onap-ui-angular/dist/form-elements/dropdown/dropdown-models";
import {DropdownValue} from "../../../../components/ui/form-components/dropdown/ui-element-dropdown.component";
@@ -33,6 +29,9 @@
import {PropertyBEModel} from "../../../../../models/properties-inputs/property-be-model";
import {PropertyParamRowComponent} from "./property-param-row/property-param-row.component";
import {PropertyFEModel} from "../../../../../models/properties-inputs/property-fe-model";
+import {DataTypeService} from "../../../../services/data-type.service";
+import {Observable} from "rxjs/Observable";
+import {DataTypeModel} from "../../../../../models/data-types";
@Component({
selector: 'operation-handler',
@@ -40,10 +39,10 @@
styleUrls: ['./interface-operation-handler.component.less'],
providers: [TranslateService]
})
-
export class InterfaceOperationHandlerComponent {
- @Output('propertyChanged') emitter: EventEmitter<PropertyFEModel> = new EventEmitter<PropertyFEModel>();
+ @Input() private modelName: string;
+ @Output('propertyChanged') emitter: EventEmitter<PropertyFEModel> = new EventEmitter<PropertyFEModel>();
input: {
toscaArtifactTypes: Array<DropdownValue>;
selectedInterface: UIInterfaceModel;
@@ -52,6 +51,8 @@
isViewOnly: boolean;
};
+ dataTypeMap$: Observable<Map<string, DataTypeModel>>;
+ dataTypeMap: Map<string, DataTypeModel>;
interfaceType: string;
artifactVersion: string;
artifactName: string;
@@ -70,23 +71,40 @@
enableAddArtifactImplementation: boolean;
propertyValueValid: boolean = true;
+ inputTypeOptions: any[];
+
+ constructor(private dataTypeService: DataTypeService) {
+ this.dataTypeMap$ = new Observable<Map<string, DataTypeModel>>(subscriber => {
+ this.dataTypeService.findAllDataTypesByModel(this.modelName)
+ .then((dataTypesMap: Map<string, DataTypeModel>) => {
+ subscriber.next(dataTypesMap);
+ });
+ });
+ this.dataTypeMap$.subscribe(value => {
+ this.dataTypeMap = value;
+ });
+
+ }
ngOnInit() {
this.isViewOnly = this.input.isViewOnly;
this.interfaceType = this.input.selectedInterface.displayType();
- this.operationToUpdate = new InterfaceOperationModel(this.input.selectedInterfaceOperation);
+ this.operationToUpdate = this.input.selectedInterfaceOperation;
this.operationToUpdate.interfaceId = this.input.selectedInterface.uniqueId;
this.operationToUpdate.interfaceType = this.input.selectedInterface.type;
+ this.initInputs();
+ this.removeImplementationQuote();
+ this.validityChanged();
+ this.loadInterfaceOperationImplementation();
+ }
+
+ private initInputs() {
if (!this.operationToUpdate.inputs) {
this.operationToUpdate.inputs = new class implements IOperationParamsList {
listToscaDataDefinition: Array<InputOperationParameter> = [];
}
}
-
- this.inputs = this.operationToUpdate.inputs.listToscaDataDefinition;
- this.removeImplementationQuote();
- this.validityChanged();
- this.loadInterfaceOperationImplementation();
+ this.inputs = Array.from(this.operationToUpdate.inputs.listToscaDataDefinition);
}
private loadInterfaceOperationImplementation() {
@@ -160,11 +178,8 @@
}
}
- onAddInput(inputOperationParameter?: InputOperationParameter): void {
- let newInput = new InputOperationParameter(inputOperationParameter)
- newInput.type = "string";
- newInput.inputId = this.generateUniqueId();
- this.inputs.push(newInput);
+ onAddInput(inputOperationParameter: InputOperationParameter) {
+ this.addInput(inputOperationParameter);
this.validityChanged();
}
@@ -196,16 +211,6 @@
}
}
- private generateUniqueId = (): string => {
- let result = '';
- const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- const charactersLength = characters.length;
- for (let i = 0; i < 36; i++ ) {
- result += characters.charAt(Math.floor(Math.random() * charactersLength));
- }
- return result;
- }
-
validityChanged = () => {
let validState = this.checkFormValidForSubmit();
this.input.validityChangedCallback(validState);
@@ -243,7 +248,7 @@
}
private isParamsValid = (): boolean => {
- const isInputValid = (input) => input.name && input.inputId;
+ const isInputValid = (input) => input.name && input.inputId && input.type;
const isValid = this.inputs.every(isInputValid);
if (!isValid) {
this.readonly = true;
@@ -255,4 +260,48 @@
return { value : val, label: val };
}
+ /**
+ * Handles the input value change event.
+ * @param changedInput the changed input
+ */
+ onInputValueChange(changedInput: InputOperationParameter) {
+ if (changedInput.value instanceof Object) {
+ changedInput.value = JSON.stringify(changedInput.value);
+ }
+ const inputOperationParameter = this.inputs.find(value => value.name == changedInput.name);
+ inputOperationParameter.value = changedInput.value;
+ }
+
+ /**
+ * Handles the add input event.
+ * @param input the input to add
+ * @private
+ */
+ private addInput(input: InputOperationParameter) {
+ this.operationToUpdate.inputs.listToscaDataDefinition.push(input);
+ this.inputs = Array.from(this.operationToUpdate.inputs.listToscaDataDefinition);
+ }
+
+ /**
+ * Return a list with current input names.
+ */
+ collectInputNames() {
+ return this.inputs.map((input) => input.name);
+ }
+
+ /**
+ * Handles the delete input event.
+ * @param inputName the name of the input to be deleted
+ */
+ onInputDelete(inputName: string) {
+ const currentInputs = this.operationToUpdate.inputs.listToscaDataDefinition;
+ const input1 = currentInputs.find(value => value.name === inputName);
+ const indexOfInput = currentInputs.indexOf(input1);
+ if (indexOfInput === -1) {
+ console.error(`Could delete input '${inputName}'. Input not found.`);
+ return;
+ }
+ currentInputs.splice(currentInputs.indexOf(input1), 1);
+ this.inputs = Array.from(currentInputs);
+ }
}
diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts
index 2595301..b212eec 100644
--- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts
+++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.module.ts
@@ -22,22 +22,27 @@
import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
-import {FormsModule} from "@angular/forms";
+import {FormsModule, ReactiveFormsModule} from "@angular/forms";
import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module";
import {TranslateModule} from "app/ng2/shared/translator/translate.module";
-import {SdcUiComponentsModule} from 'onap-ui-angular';
-import {UiElementsModule} from '../../../../components/ui/ui-elements.module';
-import {InputParamRowComponent} from './input-param-row/input-param-row.component';
-import {InterfaceOperationHandlerComponent} from "./interface-operation-handler.component";
-import {PropertyParamRowComponent} from "./property-param-row/property-param-row.component";
-import {PropertyTableModule} from "../../../../components/logic/properties-table/property-table.module";
+
+import {SdcUiComponentsModule} from "onap-ui-angular/dist";
+import { InterfaceOperationHandlerComponent } from "app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component";
+import { PropertyParamRowComponent } from "app/ng2/pages/composition/interface-operatons/operation-creator/property-param-row/property-param-row.component";
+import { UiElementsModule } from "app/ng2/components/ui/ui-elements.module";
+import { PropertyTableModule } from "app/ng2/components/logic/properties-table/property-table.module";
+import { AddInputComponent } from './add-input/add-input.component';
+import { InputListComponent } from './input-list/input-list.component';
+import { InputListItemComponent } from './input-list/input-list-item/input-list-item.component';
@NgModule({
declarations: [
InterfaceOperationHandlerComponent,
- InputParamRowComponent,
- PropertyParamRowComponent
+ PropertyParamRowComponent,
+ AddInputComponent,
+ InputListComponent,
+ InputListItemComponent
],
imports: [
CommonModule,
@@ -46,7 +51,8 @@
FormElementsModule,
TranslateModule,
UiElementsModule,
- PropertyTableModule
+ PropertyTableModule,
+ ReactiveFormsModule
],
exports: [
PropertyParamRowComponent
diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/panel-tab.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/panel-tab.component.ts
index c148a4e..df8f46d 100644
--- a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/panel-tab.component.ts
+++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/panel-tab.component.ts
@@ -1,13 +1,21 @@
-import { NgModule, Component, Compiler, ViewContainerRef, ViewChild, Input, ComponentRef, ComponentFactoryResolver, ChangeDetectorRef } from '@angular/core';
-import {Component as TopologyTemplate} from "app/models";
-import { SdcUiServices } from "onap-ui-angular";
+import {
+ Component,
+ ViewContainerRef,
+ ViewChild,
+ Input,
+ ComponentRef,
+ ComponentFactoryResolver,
+ ChangeDetectorRef,
+ OnChanges, OnDestroy, AfterViewInit
+} from '@angular/core';
+import {Component as TopologyTemplate} from 'app/models';
// Helper component to add dynamic tabs
@Component({
selector: 'panel-tab',
template: `<div #content></div>`
})
-export class PanelTabComponent {
+export class PanelTabComponent implements OnChanges, OnDestroy, AfterViewInit {
@ViewChild('content', { read: ViewContainerRef }) content;
@Input() isActive:boolean;
@Input() panelTabType;
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.module.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.module.ts
index 1cbb4e1..6690410 100644
--- a/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.module.ts
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.module.ts
@@ -1,27 +1,30 @@
-import { CommonModule } from '@angular/common';
-import { NgModule } from '@angular/core';
-import { FormsModule } from '@angular/forms';
-import { FormElementsModule } from 'app/ng2/components/ui/form-components/form-elements.module';
-import { UiElementsModule } from 'app/ng2/components/ui/ui-elements.module';
-import { TranslateModule } from '../../../shared/translator/translate.module';
-import { PropertyCreatorComponent } from './property-creator.component';
+import {CommonModule} from '@angular/common';
+import {NgModule} from '@angular/core';
+import {FormsModule} from '@angular/forms';
+import {FormElementsModule} from 'app/ng2/components/ui/form-components/form-elements.module';
+import {UiElementsModule} from 'app/ng2/components/ui/ui-elements.module';
+import {TranslateModule} from '../../../shared/translator/translate.module';
+import {PropertyCreatorComponent} from './property-creator.component';
+import {SdcUiComponentsModule} from "onap-ui-angular/dist";
@NgModule({
- declarations: [
- PropertyCreatorComponent,
- ],
- imports: [
- CommonModule,
- FormsModule,
- FormElementsModule,
- UiElementsModule,
- TranslateModule
- ],
- exports: [],
- entryComponents: [
- PropertyCreatorComponent
- ],
- providers: []
+ declarations: [
+ PropertyCreatorComponent,
+ ],
+ imports: [
+ CommonModule,
+ FormsModule,
+ SdcUiComponentsModule,
+ FormElementsModule,
+ UiElementsModule,
+ TranslateModule
+ ],
+ exports: [],
+ entryComponents: [
+ PropertyCreatorComponent
+ ],
+ providers: []
})
-export class PropertyCreatorModule {}
+export class PropertyCreatorModule {
+}
diff --git a/catalog-ui/src/app/ng2/services/data-type.service.ts b/catalog-ui/src/app/ng2/services/data-type.service.ts
index 5b08e93..70555a5 100644
--- a/catalog-ui/src/app/ng2/services/data-type.service.ts
+++ b/catalog-ui/src/app/ng2/services/data-type.service.ts
@@ -60,6 +60,10 @@
return this.dataTypeService.getAllDataTypesFromModel(modelName);
}
+ public findAllDataTypesByModel(modelName: string): Promise<Map<string, DataTypeModel>> {
+ return this.dataTypeService.findAllDataTypesByModel(modelName);
+ }
+
public getConstraintsByParentTypeAndUniqueID(rootPropertyType, propertyName){
// const property = this.dataTypes[rootPropertyType].properties.filter(property =>
// property.name == propertyName);
diff --git a/catalog-ui/src/app/services/data-types-service.ts b/catalog-ui/src/app/services/data-types-service.ts
index 08b49ae..1a0fc47 100644
--- a/catalog-ui/src/app/services/data-types-service.ts
+++ b/catalog-ui/src/app/services/data-types-service.ts
@@ -27,7 +27,7 @@
PropertyModel,
InputPropertyBase,
IAppConfigurtaion,
- SchemaProperty
+ SchemaProperty, DataTypeModel
} from "../models";
import {PROPERTY_DATA} from "../utils/constants";
@@ -40,7 +40,8 @@
selectedInstance:ComponentInstance;
selectedComponentInputs:Array<InputModel>;
//declare methods
- fetchDataTypesByModel(modelName:string):void;
+ loadDataTypesCache(modelName:string):void;
+ findAllDataTypesByModel(modelName: string): void;
getAllDataTypes():DataTypesMap;
getFirsLevelOfDataTypeProperties(dataTypeName:string):Array<DataTypePropertyModel>;
isDataTypeForSchemaType(property:SchemaProperty):boolean;
@@ -70,7 +71,7 @@
selectedInstance:ComponentInstance;
selectedComponentInputs:Array<InputModel>;
- public fetchDataTypesByModel = (modelName: string):void => {
+ public loadDataTypesCache = (modelName: string): void => {
let model;
if (modelName) {
model = {'model': modelName}
@@ -82,11 +83,35 @@
});
};
+ public fetchDataTypesByModel = (modelName: string): angular.IHttpPromise<any> => {
+ let model;
+ if (modelName) {
+ model = {'model': modelName}
+ }
+ return this.$http.get(this.baseUrl + "dataTypes", {params: model});
+ };
+
public getAllDataTypesFromModel = (modelName: string): DataTypesMap => {
- this.fetchDataTypesByModel(modelName);
+ this.loadDataTypesCache(modelName);
return this.dataTypes;
}
+ public findAllDataTypesByModel = (modelName: string): Promise<Map<string, DataTypeModel>> => {
+ return new Promise<Map<string, DataTypeModel>>((resolve, reject) => {
+ this.fetchDataTypesByModel(modelName).then(response => {
+ const dataTypes = response.data;
+ delete dataTypes[PROPERTY_DATA.ROOT_DATA_TYPE];
+ const dataTypeMap = new Map<string, DataTypeModel>();
+ for(const dataTypeKey of Object.keys(dataTypes)) {
+ dataTypeMap.set(dataTypeKey, new DataTypeModel(dataTypes[dataTypeKey]))
+ }
+ resolve(dataTypeMap);
+ }).catch(reason => {
+ reject(reason);
+ });
+ });
+ }
+
public getAllDataTypes = ():DataTypesMap => {
return this.dataTypes;
};