Retrieve node_filter capabilities
[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, Output } from '@angular/core';
17 import {
18     ButtonModel,
19     ComponentInstance,
20     InputBEModel,
21     ModalModel,
22     PropertyBEModel,
23 } from 'app/models';
24 import { ModalComponent } from 'app/ng2/components/ui/modal/modal.component';
25 import { ServiceDependenciesEditorComponent } from 'app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component';
26 import { ModalService } from 'app/ng2/services/modal.service';
27 import { ComponentGenericResponse } from 'app/ng2/services/responses/component-generic-response';
28 import { TranslateService } from 'app/ng2/shared/translator/translate.service';
29 import { ComponentMetadata } from '../../../../models/component-metadata';
30 import { ServiceInstanceObject } from '../../../../models/service-instance-properties-and-interfaces';
31 import { TopologyTemplateService } from '../../../services/component-services/topology-template.service';
32 import {DirectivesEnum, DirectiveValue} from "./directive-option";
33
34 export class ConstraintObject {
35     servicePropertyName: string;
36     constraintOperator: string;
37     sourceType: string;
38     sourceName: string;
39     value: string;
40
41     constructor(input?: any) {
42         if (input) {
43             this.servicePropertyName = input.servicePropertyName;
44             this.constraintOperator = input.constraintOperator;
45             this.sourceType = input.sourceType;
46             this.sourceName = input.sourceName;
47             this.value = input.value;
48         }
49     }
50 }
51
52 // tslint:disable-next-line:max-classes-per-file
53 export class ConstraintObjectUI extends ConstraintObject{
54     isValidValue: boolean;
55
56     constructor(input?: any) {
57         super(input);
58         if (input) {
59             this.isValidValue = input.isValidValue ? input.isValidValue : input.value !== '';
60         }
61     }
62
63     public updateValidity(isValidValue: boolean) {
64         this.isValidValue = isValidValue;
65     }
66
67     public isValidRule(isStatic) {
68         const isValidValue = isStatic ? this.isValidValue : true;
69         return this.servicePropertyName != null && this.servicePropertyName !== ''
70             && this.value != null && this.value !== '' && isValidValue;
71     }
72 }
73
74 export const OPERATOR_TYPES = {
75     EQUAL: 'equal',
76     GREATER_THAN: 'greater_than',
77     LESS_THAN: 'less_than'
78 };
79
80 // tslint:disable-next-line:max-classes-per-file
81 class I18nTexts {
82     static removeDirectiveModalTitle: string;
83     static removeDirectiveModalText: string;
84     static updateDirectiveModalTitle: string;
85     static updateDirectiveModalText: string;
86     static modalApprove: string;
87     static modalCancel: string;
88     static modalCreate: string;
89     static modalSave: string;
90     static modalDelete: string;
91     static addNodeFilterTxt: string;
92     static updateNodeFilterTxt: string;
93     static deleteNodeFilterTxt: string;
94     static deleteNodeFilterMsg: string;
95
96     public static translateTexts(translateService) {
97             I18nTexts.removeDirectiveModalTitle = translateService.translate('DIRECTIVES_AND_NODE_FILTER_REMOVE_TITLE');
98             I18nTexts.removeDirectiveModalText = translateService.translate('DIRECTIVES_AND_NODE_FILTER_REMOVE_TEXT');
99             I18nTexts.updateDirectiveModalTitle = translateService.translate('DIRECTIVES_AND_NODE_FILTER_UPDATE_TITLE');
100             I18nTexts.updateDirectiveModalText = translateService.translate('DIRECTIVES_AND_NODE_FILTER_UPDATE_TEXT');
101             I18nTexts.modalApprove = translateService.translate('MODAL_APPROVE');
102             I18nTexts.modalCancel = translateService.translate('MODAL_CANCEL');
103             I18nTexts.modalCreate = translateService.translate('MODAL_CREATE');
104             I18nTexts.modalSave = translateService.translate('MODAL_SAVE');
105             I18nTexts.modalDelete = translateService.translate('MODAL_DELETE');
106             I18nTexts.addNodeFilterTxt = translateService.translate('DIRECTIVES_AND_NODE_FILTER_ADD_NODE_FILTER');
107             I18nTexts.updateNodeFilterTxt = translateService.translate('DIRECTIVES_AND_NODE_FILTER_UPDATE_NODE_FILTER');
108             I18nTexts.deleteNodeFilterTxt = translateService.translate('DIRECTIVES_AND_NODE_FILTER_DELETE_NODE_FILTER');
109             I18nTexts.deleteNodeFilterMsg = translateService.translate('DIRECTIVES_AND_NODE_FILTER_DELETE_NODE_FILTER_MSG');
110     }
111 }
112
113 // tslint:disable-next-line:max-classes-per-file
114 @Component({
115     selector: 'service-dependencies',
116     templateUrl: './service-dependencies.component.html',
117     styleUrls: ['service-dependencies.component.less'],
118     providers: [ModalService, TranslateService]
119 })
120
121 export class ServiceDependenciesComponent {
122     modalInstance: ComponentRef<ModalComponent>;
123     isDependent: boolean;
124     isLoading: boolean;
125     parentServiceInputs: InputBEModel[] = [];
126     constraintProperties: ConstraintObject[] = [];
127     constraintCapabilities: ConstraintObject[] = [];
128     operatorTypes: any[];
129     capabilities: string = 'capabilities';
130     properties: string = 'properties';
131     private componentInstancesConstraints: ConstraintObject[] = [];
132
133     @Input() readonly: boolean;
134     @Input() compositeService: ComponentMetadata;
135     @Input() currentServiceInstance: ComponentInstance;
136     @Input() selectedInstanceSiblings: ServiceInstanceObject[];
137     @Input() selectedInstanceConstraints: ConstraintObject[] = [];
138     @Input() selectedInstanceProperties: PropertyBEModel[] = [];
139     @Input() directiveValues: any = DirectiveValue;
140     @Output() updateNodeFilterProperties: EventEmitter<ConstraintObject[]> = new EventEmitter<ConstraintObject[]>();
141     @Output() updateNodeFilterCapabilities: EventEmitter<ConstraintObject[]> = new EventEmitter<ConstraintObject[]>();
142     @Output() loadRulesListEvent:EventEmitter<any> = new EventEmitter();
143     @Output() dependencyStatus = new EventEmitter<boolean>();
144
145     constructor(private topologyTemplateService: TopologyTemplateService, private modalServiceNg2: ModalService, private translateService: TranslateService) {
146     }
147
148     ngOnInit() {
149         this.isLoading = false;
150         this.operatorTypes = [
151             {label: '>', value: OPERATOR_TYPES.GREATER_THAN},
152             {label: '<', value: OPERATOR_TYPES.LESS_THAN},
153             {label: '=', value: OPERATOR_TYPES.EQUAL}
154         ];
155         this.topologyTemplateService.getComponentInputsWithProperties(this.compositeService.componentType, this.compositeService.uniqueId).subscribe((result: ComponentGenericResponse) => {
156             this.parentServiceInputs = result.inputs;
157         });
158         this.loadNodeFilter();
159         this.translateService.languageChangedObservable.subscribe((lang) => {
160             I18nTexts.translateTexts(this.translateService);
161         });
162     }
163
164     ngOnChanges(changes) {
165         if (changes.currentServiceInstance) {
166             this.currentServiceInstance = changes.currentServiceInstance.currentValue;
167             this.isDependent = this.currentServiceInstance.isDependent();
168         }
169         if (changes.selectedInstanceConstraints && changes.selectedInstanceConstraints.currentValue !== changes.selectedInstanceConstraints.previousValue) {
170             this.selectedInstanceConstraints = changes.selectedInstanceConstraints.currentValue;
171             this.loadNodeFilter();
172         }
173     }
174
175     private getActualDirectiveValue = (): string => {
176         return this.currentServiceInstance.directives.length > 0 ? this.currentServiceInstance.directives[0] : "";
177     }
178     public openRemoveDependencyModal = (): ComponentRef<ModalComponent> => {
179         const actionButton: ButtonModel = new ButtonModel(I18nTexts.modalApprove, 'blue', this.onUncheckDependency);
180         const cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'grey', this.onCloseRemoveDependencyModal);
181         const modalModel: ModalModel = new ModalModel('sm', I18nTexts.removeDirectiveModalTitle,
182             I18nTexts.removeDirectiveModalText, [actionButton, cancelButton]);
183         this.loadNodeFilter();
184         return this.modalServiceNg2.createCustomModal(modalModel);
185     }
186
187     public openUpdateDependencyModal = (): ComponentRef<ModalComponent> => {
188         const actionButton: ButtonModel = new ButtonModel(I18nTexts.modalApprove, 'blue', this.onUncheckDependency);
189         const cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'grey', this.onCloseRemoveDependencyModal);
190         const modalModel: ModalModel = new ModalModel('sm', I18nTexts.updateDirectiveModalTitle,
191             I18nTexts.updateDirectiveModalText, [actionButton, cancelButton]);
192         return this.modalServiceNg2.createCustomModal(modalModel);
193     }
194
195     private loadNodeFilter = (): void => {
196         this.topologyTemplateService.getServiceFilterConstraints(this.compositeService.componentType, this.compositeService.uniqueId).subscribe((response) => {
197             if (response.nodeFilterforNode && response.nodeFilterforNode[this.currentServiceInstance.uniqueId]) {
198                 this.componentInstancesConstraints = response.nodeFilterforNode;
199                 const nodeFilterPropertiesResponse: ConstraintObject[] = response.nodeFilterforNode[this.currentServiceInstance.uniqueId].properties;
200                 this.constraintProperties = nodeFilterPropertiesResponse;
201                 const nodeFilterCapabilitiesResponse: ConstraintObject[] = response.nodeFilterforNode[this.currentServiceInstance.uniqueId].capabilities;
202                 this.constraintCapabilities = nodeFilterCapabilitiesResponse;
203             }
204         });
205     }
206
207     onUncheckDependency = () => {
208         this.modalServiceNg2.closeCurrentModal();
209         this.isLoading = true;
210         const isDepOrig = this.isDependent;
211         const rulesListOrig = this.componentInstancesConstraints;
212         this.currentServiceInstance.unmarkAsDependent(this.getActualDirectiveValue());
213         this.updateComponentInstance(isDepOrig, rulesListOrig);
214     }
215
216     onCloseRemoveDependencyModal = () => {
217         this.isDependent = true;
218         this.modalServiceNg2.closeCurrentModal();
219     }
220
221     onOptionsSelected(event: any) {
222         const newDirectiveValue = event.target.value;
223         if (newDirectiveValue.toLowerCase() !== this.getActualDirectiveValue()) {
224             const rulesListOrig = this.componentInstancesConstraints;
225             this.setDirectiveValue(newDirectiveValue);
226             this.constraintProperties = [];
227             this.constraintCapabilities = [];
228             this.updateComponentInstance(this.isDependent, rulesListOrig);
229         }
230     }
231
232     private onRemoveDirective() {
233         this.openRemoveDependencyModal().instance.open();
234         this.constraintProperties = [];
235         this.constraintCapabilities = [];
236     }
237
238     private setDirectiveValue(newDirectiveValue: string) {
239         if (this.isDependent) {
240             this.openUpdateDependencyModal().instance.open();
241         }
242         if (DirectivesEnum.SELECT == newDirectiveValue.toLowerCase() ||
243             DirectivesEnum.SELECTABLE == newDirectiveValue.toLowerCase()) {
244             this.currentServiceInstance.markAsSelect();
245         } else {
246             this.currentServiceInstance.markAsSubstitute();
247         }
248     }
249
250     updateComponentInstance(isDependentOrigVal: boolean, rulesListOrig: ConstraintObject[]) {
251         this.isLoading = true;
252         this.topologyTemplateService.updateComponentInstance(this.compositeService.uniqueId,
253                                                              this.compositeService.componentType,
254                                                              this.currentServiceInstance)
255                                                              .subscribe((updatedServiceIns: ComponentInstance) => {
256             this.currentServiceInstance = new ComponentInstance(updatedServiceIns);
257             this.isDependent = this.currentServiceInstance.isDependent();
258             this.dependencyStatus.emit(this.isDependent);
259             if (this.isDependent) {
260                 this.loadRulesListEvent.emit();
261             }
262             this.isLoading = false;
263         }, (err) => {
264             this.isDependent = isDependentOrigVal;
265             this.componentInstancesConstraints = rulesListOrig;
266             this.isLoading = false;
267             console.log('An error has occurred.');
268         });
269     }
270
271     onAddNodeFilter = (constraintType: string) => {
272         console.info("constraintType: ", constraintType);
273         const cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'outline white', this.modalServiceNg2.closeCurrentModal);
274         const saveButton: ButtonModel = new ButtonModel(I18nTexts.modalCreate, 'blue', () => this.createNodeFilter(constraintType), this.getDisabled);
275         const modalModel: ModalModel = new ModalModel('l', I18nTexts.addNodeFilterTxt, '', [saveButton, cancelButton], 'standard');
276         this.modalInstance = this.modalServiceNg2.createCustomModal(modalModel);
277         this.modalServiceNg2.addDynamicContentToModal(
278             this.modalInstance,
279             ServiceDependenciesEditorComponent,
280             {
281                 currentServiceName: this.currentServiceInstance.name,
282                 operatorTypes: this.operatorTypes,
283                 compositeServiceName: this.compositeService.name,
284                 parentServiceInputs: this.parentServiceInputs,
285                 selectedInstanceProperties: this.selectedInstanceProperties,
286                 selectedInstanceSiblings: this.selectedInstanceSiblings
287             }
288         );
289         this.modalInstance.instance.open();
290     }
291
292     onSelectNodeFilter(constraintType: string, index: number) {
293         const cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'outline white', this.modalServiceNg2.closeCurrentModal);
294         const saveButton: ButtonModel = new ButtonModel(I18nTexts.modalSave, 'blue', () => this.updateNodeFilter(constraintType), this.getDisabled);
295         const modalModel: ModalModel = new ModalModel('l', I18nTexts.updateNodeFilterTxt, '', [saveButton, cancelButton], 'standard');
296         this.modalInstance = this.modalServiceNg2.createCustomModal(modalModel);
297         this.modalServiceNg2.addDynamicContentToModal(
298             this.modalInstance,
299             ServiceDependenciesEditorComponent,
300             {
301                 serviceRuleIndex: index,
302                 serviceRules: _.map(this.properties == constraintType ? this.constraintProperties :
303                     this.constraintCapabilities, (rule) => new ConstraintObjectUI(rule)),
304                 currentServiceName: this.currentServiceInstance.name,
305                 operatorTypes: this.operatorTypes,
306                 compositeServiceName: this.compositeService.name,
307                 parentServiceInputs: this.parentServiceInputs,
308                 selectedInstanceProperties: this.selectedInstanceProperties,
309                 selectedInstanceSiblings: this.selectedInstanceSiblings
310             }
311         );
312         this.modalInstance.instance.open();
313     }
314
315     getDisabled = (): boolean =>  {
316         return !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit();
317     }
318
319     createNodeFilter = (constraintType: string) => {
320         const newRuleToCreate: ConstraintObject = new ConstraintObject(this.modalInstance.instance.dynamicContent.instance.currentRule);
321         this.isLoading = true;
322         this.topologyTemplateService.createServiceFilterConstraints(
323             this.compositeService.uniqueId,
324             this.currentServiceInstance.uniqueId,
325             newRuleToCreate,
326             this.compositeService.componentType,
327             constraintType
328         ).subscribe( (response) => {
329             this.emitEventOnChanges(constraintType, response);
330             this.isLoading = false;
331         }, (err) => {
332             this.isLoading = false;
333         });
334         this.modalServiceNg2.closeCurrentModal();
335     }
336
337     updateNodeFilter = (constraintType: string) => {
338         const allRulesToUpdate: ConstraintObject[] = this.modalInstance.instance.dynamicContent.instance.serviceRulesList.map((rule) => new ConstraintObject(rule));
339         this.isLoading = true;
340         this.topologyTemplateService.updateServiceFilterConstraints(
341             this.compositeService.uniqueId,
342             this.currentServiceInstance.uniqueId,
343             allRulesToUpdate,
344             this.compositeService.componentType,
345             constraintType
346         ).subscribe((response) => {
347             this.emitEventOnChanges(constraintType, response);
348             this.isLoading = false;
349         }, (err) => {
350             this.isLoading = false;
351         });
352         this.modalServiceNg2.closeCurrentModal();
353     }
354
355     getSymbol(constraintOperator) {
356         switch (constraintOperator) {
357             case OPERATOR_TYPES.LESS_THAN: return '<';
358             case OPERATOR_TYPES.EQUAL: return '=';
359             case OPERATOR_TYPES.GREATER_THAN: return '>';
360         }
361     }
362
363     onDeleteNodeFilter = (constraintType: string, index: number) => {
364         this.isLoading = true;
365         this.topologyTemplateService.deleteServiceFilterConstraints(
366             this.compositeService.uniqueId,
367             this.currentServiceInstance.uniqueId,
368             index,
369             this.compositeService.componentType,
370             constraintType
371         ).subscribe( (response) => {
372             this.emitEventOnChanges(constraintType, response);
373             this.isLoading = false;
374         }, (err) => {
375             this.isLoading = false;
376         });
377         this.modalServiceNg2.closeCurrentModal();
378     }
379
380     private emitEventOnChanges(constraintType: string, response) {
381         if (this.properties === constraintType) {
382             this.updateNodeFilterProperties.emit(response.properties);
383             this.constraintProperties = response.properties;
384         } else {
385             this.updateNodeFilterCapabilities.emit(response.capabilities);
386             this.constraintCapabilities = response.capabilities;
387         }
388     }
389
390     openDeleteModal = (constraintType: string, index: number) => {
391         this.modalServiceNg2.createActionModal(I18nTexts.deleteNodeFilterTxt, I18nTexts.deleteNodeFilterMsg,
392             I18nTexts.modalDelete, () => this.onDeleteNodeFilter(constraintType, index), I18nTexts.modalCancel).instance.open();
393     }
394
395 }