Fix unchecked artifact implementation being persisted
[sdc.git] / catalog-ui / src / app / ng2 / pages / composition / interface-operatons / interface-operations.component.ts
1 /*
2 * ============LICENSE_START=======================================================
3 * SDC
4 * ================================================================================
5 *  Copyright (C) 2021 Nordix Foundation. All rights reserved.
6 *  ================================================================================
7 *  Licensed under the Apache License, Version 2.0 (the "License");
8 *  you may not use this file except in compliance with the License.
9 *  You may obtain a copy of the License at
10 *
11 *        http://www.apache.org/licenses/LICENSE-2.0
12 *  Unless required by applicable law or agreed to in writing, software
13 *  distributed under the License is distributed on an "AS IS" BASIS,
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 *  See the License for the specific language governing permissions and
16 *  limitations under the License.
17 *
18 *  SPDX-License-Identifier: Apache-2.0
19 *  ============LICENSE_END=========================================================
20 */
21
22 import {Component, ComponentRef, Inject, Input} from '@angular/core';
23 import {TopologyTemplateService} from '../../../services/component-services/topology-template.service';
24 import {TranslateService} from "../../../shared/translator/translate.service";
25 import {ModalService} from 'app/ng2/services/modal.service';
26 import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
27 import {Component as TopologyTemplate} from "../../../../models/components/component";
28 import {PluginsService} from "app/ng2/services/plugins.service";
29 import {SelectedComponentType} from "../common/store/graph.actions";
30
31 import {WorkspaceService} from "../../workspace/workspace.service";
32 import {ComponentInstanceInterfaceModel, InterfaceOperationModel} from "../../../../models/interfaceOperation";
33 import {InterfaceOperationHandlerComponent} from "./operation-creator/interface-operation-handler.component";
34
35 import {ArtifactModel, ButtonModel, ComponentInstance, ComponentMetadata, InputBEModel, InterfaceModel, ModalModel} from 'app/models';
36 import {ArtifactGroupType} from "../../../../utils/constants";
37 import {DropdownValue} from "../../../components/ui/form-components/dropdown/ui-element-dropdown.component";
38 import {ToscaArtifactService} from "../../../services/tosca-artifact.service";
39 import {ToscaArtifactModel} from "../../../../models/toscaArtifact";
40
41 export class UIInterfaceOperationModel extends InterfaceOperationModel {
42   isCollapsed: boolean = true;
43   isEllipsis: boolean;
44   MAX_LENGTH = 75;
45   constructor(operation: InterfaceOperationModel) {
46     super(operation);
47
48     if (!operation.description) {
49       this.description = '';
50     }
51
52     if (this.description.length > this.MAX_LENGTH) {
53       this.isEllipsis = true;
54     } else {
55       this.isEllipsis = false;
56     }
57   }
58
59   getDescriptionEllipsis(): string {
60     if (this.isCollapsed && this.description.length > this.MAX_LENGTH) {
61       return this.description.substr(0, this.MAX_LENGTH - 3) + '...';
62     }
63     return this.description;
64   }
65
66   toggleCollapsed(e) {
67     e.stopPropagation();
68     this.isCollapsed = !this.isCollapsed;
69   }
70 }
71
72 class ModalTranslation {
73   EDIT_TITLE: string;
74   CANCEL_BUTTON: string;
75   CLOSE_BUTTON: string;
76   SAVE_BUTTON: string;
77
78   constructor(private TranslateService: TranslateService) {
79     this.TranslateService.languageChangedObservable.subscribe(lang => {
80       this.EDIT_TITLE = this.TranslateService.translate('INTERFACE_EDIT_TITLE');
81       this.CANCEL_BUTTON = this.TranslateService.translate("INTERFACE_CANCEL_BUTTON");
82       this.CLOSE_BUTTON = this.TranslateService.translate("INTERFACE_CLOSE_BUTTON");
83       this.SAVE_BUTTON = this.TranslateService.translate("INTERFACE_SAVE_BUTTON");
84     });
85   }
86 }
87
88 export class UIInterfaceModel extends ComponentInstanceInterfaceModel {
89   isCollapsed: boolean = false;
90
91   constructor(interf?: any) {
92     super(interf);
93     this.operations = _.map(
94         this.operations,
95         (operation) => new UIInterfaceOperationModel(operation)
96     );
97   }
98
99   toggleCollapse() {
100     this.isCollapsed = !this.isCollapsed;
101   }
102 }
103
104 @Component({
105   selector: 'app-interface-operations',
106   templateUrl: './interface-operations.component.html',
107   styleUrls: ['./interface-operations.component.less'],
108   providers: [ModalService, TranslateService]
109 })
110 export class InterfaceOperationsComponent {
111   interfaces: UIInterfaceModel[];
112   inputs: Array<InputBEModel>;
113   isLoading: boolean;
114   interfaceTypes: { [interfaceType: string]: string[] };
115   topologyTemplate: TopologyTemplate;
116   componentMetaData: ComponentMetadata;
117   componentInstanceSelected: ComponentInstance;
118   modalInstance: ComponentRef<ModalComponent>;
119   modalTranslation: ModalTranslation;
120   componentInstancesInterfaces: Map<string, InterfaceModel[]>;
121
122   deploymentArtifactsFilePath: Array<DropdownValue> = [];
123   toscaArtifactTypes: Array<DropdownValue> = [];
124
125   @Input() component: ComponentInstance;
126   @Input() isViewOnly: boolean;
127   @Input() enableMenuItems: Function;
128   @Input() disableMenuItems: Function;
129   @Input() componentType: SelectedComponentType;
130
131
132   constructor(
133       private TranslateService: TranslateService,
134       private PluginsService: PluginsService,
135       private topologyTemplateService: TopologyTemplateService,
136       private toscaArtifactService: ToscaArtifactService,
137       private modalServiceNg2: ModalService,
138       private workspaceService: WorkspaceService,
139       @Inject("Notification") private Notification: any,
140   ) {
141     this.modalTranslation = new ModalTranslation(TranslateService);
142   }
143
144   ngOnInit(): void {
145     this.componentMetaData = this.workspaceService.metadata;
146     this.loadComponentInstances();
147     this.loadDeployedArtifacts();
148     this.loadToscaArtifacts()
149   }
150
151   private loadComponentInstances() {
152     this.isLoading = true;
153     this.topologyTemplateService.getComponentInstances(this.componentMetaData.componentType, this.componentMetaData.uniqueId)
154     .subscribe((response) => {
155       this.componentInstanceSelected = response.componentInstances.find(ci => ci.uniqueId === this.component.uniqueId);
156       this.initComponentInstanceInterfaceOperations();
157       this.isLoading = false;
158     });
159   }
160
161   private initComponentInstanceInterfaceOperations() {
162     this.initInterfaces(this.componentInstanceSelected.interfaces);
163     this.sortInterfaces();
164   }
165
166   private initInterfaces(interfaces: ComponentInstanceInterfaceModel[]): void {
167     this.interfaces = _.map(interfaces, (interfaceModel) => new UIInterfaceModel(interfaceModel));
168   }
169
170   private sortInterfaces(): void {
171     this.interfaces = _.filter(this.interfaces, (interf) => interf.operations && interf.operations.length > 0); // remove empty interfaces
172     this.interfaces.sort((a, b) => a.type.localeCompare(b.type)); // sort interfaces alphabetically
173     _.forEach(this.interfaces, (interf) => {
174       interf.operations.sort((a, b) => a.name.localeCompare(b.name)); // sort operations alphabetically
175     });
176   }
177
178   collapseAll(value: boolean = true): void {
179     _.forEach(this.interfaces, (interf) => {
180       interf.isCollapsed = value;
181     });
182   }
183
184   isAllCollapsed(): boolean {
185     return _.every(this.interfaces, (interf) => interf.isCollapsed);
186   }
187
188   isAllExpanded(): boolean {
189     return _.every(this.interfaces, (interf) => !interf.isCollapsed);
190   }
191
192   isListEmpty(): boolean {
193     return _.filter(
194         this.interfaces,
195         (interf) => interf.operations && interf.operations.length > 0
196     ).length === 0;
197   }
198
199   private enableOrDisableSaveButton = (): boolean => {
200     return this.isViewOnly;
201   }
202
203   onSelectInterfaceOperation(interfaceModel: UIInterfaceModel, operation: InterfaceOperationModel) {
204
205     const buttonList = [];
206     if (this.isViewOnly) {
207       const closeButton: ButtonModel = new ButtonModel(this.modalTranslation.CLOSE_BUTTON, 'outline white', this.cancelAndCloseModal);
208       buttonList.push(closeButton);
209     } else {
210       const saveButton: ButtonModel = new ButtonModel(this.modalTranslation.SAVE_BUTTON, 'blue', () =>
211           this.updateInterfaceOperation(), this.enableOrDisableSaveButton);
212       const cancelButton: ButtonModel = new ButtonModel(this.modalTranslation.CANCEL_BUTTON, 'outline white', this.cancelAndCloseModal);
213       buttonList.push(saveButton);
214       buttonList.push(cancelButton);
215     }
216     const modalModel: ModalModel = new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', buttonList, 'custom');
217     this.modalInstance = this.modalServiceNg2.createCustomModal(modalModel);
218
219     this.modalServiceNg2.addDynamicContentToModal(
220         this.modalInstance,
221         InterfaceOperationHandlerComponent,
222         {
223           deploymentArtifactsFilePath: this.deploymentArtifactsFilePath,
224           toscaArtifactTypes: this.toscaArtifactTypes,
225           selectedInterface: interfaceModel,
226           selectedInterfaceOperation: operation,
227           validityChangedCallback: this.enableOrDisableSaveButton,
228           isViewOnly: this.isViewOnly
229         }
230     );
231     this.modalInstance.instance.open();
232   }
233
234   private cancelAndCloseModal = () => {
235     this.loadComponentInstances();
236     return this.modalServiceNg2.closeCurrentModal();
237   }
238
239   private updateInterfaceOperation() {
240     this.isLoading = true;
241     const interfaceOperationHandlerComponentInstance: InterfaceOperationHandlerComponent = this.modalInstance.instance.dynamicContent.instance;
242     const operationUpdated: InterfaceOperationModel = interfaceOperationHandlerComponentInstance.operationToUpdate;
243     const isArtifactChecked = interfaceOperationHandlerComponentInstance.enableAddArtifactImplementation;
244     if (!isArtifactChecked) {
245       let artifactName = interfaceOperationHandlerComponentInstance.artifactName;
246       artifactName = artifactName === undefined ? '' : artifactName;
247       operationUpdated.implementation = new ArtifactModel({'artifactName': artifactName} as ArtifactModel);
248     }
249     this.topologyTemplateService.updateComponentInstanceInterfaceOperation(
250         this.componentMetaData.uniqueId,
251         this.componentMetaData.componentType,
252         this.componentInstanceSelected.uniqueId,
253         operationUpdated)
254         .subscribe((updatedComponentInstance: ComponentInstance) => {
255             this.componentInstanceSelected = new ComponentInstance(updatedComponentInstance);
256             this.initComponentInstanceInterfaceOperations();
257         });
258     this.modalServiceNg2.closeCurrentModal();
259     this.isLoading = false;
260   }
261
262   loadDeployedArtifacts() {
263     this.topologyTemplateService.getArtifactsByType(this.componentMetaData.componentType, this.componentMetaData.uniqueId, ArtifactGroupType.DEPLOYMENT)
264     .subscribe(response => {
265       let artifactsDeployment = response.deploymentArtifacts;
266       if (artifactsDeployment) {
267         let deploymentArtifactsFound = <ArtifactModel[]>_.values(artifactsDeployment)
268         deploymentArtifactsFound.forEach(value => {
269           this.deploymentArtifactsFilePath.push(new DropdownValue(value, value.artifactType.concat('->').concat(value.artifactName)));
270         });
271       }}, error => {
272       this.Notification.error({
273         message: 'Failed to Load the Deployed Artifacts:' + error,
274         title: 'Failure'
275       });
276     });
277   }
278
279   loadToscaArtifacts() {
280     this.toscaArtifactService.getToscaArtifacts(this.componentMetaData.model).subscribe(response => {
281       if (response) {
282         let toscaArtifactsFound = <ToscaArtifactModel[]>_.values(response);
283         toscaArtifactsFound.forEach(value => this.toscaArtifactTypes.push(new DropdownValue(value, value.type)));
284       }
285     }, error => {
286       this.Notification.error({
287         message: 'Failed to Load Tosca Artifacts:' + error,
288         title: 'Failure'
289       });
290     });
291   }
292
293 }