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