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