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