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