Fix error adding category through UI
[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 * as _ from "lodash";
23 import {Dictionary} from "lodash";
24 import {
25     ComponentFactory,
26     ComponentState,
27     ComponentType,
28     DEFAULT_ICON,
29     EVENTS,
30     instantiationType,
31     ModalsHandler,
32     ResourceType,
33     ValidationUtils,
34     FileUtils,
35     ServiceCsarReader
36 } from "app/utils";
37 import {EventListenerService, ProgressService} from "app/services";
38 import {CacheService, ElementService, ModelService, ImportVSPService, OnboardingService} from "app/services-ng2";
39 import {Component, IAppConfigurtaion, ICsarComponent, IMainCategory, IMetadataKey, ISubCategory, IValidate, Resource, Service} from "app/models";
40 import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
41 import {CATEGORY_SERVICE_METADATA_KEYS, PREVIOUS_CSAR_COMPONENT, DEFAULT_MODEL_NAME} from "../../../../utils/constants";
42 import {Observable} from "rxjs";
43 import {Model} from "../../../../models/model";
44
45 export class Validation {
46     componentNameValidationPattern:RegExp;
47     contactIdValidationPattern:RegExp;
48     tagValidationPattern:RegExp;
49     VendorReleaseValidationPattern:RegExp;
50     VendorNameValidationPattern:RegExp;
51     VendorModelNumberValidationPattern:RegExp;
52     commentValidationPattern:RegExp;
53 }
54
55 export class componentCategories {//categories field bind to this obj in order to solve this bug: DE242059
56     selectedCategory:string;
57 }
58
59 export class componentModel {
60     selectedModel:string;
61 }
62
63 export interface IEnvironmentContext {
64     defaultValue:string;
65     validValues:Array<string>;
66 }
67
68 export interface IGeneralScope extends IWorkspaceViewModelScope {
69     validation:Validation;
70     editForm:ng.IFormController;
71     categories:Array<IMainCategory>;
72     environmentContextObj:IEnvironmentContext;
73     latestCategoryId:string;
74     latestVendorName:string;
75     importedFileExtension:any;
76     isCreate:boolean;
77     isShowFileBrowse:boolean;
78     isShowOnboardingSelectionBrowse:boolean;
79     importedToscaBrowseFileText:string;
80     importCsarProProgressKey:string;
81     browseFileLabel:string;
82     componentCategories:componentCategories;
83     componentModel:componentModel;
84     instantiationTypes:Array<instantiationType>;
85     isHiddenCategorySelected: boolean;
86     isModelRequired: boolean;
87
88     save():Promise<any>;
89     revert():void;
90     onImportFileChange():void;
91     validateField(field:any):boolean;
92     validateName(isInit:boolean):void;
93     calculateUnique(mainCategory:string, subCategory:string):string; // Build unique string from main and sub category
94     onVendorNameChange(oldVendorName:string):void;
95     convertCategoryStringToOneArray(category:string, subcategory:string):Array<IMainCategory>;
96     onCategoryChange():void;
97     onEcompGeneratedNamingChange():void;
98     onModelChange():void;
99     onBaseTypeChange():void;
100     openOnBoardingModal():void;
101     initCategories():void;
102     initEnvironmentContext():void;
103     initInstantiationTypes():void;
104     initBaseTypes():void;
105     onInstantiationTypeChange():void;
106     updateIcon():void;
107     possibleToUpdateIcon():boolean;
108     initModel():void;
109     isVspImport(): boolean;
110 }
111
112 // tslint:disable-next-line:max-classes-per-file
113 export class GeneralViewModel {
114
115     static '$inject' = [
116         '$scope',
117         'Sdc.Services.CacheService',
118         'ComponentNameValidationPattern',
119         'ContactIdValidationPattern',
120         'TagValidationPattern',
121         'VendorReleaseValidationPattern',
122         'VendorNameValidationPattern',
123         'VendorModelNumberValidationPattern',
124         'CommentValidationPattern',
125         'ValidationUtils',
126         'FileUtils',
127         'sdcConfig',
128         '$state',
129         'ModalsHandler',
130         'EventListenerService',
131         'Notification',
132         'Sdc.Services.ProgressService',
133         '$interval',
134         '$filter',
135         '$timeout',
136         'OnboardingService',
137         'ComponentFactory',
138         'ImportVSPService',
139         'ElementService',
140         'ModelService',
141         '$stateParams'
142     ];
143
144     constructor(private $scope:IGeneralScope,
145                 private cacheService:CacheService,
146                 private ComponentNameValidationPattern:RegExp,
147                 private ContactIdValidationPattern:RegExp,
148                 private TagValidationPattern:RegExp,
149                 private VendorReleaseValidationPattern:RegExp,
150                 private VendorNameValidationPattern:RegExp,
151                 private VendorModelNumberValidationPattern:RegExp,
152                 private CommentValidationPattern:RegExp,
153                 private ValidationUtils:ValidationUtils,
154                 private FileUtils: FileUtils,
155                 private sdcConfig:IAppConfigurtaion,
156                 private $state:ng.ui.IStateService,
157                 private ModalsHandler:ModalsHandler,
158                 private EventListenerService:EventListenerService,
159                 private Notification:any,
160                 private progressService:ProgressService,
161                 protected $interval:any,
162                 private $filter:ng.IFilterService,
163                 private $timeout:ng.ITimeoutService,
164                 private onBoardingService: OnboardingService,
165                 private ComponentFactory:ComponentFactory,
166                 private importVSPService: ImportVSPService,
167                 private elementService: ElementService,
168                 private modelService: ModelService,
169                 private $stateParams: any) {
170
171         this.initScopeValidation();
172         this.initScopeMethods();
173         this.initScope();
174     }
175
176
177
178
179     private initScopeValidation = ():void => {
180         this.$scope.validation = new Validation();
181         this.$scope.validation.componentNameValidationPattern = this.ComponentNameValidationPattern;
182         this.$scope.validation.contactIdValidationPattern = this.ContactIdValidationPattern;
183         this.$scope.validation.tagValidationPattern = this.TagValidationPattern;
184         this.$scope.validation.VendorReleaseValidationPattern = this.VendorReleaseValidationPattern;
185         this.$scope.validation.VendorNameValidationPattern = this.VendorNameValidationPattern;
186         this.$scope.validation.VendorModelNumberValidationPattern = this.VendorModelNumberValidationPattern;
187         this.$scope.validation.commentValidationPattern = this.CommentValidationPattern;
188     };
189
190     private loadOnboardingFileCache = (): Observable<Dictionary<Dictionary<string>>> => {
191         let onboardCsarFilesMap:Dictionary<Dictionary<string>>;
192         let onSuccess = (vsps:Array<ICsarComponent>) => {
193             onboardCsarFilesMap = {};
194             _.each(vsps, (vsp:ICsarComponent)=>{
195                 onboardCsarFilesMap[vsp.packageId] = onboardCsarFilesMap[vsp.packageId] || {};
196                 onboardCsarFilesMap[vsp.packageId][vsp.version] = vsp.vspName + " (" + vsp.version + ")";
197             });
198             this.cacheService.set('onboardCsarFilesMap', onboardCsarFilesMap);
199             return onboardCsarFilesMap;
200         };
201         let onError = (): void =>{
202             console.log("Error getting onboarding list");
203         };
204         return this.onBoardingService.getOnboardingVSPs().map(onSuccess, onError);
205     };
206
207     private setImportedFileText = ():void => {
208
209         if(!this.$scope.isShowOnboardingSelectionBrowse) return;
210
211         //these variables makes it easier to read this logic
212         let csarUUID:string = (<Resource>this.$scope.component).csarUUID;
213         let csarVersion:string = (<Resource>this.$scope.component).csarVersion;
214
215         let onboardCsarFilesMap:Dictionary<Dictionary<string>> = this.cacheService.get('onboardCsarFilesMap');
216         let assignFileName = ():void => {
217             if(this.$scope.component.vspArchived){
218                 this.$scope.importedToscaBrowseFileText = 'VSP is archived';
219             } else {
220                 if(this.$stateParams.componentCsar && this.$scope.component.lifecycleState === 'NOT_CERTIFIED_CHECKIN' && !this.$scope.isCreateMode()) {
221                     this.$scope.importedToscaBrowseFileText = this.$scope.originComponent.name + ' (' + (this.$scope.originComponent as Resource).csarVersion + ')';
222                 } else {
223                     if (onboardCsarFilesMap && onboardCsarFilesMap[csarUUID]) {
224                         this.$scope.importedToscaBrowseFileText = onboardCsarFilesMap[csarUUID][csarVersion];
225                     }
226                 }
227             }
228         }
229
230
231         if(this.$scope.component.vspArchived || (onboardCsarFilesMap && onboardCsarFilesMap[csarUUID] && onboardCsarFilesMap[csarUUID][csarVersion])){ //check that the file name is already in cache
232             assignFileName();
233         } else {
234             this.loadOnboardingFileCache().subscribe((onboardingFiles) => {
235                 onboardCsarFilesMap = onboardingFiles;
236                 this.cacheService.set('onboardCsarFilesMap', onboardingFiles);
237                 assignFileName();
238             }, ()=> {});
239         }
240
241     }
242
243     isCreateModeAvailable(verifyObj:string): boolean {
244         var isCheckout:boolean = ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState;
245         return this.$scope.isCreateMode() || (isCheckout && !verifyObj)
246     }
247
248     private initScope = ():void => {
249
250         this.$scope.importCsarProgressKey = "importCsarProgressKey";
251
252         this.$scope.browseFileLabel = (this.$scope.component.isResource() && ((<Resource>this.$scope.component).resourceType === ResourceType.VF || (<Resource>this.$scope.component).resourceType === 'SRVC')) ||  this.$scope.component.isService() ? 'Upload File:' : 'Upload VFC:';
253         this.$scope.progressService = this.progressService;
254         this.$scope.componentCategories = new componentCategories();
255         this.$scope.componentCategories.selectedCategory = this.$scope.component.selectedCategory;
256
257         // Init UIModel
258         this.$scope.component.tags = _.without(this.$scope.component.tags, this.$scope.component.name);
259
260         // Init categories
261         this.$scope.initCategories();
262
263         // Init Environment Context
264         this.$scope.initEnvironmentContext();
265
266         // Init Models
267         this.$scope.initModel();
268
269         // Init the decision if to show file browse.
270         this.$scope.isShowFileBrowse = false;
271         if (this.$scope.component.isResource()) {
272             let resource:Resource = <Resource>this.$scope.component;
273             console.log(resource.name + ": " + resource.csarUUID);
274             if (resource.importedFile) { // Component has imported file.
275                 this.$scope.isShowFileBrowse = true;
276             }
277             if (resource.resourceType === ResourceType.VF && !resource.csarUUID) {
278                 this.$scope.isShowFileBrowse = true;
279             }
280         } else if(this.$scope.component.isService()){
281             let service: Service = <Service>this.$scope.component;
282             console.log(service.name + ": " + service.csarUUID);
283             if (service.importedFile) {
284                 this.$scope.isShowFileBrowse = true;
285                 (<Service>this.$scope.component).ecompGeneratedNaming = true;
286                 let blob = this.FileUtils.base64toBlob(service.importedFile.base64, "zip");
287                 new ServiceCsarReader().read(blob).then((serviceCsar) => {
288                     serviceCsar.serviceMetadata.contactId = this.cacheService.get("user").userId;
289                     (<Service>this.$scope.component).setComponentMetadata(serviceCsar.serviceMetadata);
290                     (<Service>this.$scope.component).model = serviceCsar.serviceMetadata.model;
291                     this.$scope.onModelChange();
292                     this.$scope.componentCategories.selectedCategory = serviceCsar.serviceMetadata.selectedCategory;
293                     this.$scope.onCategoryChange();
294                     serviceCsar.extraServiceMetadata.forEach((value: string, key: string) => {
295                         if(this.getMetadataKey(key)) {
296                             (<Service>this.$scope.component).categorySpecificMetadata[key] = value;
297                         }
298                     });
299                     (<Service>this.$scope.component).derivedFromGenericType = serviceCsar.substitutionNodeType;
300                     this.$scope.onBaseTypeChange();
301                 });
302             }
303             if (this.$scope.isEditMode() && service.serviceType == 'Service' && !service.csarUUID) {
304                 this.$scope.isShowFileBrowse = true;
305             }
306             // Init Instantiation types
307             this.$scope.initInstantiationTypes();
308             this.$scope.initBaseTypes();
309         }
310
311         if (this.cacheService.get(PREVIOUS_CSAR_COMPONENT)) { //keep the old component in the cache until checkout, so we dont need to pass it around
312             this.$scope.setOriginComponent(this.cacheService.get(PREVIOUS_CSAR_COMPONENT));
313             this.cacheService.remove(PREVIOUS_CSAR_COMPONENT);
314         }
315
316         if (this.$stateParams.componentCsar && !this.$scope.isCreateMode()) {
317             this.$scope.updateUnsavedFileFlag(true);
318             this.$scope.save();
319         }
320
321         if (this.$scope.component.isResource() &&
322             (this.$scope.component as Resource).resourceType === ResourceType.VF ||
323             (this.$scope.component as Resource).resourceType === ResourceType.PNF && (this.$scope.component as Resource).csarUUID) {
324             this.$scope.isShowOnboardingSelectionBrowse = true;
325             this.setImportedFileText();
326         } else {
327             this.$scope.isShowOnboardingSelectionBrowse = false;
328         }
329
330
331         //init file extensions based on the file that was imported.
332         if (this.$scope.component.isResource() && (<Resource>this.$scope.component).importedFile) {
333             let fileName:string = (<Resource>this.$scope.component).importedFile.filename;
334             let fileExtension:string = fileName.split(".").pop();
335             if (this.sdcConfig.csarFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) {
336                 this.$scope.importedFileExtension = this.sdcConfig.csarFileExtension;
337                 (<Resource>this.$scope.component).importedFile.filetype = "csar";
338             } else if (this.sdcConfig.toscaFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) {
339                 (<Resource>this.$scope.component).importedFile.filetype = "yaml";
340                 this.$scope.importedFileExtension = this.sdcConfig.toscaFileExtension;
341             }
342             this.$scope.restoreFile = angular.copy((<Resource>this.$scope.originComponent).importedFile); //create backup
343         } else if (this.$scope.isEditMode() && (<Resource>this.$scope.component).resourceType === ResourceType.VF) {
344             this.$scope.importedFileExtension = this.sdcConfig.csarFileExtension;
345             //(<Resource>this.$scope.component).importedFile.filetype="csar";
346         }
347
348
349
350         this.$scope.setValidState(true);
351
352         this.$scope.calculateUnique = (mainCategory:string, subCategory:string):string => {
353             let uniqueId:string = mainCategory;
354             if (subCategory) {
355                 uniqueId += "_#_" + subCategory; // Set the select category combobox to show the selected category.
356             }
357             return uniqueId;
358         };
359
360         //TODO remove this after handling contact in UI
361         if (this.$scope.isCreateMode()) {
362             this.$scope.component.contactId = this.cacheService.get("user").userId;
363             this.$scope.originComponent.contactId = this.$scope.component.contactId;
364         }
365
366
367         this.$scope.$on('$destroy', () => {
368             this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE_WITH_SAVE);
369             this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE);
370         });
371
372     };
373
374     // Convert category string MainCategory_#_SubCategory to Array with one item (like the server except)
375     private convertCategoryStringToOneArray = ():IMainCategory[] => {
376         let tmp = this.$scope.component.selectedCategory.split("_#_");
377         let mainCategory = tmp[0];
378         let subCategory = tmp[1];
379
380         // Find the selected category and add the relevant sub category.
381         let selectedMainCategory:IMainCategory = <IMainCategory>_.find(this.$scope.categories, function (item) {
382             return item["name"] === mainCategory;
383
384         });
385
386         let mainCategoryClone = angular.copy(selectedMainCategory);
387         if (subCategory) {
388             let selectedSubcategory = <ISubCategory>_.find(selectedMainCategory.subcategories, function (item) {
389                 return item["name"] === subCategory;
390             });
391             mainCategoryClone['subcategories'] = [angular.copy(selectedSubcategory)];
392         }
393         let tmpSelected = <IMainCategory> mainCategoryClone;
394
395         let result:IMainCategory[] = [];
396         result.push(tmpSelected);
397
398         return result;
399     };
400
401     private updateComponentNameInBreadcrumbs = ():void => {
402         // update breadcrum after changing name
403         this.$scope.breadcrumbsModel[1].updateSelectedMenuItemText(this.$scope.component.getComponentSubType() + ': ' + this.$scope.component.name);
404         this.$scope.updateMenuComponentName(this.$scope.component.name);
405     };
406
407     //Find if a category is applicable for External API or not
408     private isHiddenCategory = (category: string) => {
409         let items: Array<any> = new Array<any>();
410         items = this.$scope.sdcMenu.component_workspace_menu_option[this.$scope.component.getComponentSubType()];
411         for(let index = 0; index < items.length; ++index) {
412             if ((items[index].hiddenCategories && items[index].hiddenCategories.indexOf(category) > -1)) {
413                 return true;
414             }
415         }
416         return false;
417     };
418
419     private filteredCategories = () => {
420         let tempCategories: Array<IMainCategory> = new Array<IMainCategory>();
421         this.$scope.categories.forEach((category) => {
422             if (!this.isHiddenCategory(category.name)
423                 && this.$scope.isCreateMode()
424             ) {
425                 tempCategories.push(category);
426             } else if ((ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState)
427                 && !this.isHiddenCategory(this.$scope.component.selectedCategory)
428                 && !this.isHiddenCategory(category.name)
429             ) {
430                 tempCategories.push(category);
431             } else if ((ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState)
432                 && this.isHiddenCategory(this.$scope.component.selectedCategory)) {
433                 tempCategories.push(category);
434             }
435         });
436
437         return tempCategories;
438     };
439
440     private initScopeMethods = ():void => {
441
442         this.$scope.initCategories = ():void => {
443             if (this.$scope.componentType === ComponentType.RESOURCE) {
444                 this.$scope.categories = this.cacheService.get('resourceCategories');
445
446             }
447             if (this.$scope.componentType === ComponentType.SERVICE) {
448                 this.$scope.categories = this.cacheService.get('serviceCategories');
449
450                 //Remove categories from dropdown applicable for External API
451                 if (this.$scope.isCreateMode() || (ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState)) {
452                     this.$scope.categories = this.filteredCategories();
453                     //Flag to disbale category if service is created through External API
454                     this.$scope.isHiddenCategorySelected = this.isHiddenCategory(this.$scope.component.selectedCategory);
455                 }
456
457             }
458         };
459
460         this.$scope.initInstantiationTypes = ():void => {
461             if (this.$scope.componentType === ComponentType.SERVICE) {
462                 this.$scope.instantiationTypes = new Array();
463                 this.$scope.instantiationTypes.push(instantiationType.A_LA_CARTE);
464                 this.$scope.instantiationTypes.push(instantiationType.MACRO);
465                 var instantiationTypeField:string =(<Service>this.$scope.component).instantiationType;
466                 if (instantiationTypeField === ""){
467                     this.$scope.instantiationTypes.push("");
468                 }
469                 else if (this.isCreateModeAvailable(instantiationTypeField)) {
470                     (<Service>this.$scope.component).instantiationType = instantiationType.A_LA_CARTE;
471
472                 }
473             }
474         };
475
476         this.$scope.initBaseTypes = ():void => {
477             if (this.$scope.componentType === ComponentType.SERVICE && this.$scope.component && this.$scope.component.categories) {
478                 if (!this.$scope.component.derivedFromGenericType) {
479                     this.$scope.component.derivedFromGenericVersion = undefined;
480                     this.$scope.showBaseTypeVersions = false;
481                     return;
482                 }
483                 let modelName = this.$scope.component.model ? this.$scope.component.model : null;
484                 const categoryName = this.$scope.component.categories[0].name;
485                 this.elementService.getCategoryBaseTypes(categoryName, modelName).subscribe((data: ListBaseTypesResponse) => {
486                     this.$scope.baseTypes = []
487                     this.$scope.baseTypeVersions = []
488                     this.$scope.isBaseTypeRequired = data.required;
489                     data.baseTypes.forEach(baseType => {
490                         this.$scope.baseTypes.push(baseType.toscaResourceName);
491                         if (baseType.toscaResourceName === this.$scope.component.derivedFromGenericType) {
492                             baseType.versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
493                         }
494                     });
495                     this.$scope.showBaseTypeVersions = true;
496                 })
497             }
498         };
499
500         this.$scope.initModel = ():void => {
501             this.$scope.isModelRequired = false;
502             this.$scope.models = [];
503             this.$scope.defaultModelOption = DEFAULT_MODEL_NAME;
504             this.$scope.showDefaultModelOption = true;
505             if (this.$scope.componentType === ComponentType.SERVICE) {
506                 this.filterCategoriesByModel(this.$scope.component.model);
507             }
508             if (this.$scope.isCreateMode() && this.$scope.isVspImport()) {
509                 if (this.$scope.component.componentMetadata.models) {
510                     this.$scope.isModelRequired = true;
511                     const modelOptions = this.$scope.component.componentMetadata.models;
512                     if (modelOptions.length == 1) {
513                         this.$scope.models = modelOptions;
514                         this.$scope.component.model = modelOptions[0];
515                         this.$scope.showDefaultModelOption = false;
516                     } else {
517                         this.$scope.models = modelOptions.sort();
518                         this.$scope.defaultModelOption = 'Select';
519                     }
520                 }
521                 return;
522             }
523
524             if (!this.$scope.isCreateMode() && this.$scope.isVspImport()){
525                 this.modelService.getModels().subscribe((modelsFound: Model[]) => {
526                     modelsFound.sort().forEach(model => {
527                         if (this.$scope.component.model != undefined) {
528                             if (model.modelType == "NORMATIVE_EXTENSION") {
529                                 this.$scope.component.model = model.derivedFrom;
530                                 this.$scope.models.push(model.derivedFrom)
531                             } else {
532                                 this.$scope.component.model = model.name;
533                                 this.$scope.models.push(model.name)
534                             }
535                         }
536                     });
537                 });
538             } else {
539                 this.modelService.getModelsOfType("normative").subscribe((modelsFound: Model[]) => {
540                     modelsFound.sort().forEach(model => {this.$scope.models.push(model.name)});
541                 });
542             }
543         };
544
545         this.$scope.isVspImport = (): boolean => {
546             if (!this.$scope.component || !this.$scope.component.isResource()) {
547                 return false;
548             }
549
550             const resource = <Resource>this.$scope.component;
551             return resource.isCsarComponent();
552         }
553
554         this.$scope.initEnvironmentContext = ():void => {
555             if (this.$scope.componentType === ComponentType.SERVICE) {
556                 this.$scope.environmentContextObj = this.cacheService.get('UIConfiguration').environmentContext;
557                 var environmentContext:string =(<Service>this.$scope.component).environmentContext;
558                 // In creation new service OR check outing old service without environmentContext parameter - set default value
559                 if(this.isCreateModeAvailable(environmentContext)){
560                     (<Service>this.$scope.component).environmentContext = this.$scope.environmentContextObj.defaultValue;
561                 }
562             }
563         };
564
565         this.$scope.validateField = (field:any):boolean => {
566             if (field && field.$dirty && field.$invalid) {
567                 return true;
568             }
569             return false;
570         };
571
572         this.$scope.openOnBoardingModal = ():void => {
573             if(this.$scope.component.vspArchived) return;
574             let csarUUID = (<Resource>this.$scope.component).csarUUID;
575             let csarVersion = (<Resource>this.$scope.component).csarVersion;
576             this.importVSPService.openOnboardingModal(csarUUID, csarVersion).subscribe((result) => {
577                 this.ComponentFactory.getComponentWithMetadataFromServer(result.type.toUpperCase(), result.previousComponent.uniqueId).then(
578                     (component:Component)=> {
579                         if (result.componentCsar && component.isResource()){
580                             this.cacheService.set(PREVIOUS_CSAR_COMPONENT, angular.copy(component));
581                             component = this.ComponentFactory.updateComponentFromCsar(result.componentCsar, <Resource>component);
582                         }
583                         this.$scope.setComponent(component);
584                         this.$scope.save();
585                         this.setImportedFileText();
586                     }, ()=> {
587                         // ERROR
588                     });
589             })
590         };
591
592         this.$scope.updateIcon = ():void => {
593             this.ModalsHandler.openUpdateIconModal(this.$scope.component).then((isDirty:boolean)=> {
594                 if(isDirty && !this.$scope.isCreateMode()){
595                     this.setUnsavedChanges(true);
596                 }
597             }, ()=> {
598                 // ERROR
599             });
600         };
601
602         this.$scope.possibleToUpdateIcon = ():boolean => {
603             if(this.$scope.componentCategories.selectedCategory && (!this.$scope.component.isResource() || this.$scope.component.vendorName) && !this.$scope.component.isAlreadyCertified()){
604                 return true;
605             }else{
606                 return false;
607             }
608         }
609
610         this.$scope.validateName = (isInit:boolean):void => {
611             if (isInit === undefined) {
612                 isInit = false;
613             }
614
615             let name = this.$scope.component.name;
616             if (!name || name === "") {
617                 if (this.$scope.editForm
618                     && this.$scope.editForm["componentName"]
619                     && this.$scope.editForm["componentName"].$error) {
620
621                     // Clear the error name already exists
622                     this.$scope.editForm["componentName"].$setValidity('nameExist', true);
623                 }
624
625                 return;
626             }
627
628             let subtype:string = ComponentType.RESOURCE == this.$scope.componentType ? this.$scope.component.getComponentSubType() : undefined;
629             if (subtype == "SRVC") {
630                 subtype = "VF"
631             }
632
633             const onFailed = (response) => {
634                 // console.info('onFaild', response);
635                 // this.$scope.isLoading = false;
636             };
637
638             const onSuccess = (validation:IValidate) => {
639                 this.$scope.editForm['componentName'].$setValidity('nameExist', validation.isValid);
640                 if (validation.isValid) {
641                     // update breadcrumb after changing name
642                     this.updateComponentNameInBreadcrumbs();
643                 }
644             };
645
646             if (isInit) {
647                 // When page is init after update
648                 if (this.$scope.component.name !== this.$scope.originComponent.name) {
649                     if (!(this.$scope.componentType === ComponentType.RESOURCE && (<Resource>this.$scope.component).csarUUID !== undefined)
650                     ) {
651                         this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed);
652                     }
653                 }
654             } else {
655                 // Validating on change (has debounce)
656                 if (this.$scope.editForm
657                     && this.$scope.editForm["componentName"]
658                     && this.$scope.editForm["componentName"].$error
659                     && !this.$scope.editForm["componentName"].$error.pattern
660                     && (!this.$scope.originComponent.name || this.$scope.component.name.toUpperCase() !== this.$scope.originComponent.name.toUpperCase())
661                 ) {
662                     if (!(this.$scope.componentType === ComponentType.RESOURCE && (this.$scope.component as Resource).csarUUID !== undefined)
663                     ) {
664                         this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed);
665                     }
666                 } else if (this.$scope.editForm && this.$scope.originComponent.name && this.$scope.component.name.toUpperCase() === this.$scope.originComponent.name.toUpperCase()) {
667                     // Clear the error
668                     this.$scope.editForm['componentName'].$setValidity('nameExist', true);
669                 }
670             }
671         };
672
673
674         this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE_WITH_SAVE, (nextState) => {
675             if (this.$state.current.data.unsavedChanges && this.$scope.isValidForm) {
676                 this.$scope.save().then(() => {
677                     this.$scope.handleChangeLifecycleState(nextState);
678                 }, () => {
679                     console.error('Save failed, unable to change lifecycle state to ' + nextState);
680                 });
681             } else if(!this.$scope.isValidForm){
682                 console.error('Form is not valid');
683             } else {
684                 let newCsarVersion:string;
685                 if(this.$scope.unsavedFile) {
686                     newCsarVersion = (this.$scope.component as Resource).csarVersion;
687                 }
688                 if(this.$stateParams.componentCsar && !this.$scope.isCreateMode()) {
689                     const onError = (): void => {
690                         if (this.$scope.component.lifecycleState === 'NOT_CERTIFIED_CHECKIN') {
691                             this.$scope.revert();
692                         }
693                     };
694                     this.$scope.handleChangeLifecycleState(nextState, newCsarVersion, onError);
695
696                 } else {
697                     this.$scope.handleChangeLifecycleState(nextState, newCsarVersion);
698                 }
699             }
700         });
701
702         this.$scope.revert = ():void => {
703             // in state of import file leave the file in place
704
705             this.$scope.setComponent(this.ComponentFactory.createComponent(this.$scope.originComponent));
706
707             if (this.$scope.component.isResource() && this.$scope.restoreFile) {
708                 (this.$scope.component as Resource).importedFile = angular.copy(this.$scope.restoreFile);
709             }
710
711             this.setImportedFileText();
712             this.$scope.updateBreadcrumbs(this.$scope.component); // update on workspace
713
714             this.$scope.componentCategories.selectedCategory = this.$scope.originComponent.selectedCategory;
715             this.setUnsavedChanges(false);
716             this.$scope.updateUnsavedFileFlag(false);
717             this.$scope.editForm.$setPristine();
718         };
719
720         this.$scope.onImportFileChange = () => {
721
722             if( !this.$scope.restoreFile && this.$scope.editForm.fileElement.value && this.$scope.editForm.fileElement.value.filename || // if file started empty but we have added a new one
723                 this.$scope.restoreFile && !angular.equals(this.$scope.restoreFile, this.$scope.editForm.fileElement.value)){ // or file was swapped for a new one
724                 this.$scope.updateUnsavedFileFlag(true);
725             } else {
726                 this.$scope.updateUnsavedFileFlag(false);
727                 this.$scope.editForm.fileElement.$setPristine();
728             }
729         };
730
731         this.$scope.$watchCollection('component.name', (newData: any): void => {
732             this.$scope.validateName(false);
733         });
734
735         // Notify the parent if this step valid or not.
736         this.$scope.$watch('editForm.$valid', (newVal, oldVal) => {
737             this.$scope.setValidState(newVal);
738         });
739
740         this.$scope.$watch('editForm.$dirty', (newVal, oldVal) => {
741             if (newVal && !this.$scope.isCreateMode()) {
742                 this.setUnsavedChanges(true);
743             }
744
745         });
746
747         this.$scope.onCategoryChange = (): void => {
748             this.$scope.component.selectedCategory = this.$scope.componentCategories.selectedCategory;
749             if (this.$scope.component.selectedCategory) {
750                 this.$scope.component.categories = this.convertCategoryStringToOneArray();
751                 this.$scope.component.icon = DEFAULT_ICON;
752                 if (this.$scope.component.categories[0].metadataKeys) {
753                     for (let metadataKey of this.$scope.component.categories[0].metadataKeys) {
754                         if (!this.$scope.component.categorySpecificMetadata[metadataKey.name]) {
755                             this.$scope.component.categorySpecificMetadata[metadataKey.name] = metadataKey.defaultValue ? metadataKey.defaultValue : "";
756                         }
757                     }
758                 }
759                 if (this.$scope.component.categories[0].subcategories && this.$scope.component.categories[0].subcategories[0].metadataKeys) {
760                     for (let metadataKey of this.$scope.component.categories[0].subcategories[0].metadataKeys) {
761                         if (!this.$scope.component.categorySpecificMetadata[metadataKey.name]) {
762                             this.$scope.component.categorySpecificMetadata[metadataKey.name] = metadataKey.defaultValue ? metadataKey.defaultValue : "";
763                         }
764                     }
765                 }
766                 if (this.$scope.componentType === ComponentType.SERVICE && this.$scope.component.categories[0]) {
767                     const modelName : string = this.$scope.component.model ? this.$scope.component.model : null;
768                     this.elementService.getCategoryBaseTypes(this.$scope.component.categories[0].name, modelName)
769                     .subscribe((data: ListBaseTypesResponse) => {
770                         if (this.$scope.isCreateMode()) {
771                             this.loadBaseTypes(data);
772                         } else {
773                             let isValidForBaseType:boolean = data.baseTypes.some(baseType => {
774                                 return !this.$scope.component.derivedFromGenericType ||
775                                     baseType.toscaResourceName === this.$scope.component.derivedFromGenericType;
776                             });
777                             this.$scope.editForm['category'].$setValidity('validForBaseType', isValidForBaseType);
778                         }
779                     });
780                 }
781             } else {
782                 this.clearBaseTypes();
783             }
784         };
785
786         this.$scope.onEcompGeneratedNamingChange = (): void => {
787             if (!(this.$scope.component as Service).ecompGeneratedNaming) {
788                 (this.$scope.component as Service).namingPolicy = '';
789             }
790         };
791
792         this.$scope.getCategoryDisplayNameOrName = (mainCategory: any): string => {
793             return mainCategory.displayName ? mainCategory.displayName : mainCategory.name ;
794         }
795
796         this.$scope.onBaseTypeChange = (): void => {
797             if (!this.$scope.component.derivedFromGenericType) {
798                 this.$scope.component.derivedFromGenericVersion = undefined;
799                 this.$scope.showBaseTypeVersions = false;
800                 return;
801             }
802
803             const modelName : string = this.$scope.component.model ? this.$scope.component.model : null;
804             const categoryName = this.$scope.component.categories[0].name;
805             this.elementService.getCategoryBaseTypes(categoryName, modelName).subscribe((baseTypeResponseList: ListBaseTypesResponse) => {
806                 this.$scope.baseTypeVersions = []
807                 baseTypeResponseList.baseTypes.forEach(baseType => {
808                     if (baseType.toscaResourceName === this.$scope.component.derivedFromGenericType) {
809                         baseType.versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
810                         this.$scope.component.derivedFromGenericVersion = baseType.versions[0];
811                     }
812                 });
813                 this.$scope.showBaseTypeVersions = true;
814             });
815         };
816
817         this.$scope.onModelChange = (): void => {
818             if (this.$scope.componentType === ComponentType.SERVICE && this.$scope.component && this.$scope.categories) {
819                 let modelName = this.$scope.component.model ? this.$scope.component.model : null;
820                 this.$scope.component.categories = undefined;
821                 this.$scope.component.selectedCategory = undefined;
822                 this.$scope.componentCategories.selectedCategory = undefined;
823                 this.filterCategoriesByModel(modelName);
824                 this.filterBaseTypesByModelAndCategory(modelName)
825             }
826         };
827
828         this.$scope.onVendorNameChange = (oldVendorName: string): void => {
829             if (this.$scope.component.icon === oldVendorName) {
830                 this.$scope.component.icon = DEFAULT_ICON;
831             }
832         };
833
834         this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.$scope.reload);
835
836         this.$scope.isMetadataKeyMandatory = (key: string): boolean => {
837             let metadataKey = this.getMetadataKey(key);
838             return metadataKey && metadataKey.mandatory;
839         }
840
841         this.$scope.getMetadataKeyValidValues = (key: string): string[] => {
842             let metadataKey = this.getMetadataKey(key);
843             if (metadataKey) {
844                 return metadataKey.validValues;
845             }
846             return [];
847         }
848
849         this.$scope.getMetadataDisplayName = (key: string): string => {
850             let metadataKey = this.getMetadataKey(key);
851             if (metadataKey) {
852                 return metadataKey.displayName ? metadataKey.displayName : metadataKey.name;
853             }
854             return "";
855         }
856
857         this.$scope.isMetadataKeyForComponentCategory = (key: string): boolean => {
858             return this.getMetadataKey(key) != null;
859         }
860
861         this.$scope.isCategoryServiceMetadataKey = (key: string): boolean => {
862             return this.isServiceMetadataKey(key);
863         }
864
865         this.$scope.isMetadataKeyForComponentCategoryService = (key: string, attribute: string): boolean => {
866             let metadatakey = this.getMetadataKey(key);
867             if (metadatakey && (!this.$scope.component[attribute] || !metadatakey.validValues.find(v => v === this.$scope.component[attribute]))) {
868                 this.$scope.component[attribute] = metadatakey.defaultValue;
869             }
870             return metadatakey != null;
871         }
872     }
873
874     private filterCategoriesByModel(modelName:string) {
875         // reload categories
876         this.$scope.initCategories();
877         this.$scope.categories = this.$scope.categories.filter(category =>
878             !modelName ? !category.models || category.models.indexOf(DEFAULT_MODEL_NAME) !== -1 : category.models !== null && category.models.indexOf(modelName) !== -1);
879     }
880
881
882     private filterBaseTypesByModelAndCategory(modelName:string) {
883         let categories = this.$scope.component.categories;
884         if (categories) {
885             this.elementService.getCategoryBaseTypes(categories[0].name, modelName).subscribe((data: ListBaseTypesResponse) => {
886                 this.loadBaseTypes(data);
887             });
888             return;
889         }
890         this.clearBaseTypes();
891     }
892
893     private loadBaseTypes(baseTypeResponseList: ListBaseTypesResponse) {
894         this.$scope.isBaseTypeRequired = baseTypeResponseList.required;
895         this.$scope.baseTypes = [];
896         this.$scope.baseTypeVersions = [];
897         baseTypeResponseList.baseTypes.forEach(baseType => this.$scope.baseTypes.push(baseType.toscaResourceName));
898         if (this.$scope.isBaseTypeRequired) {
899             const baseType = baseTypeResponseList.baseTypes[0];
900             baseType.versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
901             if(!this.$scope.component.derivedFromGenericType) {
902                 this.$scope.component.derivedFromGenericType = baseType.toscaResourceName;
903             }
904             this.$scope.component.derivedFromGenericVersion = this.$scope.baseTypeVersions[0];
905             this.$scope.showBaseTypeVersions = true;
906             return
907         }
908         this.$scope.component.derivedFromGenericType = undefined;
909         this.$scope.component.derivedFromGenericVersion = undefined;
910         this.$scope.showBaseTypeVersions = false;
911     }
912
913     private clearBaseTypes() {
914         this.$scope.isBaseTypeRequired = false;
915         this.$scope.baseTypes = [];
916         this.$scope.baseTypeVersions = [];
917         this.$scope.component.derivedFromGenericType = undefined;
918         this.$scope.component.derivedFromGenericVersion = undefined;
919         this.$scope.showBaseTypeVersions = false;
920     }
921
922     private setUnsavedChanges = (hasChanges: boolean): void => {
923         this.$state.current.data.unsavedChanges = hasChanges;
924     }
925
926     private getMetadataKey(key: string) : IMetadataKey {
927         if (this.$scope.component.categories) {
928             let metadataKey = this.getSubcategoryMetadataKey(this.$scope.component.categories, key);
929             if (!metadataKey){
930                 return this.getCategoryMetadataKey(this.$scope.component.categories, key);
931             }
932             return metadataKey;
933         }
934         return null;
935     }
936
937     private getSubcategoryMetadataKey(categories: IMainCategory[], key: string) : IMetadataKey {
938         if (categories[0].subcategories && categories[0].subcategories[0].metadataKeys && categories[0].subcategories[0].metadataKeys.some(metadataKey => metadataKey.name == key)) {
939             return categories[0].subcategories[0].metadataKeys.find(metadataKey => metadataKey.name == key);
940         }
941         return null;
942     }
943
944     private getCategoryMetadataKey(categories: IMainCategory[], key: string) : IMetadataKey {
945         if (categories[0].metadataKeys && categories[0].metadataKeys.some(metadataKey => metadataKey.name == key)) {
946             return categories[0].metadataKeys.find(metadataKey => metadataKey.name == key);
947         }
948         return null;
949     }
950
951     private isServiceMetadataKey(key: string) : boolean {
952         return CATEGORY_SERVICE_METADATA_KEYS.indexOf(key) > -1;
953     }
954
955 }