Support TOSCA functions in Node Filters
[sdc.git] / catalog-ui / src / app / ng2 / components / logic / service-dependencies / service-dependencies.component.ts
1 /*!
2  * Copyright © 2016-2018 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13  * or implied. See the License for the specific language governing
14  * permissions and limitations under the License.
15  */
16 import {Component, ComponentRef, EventEmitter, Input, OnChanges, OnInit, Output} from '@angular/core';
17 import {ButtonModel, ComponentInstance, InputBEModel, ModalModel, PropertyBEModel, PropertyModel,} from 'app/models';
18 import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
19 import {ServiceDependenciesEditorComponent} from 'app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component';
20 import {ModalService} from 'app/ng2/services/modal.service';
21 import {ComponentGenericResponse} from 'app/ng2/services/responses/component-generic-response';
22 import {TranslateService} from 'app/ng2/shared/translator/translate.service';
23 import {ComponentMetadata} from '../../../../models/component-metadata';
24 import {ServiceInstanceObject} from '../../../../models/service-instance-properties-and-interfaces';
25 import {TopologyTemplateService} from '../../../services/component-services/topology-template.service';
26 import {
27     CapabilitiesFilterPropertiesEditorComponent
28 } from "../../../pages/composition/capabilities-filter-properties-editor/capabilities-filter-properties-editor.component";
29 import {CapabilityFilterConstraintUI} from "../../../../models/capability-filter-constraint";
30 import {ToscaFilterConstraintType} from "../../../../models/tosca-filter-constraint-type.enum";
31 import {CompositionService} from "../../../pages/composition/composition.service";
32 import {FilterConstraint} from "app/models/filter-constraint";
33 import {ConstraintObjectUI} from "../../../../models/ui-models/constraint-object-ui";
34 import {FilterConstraintHelper, OPERATOR_TYPES} from "../../../../utils/filter-constraint-helper";
35
36 export enum SourceType {
37     STATIC = 'static',
38     TOSCA_FUNCTION = 'tosca_function'
39 }
40
41 class I18nTexts {
42     static removeDirectiveModalTitle: string;
43     static removeDirectiveModalText: string;
44     static updateDirectiveModalTitle: string;
45     static updateDirectiveModalText: string;
46     static modalApprove: string;
47     static modalCancel: string;
48     static modalCreate: string;
49     static modalSave: string;
50     static modalDelete: string;
51     static addNodeFilterTxt: string;
52     static updateNodeFilterTxt: string;
53     static deleteNodeFilterTxt: string;
54     static deleteNodeFilterMsg: string;
55     static validateCapabilitiesTxt: string
56     static validateCapabilitiesMsg: string
57     static validateNodePropertiesTxt: string
58     static validateNodePropertiesMsg: string
59
60     public static translateTexts(translateService) {
61             I18nTexts.removeDirectiveModalTitle = translateService.translate('DIRECTIVES_AND_NODE_FILTER_REMOVE_TITLE');
62             I18nTexts.removeDirectiveModalText = translateService.translate('DIRECTIVES_AND_NODE_FILTER_REMOVE_TEXT');
63             I18nTexts.updateDirectiveModalTitle = translateService.translate('DIRECTIVES_AND_NODE_FILTER_UPDATE_TITLE');
64             I18nTexts.updateDirectiveModalText = translateService.translate('DIRECTIVES_AND_NODE_FILTER_UPDATE_TEXT');
65             I18nTexts.modalApprove = translateService.translate('MODAL_APPROVE');
66             I18nTexts.modalCancel = translateService.translate('MODAL_CANCEL');
67             I18nTexts.modalCreate = translateService.translate('MODAL_CREATE');
68             I18nTexts.modalSave = translateService.translate('MODAL_SAVE');
69             I18nTexts.modalDelete = translateService.translate('MODAL_DELETE');
70             I18nTexts.addNodeFilterTxt = translateService.translate('DIRECTIVES_AND_NODE_FILTER_ADD_NODE_FILTER');
71             I18nTexts.updateNodeFilterTxt = translateService.translate('DIRECTIVES_AND_NODE_FILTER_UPDATE_NODE_FILTER');
72             I18nTexts.deleteNodeFilterTxt = translateService.translate('DIRECTIVES_AND_NODE_FILTER_DELETE_NODE_FILTER');
73             I18nTexts.deleteNodeFilterMsg = translateService.translate('DIRECTIVES_AND_NODE_FILTER_DELETE_NODE_FILTER_MSG');
74             I18nTexts.validateCapabilitiesTxt = translateService.translate('VALIDATE_CAPABILITIES_TXT');
75             I18nTexts.validateCapabilitiesMsg = translateService.translate('VALIDATE_CAPABILITIES_MSG');
76             I18nTexts.validateNodePropertiesTxt = translateService.translate('VALIDATE_NODE_PROPERTIES_TXT');
77             I18nTexts.validateNodePropertiesMsg = translateService.translate('VALIDATE_NODE_PROPERTIES_MSG');
78     }
79 }
80
81 @Component({
82     selector: 'service-dependencies',
83     templateUrl: './service-dependencies.component.html',
84     styleUrls: ['service-dependencies.component.less'],
85     providers: [ModalService, TranslateService]
86 })
87
88 export class ServiceDependenciesComponent implements OnInit, OnChanges {
89     modalInstance: ComponentRef<ModalComponent>;
90     isDependent: boolean;
91     isLoading: boolean;
92     parentServiceInputs: InputBEModel[] = [];
93     parentServiceProperties: PropertyBEModel[] = [];
94     constraintProperties: FilterConstraint[] = [];
95     constraintPropertyLabels: string[] = [];
96     constraintCapabilities: CapabilityFilterConstraintUI[] = [];
97     constraintCapabilityLabels: string[] = [];
98     operatorTypes: any[];
99     capabilities: string = ToscaFilterConstraintType.CAPABILITIES;
100     properties: string = ToscaFilterConstraintType.PROPERTIES;
101     private componentInstancesConstraints: FilterConstraint[] = [];
102     isEditable: boolean;
103
104     @Input() readonly: boolean;
105     @Input() compositeService: ComponentMetadata;
106     @Input() currentServiceInstance: ComponentInstance;
107     @Input() selectedInstanceSiblings: ServiceInstanceObject[];
108     @Input() selectedInstanceConstraints: FilterConstraint[] = [];
109     @Input() selectedInstanceProperties: PropertyBEModel[] = [];
110     @Input() componentInstanceCapabilitiesMap: Map<string, PropertyModel[]>;
111     @Output() updateRulesListEvent: EventEmitter<FilterConstraint[]> = new EventEmitter<FilterConstraint[]>();
112     @Output() updateNodeFilterProperties: EventEmitter<FilterConstraint[]> = new EventEmitter<FilterConstraint[]>();
113     @Output() updateNodeFilterCapabilities: EventEmitter<CapabilityFilterConstraintUI[]> = new EventEmitter<CapabilityFilterConstraintUI[]>();
114     @Output() loadRulesListEvent:EventEmitter<any> = new EventEmitter();
115     @Output() dependencyStatus = new EventEmitter<boolean>();
116
117     constructor(private topologyTemplateService: TopologyTemplateService,
118                 private modalServiceNg2: ModalService,
119                 private translateService: TranslateService,
120                 private compositionService: CompositionService) {
121     }
122
123     ngOnInit(): void {
124         this.isLoading = false;
125         this.operatorTypes = [
126             {label: FilterConstraintHelper.convertToSymbol(OPERATOR_TYPES.GREATER_THAN), value: OPERATOR_TYPES.GREATER_THAN},
127             {label: FilterConstraintHelper.convertToSymbol(OPERATOR_TYPES.LESS_THAN), value: OPERATOR_TYPES.LESS_THAN},
128             {label: FilterConstraintHelper.convertToSymbol(OPERATOR_TYPES.EQUAL), value: OPERATOR_TYPES.EQUAL},
129             {label: FilterConstraintHelper.convertToSymbol(OPERATOR_TYPES.GREATER_OR_EQUAL), value: OPERATOR_TYPES.GREATER_OR_EQUAL},
130             {label: FilterConstraintHelper.convertToSymbol(OPERATOR_TYPES.LESS_OR_EQUAL), value: OPERATOR_TYPES.LESS_OR_EQUAL}
131         ];
132         this.topologyTemplateService.getComponentInputsWithProperties(this.compositeService.componentType, this.compositeService.uniqueId)
133         .subscribe((result: ComponentGenericResponse) => {
134             this.parentServiceInputs = result.inputs;
135             this.parentServiceProperties = result.properties;
136         });
137         this.loadNodeFilter();
138         this.translateService.languageChangedObservable.subscribe((lang) => {
139             I18nTexts.translateTexts(this.translateService);
140         });
141     }
142
143     ngOnChanges(changes): void {
144         if (changes.currentServiceInstance) {
145             this.currentServiceInstance = changes.currentServiceInstance.currentValue;
146             this.isDependent = this.currentServiceInstance.isDependent();
147         }
148         if (changes.selectedInstanceConstraints && changes.selectedInstanceConstraints.currentValue !== changes.selectedInstanceConstraints.previousValue) {
149             this.selectedInstanceConstraints = changes.selectedInstanceConstraints.currentValue;
150             this.loadNodeFilter();
151         }
152     }
153
154     private getActualDirectiveValue = (): string[] => {
155         return this.currentServiceInstance.directives.length > 0 ? this.currentServiceInstance.directives : [];
156     }
157
158     public openRemoveDependencyModal = (): ComponentRef<ModalComponent> => {
159         const actionButton: ButtonModel = new ButtonModel(I18nTexts.modalApprove, 'blue', this.onUncheckDependency);
160         const cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'grey', this.onCloseRemoveDependencyModal);
161         const modalModel: ModalModel = new ModalModel('sm', I18nTexts.removeDirectiveModalTitle,
162             I18nTexts.removeDirectiveModalText, [actionButton, cancelButton]);
163         this.loadNodeFilter();
164         return this.modalServiceNg2.createCustomModal(modalModel);
165     }
166
167     private loadNodeFilter = (): void => {
168         this.topologyTemplateService.getServiceFilterConstraints(this.compositeService.componentType, this.compositeService.uniqueId).subscribe((response) => {
169             if (response.nodeFilterforNode && response.nodeFilterforNode[this.currentServiceInstance.uniqueId]) {
170                 this.componentInstancesConstraints = response.nodeFilterforNode;
171                 this.constraintProperties = response.nodeFilterforNode[this.currentServiceInstance.uniqueId].properties;
172                 this.buildConstraintPropertyLabels();
173                 this.constraintCapabilities = response.nodeFilterforNode[this.currentServiceInstance.uniqueId].capabilities;
174                 this.buildCapabilityFilterConstraintLabels();
175             }
176         });
177     }
178
179     onUncheckDependency = (): void => {
180         this.modalServiceNg2.closeCurrentModal();
181         this.isLoading = true;
182         const isDepOrig = this.isDependent;
183         const rulesListOrig = this.componentInstancesConstraints;
184         this.currentServiceInstance.unmarkAsDependent(this.getActualDirectiveValue());
185         this.updateComponentInstance(isDepOrig, rulesListOrig);
186     }
187
188     onCloseRemoveDependencyModal = (): void => {
189         this.isDependent = true;
190         this.modalServiceNg2.closeCurrentModal();
191     }
192
193     onAddDirectives(directives: string[]): void {
194         this.isEditable = false;
195         this.setDirectiveValue(directives);
196         const rulesListOrig = this.componentInstancesConstraints;
197         this.constraintProperties = [];
198         this.constraintPropertyLabels = [];
199         this.constraintCapabilities = [];
200         this.constraintCapabilityLabels = [];
201         this.loadNodeFilter();
202         this.updateComponentInstance(this.isDependent, rulesListOrig);
203     }
204
205     private onRemoveDirective(): void {
206         this.openRemoveDependencyModal().instance.open();
207         this.constraintProperties = [];
208         this.constraintPropertyLabels = [];
209         this.constraintCapabilities = [];
210         this.constraintCapabilityLabels = [];
211     }
212
213     private onEditDirectives(): void {
214         this.isEditable = true;
215     }
216
217     private setDirectiveValue(newDirectiveValues: string[]): void {
218         this.currentServiceInstance.setDirectiveValue(newDirectiveValues);
219     }
220
221     updateComponentInstance(isDependentOrigVal: boolean, rulesListOrig: FilterConstraint[]): void {
222         this.isLoading = true;
223         this.topologyTemplateService.updateComponentInstance(this.compositeService.uniqueId,
224                                                              this.compositeService.componentType,
225                                                              this.currentServiceInstance)
226                                                              .subscribe((updatedServiceIns: ComponentInstance) => {
227             const selectedComponentInstance = this.compositionService.getComponentInstances()
228             .find(componentInstance => componentInstance.uniqueId == this.currentServiceInstance.uniqueId);
229             selectedComponentInstance.directives = updatedServiceIns.directives;
230             this.currentServiceInstance = new ComponentInstance(updatedServiceIns);
231             this.isDependent = this.currentServiceInstance.isDependent();
232             this.dependencyStatus.emit(this.isDependent);
233             if (this.isDependent) {
234                 this.loadRulesListEvent.emit();
235             }
236             this.isLoading = false;
237         }, (err) => {
238             this.isDependent = isDependentOrigVal;
239             this.componentInstancesConstraints = rulesListOrig;
240             this.isLoading = false;
241             console.error('An error has occurred.', err);
242         });
243     }
244
245     onAddNodeFilter = (): void => {
246         if (!this.selectedInstanceProperties) {
247             this.modalServiceNg2.openAlertModal(I18nTexts.validateNodePropertiesTxt, I18nTexts.validateNodePropertiesMsg);
248         } else {
249             const cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'outline white', this.modalServiceNg2.closeCurrentModal);
250             const saveButton: ButtonModel = new ButtonModel(I18nTexts.modalCreate, 'blue', () => this.createNodeFilter(this.properties), this.getDisabled);
251             const modalModel: ModalModel = new ModalModel('l', I18nTexts.addNodeFilterTxt, '', [saveButton, cancelButton], 'standard');
252             this.modalInstance = this.modalServiceNg2.createCustomModal(modalModel);
253             this.modalServiceNg2.addDynamicContentToModal(
254                 this.modalInstance,
255                 ServiceDependenciesEditorComponent,
256                 {
257                     currentServiceName: this.currentServiceInstance.name,
258                     operatorTypes: this.operatorTypes,
259                     compositeServiceName: this.compositeService.name,
260                     parentServiceInputs: this.parentServiceInputs,
261                     parentServiceProperties: this.parentServiceProperties,
262                     selectedInstanceProperties: this.selectedInstanceProperties,
263                     selectedInstanceSiblings: this.selectedInstanceSiblings
264                 }
265             );
266             this.modalInstance.instance.open();
267         }
268     }
269
270     onAddNodeFilterCapabilities = (): void => {
271         if (this.componentInstanceCapabilitiesMap.size == 0) {
272             this.modalServiceNg2.openAlertModal(I18nTexts.validateCapabilitiesTxt, I18nTexts.validateCapabilitiesMsg);
273         } else {
274             const cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'outline white', this.modalServiceNg2.closeCurrentModal);
275             const saveButton: ButtonModel = new ButtonModel(I18nTexts.modalCreate, 'blue', () => this.createNodeFilterCapabilities(this.capabilities), this.getDisabled);
276             const modalModel: ModalModel = new ModalModel('l', I18nTexts.addNodeFilterTxt, '', [saveButton, cancelButton], 'standard');
277             this.modalInstance = this.modalServiceNg2.createCustomModal(modalModel);
278             this.modalServiceNg2.addDynamicContentToModal(
279                 this.modalInstance,
280                 CapabilitiesFilterPropertiesEditorComponent,
281                 {
282                     currentServiceName: this.currentServiceInstance.name,
283                     operatorTypes: this.operatorTypes,
284                     compositeServiceName: this.compositeService.name,
285                     parentServiceInputs: this.parentServiceInputs,
286                     selectedInstanceProperties: this.selectedInstanceProperties,
287                     selectedInstanceSiblings: this.selectedInstanceSiblings,
288                     componentInstanceCapabilitiesMap: this.componentInstanceCapabilitiesMap
289                 }
290             );
291             this.modalInstance.instance.open();
292         }
293     }
294
295     createNodeFilter = (constraintType: string): void => {
296         this.isLoading = true;
297         this.topologyTemplateService.createServiceFilterConstraints(
298             this.compositeService.uniqueId,
299             this.currentServiceInstance.uniqueId,
300             new FilterConstraint(this.modalInstance.instance.dynamicContent.instance.currentRule),
301             this.compositeService.componentType,
302             constraintType
303         ).subscribe( (response) => {
304             this.emitEventOnChanges(constraintType, response);
305             this.isLoading = false;
306         }, (err) => {
307             this.isLoading = false;
308         });
309         this.modalServiceNg2.closeCurrentModal();
310     }
311
312     createNodeFilterCapabilities = (constraintType: string): void => {
313         this.isLoading = true;
314         this.topologyTemplateService.createServiceFilterCapabilitiesConstraints(
315             this.compositeService.uniqueId,
316             this.currentServiceInstance.uniqueId,
317             new CapabilityFilterConstraintUI(this.modalInstance.instance.dynamicContent.instance.currentRule),
318             this.compositeService.componentType,
319             constraintType
320         ).subscribe( (response) => {
321             this.emitEventOnChanges(constraintType, response);
322             this.isLoading = false;
323         }, (err) => {
324             this.isLoading = false;
325         });
326         this.modalServiceNg2.closeCurrentModal();
327     }
328
329     onSelectNodeFilterCapability(constraintType: string, index: number): void {
330         const cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'outline white', this.modalServiceNg2.closeCurrentModal);
331         const saveButton: ButtonModel = new ButtonModel(I18nTexts.modalSave, 'blue', () => this.updateNodeFilterCapability(constraintType, index), this.getDisabled);
332         const modalModel: ModalModel = new ModalModel('l', I18nTexts.updateNodeFilterTxt, '', [saveButton, cancelButton], 'standard');
333         this.modalInstance = this.modalServiceNg2.createCustomModal(modalModel);
334
335         this.modalServiceNg2.addDynamicContentToModal(
336             this.modalInstance,
337             CapabilitiesFilterPropertiesEditorComponent,
338             {
339                 serviceRuleIndex: index,
340                 serviceRules: _.map(this.constraintCapabilities, (rule) => new CapabilityFilterConstraintUI(rule)),
341                 currentServiceName: this.currentServiceInstance.name,
342                 operatorTypes: this.operatorTypes,
343                 compositeServiceName: this.compositeService.name,
344                 parentServiceInputs: this.parentServiceInputs,
345                 selectedInstanceProperties: this.selectedInstanceProperties,
346                 selectedInstanceSiblings: this.selectedInstanceSiblings,
347                 componentInstanceCapabilitiesMap: this.componentInstanceCapabilitiesMap
348             }
349         );
350         this.modalInstance.instance.open();
351     }
352
353     onSelectNodeFilter(constraintType: string, index: number): void {
354         const cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'outline white', this.modalServiceNg2.closeCurrentModal);
355         const saveButton: ButtonModel = new ButtonModel(I18nTexts.modalSave, 'blue', () => this.updateNodeFilter(constraintType, index), this.getDisabled);
356         const modalModel: ModalModel = new ModalModel('l', I18nTexts.updateNodeFilterTxt, '', [saveButton, cancelButton], 'standard');
357         this.modalInstance = this.modalServiceNg2.createCustomModal(modalModel);
358         this.modalServiceNg2.addDynamicContentToModal(
359             this.modalInstance,
360             ServiceDependenciesEditorComponent,
361             {
362                 serviceRuleIndex: index,
363                 serviceRules: this.constraintProperties.map(rule => new ConstraintObjectUI(rule)),
364                 currentServiceName: this.currentServiceInstance.name,
365                 operatorTypes: this.operatorTypes,
366                 compositeServiceName: this.compositeService.name,
367                 parentServiceInputs: this.parentServiceInputs,
368                 parentServiceProperties: this.parentServiceProperties,
369                 selectedInstanceProperties: this.selectedInstanceProperties,
370                 selectedInstanceSiblings: this.selectedInstanceSiblings
371             }
372         );
373         this.modalInstance.instance.open();
374     }
375
376     getDisabled = (): boolean =>  {
377         return !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit();
378     }
379
380     updateNodeFilter = (constraintType: string, index: number): void => {
381         this.isLoading = true;
382         this.topologyTemplateService.updateServiceFilterConstraints(
383             this.compositeService.uniqueId,
384             this.currentServiceInstance.uniqueId,
385             new FilterConstraint(this.modalInstance.instance.dynamicContent.instance.currentRule),
386             this.compositeService.componentType,
387             constraintType,
388             index
389         ).subscribe((response) => {
390             this.emitEventOnChanges(constraintType, response);
391             this.isLoading = false;
392         }, (err) => {
393             this.isLoading = false;
394         });
395         this.modalServiceNg2.closeCurrentModal();
396     }
397
398     updateNodeFilterCapability = (constraintType: string, index: number): void => {
399         this.isLoading = true;
400         this.topologyTemplateService.updateServiceFilterCapabilitiesConstraint(
401             this.compositeService.uniqueId,
402             this.currentServiceInstance.uniqueId,
403             new CapabilityFilterConstraintUI(this.modalInstance.instance.dynamicContent.instance.currentRule),
404             this.compositeService.componentType,
405             constraintType,
406             index
407         ).subscribe((response) => {
408             this.emitEventOnChanges(constraintType, response);
409             this.isLoading = false;
410         }, (err) => {
411             this.isLoading = false;
412         });
413         this.modalServiceNg2.closeCurrentModal();
414     }
415
416     onDeleteNodeFilter = (constraintType: string, index: number): void => {
417         this.isLoading = true;
418         this.topologyTemplateService.deleteServiceFilterConstraints(
419             this.compositeService.uniqueId,
420             this.currentServiceInstance.uniqueId,
421             index,
422             this.compositeService.componentType,
423             constraintType
424         ).subscribe( (response) => {
425             this.emitEventOnChanges(constraintType, response);
426             this.isLoading = false;
427         }, (err) => {
428             this.isLoading = false;
429         });
430         this.modalServiceNg2.closeCurrentModal();
431     }
432
433     private emitEventOnChanges(constraintType: string, response) {
434         if (this.properties === constraintType) {
435             this.updateNodeFilterProperties.emit(response.properties);
436             this.constraintProperties = response.properties;
437             this.buildConstraintPropertyLabels();
438         } else {
439             this.updateNodeFilterCapabilities.emit(response.capabilities);
440             this.constraintCapabilities = response.capabilities;
441             this.buildCapabilityFilterConstraintLabels();
442         }
443     }
444
445     openDeleteModal = (constraintType: string, index: number): void => {
446         this.modalServiceNg2.createActionModal(I18nTexts.deleteNodeFilterTxt, I18nTexts.deleteNodeFilterMsg,
447             I18nTexts.modalDelete, () => this.onDeleteNodeFilter(constraintType, index), I18nTexts.modalCancel).instance.open();
448     }
449
450     private buildConstraintPropertyLabels(): void {
451         this.constraintPropertyLabels = [];
452         if (!this.constraintProperties) {
453             return;
454         }
455         this.constraintProperties.forEach(
456             constraint => this.constraintPropertyLabels.push(FilterConstraintHelper.buildFilterConstraintLabel(constraint))
457         )
458     }
459
460     private buildCapabilityFilterConstraintLabels(): void {
461         this.constraintCapabilityLabels = [];
462         if (!this.constraintCapabilities) {
463             return;
464         }
465         this.constraintCapabilities.forEach(
466             constraint => this.constraintCapabilityLabels.push(FilterConstraintHelper.buildFilterConstraintLabel(constraint))
467         )
468     }
469
470 }