UI Support for operation milestones
[sdc.git] / catalog-ui / src / app / ng2 / pages / composition / interface-operatons / operation-creator / input-list / input-list-item / input-list-item.component.ts
1 /*
2  * -
3  *  ============LICENSE_START=======================================================
4  *  Copyright (C) 2022 Nordix Foundation.
5  *  ================================================================================
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *       http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  *
18  *  SPDX-License-Identifier: Apache-2.0
19  *  ============LICENSE_END=========================================================
20  */
21
22 import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
23 import {DataTypeModel} from '../../../../../../../models/data-types';
24 import {SchemaProperty, SchemaPropertyGroupModel} from '../../../../../../../models/schema-property';
25 import {PropertyBEModel} from '../../../../../../../models/properties-inputs/property-be-model';
26 import {PROPERTY_DATA, PROPERTY_TYPES} from '../../../../../../../utils/constants';
27 import {ToscaFunction} from '../../../../../../../models/tosca-function';
28 import {ToscaFunctionType} from "../../../../../../../models/tosca-function-type.enum";
29 import {ToscaFunctionValidationEvent} from "../../../../../properties-assignment/tosca-function/tosca-function.component";
30 import {InstanceFeDetails} from "../../../../../../../models/instance-fe-details";
31 import {ToscaTypeHelper} from "app/utils/tosca-type-helper";
32 import {CustomToscaFunction} from "../../../../../../../models/default-custom-functions";
33 import {SubPropertyToscaFunction} from "../../../../../../../models/sub-property-tosca-function";
34
35 @Component({
36   selector: 'app-input-list-item',
37   templateUrl: './input-list-item.component.html',
38   styleUrls: ['./input-list-item.component.less']
39 })
40 export class InputListItemComponent implements OnInit {
41
42   @Input() valueObjRef: any;
43   @Input() name: string;
44   @Input() dataTypeMap: Map<string, DataTypeModel>;
45   @Input() type: DataTypeModel;
46   @Input() schema: SchemaPropertyGroupModel;
47   @Input() nestingLevel: number;
48   @Input() isExpanded: boolean = false;
49   @Input() isListChild: boolean = false;
50   @Input() isMapChild: boolean = false;
51   @Input() showToscaFunctionOption: boolean = false;
52   @Input() listIndex: number;
53   @Input() subPropertyToscaFunctions: SubPropertyToscaFunction[];
54   @Input() isViewOnly: boolean;
55   @Input() allowDeletion: boolean = false;
56   @Input() toscaFunction: ToscaFunction;
57   @Input() componentInstanceMap: Map<string, InstanceFeDetails> = new Map();
58   @Input() customToscaFunctions: Array<CustomToscaFunction> = [];
59   @Output('onValueChange') onValueChangeEvent: EventEmitter<any> = new EventEmitter<any>();
60   @Output('onDelete') onDeleteEvent: EventEmitter<string> = new EventEmitter<string>();
61   @Output('onChildListItemDelete') onChildListItemDeleteEvent: EventEmitter<number> = new EventEmitter<number>();
62
63   mapEntryName: string;
64   isToscaFunction: boolean = false;
65   property: PropertyBEModel;
66
67   ngOnInit(): void {
68     if (!this.nestingLevel) {
69       this.nestingLevel = 0;
70     }
71     if (this.type.properties) {
72       this.type.properties.forEach(property => {
73         this.initEmptyPropertyInValueObjRef(property);
74       });
75     }
76
77     this.property = new PropertyBEModel();
78     this.property.type = this.type.name;
79     if (this.schema) {
80       this.property.schema = this.schema;
81       this.property.schemaType = this.schema.property.type;
82     }
83     if (this.toscaFunction) {
84       this.property.toscaFunction = this.toscaFunction;
85       this.valueObjRef = this.toscaFunction.value;
86       this.isToscaFunction = true;
87     }
88     if (this.property.type == PROPERTY_TYPES.JSON) {
89       this.valueObjRef = JSON.stringify(this.valueObjRef);
90     }
91   }
92
93   ngOnChanges(): void {
94     if (this.isToscaFunction) {
95       this.property.toscaFunction = this.toscaFunction;
96       this.valueObjRef = this.toscaFunction.value;
97     } else {
98       this.property = this.property ? this.property : new PropertyBEModel();
99       this.property.toscaFunction = undefined;
100     }
101   }
102
103   private initEmptyPropertyInValueObjRef(property: PropertyBEModel) {
104     if (this.valueObjRef[property.name] == undefined) {
105       if (this.isTypeComplex(property.type) || this.isTypeMap(property.type)) {
106         this.valueObjRef[property.name] = {};
107       } else if (this.isTypeList(property.type) || this.isTypeRange(property.type)) {
108         this.valueObjRef[property.name] = [];
109       } else {
110         this.valueObjRef[property.name] = null;
111       }
112     }
113   }
114
115   getToscaFunction(key: any): any {
116     if (this.subPropertyToscaFunctions) {
117       for (let subPropertyToscaFunction of this.subPropertyToscaFunctions) {
118         let found = subPropertyToscaFunction.subPropertyPath ? subPropertyToscaFunction.subPropertyPath.find(value => value === key) : false;
119         if (found) {
120           return subPropertyToscaFunction.toscaFunction;
121         }
122       }
123     }
124     return undefined;
125   }
126
127   isTypeSimple(typeName: string): boolean {
128     return ToscaTypeHelper.isTypeSimple(typeName) || this.isTypeDerivedFromSimple(typeName) && (this.isTypeWithoutProperties(typeName));
129   }
130
131   isTypeRange(typeName: string): boolean {
132     return ToscaTypeHelper.isTypeRange(typeName);
133   }
134
135   isTypeWithoutProperties(typeName: string): boolean {
136     if (this.dataTypeMap.get(typeName) === undefined) {
137       return true;
138     }
139     return this.dataTypeMap.get(typeName).properties === undefined ||
140         this.dataTypeMap.get(typeName).properties.length == 0;
141   }
142
143   isTypeDerivedFromSimple(typeName: string): boolean {
144     if (typeName === undefined) {
145       return false;
146     }
147     if (this.dataTypeMap.get(typeName) !== undefined) {
148       if (this.isTypeRange(typeName)) {
149         return false;
150       }
151       if (this.dataTypeMap.get(typeName).derivedFromName == PROPERTY_DATA.ROOT_DATA_TYPE) {
152         return false;
153       } else if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(this.dataTypeMap.get(typeName).derivedFromName) > -1) {
154         return true;
155       } else {
156         return this.isTypeDerivedFromSimple(this.dataTypeMap.get(typeName).derivedFromName);
157       }
158     }
159     return true;
160   }
161
162   isTypeList(typeName: string): boolean {
163     return ToscaTypeHelper.isTypeList(typeName);
164   }
165
166   isTypeMap(typeName: string): boolean {
167     return ToscaTypeHelper.isTypeMap(typeName);
168   }
169
170   isTypeComplex(typeName: string): boolean {
171     return ToscaTypeHelper.isTypeComplex(typeName);
172   }
173
174   isTypeNumber(type: string): boolean {
175     return ToscaTypeHelper.isTypeNumber(type);
176   }
177
178   isTypeBoolean(type: string): boolean {
179     return ToscaTypeHelper.isTypeBoolean(type);
180   }
181
182   isTypeLiteral(type: string): boolean {
183     return ToscaTypeHelper.isTypeLiteral(type);
184   }
185
186   expandAndCollapse() {
187     this.isExpanded = !this.isExpanded;
188   }
189
190   getDataType(type: string) {
191     return this.dataTypeMap.get(type);
192   }
193
194   onValueTypeChange () {
195     if ( !this.isToscaFunction ) {
196       this.onValueChange(this.valueObjRef);
197     }
198   }
199
200   onToscaFunctionValidityChange(validationEvent: ToscaFunctionValidationEvent):void {
201     if (validationEvent.isValid) {
202       this.emitValueChangeEvent(validationEvent.toscaFunction, true);
203       return;
204     }
205     this.emitValueChangeEvent(undefined, true);
206   }
207
208   onValueChange(value: any): void {
209     if (this.isTypeNumber(this.type.name)) {
210       this.emitValueChangeEvent(this.parseNumber(value));
211       return;
212     }
213     if (this.type.name == PROPERTY_TYPES.BOOLEAN) {
214       this.emitValueChangeEvent(this.parseBoolean(value));
215       return;
216     }
217     this.emitValueChangeEvent(value);
218   }
219
220   onListValueChange(): void {
221     this.emitValueChangeEvent(this.valueObjRef);
222   }
223
224   onPropertyValueChange($event: any) {
225     this.valueObjRef[$event.name] = $event.value;
226     this.emitValueChangeEvent(this.valueObjRef);
227   }
228
229   private emitValueChangeEvent(value: any, isToscaFunction=false) {
230     let emitValue = {
231       name: this.name,
232       value: value,
233       isToscaFunction:isToscaFunction
234     };
235     this.onValueChangeEvent.emit(emitValue);
236   }
237
238   isRoot(): boolean {
239     return this.nestingLevel === 0;
240   }
241
242   showListItemDelete(): boolean {
243     return !this.isViewOnly && (this.isListChild && this.nestingLevel > 0);
244   }
245
246   showInputDelete(): boolean {
247     return this.allowDeletion && !this.isViewOnly && (this.isRoot() || this.isMapChild);
248   }
249
250   resolveType(): string {
251     if (this.isTypeList(this.type.name)) {
252       return `list of value type ${this.schema.property.type}`
253     }
254     if (this.isTypeMap(this.type.name)) {
255       return `map of 'string' keys and '${this.schema.property.type}' values`
256     }
257     return this.type.name;
258   }
259
260   onInputDelete() {
261     this.onDeleteEvent.emit(this.name);
262   }
263
264   onListItemDelete(index: number): void {
265     this.valueObjRef.splice(index, 1);
266     this.emitValueChangeEvent(this.valueObjRef);
267   }
268
269   addListElement() {
270     if (!this.valueObjRef) {
271       this.valueObjRef = [];
272     }
273     if (this.isTypeSimple(this.schema.property.type)) {
274       this.valueObjRef.push('');
275     } else if (this.isTypeComplex(this.schema.property.type) || this.isTypeMap(this.schema.property.type)) {
276       this.valueObjRef.push({});
277     } else if (this.isTypeList(this.schema.property.type)) {
278       this.valueObjRef.push([]);
279     }
280   }
281
282   trackByIndex(index: number, value: string): number {
283     return index;
284   }
285
286   onChildListItemDelete() {
287     this.onChildListItemDeleteEvent.emit(this.listIndex);
288   }
289
290   getObjectEntries(valueObjRef: object) {
291     return Object.keys(valueObjRef);
292   }
293
294   onMapValueChange() {
295     this.emitValueChangeEvent(this.valueObjRef);
296   }
297
298   onMapKeyDelete(key: string) {
299     delete this.valueObjRef[key]
300     this.emitValueChangeEvent(this.valueObjRef);
301   }
302
303   addMapEntry() {
304     let newKey;
305     if (this.mapEntryName) {
306       newKey = this.mapEntryName.trim();
307     }
308     if (!newKey) {
309       return;
310     }
311     if (Object.keys(this.valueObjRef).indexOf(newKey) !== -1) {
312       return;
313     }
314     this.mapEntryName = '';
315     if (this.isTypeSimple(this.schema.property.type)) {
316       this.valueObjRef[newKey] = '';
317     } else if (this.isTypeComplex(this.schema.property.type) || this.isTypeMap(this.schema.property.type)) {
318       this.valueObjRef[newKey] = {};
319     } else if (this.isTypeList(this.schema.property.type)) {
320       this.valueObjRef[newKey] = [];
321     }
322     this.emitValueChangeEvent(this.valueObjRef);
323   }
324
325   getSimpleValueInputType() {
326     if (this.isTypeNumber(this.type.name)){
327       return 'number';
328     }
329     return 'text';
330   }
331
332   buildSchemaGroupProperty(): SchemaPropertyGroupModel {
333     const schemaProperty = new SchemaProperty();
334     if (this.schema.property.type === PROPERTY_TYPES.MAP || this.schema.property.type === PROPERTY_TYPES.LIST) {
335       schemaProperty.type = PROPERTY_TYPES.STRING;
336     } else {
337       schemaProperty.type = this.schema.property.type
338     }
339     return new SchemaPropertyGroupModel(schemaProperty);
340   }
341
342   private parseBoolean(value: any) {
343     if (value === 'true') {
344       return true;
345     }
346     if (value === 'false') {
347       return false;
348     }
349     return null;
350   }
351
352   private parseNumber(value: any) {
353     const number = parseInt(value);
354     return isNaN(number) ? null : number;
355   }
356
357 }