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