5f26bd7547080866820e72e1135b90503d3a8ced
[sdc.git] / catalog-ui / src / app / ng2 / pages / service-dependencies-editor / service-dependencies-editor.component.ts
1 /*!
2  * Copyright © 2016-2018 European Support Limited
3  * Modification Copyright (C) 2022 Nordix Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14  * or implied. See the License for the specific language governing
15  * permissions and limitations under the License.
16  */
17 import {Component, Input, OnInit} from '@angular/core';
18 import {InputBEModel, PropertyBEModel, PropertyFEModel, PropertyModel} from 'app/models';
19 import {SourceType} from 'app/ng2/components/logic/service-dependencies/service-dependencies.component';
20 import {DropdownValue} from 'app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component';
21 import {ServiceServiceNg2} from 'app/ng2/services/component-services/service.service';
22 import {PROPERTY_DATA, PROPERTY_TYPES} from 'app/utils';
23 import {PropertiesUtils} from '../properties-assignment/services/properties.utils';
24 import {ToscaFunctionValidationEvent} from "../properties-assignment/tosca-function/tosca-function.component";
25 import {InstanceFeDetails} from "../../../models/instance-fe-details";
26 import {CompositionService} from "../composition/composition.service";
27 import {ToscaGetFunction} from "../../../models/tosca-get-function";
28 import {PropertyFilterConstraintUi} from "../../../models/ui-models/property-filter-constraint-ui";
29 import {ConstraintOperatorType, FilterConstraintHelper} from "../../../utils/filter-constraint-helper";
30 import {ToscaFunctionHelper} from "../../../utils/tosca-function-helper";
31 import {TopologyTemplateService} from "app/ng2/services/component-services/topology-template.service";
32 import {CustomToscaFunction} from "../../../models/default-custom-functions";
33 import {ToscaFunction} from "../../../models/tosca-function";
34
35 @Component({
36     selector: 'service-dependencies-editor',
37     templateUrl: './service-dependencies-editor.component.html',
38     styleUrls: ['./service-dependencies-editor.component.less'],
39     providers: [ServiceServiceNg2]
40 })
41 export class ServiceDependenciesEditorComponent implements OnInit {
42
43     @Input() serviceRuleIndex: number;
44     @Input() serviceRules: PropertyFilterConstraintUi[];
45     @Input() compositeServiceName: string;
46     @Input() currentServiceName: string;
47     @Input() parentServiceInputs: InputBEModel[];
48     @Input() parentServiceProperties: PropertyBEModel[];
49     @Input() selectedInstanceProperties: PropertyBEModel[];
50     @Input() allowedOperators: ConstraintOperatorType[] = [
51         ConstraintOperatorType.GREATER_THAN,
52         ConstraintOperatorType.LESS_THAN,
53         ConstraintOperatorType.EQUAL,
54         ConstraintOperatorType.GREATER_OR_EQUAL,
55         ConstraintOperatorType.LESS_OR_EQUAL,
56         ConstraintOperatorType.IN_RANGE,
57         ConstraintOperatorType.VALID_VALUES,
58         ConstraintOperatorType.LENGTH,
59         ConstraintOperatorType.MIN_LENGTH,
60         ConstraintOperatorType.MAX_LENGTH,
61         ConstraintOperatorType.PATTERN
62     ];
63     @Input() comparableAllowedOperators: ConstraintOperatorType[] = [
64         ConstraintOperatorType.GREATER_THAN,
65         ConstraintOperatorType.LESS_THAN,
66         ConstraintOperatorType.EQUAL,
67         ConstraintOperatorType.GREATER_OR_EQUAL,
68         ConstraintOperatorType.LESS_OR_EQUAL,
69         ConstraintOperatorType.IN_RANGE,
70         ConstraintOperatorType.VALID_VALUES,
71     ];
72     @Input() capabilityNameAndPropertiesMap: Map<string, PropertyModel[]>;
73     @Input() filterType: FilterType;
74     @Input() filterConstraint: PropertyFilterConstraintUi;
75   @Input() customToscaFunctions: Array<CustomToscaFunction>;
76     //output
77     currentRule: PropertyFilterConstraintUi;
78
79     FILTER_TYPE_CAPABILITY: FilterType = FilterType.CAPABILITY
80
81     listAllowedOperators: ConstraintOperatorType[] = [
82         ConstraintOperatorType.EQUAL,
83         ConstraintOperatorType.LENGTH,
84         ConstraintOperatorType.MIN_LENGTH,
85         ConstraintOperatorType.MAX_LENGTH
86     ];
87
88     operatorTypes: DropdownValue[] = [
89         {label: FilterConstraintHelper.convertToSymbol(ConstraintOperatorType.GREATER_THAN), value: ConstraintOperatorType.GREATER_THAN},
90         {label: FilterConstraintHelper.convertToSymbol(ConstraintOperatorType.LESS_THAN), value: ConstraintOperatorType.LESS_THAN},
91         {label: FilterConstraintHelper.convertToSymbol(ConstraintOperatorType.EQUAL), value: ConstraintOperatorType.EQUAL},
92         {label: FilterConstraintHelper.convertToSymbol(ConstraintOperatorType.GREATER_OR_EQUAL), value: ConstraintOperatorType.GREATER_OR_EQUAL},
93         {label: FilterConstraintHelper.convertToSymbol(ConstraintOperatorType.LESS_OR_EQUAL), value: ConstraintOperatorType.LESS_OR_EQUAL}
94     ];
95     lengthArray: string[] = [ConstraintOperatorType.LENGTH,
96         ConstraintOperatorType.MIN_LENGTH,
97         ConstraintOperatorType.MAX_LENGTH];
98
99     servicePropertyDropdownList: DropdownValue[];
100     isLoading: false;
101     selectedProperty: PropertyFEModel;
102     selectedSourceType: string;
103     componentInstanceMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();
104
105     capabilityDropdownList: DropdownValue[] = [];
106     validValuesToscaFunctionList: ToscaFunction[];
107     rangeToscaFunctionList: ToscaFunction[];
108     overridingType = PROPERTY_TYPES.INTEGER;
109
110     SOURCE_TYPES = {
111         STATIC: {label: 'Static', value: SourceType.STATIC},
112         TOSCA_FUNCTION: {label: 'Tosca Function', value: SourceType.TOSCA_FUNCTION},
113         TOSCA_FUNCTION_LIST: {label: 'Tosca Function List', value: SourceType.TOSCA_FUNCTION_LIST}
114     };
115
116     constructor(private propertiesUtils: PropertiesUtils, private compositionService: CompositionService, private topologyTemplateService: TopologyTemplateService) {
117     }
118
119     ngOnInit(): void {
120         if (this.compositionService.componentInstances) {
121             this.compositionService.componentInstances.forEach(value => {
122                 this.componentInstanceMap.set(value.uniqueId, <InstanceFeDetails>{
123                     name: value.name
124                 });
125             });
126         }
127         this.initCustomToscaFunctions();
128         this.initCapabilityDropdown();
129         this.initCurrentRule();
130         this.initConstraintOperatorOptions();
131         this.initSelectedSourceType();
132         this.initPropertyDropdown();
133         this.syncRuleData();
134         this.generateRangeToscaFunctionList();
135     }
136
137     private initCustomToscaFunctions() {
138       if (!this.customToscaFunctions) {
139           this.customToscaFunctions = [];
140           this.topologyTemplateService.getDefaultCustomFunction().toPromise().then((data) => {
141               for (let customFunction of data) {
142                   this.customToscaFunctions.push(new CustomToscaFunction(customFunction));
143               }
144           });
145       }
146  }
147
148     private initCapabilityDropdown(): void {
149         if (this.filterType == FilterType.CAPABILITY) {
150             this.capabilityDropdownList = [
151                 new DropdownValue(undefined, 'Select'),
152                 ...Array.from(this.capabilityNameAndPropertiesMap.keys()).map(capabilityName => new DropdownValue(capabilityName, capabilityName))
153             ];
154         }
155     }
156
157     private initPropertyDropdown(): void {
158         let propertyList: PropertyBEModel[] = [];
159         if (this.filterType == FilterType.CAPABILITY) {
160             if (this.currentRule.capabilityName) {
161                 propertyList = this.capabilityNameAndPropertiesMap.get(this.currentRule.capabilityName);
162             }
163         } else {
164             propertyList = this.selectedInstanceProperties;
165         }
166         let selectLabel;
167         if (this.filterType == FilterType.CAPABILITY) {
168             selectLabel = this.currentRule.capabilityName ? 'Select' : 'Select a Capability';
169         } else {
170             selectLabel = 'Select';
171         }
172         this.servicePropertyDropdownList = [new DropdownValue(undefined, selectLabel), ...propertyList.map(prop => new DropdownValue(prop.name, prop.name)).sort((prop1, prop2) => prop1.value.localeCompare(prop2.value))];
173     }
174
175     private initConstraintOperatorOptions(): void {
176         if (!this.selectedProperty) {
177             this.operatorTypes = [this.setOperatorDropdownValue(undefined)];
178             return;
179         }
180         const operatorList: DropdownValue[] = [];
181         switch (true) {
182             case this.selectedProperty.type === PROPERTY_TYPES.RANGE:
183                 if (this.currentRule.constraintOperator !== ConstraintOperatorType.IN_RANGE) {
184                     this.currentRule.constraintOperator = ConstraintOperatorType.IN_RANGE;
185                 }
186                 this.operatorTypes = [this.setOperatorDropdownValue(ConstraintOperatorType.IN_RANGE)];
187                 break;
188             case this.selectedProperty.type === PROPERTY_TYPES.STRING:
189                 this.allowedOperators.forEach(constraintOperatorType =>
190                     operatorList.push(this.setOperatorDropdownValue(constraintOperatorType))
191                 );
192                 this.operatorTypes = operatorList;
193                 break;
194             case  this.selectedProperty.type != PROPERTY_TYPES.STRING &&
195             ((PROPERTY_DATA.SIMPLE_TYPES_COMPARABLE.indexOf(this.selectedProperty.type) > -1) ||
196                 (PROPERTY_DATA.COMPARABLE_TYPES.indexOf(this.selectedProperty.type) > -1)):
197                 this.comparableAllowedOperators.forEach(constraintOperatorType =>
198                     operatorList.push(this.setOperatorDropdownValue(constraintOperatorType))
199                 );
200                 this.operatorTypes = operatorList;
201                 break;
202             case this.selectedProperty.type === PROPERTY_TYPES.LIST:
203                 this.listAllowedOperators.forEach(constraintOperatorType =>
204                     operatorList.push(this.setOperatorDropdownValue(constraintOperatorType))
205                 );
206                 this.operatorTypes = operatorList;
207                 break;
208             default:
209                 if (this.currentRule.constraintOperator !== ConstraintOperatorType.EQUAL) {
210                     this.currentRule.constraintOperator = ConstraintOperatorType.EQUAL;
211                 }
212                 this.operatorTypes = [this.setOperatorDropdownValue(ConstraintOperatorType.EQUAL)];
213                 break;
214         }
215     }
216
217     private setOperatorDropdownValue(constraintOperatorType: ConstraintOperatorType) {
218         if (constraintOperatorType === undefined) {
219             return new DropdownValue(undefined, 'Select a Property');
220         }
221         return new DropdownValue(constraintOperatorType, FilterConstraintHelper.convertToSymbol(constraintOperatorType));
222     }
223
224     private initSelectedSourceType(): void {
225         if (!this.currentRule.sourceType || this.currentRule.sourceType === SourceType.STATIC) {
226             this.selectedSourceType = SourceType.STATIC;
227         } else {
228             if (!this.isValidValuesOperator() && !this.isRangeType() && !this.isInRangeOperator()) {
229                 this.selectedSourceType = SourceType.TOSCA_FUNCTION;
230             } else {
231                 this.selectedSourceType = SourceType.TOSCA_FUNCTION_LIST;
232             }
233         }
234     }
235
236     private initCurrentRule(): void {
237         if (this.filterConstraint) {
238             this.currentRule = new PropertyFilterConstraintUi(this.filterConstraint);
239         } else {
240             this.currentRule = new PropertyFilterConstraintUi({
241                 sourceName: SourceType.STATIC,
242                 sourceType: SourceType.STATIC,
243                 constraintOperator: ConstraintOperatorType.EQUAL,
244                 value: undefined,
245                 originalType: undefined
246             });
247         }
248         let propertyList: PropertyBEModel[] = [];
249         if (this.filterType == FilterType.CAPABILITY) {
250             if (this.currentRule.capabilityName) {
251                 propertyList = this.capabilityNameAndPropertiesMap.get(this.currentRule.capabilityName);
252             }
253         } else {
254             propertyList = this.selectedInstanceProperties;
255         }
256         if (this.filterConstraint) {
257             this.filterConstraint.originalType = propertyList.find(prop => prop.name == this.filterConstraint.servicePropertyName).type;
258         }
259     }
260
261     onCapabilityChange(): void {
262         this.initPropertyDropdown();
263         this.resetSelectedProperty();
264     }
265
266     onPropertyChange(): void {
267         this.currentRule.value = undefined;
268         this.onValueChange(false);
269         this.updateSelectedProperty();
270         this.initConstraintOperatorOptions();
271     }
272
273     syncRuleData(): void {
274         if (!this.currentRule.sourceName || this.currentRule.sourceType === SourceType.STATIC) {
275             this.currentRule.sourceName = SourceType.STATIC;
276             this.currentRule.sourceType = SourceType.STATIC;
277         }
278         this.initSelectedProperty();
279         this.initConstraintOperatorOptions();
280     }
281
282     onValueChange(isValidValue: any): void {
283         this.currentRule.updateValidity(isValidValue);
284     }
285
286     checkFormValidForSubmit(): boolean {
287         return this.currentRule.isValidRule();
288     }
289
290     initSelectedProperty(): void {
291         if (!this.currentRule.servicePropertyName) {
292             this.selectedProperty = undefined;
293             return;
294         }
295         let newProperty;
296         if (this.filterType === FilterType.CAPABILITY) {
297             const currentProperty = this.capabilityNameAndPropertiesMap.get(this.currentRule.capabilityName)
298                 .find(property => property.name === this.currentRule.servicePropertyName);
299             newProperty = new PropertyFEModel(currentProperty);
300         } else {
301             newProperty = new PropertyFEModel(this.selectedInstanceProperties.find(property => property.name === this.currentRule.servicePropertyName));
302         }
303         newProperty.value = undefined;
304         newProperty.toscaFunction = undefined;
305
306         if (typeof this.currentRule.value === 'string') {
307             newProperty.value = this.currentRule.value;
308             this.propertiesUtils.initValueObjectRef(newProperty);
309         } else if (ToscaFunctionHelper.isValueToscaFunction(this.currentRule.value)) {
310             newProperty.toscaFunction = ToscaFunctionHelper.convertObjectToToscaFunction(this.currentRule.value);
311             newProperty.value = newProperty.toscaFunction.buildValueString();
312         } else if (Array.isArray(this.currentRule.value) &&
313             typeof this.currentRule.value[0] === "object" &&
314             this.currentRule.value[0]['propertySource'] != undefined) {
315             this.validValuesToscaFunctionList = this.currentRule.value;
316             this.rangeToscaFunctionList = this.currentRule.value;
317             newProperty.toscaFunction = this.currentRule.value;
318         } else {
319             newProperty.value = JSON.stringify(this.currentRule.value);
320             this.propertiesUtils.initValueObjectRef(newProperty);
321         }
322
323         this.selectedProperty = newProperty;
324         this.currentRule.originalType = this.selectedProperty.type;
325     }
326
327     updateSelectedProperty(): void {
328         this.selectedProperty = undefined;
329         if (!this.currentRule.servicePropertyName) {
330             return;
331         }
332
333         let newProperty;
334         if (this.filterType === FilterType.CAPABILITY) {
335             const currentProperty = this.capabilityNameAndPropertiesMap.get(this.currentRule.capabilityName)
336                 .find(property => property.name === this.currentRule.servicePropertyName);
337             newProperty = new PropertyFEModel(currentProperty);
338         } else {
339             newProperty = new PropertyFEModel(this.selectedInstanceProperties.find(property => property.name === this.currentRule.servicePropertyName));
340         }
341         newProperty.value = undefined;
342         newProperty.toscaFunction = undefined;
343
344         this.propertiesUtils.initValueObjectRef(newProperty);
345         this.selectedProperty = newProperty;
346         this.currentRule.originalType = this.selectedProperty.type;
347     }
348
349     isStaticSource(): boolean {
350         return this.selectedSourceType === SourceType.STATIC
351     }
352
353     isToscaFunctionSource(): boolean {
354         return this.selectedSourceType === SourceType.TOSCA_FUNCTION
355     }
356
357     isToscaFunctionListSource(): boolean {
358         return this.selectedSourceType === SourceType.TOSCA_FUNCTION_LIST
359     }
360
361     isComplexListMapType(): boolean {
362         return this.selectedProperty && this.selectedProperty.derivedDataType > 0;
363     }
364
365     isRangeType(): boolean {
366         return this.selectedProperty && this.selectedProperty.derivedDataType == 4;
367     }
368
369     isLengthOperator(): boolean {
370         return this.lengthArray.indexOf(this.currentRule.constraintOperator) > -1;
371     }
372
373     isInRangeOperator(): boolean {
374         return this.currentRule.constraintOperator && this.currentRule.constraintOperator === ConstraintOperatorType.IN_RANGE;
375     }
376
377     isValidValuesOperator(): boolean {
378         return this.currentRule.constraintOperator && this.currentRule.constraintOperator === ConstraintOperatorType.VALID_VALUES;
379     }
380
381     updateComplexListMapTypeRuleValue(): void {
382         this.currentRule.value = PropertyFEModel.cleanValueObj(this.selectedProperty.valueObj);
383         this.onValueChange(this.selectedProperty.valueObjIsValid);
384     }
385
386     onToscaFunctionValidityChange(validationEvent: ToscaFunctionValidationEvent): void {
387         if (validationEvent.isValid && validationEvent.toscaFunction) {
388             if (this.isValidValuesOperator()) {
389                 this.currentRule.value = this.validValuesToscaFunctionList;
390                 this.currentRule.sourceType = SourceType.TOSCA_FUNCTION_LIST;
391                 if (validationEvent.toscaFunction instanceof ToscaGetFunction) {
392                     this.currentRule.sourceName = SourceType.TOSCA_FUNCTION_LIST;
393                 }
394             } else {
395                 if (this.isLengthOperator()) {
396                     this.overridingType = PROPERTY_TYPES.INTEGER;
397                 }
398                 this.currentRule.value = validationEvent.toscaFunction;
399                 this.currentRule.sourceType = validationEvent.toscaFunction.type
400                 if (validationEvent.toscaFunction instanceof ToscaGetFunction) {
401                     this.currentRule.sourceName = validationEvent.toscaFunction.sourceName;
402                 }
403             }
404         } else {
405             this.currentRule.updateValidity(false);
406             this.currentRule.value = undefined;
407             this.currentRule.sourceType = undefined;
408             this.currentRule.sourceName = undefined;
409         }
410     }
411
412     onToscaFunctionListValidityChange(validationEvent: ToscaFunctionValidationEvent, valueIndex: number): void {
413         if (validationEvent.isValid && validationEvent.toscaFunction) {
414             this.validValuesToscaFunctionList.splice(this.validValuesToscaFunctionList.length - 1, 1, validationEvent.toscaFunction);
415             this.currentRule.value = this.validValuesToscaFunctionList;
416             this.currentRule.sourceType = 'SEVERAL';
417             if (validationEvent.toscaFunction instanceof ToscaGetFunction) {
418                 this.currentRule.sourceName = validationEvent.toscaFunction.sourceName;
419             }
420         } else {
421             this.currentRule.updateValidity(false);
422             this.currentRule.value = undefined;
423             this.currentRule.sourceType = undefined;
424             this.currentRule.sourceName = undefined;
425         }
426     }
427
428     onToscaRangeFunctionListValidityChange(validationEvent: ToscaFunctionValidationEvent, valueIndex: number): void {
429         if (validationEvent.isValid && validationEvent.toscaFunction) {
430             this.rangeToscaFunctionList.splice(valueIndex, 1, validationEvent.toscaFunction);
431             this.currentRule.value = this.rangeToscaFunctionList;
432             this.currentRule.sourceType = 'SEVERAL';
433             if (validationEvent.toscaFunction instanceof ToscaGetFunction) {
434                 this.currentRule.sourceName = validationEvent.toscaFunction.sourceName;
435             }
436         } else {
437             this.currentRule.updateValidity(false);
438             this.currentRule.value = undefined;
439             this.currentRule.sourceType = undefined;
440             this.currentRule.sourceName = undefined;
441         }
442     }
443
444     onSourceTypeChange(): void {
445         this.currentRule.value = undefined;
446         if (!this.isStaticSource() && (this.isValidValuesOperator() || this.isRangeType() || this.isInRangeOperator())) {
447             this.selectedSourceType = SourceType.TOSCA_FUNCTION_LIST;
448         }
449         this.currentRule.sourceType = this.selectedSourceType;
450         if (this.isStaticSource()) {
451             this.currentRule.sourceName = SourceType.STATIC;
452         }
453         if (this.isToscaFunctionListSource()) {
454             this.currentRule.sourceName = SourceType.TOSCA_FUNCTION_LIST;
455         }
456         this.updateSelectedProperty();
457     }
458
459     private resetSelectedProperty(): void {
460         this.currentRule.servicePropertyName = undefined;
461         this.selectedProperty = undefined;
462         this.onPropertyChange();
463     }
464
465     addToList() {
466         if (!this.validValuesToscaFunctionList) {
467             this.validValuesToscaFunctionList = new Array();
468         }
469         this.validValuesToscaFunctionList.push(ToscaFunctionHelper.convertObjectToToscaFunction(undefined));
470     }
471
472     generateRangeToscaFunctionList() {
473         if (!this.rangeToscaFunctionList) {
474             this.rangeToscaFunctionList = new Array();
475             this.rangeToscaFunctionList.push(ToscaFunctionHelper.convertObjectToToscaFunction(undefined));
476             this.rangeToscaFunctionList.push(ToscaFunctionHelper.convertObjectToToscaFunction(undefined));
477         }
478     }
479
480     trackByFn(index) {
481         return index;
482     }
483
484     removeFromList(valueIndex: number) {
485         this.validValuesToscaFunctionList.splice(valueIndex, 1);
486         this.currentRule.updateValidity(!this.doesArrayContainsEmptyValues(this.validValuesToscaFunctionList) && !(this.validValuesToscaFunctionList.length === 0));
487         if (this.doesArrayContainsEmptyValues(this.validValuesToscaFunctionList) || (this.validValuesToscaFunctionList.length === 0)) {
488             this.currentRule.value = undefined;
489             this.currentRule.sourceType = undefined;
490             this.currentRule.sourceName = undefined;
491         }
492     }
493
494     private doesArrayContainsEmptyValues(arr) {
495         for (const element of arr) {
496             if (element === undefined) return true;
497         }
498         return false;
499     }
500 }
501
502 export enum FilterType {
503     CAPABILITY,
504     PROPERTY
505 }