Fix formatting error for operation input of complex type
[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
34 @Component({
35   selector: 'app-input-list-item',
36   templateUrl: './input-list-item.component.html',
37   styleUrls: ['./input-list-item.component.less']
38 })
39 export class InputListItemComponent implements OnInit {
40
41   @Input() valueObjRef: any;
42   @Input() name: string;
43   @Input() dataTypeMap: Map<string, DataTypeModel>;
44   @Input() type: DataTypeModel;
45   @Input() schema: SchemaPropertyGroupModel;
46   @Input() nestingLevel: number;
47   @Input() isExpanded: boolean = false;
48   @Input() isListChild: boolean = false;
49   @Input() isMapChild: boolean = false;
50   @Input() showToscaFunctionOption: boolean = false;
51   @Input() listIndex: number;
52   @Input() subPropertyToscaFunctions: SubPropertyToscaFunctions[];
53   @Input() isViewOnly: boolean;
54   @Input() allowDeletion: boolean = false;
55   @Input() toscaFunction: ToscaFunction;
56   @Input() componentInstanceMap: Map<string, InstanceFeDetails> = new Map();
57   @Input() customToscaFunctions: Array<CustomToscaFunction> = [];
58   @Output('onValueChange') onValueChangeEvent: EventEmitter<any> = new EventEmitter<any>();
59   @Output('onDelete') onDeleteEvent: EventEmitter<string> = new EventEmitter<string>();
60   @Output('onChildListItemDelete') onChildListItemDeleteEvent: EventEmitter<number> = new EventEmitter<number>();
61
62   mapEntryName: string;
63   isToscaFunction: boolean = false;
64   property: PropertyBEModel;
65
66   ngOnInit(): void {
67     if (!this.nestingLevel) {
68       this.nestingLevel = 0;
69     }
70     if (this.type.properties) {
71       this.type.properties.forEach(property => {
72         this.initEmptyPropertyInValueObjRef(property);
73       });
74     }
75
76     this.property = new PropertyBEModel();
77     this.property.type = this.type.name;
78     if (this.schema) {
79       this.property.schema = this.schema;
80       this.property.schemaType = this.schema.property.type;
81     }
82     if (this.toscaFunction) {
83       this.property.toscaFunction = this.toscaFunction;
84       this.valueObjRef = this.toscaFunction.value;
85       this.isToscaFunction = true;
86     }
87   }
88
89   private initEmptyPropertyInValueObjRef(property: PropertyBEModel) {
90     if (this.valueObjRef[property.name] == undefined) {
91       if (this.isTypeComplex(property.type) || this.isTypeMap(property.type)) {
92         this.valueObjRef[property.name] = {};
93       } else if (this.isTypeList(property.type) || this.isTypeRange(property.type)) {
94         this.valueObjRef[property.name] = [];
95       } else {
96         this.valueObjRef[property.name] = null;
97       }
98     }
99   }
100
101   getToscaFunction(key: any): any {
102     if (this.subPropertyToscaFunctions) {
103       for (let subPropertyToscaFunction of this.subPropertyToscaFunctions) {
104         let found = subPropertyToscaFunction.subPropertyPath.find(value => value === key);
105         if (found) {
106           return subPropertyToscaFunction.toscaFunction;
107         }
108       }
109     }
110     if ((key && this.valueObjRef[key] && this.valueObjRef[key].type)) {
111       const type = this.valueObjRef[key].type;
112       if (type in ToscaFunctionType) {
113         return <ToscaFunction> this.valueObjRef[key];
114       }
115     }
116     return undefined;
117   }
118
119   isTypeSimple(typeName: string): boolean {
120     return ToscaTypeHelper.isTypeSimple(typeName) || this.isTypeDerivedFromSimple(typeName) && (this.isTypeWithoutProperties(typeName));
121   }
122
123   isTypeRange(typeName: string): boolean {
124     return ToscaTypeHelper.isTypeRange(typeName);
125   }
126
127   isTypeWithoutProperties(typeName: string): boolean {
128     if (this.dataTypeMap.get(typeName) === undefined) {
129       return true;
130     }
131     return this.dataTypeMap.get(typeName).properties === undefined ||
132         this.dataTypeMap.get(typeName).properties.length == 0;
133   }
134
135   isTypeDerivedFromSimple(typeName: string): boolean {
136     if (typeName === undefined) {
137       return false;
138     }
139     if (this.dataTypeMap.get(typeName) !== undefined) {
140       if (this.isTypeRange(typeName)) {
141         return false;
142       }
143       if (this.dataTypeMap.get(typeName).derivedFromName == PROPERTY_DATA.ROOT_DATA_TYPE) {
144         return false;
145       } else if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(this.dataTypeMap.get(typeName).derivedFromName) > -1) {
146         return true;
147       } else {
148         return this.isTypeDerivedFromSimple(this.dataTypeMap.get(typeName).derivedFromName);
149       }
150     }
151     return true;
152   }
153
154   isTypeList(typeName: string): boolean {
155     return ToscaTypeHelper.isTypeList(typeName);
156   }
157
158   isTypeMap(typeName: string): boolean {
159     return ToscaTypeHelper.isTypeMap(typeName);
160   }
161
162   isTypeComplex(typeName: string): boolean {
163     return ToscaTypeHelper.isTypeComplex(typeName);
164   }
165
166   isTypeNumber(type: string): boolean {
167     return ToscaTypeHelper.isTypeNumber(type);
168   }
169
170   isTypeBoolean(type: string): boolean {
171     return ToscaTypeHelper.isTypeBoolean(type);
172   }
173
174   isTypeLiteral(type: string): boolean {
175     return ToscaTypeHelper.isTypeLiteral(type);
176   }
177
178   expandAndCollapse() {
179     this.isExpanded = !this.isExpanded;
180   }
181
182   getDataType(type: string) {
183     return this.dataTypeMap.get(type);
184   }
185
186   onValueTypeChange () {
187     if ( !this.isToscaFunction ) {
188       this.onValueChange(this.valueObjRef);
189     }
190   }
191
192   onToscaFunctionValidityChange(validationEvent: ToscaFunctionValidationEvent):void {
193     if (validationEvent.isValid) {
194       this.emitValueChangeEvent(validationEvent.toscaFunction, true);
195       return;
196     }
197     this.emitValueChangeEvent(undefined, true);
198   }
199
200   onValueChange(value: any): void {
201     if (this.isTypeNumber(this.type.name)) {
202       this.emitValueChangeEvent(this.parseNumber(value));
203       return;
204     }
205     if (this.type.name == PROPERTY_TYPES.BOOLEAN) {
206       this.emitValueChangeEvent(this.parseBoolean(value));
207       return;
208     }
209     this.emitValueChangeEvent(value);
210   }
211
212   onListValueChange(): void {
213     this.emitValueChangeEvent(this.valueObjRef);
214   }
215
216   onPropertyValueChange($event: any) {
217     this.valueObjRef[$event.name] = $event.value;
218     this.emitValueChangeEvent(this.valueObjRef);
219   }
220
221   private emitValueChangeEvent(value: any, isToscaFunction=false) {
222     let emitValue = {
223       name: this.name,
224       value: value,
225       isToscaFunction:isToscaFunction
226     };
227     this.onValueChangeEvent.emit(emitValue);
228   }
229
230   isRoot(): boolean {
231     return this.nestingLevel === 0;
232   }
233
234   showListItemDelete(): boolean {
235     return !this.isViewOnly && (this.isListChild && this.nestingLevel > 0);
236   }
237
238   showInputDelete(): boolean {
239     return this.allowDeletion && !this.isViewOnly && (this.isRoot() || this.isMapChild);
240   }
241
242   resolveType(): string {
243     if (this.isTypeList(this.type.name)) {
244       return `list of value type ${this.schema.property.type}`
245     }
246     if (this.isTypeMap(this.type.name)) {
247       return `map of 'string' keys and '${this.schema.property.type}' values`
248     }
249     return this.type.name;
250   }
251
252   onInputDelete() {
253     this.onDeleteEvent.emit(this.name);
254   }
255
256   onListItemDelete(index: number): void {
257     this.valueObjRef.splice(index, 1);
258     this.emitValueChangeEvent(this.valueObjRef);
259   }
260
261   addListElement() {
262     if (!this.valueObjRef) {
263       this.valueObjRef = [];
264     }
265     if (this.isTypeSimple(this.schema.property.type)) {
266       this.valueObjRef.push('');
267     } else if (this.isTypeComplex(this.schema.property.type) || this.isTypeMap(this.schema.property.type)) {
268       this.valueObjRef.push({});
269     } else if (this.isTypeList(this.schema.property.type)) {
270       this.valueObjRef.push([]);
271     }
272   }
273
274   trackByIndex(index: number, value: string): number {
275     return index;
276   }
277
278   onChildListItemDelete() {
279     this.onChildListItemDeleteEvent.emit(this.listIndex);
280   }
281
282   getObjectEntries(valueObjRef: object) {
283     return Object.keys(valueObjRef);
284   }
285
286   onMapValueChange() {
287     this.emitValueChangeEvent(this.valueObjRef);
288   }
289
290   onMapKeyDelete(key: string) {
291     delete this.valueObjRef[key]
292     this.emitValueChangeEvent(this.valueObjRef);
293   }
294
295   addMapEntry() {
296     let newKey;
297     if (this.mapEntryName) {
298       newKey = this.mapEntryName.trim();
299     }
300     if (!newKey) {
301       return;
302     }
303     if (Object.keys(this.valueObjRef).indexOf(newKey) !== -1) {
304       return;
305     }
306     this.mapEntryName = '';
307     if (this.isTypeSimple(this.schema.property.type)) {
308       this.valueObjRef[newKey] = '';
309     } else if (this.isTypeComplex(this.schema.property.type) || this.isTypeMap(this.schema.property.type)) {
310       this.valueObjRef[newKey] = {};
311     } else if (this.isTypeList(this.schema.property.type)) {
312       this.valueObjRef[newKey] = [];
313     }
314     this.emitValueChangeEvent(this.valueObjRef);
315   }
316
317   getSimpleValueInputType() {
318     if (this.isTypeNumber(this.type.name)){
319       return 'number';
320     }
321     return 'text';
322   }
323
324   buildSchemaGroupProperty(): SchemaPropertyGroupModel {
325     const schemaProperty = new SchemaProperty();
326     if (this.schema.property.type === PROPERTY_TYPES.MAP || this.schema.property.type === PROPERTY_TYPES.LIST) {
327       schemaProperty.type = PROPERTY_TYPES.STRING;
328     } else {
329       schemaProperty.type = this.schema.property.type
330     }
331     return new SchemaPropertyGroupModel(schemaProperty);
332   }
333
334   private parseBoolean(value: any) {
335     if (value === 'true') {
336       return true;
337     }
338     if (value === 'false') {
339       return false;
340     }
341     return null;
342   }
343
344   private parseNumber(value: any) {
345     const number = parseInt(value);
346     return isNaN(number) ? null : number;
347   }
348
349 }
350
351 export interface SubPropertyToscaFunctions {
352   subPropertyPath: string[];
353   toscaFunction: ToscaFunction;
354 }