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