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