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