Add UI feedback when saving a interface operation
[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 {
24     TopologyTemplateService
25 } from '../../../services/component-services/topology-template.service';
26 import {TranslateService} from "../../../shared/translator/translate.service";
27 import {ModalService} from 'app/ng2/services/modal.service';
28 import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
29 import {Component as TopologyTemplate} from "../../../../models/components/component";
30 import {PluginsService} from "app/ng2/services/plugins.service";
31 import {SelectedComponentType} from "../common/store/graph.actions";
32
33 import {WorkspaceService} from "../../workspace/workspace.service";
34 import {
35     ComponentInterfaceDefinitionModel,
36     InterfaceOperationModel
37 } from "../../../../models/interfaceOperation";
38 import {
39     InterfaceOperationHandlerComponent
40 } from "./operation-creator/interface-operation-handler.component";
41
42 import {
43     ArtifactModel,
44     ButtonModel,
45     ComponentInstance,
46     ComponentMetadata,
47     InputBEModel,
48     InterfaceModel,
49     ModalModel
50 } from 'app/models';
51 import {ArtifactGroupType} from "../../../../utils/constants";
52 import {
53     DropdownValue
54 } from "../../../components/ui/form-components/dropdown/ui-element-dropdown.component";
55 import {ToscaArtifactService} from "../../../services/tosca-artifact.service";
56 import {ToscaArtifactModel} from "../../../../models/toscaArtifact";
57
58 export class UIInterfaceOperationModel extends InterfaceOperationModel {
59     isCollapsed: boolean = true;
60     isEllipsis: boolean;
61     MAX_LENGTH = 75;
62
63     constructor(operation: InterfaceOperationModel) {
64         super(operation);
65
66         if (!operation.description) {
67             this.description = '';
68         }
69
70         if (this.description.length > this.MAX_LENGTH) {
71             this.isEllipsis = true;
72         } else {
73             this.isEllipsis = false;
74         }
75     }
76
77     getDescriptionEllipsis(): string {
78         if (this.isCollapsed && this.description.length > this.MAX_LENGTH) {
79             return this.description.substr(0, this.MAX_LENGTH - 3) + '...';
80         }
81         return this.description;
82     }
83
84     toggleCollapsed(e) {
85         e.stopPropagation();
86         this.isCollapsed = !this.isCollapsed;
87     }
88 }
89
90 class ModalTranslation {
91     EDIT_TITLE: string;
92     CANCEL_BUTTON: string;
93     CLOSE_BUTTON: string;
94     SAVE_BUTTON: string;
95
96     constructor(private TranslateService: TranslateService) {
97         this.TranslateService.languageChangedObservable.subscribe(lang => {
98             this.EDIT_TITLE = this.TranslateService.translate('INTERFACE_EDIT_TITLE');
99             this.CANCEL_BUTTON = this.TranslateService.translate("INTERFACE_CANCEL_BUTTON");
100             this.CLOSE_BUTTON = this.TranslateService.translate("INTERFACE_CLOSE_BUTTON");
101             this.SAVE_BUTTON = this.TranslateService.translate("INTERFACE_SAVE_BUTTON");
102         });
103     }
104 }
105
106 export class UIInterfaceModel extends ComponentInterfaceDefinitionModel {
107     isCollapsed: boolean = false;
108
109     constructor(interf?: any) {
110         super(interf);
111         this.operations = _.map(
112             this.operations,
113             (operation) => new UIInterfaceOperationModel(operation)
114         );
115     }
116
117     toggleCollapse() {
118         this.isCollapsed = !this.isCollapsed;
119     }
120 }
121
122 @Component({
123     selector: 'app-interface-operations',
124     templateUrl: './interface-operations.component.html',
125     styleUrls: ['./interface-operations.component.less'],
126     providers: [ModalService, TranslateService]
127 })
128 export class InterfaceOperationsComponent {
129     interfaces: UIInterfaceModel[];
130     inputs: Array<InputBEModel>;
131     isLoading: boolean;
132     interfaceTypes: { [interfaceType: string]: string[] };
133     topologyTemplate: TopologyTemplate;
134     componentMetaData: ComponentMetadata;
135     componentInstanceSelected: ComponentInstance;
136     modalInstance: ComponentRef<ModalComponent>;
137     modalTranslation: ModalTranslation;
138     componentInstancesInterfaces: Map<string, InterfaceModel[]>;
139
140     deploymentArtifactsFilePath: Array<DropdownValue> = [];
141     toscaArtifactTypes: Array<DropdownValue> = [];
142
143     @Input() component: ComponentInstance;
144     @Input() isViewOnly: boolean;
145     @Input() enableMenuItems: Function;
146     @Input() disableMenuItems: Function;
147     @Input() componentType: SelectedComponentType;
148
149
150     constructor(
151         private TranslateService: TranslateService,
152         private PluginsService: PluginsService,
153         private topologyTemplateService: TopologyTemplateService,
154         private toscaArtifactService: ToscaArtifactService,
155         private modalServiceNg2: ModalService,
156         private workspaceService: WorkspaceService,
157         @Inject("Notification") private Notification: any,
158     ) {
159         this.modalTranslation = new ModalTranslation(TranslateService);
160     }
161
162     ngOnInit(): void {
163         this.componentMetaData = this.workspaceService.metadata;
164         this.loadComponentInstances();
165         this.loadDeployedArtifacts();
166         this.loadToscaArtifacts()
167     }
168
169     private loadComponentInstances() {
170         this.isLoading = true;
171         this.topologyTemplateService.getComponentInstances(this.componentMetaData.componentType, this.componentMetaData.uniqueId)
172         .subscribe((response) => {
173             this.componentInstanceSelected = response.componentInstances.find(ci => ci.uniqueId === this.component.uniqueId);
174             this.initComponentInstanceInterfaceOperations();
175             this.isLoading = false;
176         });
177     }
178
179     private initComponentInstanceInterfaceOperations() {
180         this.initInterfaces(this.componentInstanceSelected.interfaces);
181         this.sortInterfaces();
182     }
183
184     private initInterfaces(interfaces: ComponentInterfaceDefinitionModel[]): void {
185         this.interfaces = _.map(interfaces, (interfaceModel) => new UIInterfaceModel(interfaceModel));
186     }
187
188     private sortInterfaces(): void {
189         this.interfaces = _.filter(this.interfaces, (interf) => interf.operations && interf.operations.length > 0); // remove empty interfaces
190         this.interfaces.sort((a, b) => a.type.localeCompare(b.type)); // sort interfaces alphabetically
191         _.forEach(this.interfaces, (interf) => {
192             interf.operations.sort((a, b) => a.name.localeCompare(b.name)); // sort operations alphabetically
193         });
194     }
195
196     collapseAll(value: boolean = true): void {
197         _.forEach(this.interfaces, (interf) => {
198             interf.isCollapsed = value;
199         });
200     }
201
202     isAllCollapsed(): boolean {
203         return _.every(this.interfaces, (interf) => interf.isCollapsed);
204     }
205
206     isAllExpanded(): boolean {
207         return _.every(this.interfaces, (interf) => !interf.isCollapsed);
208     }
209
210     isListEmpty(): boolean {
211         return _.filter(
212             this.interfaces,
213             (interf) => interf.operations && interf.operations.length > 0
214         ).length === 0;
215     }
216
217     private enableOrDisableSaveButton = (): boolean => {
218         return this.isViewOnly;
219     }
220
221     onSelectInterfaceOperation(interfaceModel: UIInterfaceModel, operation: InterfaceOperationModel) {
222
223         const buttonList = [];
224         if (this.isViewOnly) {
225             const closeButton: ButtonModel = new ButtonModel(this.modalTranslation.CLOSE_BUTTON, 'outline white', this.cancelAndCloseModal);
226             buttonList.push(closeButton);
227         } else {
228             const saveButton: ButtonModel = new ButtonModel(this.modalTranslation.SAVE_BUTTON, 'blue', () =>
229                 this.updateInterfaceOperation(), this.enableOrDisableSaveButton);
230             const cancelButton: ButtonModel = new ButtonModel(this.modalTranslation.CANCEL_BUTTON, 'outline white', this.cancelAndCloseModal);
231             buttonList.push(saveButton);
232             buttonList.push(cancelButton);
233         }
234         const modalModel: ModalModel = new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', buttonList, 'custom');
235         this.modalInstance = this.modalServiceNg2.createCustomModal(modalModel);
236
237         this.modalServiceNg2.addDynamicContentToModal(
238             this.modalInstance,
239             InterfaceOperationHandlerComponent,
240             {
241                 deploymentArtifactsFilePath: this.deploymentArtifactsFilePath,
242                 toscaArtifactTypes: this.toscaArtifactTypes,
243                 selectedInterface: interfaceModel ? interfaceModel : new UIInterfaceModel(),
244                 selectedInterfaceOperation: operation ? operation : new InterfaceOperationModel(),
245                 validityChangedCallback: this.enableOrDisableSaveButton,
246                 isViewOnly: this.isViewOnly
247             }
248         );
249         this.modalInstance.instance.open();
250     }
251
252     private cancelAndCloseModal = () => {
253         this.loadComponentInstances();
254         return this.modalServiceNg2.closeCurrentModal();
255     }
256
257     private updateInterfaceOperation() {
258         this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = true;
259         const interfaceOperationHandlerComponentInstance: InterfaceOperationHandlerComponent = this.modalInstance.instance.dynamicContent.instance;
260         const operationUpdated: InterfaceOperationModel = interfaceOperationHandlerComponentInstance.operationToUpdate;
261         const isArtifactChecked = interfaceOperationHandlerComponentInstance.enableAddArtifactImplementation;
262         if (!isArtifactChecked) {
263             let artifactName = interfaceOperationHandlerComponentInstance.artifactName;
264             artifactName = artifactName === undefined ? '' : artifactName;
265             operationUpdated.implementation = new ArtifactModel({'artifactName': artifactName} as ArtifactModel);
266         }
267         this.topologyTemplateService.updateComponentInstanceInterfaceOperation(
268             this.componentMetaData.uniqueId,
269             this.componentMetaData.componentType,
270             this.componentInstanceSelected.uniqueId,
271             operationUpdated)
272         .subscribe((updatedComponentInstance: ComponentInstance) => {
273             this.componentInstanceSelected = new ComponentInstance(updatedComponentInstance);
274             this.initComponentInstanceInterfaceOperations();
275             this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
276             this.modalServiceNg2.closeCurrentModal();
277         }, () => {
278             this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
279             this.modalServiceNg2.closeCurrentModal();
280         });
281     }
282
283     loadDeployedArtifacts() {
284         this.topologyTemplateService.getArtifactsByType(this.componentMetaData.componentType, this.componentMetaData.uniqueId, ArtifactGroupType.DEPLOYMENT)
285         .subscribe(response => {
286             let artifactsDeployment = response.deploymentArtifacts;
287             if (artifactsDeployment) {
288                 let deploymentArtifactsFound = <ArtifactModel[]>_.values(artifactsDeployment)
289                 deploymentArtifactsFound.forEach(value => {
290                     this.deploymentArtifactsFilePath.push(new DropdownValue(value, value.artifactType.concat('->').concat(value.artifactName)));
291                 });
292             }
293         }, error => {
294             this.Notification.error({
295                 message: 'Failed to Load the Deployed Artifacts:' + error,
296                 title: 'Failure'
297             });
298         });
299     }
300
301     loadToscaArtifacts() {
302         this.toscaArtifactService.getToscaArtifacts(this.componentMetaData.model).subscribe(response => {
303             if (response) {
304                 let toscaArtifactsFound = <ToscaArtifactModel[]>_.values(response);
305                 toscaArtifactsFound.forEach(value => this.toscaArtifactTypes.push(new DropdownValue(value, value.type)));
306             }
307         }, error => {
308             this.Notification.error({
309                 message: 'Failed to Load Tosca Artifacts:' + error,
310                 title: 'Failure'
311             });
312         });
313     }
314
315 }