re base code
[sdc.git] / catalog-ui / src / app / view-models / forms / property-forms / component-property-form / property-form-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 {
24     PROPERTY_TYPES, ModalsHandler, ValidationUtils, PROPERTY_VALUE_CONSTRAINTS, FormState, PROPERTY_DATA} from "app/utils";
25 import {DataTypesService} from "app/services";
26 import {PropertyModel, DataTypesMap, Component, GroupInstance, PolicyInstance, PropertyBEModel} from "app/models";
27 import {ComponentInstance} from "../../../../models/componentsInstances/componentInstance";
28 import { ComponentInstanceServiceNg2 } from "app/ng2/services/component-instance-services/component-instance.service";
29
30 export interface IEditPropertyModel {
31     property:PropertyModel;
32     types:Array<string>;
33     simpleTypes:Array<string>;
34 }
35
36 interface IPropertyFormViewModelScope extends ng.IScope {
37     forms:any;
38     editForm:ng.IFormController;
39     footerButtons:Array<any>;
40     isNew:boolean;
41     isLoading:boolean;
42     isService:boolean;
43     validationPattern:RegExp;
44     propertyNameValidationPattern:RegExp;
45     commentValidationPattern:RegExp;
46     editPropertyModel:IEditPropertyModel;
47     modalInstanceProperty:ng.ui.bootstrap.IModalServiceInstance;
48     currentPropertyIndex:number;
49     isLastProperty:boolean;
50     myValue:any;
51     nonPrimitiveTypes:Array<string>;
52     dataTypes:DataTypesMap;
53     isTypeDataType:boolean;
54     maxLength:number;
55     isPropertyValueOwner:boolean;
56     isVnfConfiguration:boolean;
57
58     validateJson(json:string):boolean;
59     save(doNotCloseModal?:boolean):void;
60     getValidationPattern(type:string):RegExp;
61     validateIntRange(value:string):boolean;
62     close():void;
63     onValueChange():void;
64     onSchemaTypeChange():void;
65     onTypeChange(resetSchema:boolean):void;
66     showSchema():boolean;
67     delete(property:PropertyModel):void;
68     getPrev():void;
69     getNext():void;
70     isSimpleType(typeName:string):boolean;
71     getDefaultValue():any;
72 }
73
74 export class PropertyFormViewModel {
75
76     static '$inject' = [
77         '$scope',
78         'Sdc.Services.DataTypesService',
79         '$uibModalInstance',
80         'property',
81         'ValidationPattern',
82         'PropertyNameValidationPattern',
83         'CommentValidationPattern',
84         'ValidationUtils',
85         'component',
86         '$filter',
87         'ModalsHandler',
88         'filteredProperties',
89         '$timeout',
90         'isPropertyValueOwner',
91         'propertyOwnerType',
92         'propertyOwnerId',
93         'ComponentInstanceServiceNg2'
94     ];
95
96     private formState:FormState;
97
98     constructor(private $scope:IPropertyFormViewModelScope,
99                 private DataTypesService:DataTypesService,
100                 private $uibModalInstance:ng.ui.bootstrap.IModalServiceInstance,
101                 private property:PropertyModel,
102                 private ValidationPattern:RegExp,
103                 private PropertyNameValidationPattern:RegExp,
104                 private CommentValidationPattern:RegExp,
105                 private ValidationUtils:ValidationUtils,
106                 private component:Component,
107                 private $filter:ng.IFilterService,
108                 private ModalsHandler:ModalsHandler,
109                 private filteredProperties:Array<PropertyModel>,
110                 private $timeout:ng.ITimeoutService,
111                 private isPropertyValueOwner:boolean,
112                 private propertyOwnerType:string,
113                 private propertyOwnerId:string,
114                 private ComponentInstanceServiceNg2: ComponentInstanceServiceNg2) {
115
116         this.formState = angular.isDefined(property.name) ? FormState.UPDATE : FormState.CREATE;
117         this.initScope();
118     }
119
120     private initResource = ():void => {
121         this.$scope.editPropertyModel.property = new PropertyModel(this.property);
122         this.$scope.editPropertyModel.property.type = this.property.type ? this.property.type : null;
123         this.$scope.editPropertyModel.property.value = this.$scope.editPropertyModel.property.value || this.$scope.editPropertyModel.property.defaultValue;
124         this.setMaxLength();
125         this.initAddOnLabels();
126     };
127
128     //init property add-ons labels that show up at the left side of the input.
129     private initAddOnLabels = () => {
130         if (this.$scope.editPropertyModel.property.name == 'network_role' && this.$scope.isService) {
131             //the server sends back the normalized name. Remove it (to prevent interference with validation) and set the addon label to the component name directly.
132             //Note: this cant be done in properties.ts because we dont have access to the component
133             if (this.$scope.editPropertyModel.property.value) {
134                 let splitProp = this.$scope.editPropertyModel.property.value.split(new RegExp(this.component.normalizedName + '.', "gi"));
135                 this.$scope.editPropertyModel.property.value = splitProp.pop();
136             }
137             this.$scope.editPropertyModel.property.addOn = this.component.name;
138         }
139     }
140
141     private initEditPropertyModel = ():void => {
142         this.$scope.editPropertyModel = {
143             property: null,
144             types: PROPERTY_DATA.TYPES,
145             simpleTypes: PROPERTY_DATA.SIMPLE_TYPES
146         };
147
148         this.initResource();
149     };
150
151     private initForNotSimpleType = ():void => {
152         let property = this.$scope.editPropertyModel.property;
153         this.$scope.isTypeDataType = this.DataTypesService.isDataTypeForPropertyType(this.$scope.editPropertyModel.property);
154         if (property.type && this.$scope.editPropertyModel.simpleTypes.indexOf(property.type) == -1) {
155             if (!(property.value || property.defaultValue)) {
156                 switch (property.type) {
157                     case PROPERTY_TYPES.MAP:
158                         this.$scope.myValue = {'': null};
159                         break;
160                     case PROPERTY_TYPES.LIST:
161                         this.$scope.myValue = [];
162                         break;
163                     default:
164                         this.$scope.myValue = {};
165                 }
166             } else {
167                 this.$scope.myValue = JSON.parse(property.value || property.defaultValue);
168             }
169         }
170     };
171
172     private setMaxLength = ():void => {
173         switch (this.$scope.editPropertyModel.property.type) {
174             case PROPERTY_TYPES.MAP:
175             case PROPERTY_TYPES.LIST:
176                 this.$scope.maxLength = this.$scope.editPropertyModel.property.schema.property.type == PROPERTY_TYPES.JSON ?
177                     PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH :
178                     PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
179                 break;
180             case PROPERTY_TYPES.JSON:
181                 this.$scope.maxLength = PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH;
182                 break;
183             default:
184                 this.$scope.maxLength =PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
185         }
186     };
187
188
189     private initScope = ():void => {
190
191         //scope properties
192         this.$scope.forms = {};
193         this.$scope.validationPattern = this.ValidationPattern;
194         this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
195         this.$scope.commentValidationPattern = this.CommentValidationPattern;
196         this.$scope.isLoading = false;
197         this.$scope.isNew = (this.formState === FormState.CREATE);
198         this.$scope.isService = this.component.isService();
199         this.$scope.modalInstanceProperty = this.$uibModalInstance;
200         this.$scope.currentPropertyIndex = _.findIndex(this.filteredProperties, i=> i.name == this.property.name);
201         this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1);
202         this.$scope.dataTypes = this.DataTypesService.getAllDataTypes();
203         this.$scope.isPropertyValueOwner = this.isPropertyValueOwner;
204         this.$scope.propertyOwnerType = this.propertyOwnerType;
205         this.initEditPropertyModel();
206
207         //check if property of VnfConfiguration
208         this.$scope.isVnfConfiguration = false;
209         if(this.propertyOwnerType == "component" && angular.isArray(this.component.componentInstances)) {
210
211             var componentPropertyOwner:ComponentInstance = this.component.componentInstances.find((ci:ComponentInstance) => {
212                 return ci.uniqueId === this.property.resourceInstanceUniqueId;
213             });
214             if (componentPropertyOwner && componentPropertyOwner.componentName === 'vnfConfiguration') {
215                 this.$scope.isVnfConfiguration = true;
216             }
217         }
218
219         this.$scope.nonPrimitiveTypes = _.filter(Object.keys(this.$scope.dataTypes), (type:string)=> {
220             return this.$scope.editPropertyModel.types.indexOf(type) == -1;
221         });
222         this.initForNotSimpleType();
223
224
225         this.$scope.validateJson = (json:string):boolean => {
226             if (!json) {
227                 return true;
228             }
229             return this.ValidationUtils.validateJson(json);
230         };
231
232
233         //scope methods
234         this.$scope.save = (doNotCloseModal?:boolean):void => {
235             let property:PropertyModel = this.$scope.editPropertyModel.property;
236             this.$scope.editPropertyModel.property.description = this.ValidationUtils.stripAndSanitize(this.$scope.editPropertyModel.property.description);
237             //if read only - or no changes made - just closes the modal
238             //need to check for property.value changes manually to detect if map properties deleted
239             if ((this.$scope.editPropertyModel.property.readonly && !this.$scope.isPropertyValueOwner)
240                 || (!this.$scope.forms.editForm.$dirty && angular.equals(JSON.stringify(this.$scope.myValue), this.$scope.editPropertyModel.property.value))) {
241                 this.$uibModalInstance.close();
242                 return;
243             }
244
245             this.$scope.isLoading = true;
246
247             let onPropertyFaild = (response):void => {
248                 console.info('onFaild', response);
249                 this.$scope.isLoading = false;
250             };
251
252             let onPropertySuccess = (propertyFromBE:PropertyModel):void => {
253                 console.info('onPropertyResourceSuccess : ', propertyFromBE);
254                 this.$scope.isLoading = false;
255                 this.filteredProperties[this.$scope.currentPropertyIndex] = propertyFromBE;
256                 if (!doNotCloseModal) {
257                     this.$uibModalInstance.close(propertyFromBE);
258                 } else {
259                     this.$scope.forms.editForm.$setPristine();
260                     this.$scope.editPropertyModel.property = new PropertyModel();
261                 }
262             };
263
264             //Not clean, but doing this as a temporary fix until we update the property right panel modals
265             if(this.propertyOwnerType == "group"){
266                 this.ComponentInstanceServiceNg2.updateComponentGroupInstanceProperties(this.component, this.propertyOwnerId, [property])
267                     .subscribe((propertiesFromBE) => { onPropertySuccess(<PropertyModel>propertiesFromBE[0])}, error => onPropertyFaild);
268             } else if(this.propertyOwnerType == "policy"){
269                 this.ComponentInstanceServiceNg2.updateComponentPolicyInstanceProperties(this.component, this.propertyOwnerId, [property])
270                     .subscribe((propertiesFromBE) => { onPropertySuccess(<PropertyModel>propertiesFromBE[0])}, error => onPropertyFaild);
271             } else {
272                 //in case we have uniqueId we call update method
273                 if (this.$scope.isPropertyValueOwner) {
274                     if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) {
275                         let myValueString:string = JSON.stringify(this.$scope.myValue);
276                         property.value = myValueString;
277                     }
278                     this.component.updateInstanceProperties(property.resourceInstanceUniqueId, [property]).then((propertiesFromBE) => onPropertySuccess(propertiesFromBE[0]), onPropertyFaild);
279                 } else {
280                     if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) {
281                         let myValueString:string = JSON.stringify(this.$scope.myValue);
282                         property.defaultValue = myValueString;
283                     } else {
284                         this.$scope.editPropertyModel.property.defaultValue = this.$scope.editPropertyModel.property.value;
285                     }
286                     this.component.addOrUpdateProperty(property).then(onPropertySuccess, onPropertyFaild);
287                 }
288             }
289         };
290
291         this.$scope.getPrev = ():void=> {
292             this.property = this.filteredProperties[--this.$scope.currentPropertyIndex];
293             this.initResource();
294             this.initForNotSimpleType();
295             this.$scope.isLastProperty = false;
296         };
297
298         this.$scope.getNext = ():void=> {
299             this.property = this.filteredProperties[++this.$scope.currentPropertyIndex];
300             this.initResource();
301             this.initForNotSimpleType();
302             this.$scope.isLastProperty = this.$scope.currentPropertyIndex == (this.filteredProperties.length - 1);
303         };
304
305         this.$scope.isSimpleType = (typeName:string):boolean=> {
306             return typeName && this.$scope.editPropertyModel.simpleTypes.indexOf(typeName) != -1;
307         };
308
309         this.$scope.showSchema = ():boolean => {
310             return [PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP].indexOf(this.$scope.editPropertyModel.property.type) > -1;
311         };
312
313         this.$scope.getValidationPattern = (type:string):RegExp => {
314             return this.ValidationUtils.getValidationPattern(type);
315         };
316
317         this.$scope.validateIntRange = (value:string):boolean => {
318             return !value || this.ValidationUtils.validateIntRange(value);
319         };
320
321         this.$scope.close = ():void => {
322             this.$uibModalInstance.close();
323         };
324
325         // put default value when instance value is empty
326         this.$scope.onValueChange = ():void => {
327             if (!this.$scope.editPropertyModel.property.value) {
328                 if (this.$scope.isPropertyValueOwner) {
329                     this.$scope.editPropertyModel.property.value = this.$scope.editPropertyModel.property.defaultValue;
330                 }
331             }
332         };
333
334         // Add the done button at the footer.
335         this.$scope.footerButtons = [
336             {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save},
337             {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
338         ];
339
340         this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => {
341             this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
342         });
343
344         this.$scope.getDefaultValue = ():any => {
345             return this.$scope.isPropertyValueOwner ? this.$scope.editPropertyModel.property.defaultValue : null;
346         };
347
348         this.$scope.onTypeChange = ():void => {
349             this.$scope.editPropertyModel.property.value = '';
350             this.$scope.editPropertyModel.property.defaultValue = '';
351             this.setMaxLength();
352             this.initForNotSimpleType();
353         };
354
355         this.$scope.onSchemaTypeChange = ():void => {
356             if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.MAP) {
357                 this.$scope.myValue = {'': null};
358             } else if (this.$scope.editPropertyModel.property.type == PROPERTY_TYPES.LIST) {
359                 this.$scope.myValue = [];
360             }
361             this.setMaxLength();
362         };
363
364         this.$scope.delete = (property:PropertyModel):void => {
365             let onOk = ():void => {
366                 this.component.deleteProperty(property.uniqueId).then(
367                     this.$scope.close
368                 );
369             };
370             let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE");
371             let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}");
372             this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
373         };
374     }
375 }