Support complex types in artifact properties
[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 {SchemaPropertyGroupModel} from '../../../../../../../models/schema-property';
25 import {DerivedPropertyType, PropertyBEModel} from '../../../../../../../models/properties-inputs/property-be-model';
26 import {PROPERTY_DATA, PROPERTY_TYPES} from '../../../../../../../utils/constants';
27
28 @Component({
29   selector: 'app-input-list-item',
30   templateUrl: './input-list-item.component.html',
31   styleUrls: ['./input-list-item.component.less']
32 })
33 export class InputListItemComponent implements OnInit {
34
35   @Input() valueObjRef: any;
36   @Input() name: string;
37   @Input() dataTypeMap: Map<string, DataTypeModel>;
38   @Input() type: DataTypeModel;
39   @Input() schema: SchemaPropertyGroupModel;
40   @Input() nestingLevel: number;
41   @Input() isListChild: boolean = false;
42   @Input() isMapChild: boolean = false;
43   @Input() listIndex: number;
44   @Input() isViewOnly: boolean;
45   @Input() allowDeletion: boolean = false;
46   @Output('onValueChange') onValueChangeEvent: EventEmitter<any> = new EventEmitter<any>();
47   @Output('onDelete') onDeleteEvent: EventEmitter<string> = new EventEmitter<string>();
48   @Output('onChildListItemDelete') onChildListItemDeleteEvent: EventEmitter<number> = new EventEmitter<number>();
49
50   isExpanded: boolean = false;
51   mapEntryName: string;
52
53   ngOnInit() {
54     if (!this.nestingLevel) {
55       this.nestingLevel = 0;
56     }
57     if (this.type.properties) {
58       this.type.properties.forEach(property => {
59         this.initEmptyPropertyInValueObjRef(property);
60       });
61     }
62   }
63
64   private initEmptyPropertyInValueObjRef(property: PropertyBEModel) {
65     if (this.valueObjRef[property.name] == undefined) {
66       if (this.isTypeComplex(property.type) || this.isTypeMap(property.type)) {
67         this.valueObjRef[property.name] = {};
68       } else if (this.isTypeList(property.type)) {
69         this.valueObjRef[property.name] = [];
70       } else {
71         this.valueObjRef[property.name] = null;
72       }
73     }
74   }
75
76   getType(typeName: string): DerivedPropertyType {
77     if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(typeName) > -1) {
78       return DerivedPropertyType.SIMPLE;
79     } else if (typeName === PROPERTY_TYPES.LIST) {
80       return DerivedPropertyType.LIST;
81     } else if (typeName === PROPERTY_TYPES.MAP) {
82       return DerivedPropertyType.MAP;
83     } else {
84       return DerivedPropertyType.COMPLEX;
85     }
86   }
87
88   isTypeSimple(typeName: string): boolean {
89     return this.getType(typeName) == DerivedPropertyType.SIMPLE;
90   }
91
92   isTypeList(typeName: string): boolean {
93     return this.getType(typeName) == DerivedPropertyType.LIST;
94   }
95
96   isTypeMap(typeName: string): boolean {
97     return this.getType(typeName) == DerivedPropertyType.MAP;
98   }
99
100   isTypeComplex(typeName: string): boolean {
101     return !this.isTypeSimple(typeName) && !this.isTypeList(typeName) && !this.isTypeMap(typeName);
102   }
103
104   expandAndCollapse() {
105     this.isExpanded = !this.isExpanded;
106   }
107
108   getDataType(type: string) {
109     return this.dataTypeMap.get(type);
110   }
111
112   onValueChange(value: any): void {
113     if (this.type.name == PROPERTY_TYPES.INTEGER || this.type.name == PROPERTY_TYPES.FLOAT) {
114       this.emitValueChangeEvent(this.parseNumber(value));
115       return;
116     }
117     if (this.type.name == PROPERTY_TYPES.BOOLEAN) {
118       this.emitValueChangeEvent(this.parseBoolean(value));
119       return;
120     }
121     this.emitValueChangeEvent(value);
122   }
123
124   onListValueChange(): void {
125     this.emitValueChangeEvent(this.valueObjRef);
126   }
127
128   onPropertyValueChange($event: any) {
129     this.valueObjRef[$event.name] = $event.value;
130     this.emitValueChangeEvent(this.valueObjRef);
131   }
132
133   private emitValueChangeEvent(value: any) {
134     this.onValueChangeEvent.emit({
135       name: this.name,
136       value: value
137     });
138   }
139
140   isRoot(): boolean {
141     return this.nestingLevel === 0;
142   }
143
144   showListItemDelete(): boolean {
145     return !this.isViewOnly && (this.isListChild && this.nestingLevel > 0);
146   }
147
148   showInputDelete(): boolean {
149     return this.allowDeletion && !this.isViewOnly && (this.isRoot() || this.isMapChild);
150   }
151
152   resolveType(): string {
153     if (this.isTypeList(this.type.name)) {
154       return `list of value type ${this.schema.property.type}`
155     }
156     if (this.isTypeMap(this.type.name)) {
157       return `map of 'string' keys and '${this.schema.property.type}' values`
158     }
159     return this.type.name;
160   }
161
162   onInputDelete() {
163     this.onDeleteEvent.emit(this.name);
164   }
165
166   onListItemDelete(index: number): void {
167     this.valueObjRef.splice(index, 1);
168     this.emitValueChangeEvent(this.valueObjRef);
169   }
170
171   addListElement() {
172     if (this.isTypeSimple(this.schema.property.type)) {
173       this.valueObjRef.push('');
174     } else if (this.isTypeComplex(this.schema.property.type) || this.isTypeMap(this.schema.property.type)) {
175       this.valueObjRef.push({});
176     } else if (this.isTypeList(this.schema.property.type)) {
177       this.valueObjRef.push([]);
178     }
179   }
180
181   trackByIndex(index: number, value: string): number {
182     return index;
183   }
184
185   onChildListItemDelete() {
186     this.onChildListItemDeleteEvent.emit(this.listIndex);
187   }
188
189   getObjectEntries(valueObjRef: object) {
190     return Object.keys(valueObjRef);
191   }
192
193   onMapValueChange() {
194     this.emitValueChangeEvent(this.valueObjRef);
195   }
196
197   onMapKeyDelete(key: string) {
198     delete this.valueObjRef[key]
199     this.emitValueChangeEvent(this.valueObjRef);
200   }
201
202   addMapEntry() {
203     let newKey;
204     if (this.mapEntryName) {
205       newKey = this.mapEntryName.trim();
206     }
207     if (!newKey) {
208       return;
209     }
210     if (Object.keys(this.valueObjRef).indexOf(newKey) !== -1) {
211       return;
212     }
213     this.mapEntryName = '';
214     if (this.isTypeSimple(this.schema.property.type)) {
215       this.valueObjRef[newKey] = '';
216     } else if (this.isTypeComplex(this.schema.property.type) || this.isTypeMap(this.schema.property.type)) {
217       this.valueObjRef[newKey] = {};
218     } else if (this.isTypeList(this.schema.property.type)) {
219       this.valueObjRef[newKey] = [];
220     }
221     this.emitValueChangeEvent(this.valueObjRef);
222   }
223
224   getSimpleValueInputType() {
225     if (this.type.name == PROPERTY_TYPES.INTEGER || this.type.name == PROPERTY_TYPES.FLOAT) {
226       return 'number';
227     }
228     return 'text';
229   }
230
231   private parseBoolean(value: any) {
232     if (value === 'true') {
233       return true;
234     }
235     if (value === 'false') {
236       return false;
237     }
238     return null;
239   }
240
241   private parseNumber(value: any) {
242     const number = parseInt(value);
243     return isNaN(number) ? null : number;
244   }
245
246 }