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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
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 import {SubPropertyToscaFunction} from "../../../../../../../models/sub-property-tosca-function";
36 selector: 'app-input-list-item',
37 templateUrl: './input-list-item.component.html',
38 styleUrls: ['./input-list-item.component.less']
40 export class InputListItemComponent implements OnInit {
42 @Input() valueObjRef: any;
43 @Input() name: string;
44 @Input() dataTypeMap: Map<string, DataTypeModel>;
45 @Input() type: DataTypeModel;
46 @Input() schema: SchemaPropertyGroupModel;
47 @Input() nestingLevel: number;
48 @Input() isExpanded: boolean = false;
49 @Input() isListChild: boolean = false;
50 @Input() isMapChild: boolean = false;
51 @Input() showToscaFunctionOption: boolean = false;
52 @Input() listIndex: number;
53 @Input() subPropertyToscaFunctions: SubPropertyToscaFunction[];
54 @Input() isViewOnly: boolean;
55 @Input() allowDeletion: boolean = false;
56 @Input() toscaFunction: ToscaFunction;
57 @Input() componentInstanceMap: Map<string, InstanceFeDetails> = new Map();
58 @Input() customToscaFunctions: Array<CustomToscaFunction> = [];
59 @Output('onValueChange') onValueChangeEvent: EventEmitter<any> = new EventEmitter<any>();
60 @Output('onDelete') onDeleteEvent: EventEmitter<string> = new EventEmitter<string>();
61 @Output('onChildListItemDelete') onChildListItemDeleteEvent: EventEmitter<number> = new EventEmitter<number>();
64 isToscaFunction: boolean = false;
65 property: PropertyBEModel;
68 if (!this.nestingLevel) {
69 this.nestingLevel = 0;
71 if (this.type.properties) {
72 this.type.properties.forEach(property => {
73 this.initEmptyPropertyInValueObjRef(property);
77 this.property = new PropertyBEModel();
78 this.property.type = this.type.name;
80 this.property.schema = this.schema;
81 this.property.schemaType = this.schema.property.type;
83 if (this.toscaFunction) {
84 this.property.toscaFunction = this.toscaFunction;
85 this.valueObjRef = this.toscaFunction.value;
86 this.isToscaFunction = true;
88 if (this.property.type == PROPERTY_TYPES.JSON) {
89 this.valueObjRef = JSON.stringify(this.valueObjRef);
94 if (this.isToscaFunction) {
95 this.property.toscaFunction = this.toscaFunction;
96 this.valueObjRef = this.toscaFunction.value;
98 this.property = this.property ? this.property : new PropertyBEModel();
99 this.property.toscaFunction = undefined;
103 private initEmptyPropertyInValueObjRef(property: PropertyBEModel) {
104 if (this.valueObjRef[property.name] == undefined) {
105 if (this.isTypeComplex(property.type) || this.isTypeMap(property.type)) {
106 this.valueObjRef[property.name] = {};
107 } else if (this.isTypeList(property.type) || this.isTypeRange(property.type)) {
108 this.valueObjRef[property.name] = [];
110 this.valueObjRef[property.name] = null;
115 getToscaFunction(key: any): any {
116 if (this.subPropertyToscaFunctions) {
117 for (let subPropertyToscaFunction of this.subPropertyToscaFunctions) {
118 let found = subPropertyToscaFunction.subPropertyPath ? subPropertyToscaFunction.subPropertyPath.find(value => value === key) : false;
120 return subPropertyToscaFunction.toscaFunction;
127 isTypeSimple(typeName: string): boolean {
128 return ToscaTypeHelper.isTypeSimple(typeName) || this.isTypeDerivedFromSimple(typeName) && (this.isTypeWithoutProperties(typeName));
131 isTypeRange(typeName: string): boolean {
132 return ToscaTypeHelper.isTypeRange(typeName);
135 isTypeWithoutProperties(typeName: string): boolean {
136 if (this.dataTypeMap.get(typeName) === undefined) {
139 return this.dataTypeMap.get(typeName).properties === undefined ||
140 this.dataTypeMap.get(typeName).properties.length == 0;
143 isTypeDerivedFromSimple(typeName: string): boolean {
144 if (typeName === undefined) {
147 if (this.dataTypeMap.get(typeName) !== undefined) {
148 if (this.isTypeRange(typeName)) {
151 if (this.dataTypeMap.get(typeName).derivedFromName == PROPERTY_DATA.ROOT_DATA_TYPE) {
153 } else if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(this.dataTypeMap.get(typeName).derivedFromName) > -1) {
156 return this.isTypeDerivedFromSimple(this.dataTypeMap.get(typeName).derivedFromName);
162 isTypeList(typeName: string): boolean {
163 return ToscaTypeHelper.isTypeList(typeName);
166 isTypeMap(typeName: string): boolean {
167 return ToscaTypeHelper.isTypeMap(typeName);
170 isTypeComplex(typeName: string): boolean {
171 return ToscaTypeHelper.isTypeComplex(typeName);
174 isTypeNumber(type: string): boolean {
175 return ToscaTypeHelper.isTypeNumber(type);
178 isTypeBoolean(type: string): boolean {
179 return ToscaTypeHelper.isTypeBoolean(type);
182 isTypeLiteral(type: string): boolean {
183 return ToscaTypeHelper.isTypeLiteral(type);
186 expandAndCollapse() {
187 this.isExpanded = !this.isExpanded;
190 getDataType(type: string) {
191 return this.dataTypeMap.get(type);
194 onValueTypeChange () {
195 if ( !this.isToscaFunction ) {
196 this.onValueChange(this.valueObjRef);
200 onToscaFunctionValidityChange(validationEvent: ToscaFunctionValidationEvent):void {
201 if (validationEvent.isValid) {
202 this.emitValueChangeEvent(validationEvent.toscaFunction, true);
205 this.emitValueChangeEvent(undefined, true);
208 onValueChange(value: any): void {
209 if (this.isTypeNumber(this.type.name)) {
210 this.emitValueChangeEvent(this.parseNumber(value));
213 if (this.type.name == PROPERTY_TYPES.BOOLEAN) {
214 this.emitValueChangeEvent(this.parseBoolean(value));
217 this.emitValueChangeEvent(value);
220 onListValueChange(): void {
221 this.emitValueChangeEvent(this.valueObjRef);
224 onPropertyValueChange($event: any) {
225 this.valueObjRef[$event.name] = $event.value;
226 this.emitValueChangeEvent(this.valueObjRef);
229 private emitValueChangeEvent(value: any, isToscaFunction=false) {
233 isToscaFunction:isToscaFunction
235 this.onValueChangeEvent.emit(emitValue);
239 return this.nestingLevel === 0;
242 showListItemDelete(): boolean {
243 return !this.isViewOnly && (this.isListChild && this.nestingLevel > 0);
246 showInputDelete(): boolean {
247 return this.allowDeletion && !this.isViewOnly && (this.isRoot() || this.isMapChild);
250 resolveType(): string {
251 if (this.isTypeList(this.type.name)) {
252 return `list of value type ${this.schema.property.type}`
254 if (this.isTypeMap(this.type.name)) {
255 return `map of 'string' keys and '${this.schema.property.type}' values`
257 return this.type.name;
261 this.onDeleteEvent.emit(this.name);
264 onListItemDelete(index: number): void {
265 this.valueObjRef.splice(index, 1);
266 this.emitValueChangeEvent(this.valueObjRef);
270 if (!this.valueObjRef) {
271 this.valueObjRef = [];
273 if (this.isTypeSimple(this.schema.property.type)) {
274 this.valueObjRef.push('');
275 } else if (this.isTypeComplex(this.schema.property.type) || this.isTypeMap(this.schema.property.type)) {
276 this.valueObjRef.push({});
277 } else if (this.isTypeList(this.schema.property.type)) {
278 this.valueObjRef.push([]);
282 trackByIndex(index: number, value: string): number {
286 onChildListItemDelete() {
287 this.onChildListItemDeleteEvent.emit(this.listIndex);
290 getObjectEntries(valueObjRef: object) {
291 return Object.keys(valueObjRef);
295 this.emitValueChangeEvent(this.valueObjRef);
298 onMapKeyDelete(key: string) {
299 delete this.valueObjRef[key]
300 this.emitValueChangeEvent(this.valueObjRef);
305 if (this.mapEntryName) {
306 newKey = this.mapEntryName.trim();
311 if (Object.keys(this.valueObjRef).indexOf(newKey) !== -1) {
314 this.mapEntryName = '';
315 if (this.isTypeSimple(this.schema.property.type)) {
316 this.valueObjRef[newKey] = '';
317 } else if (this.isTypeComplex(this.schema.property.type) || this.isTypeMap(this.schema.property.type)) {
318 this.valueObjRef[newKey] = {};
319 } else if (this.isTypeList(this.schema.property.type)) {
320 this.valueObjRef[newKey] = [];
322 this.emitValueChangeEvent(this.valueObjRef);
325 getSimpleValueInputType() {
326 if (this.isTypeNumber(this.type.name)){
332 buildSchemaGroupProperty(): SchemaPropertyGroupModel {
333 const schemaProperty = new SchemaProperty();
334 if (this.schema.property.type === PROPERTY_TYPES.MAP || this.schema.property.type === PROPERTY_TYPES.LIST) {
335 schemaProperty.type = PROPERTY_TYPES.STRING;
337 schemaProperty.type = this.schema.property.type
339 return new SchemaPropertyGroupModel(schemaProperty);
342 private parseBoolean(value: any) {
343 if (value === 'true') {
346 if (value === 'false') {
352 private parseNumber(value: any) {
353 const number = parseInt(value);
354 return isNaN(number) ? null : number;