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