606013019181a84f6c064f98b06e7be58d1656b2
[sdc.git] / catalog-ui / src / app / view-models / workspace / tabs / general / general-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 {ModalsHandler, ValidationUtils, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, DEFAULT_ICON,
23     ResourceType, ComponentState} from "app/utils";
24 import {CacheService, EventListenerService, ProgressService, OnboardingService} from "app/services";
25 import {IAppConfigurtaion, IValidate, IMainCategory, Resource, ISubCategory,Service, ICsarComponent} from "app/models";
26 import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
27 import {Dictionary} from "lodash";
28
29 export class Validation {
30     componentNameValidationPattern:RegExp;
31     contactIdValidationPattern:RegExp;
32     tagValidationPattern:RegExp;
33     VendorReleaseValidationPattern:RegExp;
34     VendorNameValidationPattern:RegExp;
35     VendorModelNumberValidationPattern:RegExp;
36     commentValidationPattern:RegExp;
37     projectCodeValidationPattern:RegExp;
38 }
39
40 export class componentCategories {//categories field bind to this obj in order to solve this bug: DE242059
41     selectedCategory:string;
42 }
43
44 export interface IEnvironmentContext {
45     defaultValue:string;
46     validValues:Array<string>;
47 }
48
49 export interface IGeneralScope extends IWorkspaceViewModelScope {
50     validation:Validation;
51     editForm:ng.IFormController;
52     categories:Array<IMainCategory>;
53     environmentContextObj:IEnvironmentContext;
54     latestCategoryId:string;
55     latestVendorName:string;
56     importedFileExtension:any;
57     isCreate:boolean;
58     isShowFileBrowse:boolean;
59     isShowOnboardingSelectionBrowse:boolean;
60     importedToscaBrowseFileText:string;
61     importCsarProgressKey:string;
62     browseFileLabel:string;
63     componentCategories:componentCategories;
64
65     onToscaFileChange():void;
66     validateField(field:any):boolean;
67     validateName(isInit:boolean):void;
68     calculateUnique(mainCategory:string, subCategory:string):string; // Build unique string from main and sub category
69     onVendorNameChange(oldVendorName:string):void;
70     convertCategoryStringToOneArray(category:string, subcategory:string):Array<IMainCategory>;
71     onCategoryChange():void;
72     onEcompGeneratedNamingChange():void;
73     openOnBoardingModal():void;
74     initCategoreis():void;
75     initEnvironmentContext():void;
76     updateIcon():void;
77     possibleToUpdateIcon():boolean;
78 }
79
80 export class GeneralViewModel {
81
82     static '$inject' = [
83         '$scope',
84         'Sdc.Services.CacheService',
85         'ComponentNameValidationPattern',
86         'ContactIdValidationPattern',
87         'TagValidationPattern',
88         'VendorReleaseValidationPattern',
89         'VendorNameValidationPattern',
90         'VendorModelNumberValidationPattern',
91         'CommentValidationPattern',
92         'ValidationUtils',
93         'sdcConfig',
94         'ProjectCodeValidationPattern',
95         '$state',
96         'ModalsHandler',
97         'EventListenerService',
98         'Notification',
99         'Sdc.Services.ProgressService',
100         '$interval',
101         '$filter',
102         '$timeout',
103         'Sdc.Services.OnboardingService'
104     ];
105
106     constructor(private $scope:IGeneralScope,
107                 private cacheService:CacheService,
108                 private ComponentNameValidationPattern:RegExp,
109                 private ContactIdValidationPattern:RegExp,
110                 private TagValidationPattern:RegExp,
111                 private VendorReleaseValidationPattern:RegExp,
112                 private VendorNameValidationPattern:RegExp,
113                 private VendorModelNumberValidationPattern:RegExp,
114                 private CommentValidationPattern:RegExp,
115                 private ValidationUtils:ValidationUtils,
116                 private sdcConfig:IAppConfigurtaion,
117                 private ProjectCodeValidationPattern:RegExp,
118                 private $state:ng.ui.IStateService,
119                 private ModalsHandler:ModalsHandler,
120                 private EventListenerService:EventListenerService,
121                 private Notification:any,
122                 private progressService:ProgressService,
123                 protected $interval:any,
124                 private $filter:ng.IFilterService,
125                 private $timeout:ng.ITimeoutService,
126                 private onBoardingService:OnboardingService) {
127
128         this.initScopeValidation();
129         this.initScopeMethods();
130         this.initScope();
131     }
132
133
134
135
136     private initScopeValidation = ():void => {
137         this.$scope.validation = new Validation();
138         this.$scope.validation.componentNameValidationPattern = this.ComponentNameValidationPattern;
139         this.$scope.validation.contactIdValidationPattern = this.ContactIdValidationPattern;
140         this.$scope.validation.tagValidationPattern = this.TagValidationPattern;
141         this.$scope.validation.VendorReleaseValidationPattern = this.VendorReleaseValidationPattern;
142         this.$scope.validation.VendorNameValidationPattern = this.VendorNameValidationPattern;
143         this.$scope.validation.VendorModelNumberValidationPattern = this.VendorModelNumberValidationPattern;
144         this.$scope.validation.commentValidationPattern = this.CommentValidationPattern;
145         this.$scope.validation.projectCodeValidationPattern = this.ProjectCodeValidationPattern;
146     };
147
148     private initImportedToscaBrowseFile = ():void =>{
149         // Init the decision if to show onboarding
150         this.$scope.isShowOnboardingSelectionBrowse = false;
151         if (this.$scope.component.isResource() &&
152             this.$scope.isEditMode() &&
153             (<Resource>this.$scope.component).resourceType == ResourceType.VF &&
154             (<Resource>this.$scope.component).csarUUID) {
155             this.$scope.isShowOnboardingSelectionBrowse = true;
156             let onboardCsarFilesMap:Dictionary<Dictionary<string>> = this.cacheService.get('onboardCsarFilesMap');
157             // The onboardCsarFilesMap in cache contains map of [packageId]:[vsp display name for brows]
158             // if the map is empty - Do request to BE
159             if(onboardCsarFilesMap) {
160                 if (onboardCsarFilesMap[(<Resource>this.$scope.component).csarUUID]){
161                     this.$scope.importedToscaBrowseFileText = onboardCsarFilesMap[(<Resource>this.$scope.component).csarUUID][(<Resource>this.$scope.component).csarVersion];
162                 }
163             }
164             if(!onboardCsarFilesMap || !this.$scope.importedToscaBrowseFileText){
165
166                 let onSuccess = (vsps:Array<ICsarComponent>): void =>{
167                     onboardCsarFilesMap = {};
168                     _.each(vsps, (vsp:ICsarComponent)=>{
169                         onboardCsarFilesMap[vsp.packageId] = onboardCsarFilesMap[vsp.packageId] || {};
170                         onboardCsarFilesMap[vsp.packageId][vsp.version] = vsp.vspName + " (" + vsp.version + ")";
171                     });
172                     this.cacheService.set('onboardCsarFilesMap', onboardCsarFilesMap);
173                     this.$scope.importedToscaBrowseFileText = onboardCsarFilesMap[(<Resource>this.$scope.component).csarUUID][(<Resource>this.$scope.component).csarVersion];
174                 };
175
176                 let onError = (): void =>{
177                     console.log("Error getting onboarding list");
178                 };
179
180                 this.onBoardingService.getOnboardingVSPs().then(onSuccess, onError);
181             }
182         }
183     };
184
185     private initScope = ():void => {
186
187         // Work around to change the csar version
188         if (this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG)) {
189             (<Resource>this.$scope.component).csarVersion = this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG);
190         }
191
192         this.$scope.importCsarProgressKey = "importCsarProgressKey";
193         this.$scope.browseFileLabel = this.$scope.component.isResource() && (<Resource>this.$scope.component).resourceType === ResourceType.VF ? "Upload file" : "Upload VFC";
194         this.$scope.progressService = this.progressService;
195         this.$scope.componentCategories = new componentCategories();
196         this.$scope.componentCategories.selectedCategory = this.$scope.component.selectedCategory;
197
198         // Init UIModel
199         this.$scope.component.tags = _.without(this.$scope.component.tags, this.$scope.component.name);
200
201         // Init categories
202         this.$scope.initCategoreis();
203
204         // Init Environment Context
205         this.$scope.initEnvironmentContext();
206
207         // Init the decision if to show file browse.
208         this.$scope.isShowFileBrowse = false;
209         if (this.$scope.component.isResource()) {
210             let resource:Resource = <Resource>this.$scope.component;
211             console.log(resource.name + ": " + resource.csarUUID);
212             if (resource.importedFile) { // Component has imported file.
213                 this.$scope.isShowFileBrowse = true;
214             }
215             if (this.$scope.isEditMode() && resource.resourceType == ResourceType.VF && !resource.csarUUID) {
216                 this.$scope.isShowFileBrowse = true;
217             }
218         }
219
220         this.initImportedToscaBrowseFile();
221
222         //init file extensions based on the file that was imported.
223         if (this.$scope.component.isResource() && (<Resource>this.$scope.component).importedFile) {
224             let fileName:string = (<Resource>this.$scope.component).importedFile.filename;
225             let fileExtension:string = fileName.split(".").pop();
226             if (this.sdcConfig.csarFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) {
227                 this.$scope.importedFileExtension = this.sdcConfig.csarFileExtension;
228                 (<Resource>this.$scope.component).importedFile.filetype = "csar";
229             } else if (this.sdcConfig.toscaFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) {
230                 (<Resource>this.$scope.component).importedFile.filetype = "yaml";
231                 this.$scope.importedFileExtension = this.sdcConfig.toscaFileExtension;
232             }
233         } else if (this.$scope.isEditMode() && (<Resource>this.$scope.component).resourceType === ResourceType.VF) {
234             this.$scope.importedFileExtension = this.sdcConfig.csarFileExtension;
235             //(<Resource>this.$scope.component).importedFile.filetype="csar";
236         }
237
238         this.$scope.setValidState(true);
239
240         this.$scope.calculateUnique = (mainCategory:string, subCategory:string):string => {
241             let uniqueId:string = mainCategory;
242             if (subCategory) {
243                 uniqueId += "_#_" + subCategory; // Set the select category combobox to show the selected category.
244             }
245             return uniqueId;
246         };
247
248         //TODO remove this after handling contact in UI
249         if (this.$scope.isCreateMode()) {
250             this.$scope.component.contactId = this.cacheService.get("user").userId;
251             this.$scope.originComponent.contactId = this.$scope.component.contactId;
252         }
253
254     };
255
256     // Convert category string MainCategory_#_SubCategory to Array with one item (like the server except)
257     private convertCategoryStringToOneArray = ():Array<IMainCategory> => {
258         let tmp = this.$scope.component.selectedCategory.split("_#_");
259         let mainCategory = tmp[0];
260         let subCategory = tmp[1];
261
262         // Find the selected category and add the relevant sub category.
263         let selectedMainCategory:IMainCategory = <IMainCategory>_.find(this.$scope.categories, function (item) {
264             return item["name"] === mainCategory;
265
266         });
267
268         let mainCategoryClone = angular.copy(selectedMainCategory);
269         if (subCategory) {
270             let selectedSubcategory = <ISubCategory>_.find(selectedMainCategory.subcategories, function (item) {
271                 return item["name"] === subCategory;
272             });
273             mainCategoryClone['subcategories'] = [angular.copy(selectedSubcategory)];
274         }
275         let tmpSelected = <IMainCategory> mainCategoryClone;
276
277         let result:Array<IMainCategory> = [];
278         result.push(tmpSelected);
279
280         return result;
281     };
282
283     private updateComponentNameInBreadcrumbs = ():void => {
284         //update breadcrum after changing name
285         this.$scope.breadcrumbsModel[1].updateSelectedMenuItemText(this.$scope.component.getComponentSubType() + ': ' + this.$scope.component.name);
286         this.$scope.updateMenuComponentName(this.$scope.component.name);
287     };
288
289     private initScopeMethods = ():void => {
290
291         this.$scope.initCategoreis = ():void => {
292             if (this.$scope.componentType === ComponentType.RESOURCE) {
293                 this.$scope.categories = this.cacheService.get('resourceCategories');
294
295             }
296             if (this.$scope.componentType === ComponentType.SERVICE) {
297                 this.$scope.categories = this.cacheService.get('serviceCategories');
298             }
299         };
300
301
302         this.$scope.initEnvironmentContext = ():void => {
303             if (this.$scope.componentType === ComponentType.SERVICE) {
304                 this.$scope.environmentContextObj = this.cacheService.get('UIConfiguration').environmentContext;
305                 var environmentContext:string =(<Service>this.$scope.component).environmentContext;
306                 var isCheckout:boolean = ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState;
307                 // In creation new service OR check outing old service without environmentContext parameter - set default value
308                 if(this.$scope.isCreateMode() || (isCheckout && !environmentContext)){
309                     (<Service>this.$scope.component).environmentContext = this.$scope.environmentContextObj.defaultValue;
310                 }
311             }
312         };
313
314         this.$scope.validateField = (field:any):boolean => {
315             if (field && field.$dirty && field.$invalid) {
316                 return true;
317             }
318             return false;
319         };
320
321         this.$scope.openOnBoardingModal = ():void => {
322             let csarUUID = (<Resource>this.$scope.component).csarUUID;
323             this.ModalsHandler.openOnboadrdingModal('Update', csarUUID).then(()=> {
324                 // OK
325                 this.$scope.uploadFileChangedInGeneralTab();
326             }, ()=> {
327                 // ERROR
328             });
329         };
330
331         this.$scope.updateIcon = ():void => {
332             this.ModalsHandler.openUpdateIconModal(this.$scope.component).then((isDirty:boolean)=> {
333                 if(!this.$scope.isCreateMode()){
334                     this.$state.current.data.unsavedChanges = this.$state.current.data.unsavedChanges || isDirty;
335                 }
336             }, ()=> {
337                 // ERROR
338             });
339         };
340
341         this.$scope.possibleToUpdateIcon = ():boolean => {
342             if(this.$scope.componentCategories.selectedCategory && (!this.$scope.component.isResource() || this.$scope.component.vendorName)){
343                 return true;
344             }else{
345                 return false;
346             }
347         }
348
349         this.$scope.validateName = (isInit:boolean):void => {
350             if (isInit === undefined) {
351                 isInit = false;
352             }
353
354             let name = this.$scope.component.name;
355             if (!name || name === "") {
356                 if (this.$scope.editForm
357                     && this.$scope.editForm["componentName"]
358                     && this.$scope.editForm["componentName"].$error) {
359
360                     // Clear the error name already exists
361                     this.$scope.editForm["componentName"].$setValidity('nameExist', true);
362                 }
363
364                 return;
365             }
366             //?????????????????????????
367             let subtype:string = ComponentType.RESOURCE == this.$scope.componentType ? this.$scope.component.getComponentSubType() : undefined;
368
369             let onFailed = (response) => {
370                 //console.info('onFaild', response);
371                 //this.$scope.isLoading = false;
372             };
373
374             let onSuccess = (validation:IValidate) => {
375                 this.$scope.editForm["componentName"].$setValidity('nameExist', validation.isValid);
376                 if (validation.isValid) {
377                     //update breadcrumb after changing name
378                     this.updateComponentNameInBreadcrumbs();
379                 }
380             };
381
382             if (isInit) {
383                 // When page is init after update
384                 if (this.$scope.component.name !== this.$scope.originComponent.name) {
385                     if (!(this.$scope.componentType === ComponentType.RESOURCE && (<Resource>this.$scope.component).csarUUID !== undefined)
386                     ) {
387                         this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed);
388                     }
389                 }
390             } else {
391                 // Validating on change (has debounce)
392                 if (this.$scope.editForm
393                     && this.$scope.editForm["componentName"]
394                     && this.$scope.editForm["componentName"].$error
395                     && !this.$scope.editForm["componentName"].$error.pattern
396                     && (!this.$scope.originComponent.name || this.$scope.component.name.toUpperCase() !== this.$scope.originComponent.name.toUpperCase())
397                 ) {
398                     if (!(this.$scope.componentType === ComponentType.RESOURCE && (<Resource>this.$scope.component).csarUUID !== undefined)
399                     ) {
400                         this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed);
401                     }
402                 } else if (this.$scope.originComponent.name && this.$scope.component.name.toUpperCase() === this.$scope.originComponent.name.toUpperCase()) {
403                     // Clear the error
404                     this.$scope.editForm["componentName"].$setValidity('nameExist', true);
405                 }
406             }
407         };
408
409         this.$scope.$watchCollection('component.name', (newData:any):void => {
410             this.$scope.validateName(false);
411         });
412
413         // Notify the parent if this step valid or not.
414         this.$scope.$watch("editForm.$valid", (newVal, oldVal) => {
415             this.$scope.setValidState(newVal);
416         });
417
418         this.$scope.$watch("editForm.$dirty", (newVal, oldVal) => {
419             if (newVal !== oldVal) {
420                 this.$state.current.data.unsavedChanges = newVal && !this.$scope.isCreateMode();
421             }
422         });
423
424         this.$scope.onCategoryChange = ():void => {
425             this.$scope.component.selectedCategory = this.$scope.componentCategories.selectedCategory;
426             this.$scope.component.categories = this.convertCategoryStringToOneArray();
427             this.$scope.component.icon = DEFAULT_ICON;
428         };
429
430         this.$scope.onEcompGeneratedNamingChange = ():void =>{
431             if(!(<Service>this.$scope.component).ecompGeneratedNaming){
432                 (<Service>this.$scope.component).namingPolicy = '';
433             }
434         };
435
436         this.$scope.onVendorNameChange = (oldVendorName:string):void => {
437             if (this.$scope.component.icon === oldVendorName) {
438                 this.$scope.component.icon = DEFAULT_ICON;
439             }
440         };
441         this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.$scope.reload);
442         this.EventListenerService.registerObserverCallback(EVENTS.ON_REVERT, ()=>{
443             this.$scope.componentCategories.selectedCategory = this.$scope.originComponent.selectedCategory;
444         });
445     };
446 }