Sync Integ to Master
[sdc.git] / catalog-ui / src / app / view-models / workspace / tabs / inputs / service-input / service-inputs-view-model.ts
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. 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  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 'use strict';
22 import * as _ from "lodash";
23 import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
24 import {ComponentInstance, InstancesInputsOrPropertiesMapData, Service, IAppMenu, InputModel, PropertyModel, InputPropertyBase} from "app/models";
25 import {DataTypesService} from "app/services";
26 import {ModalsHandler, ResourceType} from "app/utils";
27
28
29 interface IServiceInputsViewModelScope extends IWorkspaceViewModelScope {
30
31     vfInstancesList:Array<ComponentInstance>;
32     instanceInputsMap:InstancesInputsOrPropertiesMapData; //this is tha map object that hold the selected inputs and the inputs we already used
33     instancePropertiesMap:InstancesInputsOrPropertiesMapData;
34     component:Service;
35     sdcMenu:IAppMenu;
36     isViewOnly:boolean;
37     isArrowDisabled:boolean;
38     onArrowPressed():void;
39     checkArrowState():void;
40     loadComponentInputs():void;
41     loadInstanceInputs(instance:ComponentInstance):ng.IPromise<boolean> ;
42     loadInstanceProperties(instance:ComponentInstance):ng.IPromise<boolean> ;
43     loadInputPropertiesForInstance(instanceId:string, input:InputModel):ng.IPromise<boolean> ;
44     loadInputInputs(input:InputModel):ng.IPromise<boolean>;
45     deleteInput(input:InputModel):void
46     openEditValueModal(input:InputModel):void;
47     openSelectPropertyDataTypeViewModel(instanceId:string, property:PropertyModel):void;
48     openEditPropertyDataTypeViewModel(property:PropertyModel):void;
49     dataTypesService:DataTypesService;
50 }
51
52 export class ServiceInputsViewModel {
53
54     static '$inject' = [
55         '$scope',
56         '$q',
57         'ModalsHandler',
58         'Sdc.Services.DataTypesService'
59     ];
60
61     constructor(private $scope:IServiceInputsViewModelScope,
62                 private $q:ng.IQService,
63                 private ModalsHandler:ModalsHandler,
64                 private DataTypesService:DataTypesService) {
65         this.initScope();
66         this.$scope.isViewOnly = this.$scope.isViewMode();
67     }
68
69
70     private getInputsOrPropertiesAlreadySelected = (instanceNormalizeName:string, arrayToFilter:Array<InputPropertyBase>):Array<any> => {
71         let alreadySelectedInput = [];
72         _.forEach(arrayToFilter, (inputOrProperty:InputPropertyBase) => {
73             let expectedServiceInputName = instanceNormalizeName + '_' + inputOrProperty.name;
74             let inputAlreadyInService = _.find(this.$scope.component.inputs, (serviceInput:InputModel) => {
75                 //Checking if the input prefix is the instance name + '_' + property/input name (prefix because we don't need to check full name for complex dataType)
76                 return serviceInput.name.substring(0, expectedServiceInputName.length) === expectedServiceInputName;
77             });
78             if (inputAlreadyInService) {
79                 inputOrProperty.isAlreadySelected = true;
80                 alreadySelectedInput.push(inputOrProperty);
81             } else {
82                 inputOrProperty.isAlreadySelected = false;
83             }
84         });
85         return alreadySelectedInput;
86     };
87
88
89     /*
90      * When loading the screen again, we need to disabled the inputs that already created on the service,
91      * we do that by comparing the service input name, to the instance name + '_' + the resource instance input name.
92      */
93     private disableEnableSelectedInputsOrPropertiesOnInit = (instance:ComponentInstance):void => {
94
95         if (instance.originType === ResourceType.VF) {
96             this.$scope.instanceInputsMap[instance.uniqueId] = this.getInputsOrPropertiesAlreadySelected(instance.normalizedName, instance.inputs);
97         } else {
98             this.$scope.instancePropertiesMap[instance.uniqueId] = this.getInputsOrPropertiesAlreadySelected(instance.normalizedName, instance.properties);
99         }
100     };
101
102     /*
103      * Enable Input/Property after delete
104      */
105     private enableInputsAfterDelete = (propertiesOrInputsDeletes:Array<InputPropertyBase>):void => {
106
107         _.forEach(propertiesOrInputsDeletes, (deletedInputInput:InputPropertyBase) => { //Enable all component instance inputs deleted
108
109             let inputOrPropertyDeleted:InputPropertyBase = _.find(this.$scope.instanceInputsMap[deletedInputInput.componentInstanceId], (inputOrProperty:InputPropertyBase) => {
110                 return inputOrProperty.uniqueId === deletedInputInput.uniqueId;
111             });
112             inputOrPropertyDeleted.isAlreadySelected = false;
113             delete _.remove(this.$scope.instanceInputsMap[deletedInputInput.componentInstanceId], {uniqueId: inputOrPropertyDeleted.uniqueId})[0];
114         });
115     };
116
117     /*
118      * Enable Input/Property after delete
119      */
120     private enablePropertiesAfterDelete = (propertiesOrInputsDeletes:Array<InputPropertyBase>):void => {
121
122         _.forEach(propertiesOrInputsDeletes, (deletedInputInput:InputPropertyBase) => { //Enable all component instance inputs deleted
123             let componentInstance = _.find(this.$scope.vfInstancesList, (instance:ComponentInstance) => {
124                 return instance.uniqueId === deletedInputInput.componentInstanceId;
125             });
126             let inputOrPropertyDeleted:InputPropertyBase = _.find(this.$scope.instancePropertiesMap[deletedInputInput.componentInstanceId], (inputOrProperty:InputPropertyBase) => {
127                 return inputOrProperty.uniqueId === deletedInputInput.uniqueId;
128             });
129
130             let expectedName = componentInstance.normalizedName + '_' + inputOrPropertyDeleted.name;
131             let isAnotherInputExist = _.find(this.$scope.component.inputs, (input:InputModel) => {
132                 return input.name.substring(0, expectedName.length) === expectedName;
133             });
134             if (!isAnotherInputExist) {
135                 inputOrPropertyDeleted.isAlreadySelected = false;
136                 delete _.remove(this.$scope.instancePropertiesMap[deletedInputInput.componentInstanceId], {uniqueId: inputOrPropertyDeleted.uniqueId})[0];
137             }
138         });
139     };
140
141     private initScope = ():void => {
142
143         this.$scope.instanceInputsMap = new InstancesInputsOrPropertiesMapData();
144         this.$scope.instancePropertiesMap = new InstancesInputsOrPropertiesMapData();
145         this.$scope.isLoading = true;
146         this.$scope.isArrowDisabled = true;
147         // Why do we need this? we call this later.
148         //this.$scope.component.getComponentInputs();
149
150         let onSuccess = (componentInstances:Array<ComponentInstance>) => {
151             console.log("component instances loaded: ", componentInstances);
152             this.$scope.vfInstancesList = componentInstances;
153             this.$scope.isLoading = false;
154         };
155
156         //This function will get al component instance for the left table - in
157         // future the instances will be filter according to search text
158         this.$scope.component.getComponentInstancesFilteredByInputsAndProperties().then(onSuccess);
159
160         // This function will get the service inputs for the right table
161         this.$scope.component.getComponentInputs();
162
163         /*
164          * When clicking on instance in the left table, this function will load all instance inputs
165          */
166         this.$scope.loadInstanceInputs = (instance:ComponentInstance):ng.IPromise<boolean> => {
167             let deferred = this.$q.defer();
168
169             let onSuccess = (inputs:Array<InputModel>) => {
170                 instance.inputs = inputs;
171                 this.disableEnableSelectedInputsOrPropertiesOnInit(instance);
172                 deferred.resolve(true);
173             };
174
175             let onError = () => {
176                 deferred.resolve(false);
177             };
178
179             if (!instance.inputs) {
180                 this.$scope.component.getComponentInstanceInputs(instance.uniqueId, instance.componentUid).then(onSuccess, onError);
181                 //this.disableEnableSelectedInputs(instance);
182             } else {
183                 deferred.resolve(true);
184             }
185             return deferred.promise;
186         };
187
188
189         this.$scope.loadInstanceProperties = (instance:ComponentInstance):ng.IPromise<boolean> => {
190             let deferred = this.$q.defer();
191
192             let onSuccess = (properties:Array<PropertyModel>) => {
193                 instance.properties = properties;
194                 this.disableEnableSelectedInputsOrPropertiesOnInit(instance);
195                 deferred.resolve(true);
196             };
197
198             let onError = () => {
199                 deferred.resolve(false);
200             };
201
202             if (!instance.properties) {
203                 this.$scope.component.getComponentInstanceProperties(instance.uniqueId).then(onSuccess, onError);
204             } else {
205                 deferred.resolve(true);
206             }
207             return deferred.promise;
208         };
209
210         /*
211          * When clicking on instance input in the left or right table, this function will load all properties of the selected input
212          */
213         this.$scope.loadInputPropertiesForInstance = (instanceId:string, input:InputModel):ng.IPromise<boolean> => {
214             let deferred = this.$q.defer();
215
216             let onSuccess = (properties:Array<PropertyModel>) => {
217                 input.properties = properties;
218                 deferred.resolve(true);
219             };
220
221             let onError = () => {
222                 deferred.resolve(false)
223             };
224
225             if (!input.properties) {
226                 this.$scope.component.getComponentInstanceInputProperties(instanceId, input.uniqueId).then(onSuccess, onError);
227             } else {
228                 deferred.resolve(true);
229             }
230             return deferred.promise;
231         };
232
233         /*
234          * When clicking on input in the right table, this function will load all inputs of the selected input
235          */
236         this.$scope.loadInputInputs = (input:InputModel):ng.IPromise<boolean> => {
237             let deferred = this.$q.defer();
238
239             let onSuccess = () => {
240                 deferred.resolve(true);
241             };
242             let onError = () => {
243                 deferred.resolve(false);
244             };
245
246             if (!input.inputs) { // Caching, if exists do not get it.
247                 this.$scope.component.getServiceInputInputsAndProperties(input.uniqueId).then(onSuccess, onError);
248             } else {
249                 deferred.resolve(true);
250             }
251             return deferred.promise;
252         };
253
254         /*
255          * When pressing the arrow, we create service inputs from the inputs selected
256          */
257         this.$scope.onArrowPressed = ():void => {
258             let onSuccess = (inputsCreated:Array<InputModel>) => {
259
260                 //disabled all the inputs in the left table
261                 _.forEach(this.$scope.instanceInputsMap, (inputs:Array<InputModel>, instanceId:string) => {
262                     _.forEach(inputs, (input:InputModel) => {
263                         input.isAlreadySelected = true;
264                     });
265                 });
266                 _.forEach(this.$scope.instancePropertiesMap, (properties:Array<PropertyModel>, instanceId:string) => {
267                     _.forEach(properties, (property:PropertyModel) => {
268                         property.isAlreadySelected = true;
269                     });
270                 });
271                 this.addColorToItems(inputsCreated);
272             };
273
274             let onFailed = (error:any) => {
275                 this.$scope.isArrowDisabled = false;
276                 console.log("Error declaring input/property");
277             };
278
279             this.$scope.isArrowDisabled = true;
280             this.$scope.component.createInputsFormInstances(this.$scope.instanceInputsMap, this.$scope.instancePropertiesMap).then(onSuccess, onFailed);
281         };
282
283
284         /* Iterates through array of selected inputs and properties and returns true if there is at least one new selection on left */
285         this.$scope.checkArrowState = ()=> {
286
287             let newInputSelected:boolean = _.some(this.$scope.instanceInputsMap, (inputs:Array<InputModel>) => {
288                 return _.some(inputs, (input:InputModel)=> {
289                     return input.isAlreadySelected === false;
290                 });
291             });
292
293             let newPropSelected:boolean = _.some(this.$scope.instancePropertiesMap, (properties:Array<PropertyModel>) => {
294                 return _.some(properties, (property:PropertyModel) => {
295                     return property.isAlreadySelected === false;
296                 });
297             });
298
299             this.$scope.isArrowDisabled = !(newInputSelected || newPropSelected);
300
301         };
302
303         this.$scope.deleteInput = (inputToDelete:InputModel):void => {
304
305             let onDelete = ():void => {
306
307                 let onSuccess = (deletedInput:InputModel):void => {
308                     if (deletedInput.inputs && deletedInput.inputs.length > 0) { // Enable input declared from input
309                         this.enableInputsAfterDelete(deletedInput.inputs);
310                     }
311
312                     if (deletedInput.properties && deletedInput.properties.length > 0) { // Enable properties
313                         this.enablePropertiesAfterDelete(deletedInput.properties);
314                     }
315                     deletedInput.isDeleteDisabled = false;
316                     this.$scope.checkArrowState();
317
318                 };
319
320                 let onFailed = (error:any):void => {
321                     console.log("Error deleting input");
322                     inputToDelete.isDeleteDisabled = false;
323                 };
324
325                 inputToDelete.isDeleteDisabled = true;
326                 this.addColorToItems([inputToDelete]);
327                 this.$scope.component.deleteServiceInput(inputToDelete.uniqueId).then((deletedInput:InputModel):void => {
328                     onSuccess(deletedInput);
329                 }, onFailed);
330             };
331
332             // Get confirmation modal text from menu.json
333             let state = "deleteInput";
334             let title:string = this.$scope.sdcMenu.alertMessages[state].title;
335             let message:string = this.$scope.sdcMenu.alertMessages[state].message.format([inputToDelete.name]);
336
337             // Open confirmation modal
338             this.ModalsHandler.openAlertModal(title, message).then(onDelete);
339         };
340
341         this.$scope.openEditValueModal = (input:InputModel) => {
342             this.ModalsHandler.openEditInputValueModal(input);
343         };
344
345         this.$scope.openSelectPropertyDataTypeViewModel = (instanceId:string, property:PropertyModel) => {
346             //to open the select data type modal
347             let selectedInstance = _.find(this.$scope.vfInstancesList, {uniqueId: instanceId});
348             this.DataTypesService.selectedInstance = selectedInstance; //set the selected instance on the service for compering the input name on the service & the complex property
349             this.DataTypesService.selectedComponentInputs = this.$scope.component.inputs; // set all the service inputs on the data type service
350             let filteredPropertiesMap = _.filter(this.$scope.instancePropertiesMap[instanceId], (instanceProperty)=> {
351                 return instanceProperty.name == property.name;
352             });//get all properties under the specific property
353             this.DataTypesService.selectedPropertiesName = property.propertiesName;
354
355             this.ModalsHandler.openSelectDataTypeModal(property, this.$scope.component, this.$scope.component.properties, filteredPropertiesMap).then((selectedProperty:PropertyModel)=> {
356                 if (selectedProperty && selectedProperty.propertiesName) {
357                     let propertyToUpdate:PropertyModel = _.find(selectedInstance.properties, {uniqueId: selectedProperty.uniqueId});
358                     let existingProperty:PropertyModel = (<PropertyModel>_.find(this.$scope.instancePropertiesMap[instanceId], {uniqueId: propertyToUpdate.uniqueId}));
359
360                     if (existingProperty) {
361                         existingProperty.propertiesName = selectedProperty.propertiesName;
362                         existingProperty.input = selectedProperty.input;
363                         existingProperty.isAlreadySelected = false;
364                     } else {
365                         propertyToUpdate.propertiesName = selectedProperty.propertiesName;
366                         propertyToUpdate.input = selectedProperty.input;
367                         this.$scope.instancePropertiesMap[instanceId].push(propertyToUpdate);
368
369                     }
370                     this.$scope.checkArrowState();
371
372                 }
373             });
374         };
375
376
377         this.$scope.openEditPropertyDataTypeViewModel = (property:PropertyModel)=> {
378             this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.component.properties, false).then(() => {
379             });
380         }
381     };
382
383     private addColorToItems = (inputsCreated:Array<InputModel>):void => {
384
385         // Adding color to the new inputs (right table)
386         _.forEach(inputsCreated, (input) => {
387             input.isNew = true;
388         });
389
390         // Removing color to the new inputs (right table)
391         setTimeout(() => {
392             _.forEach(inputsCreated, (input) => {
393                 input.isNew = false;
394             });
395             this.$scope.$apply();
396         }, 3000);
397     };
398 }