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