View Interface definition on VFC
[sdc.git] / catalog-ui / src / app / view-models / workspace / tabs / general / general-view-model.ts
index e270928..a707e56 100644 (file)
 
 'use strict';
 import * as _ from "lodash";
-import {ModalsHandler, ValidationUtils, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, DEFAULT_ICON,
-    ResourceType, ComponentState, instantiationType, ComponentFactory} from "app/utils";
-import { EventListenerService, ProgressService} from "app/services";
-import {CacheService, OnboardingService, ImportVSPService} from "app/services-ng2";
-import {IAppConfigurtaion, IValidate, IMainCategory, Resource, ISubCategory,Service, ICsarComponent, Component} from "app/models";
-import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
 import {Dictionary} from "lodash";
-import { PREVIOUS_CSAR_COMPONENT } from "../../../../utils/constants";
-import { Observable, Subject } from "rxjs";
-
+import {
+    ComponentFactory,
+    ComponentState,
+    ComponentType,
+    DEFAULT_ICON,
+    EVENTS,
+    instantiationType,
+    ModalsHandler,
+    ResourceType,
+    ValidationUtils
+} from "app/utils";
+import {EventListenerService, ProgressService} from "app/services";
+import {CacheService, ElementService, ModelService, ImportVSPService, OnboardingService} from "app/services-ng2";
+import {Component, IAppConfigurtaion, ICsarComponent, IMainCategory, IMetadataKey, ISubCategory, IValidate, Resource, Service} from "app/models";
+import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
+import {CATEGORY_SERVICE_METADATA_KEYS, PREVIOUS_CSAR_COMPONENT, DEFAULT_MODEL_NAME} from "../../../../utils/constants";
+import {Observable} from "rxjs";
+import {Model} from "../../../../models/model";
 
 export class Validation {
     componentNameValidationPattern:RegExp;
@@ -45,6 +54,10 @@ export class componentCategories {//categories field bind to this obj in order t
     selectedCategory:string;
 }
 
+export class componentModel {
+    selectedModel:string;
+}
+
 export interface IEnvironmentContext {
     defaultValue:string;
     validValues:Array<string>;
@@ -65,8 +78,10 @@ export interface IGeneralScope extends IWorkspaceViewModelScope {
     importCsarProProgressKey:string;
     browseFileLabel:string;
     componentCategories:componentCategories;
+    componentModel:componentModel;
     instantiationTypes:Array<instantiationType>;
     isHiddenCategorySelected: boolean;
+    isModelRequired: boolean;
 
     save():Promise<any>;
     revert():void;
@@ -78,13 +93,18 @@ export interface IGeneralScope extends IWorkspaceViewModelScope {
     convertCategoryStringToOneArray(category:string, subcategory:string):Array<IMainCategory>;
     onCategoryChange():void;
     onEcompGeneratedNamingChange():void;
+    onModelChange():void;
+    onBaseTypeChange():void;
     openOnBoardingModal():void;
-    initCategoreis():void;
+    initCategories():void;
     initEnvironmentContext():void;
     initInstantiationTypes():void;
+    initBaseTypes():void;
     onInstantiationTypeChange():void;
     updateIcon():void;
     possibleToUpdateIcon():boolean;
+    initModel():void;
+    isVspImport(): boolean;
 }
 
 // tslint:disable-next-line:max-classes-per-file
@@ -113,6 +133,8 @@ export class GeneralViewModel {
         'OnboardingService',
         'ComponentFactory',
         'ImportVSPService',
+        'ElementService',
+        'ModelService',
         '$stateParams'
     ];
 
@@ -138,6 +160,8 @@ export class GeneralViewModel {
                 private onBoardingService: OnboardingService,
                 private ComponentFactory:ComponentFactory,
                 private importVSPService: ImportVSPService,
+                private elementService: ElementService,
+                private modelService: ModelService,
                 private $stateParams: any) {
 
         this.initScopeValidation();
@@ -219,7 +243,7 @@ export class GeneralViewModel {
 
         this.$scope.importCsarProgressKey = "importCsarProgressKey";
 
-        this.$scope.browseFileLabel = this.$scope.component.isResource() && (<Resource>this.$scope.component).resourceType === ResourceType.VF ? 'VSP' : 'Upload VFC';
+        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:';
         this.$scope.progressService = this.progressService;
         this.$scope.componentCategories = new componentCategories();
         this.$scope.componentCategories.selectedCategory = this.$scope.component.selectedCategory;
@@ -228,11 +252,14 @@ export class GeneralViewModel {
         this.$scope.component.tags = _.without(this.$scope.component.tags, this.$scope.component.name);
 
         // Init categories
-        this.$scope.initCategoreis();
+        this.$scope.initCategories();
 
         // Init Environment Context
         this.$scope.initEnvironmentContext();
 
+        // Init Models
+        this.$scope.initModel();
+
         // Init the decision if to show file browse.
         this.$scope.isShowFileBrowse = false;
         if (this.$scope.component.isResource()) {
@@ -245,8 +272,18 @@ export class GeneralViewModel {
                 this.$scope.isShowFileBrowse = true;
             }
         } else if(this.$scope.component.isService()){
+            let service: Service = <Service>this.$scope.component;
+            console.log(service.name + ": " + service.csarUUID);
+            if (service.importedFile) { // Component has imported file.
+                this.$scope.isShowFileBrowse = true;
+                (<Service>this.$scope.component).serviceType = 'Service';
+            }
+            if (this.$scope.isEditMode() && service.serviceType == 'Service' && !service.csarUUID) {
+                this.$scope.isShowFileBrowse = true;
+            }
             // Init Instantiation types
             this.$scope.initInstantiationTypes();
+            this.$scope.initBaseTypes();
         }
 
         if (this.cacheService.get(PREVIOUS_CSAR_COMPONENT)) { //keep the old component in the cache until checkout, so we dont need to pass it around
@@ -256,22 +293,12 @@ export class GeneralViewModel {
 
         if (this.$stateParams.componentCsar && !this.$scope.isCreateMode()) {
             this.$scope.updateUnsavedFileFlag(true);
-            // We are coming from update VSP modal we need to automatically checkout (if needed) and save the VF
-            if (this.$scope.component.lifecycleState !== ComponentState.NOT_CERTIFIED_CHECKOUT) {
-                // Checkout is needed after that a save will be invoked in workspace-view.handleLifeCycleStateChange
-                this.EventListenerService.notifyObservers(EVENTS.ON_LIFECYCLE_CHANGE_WITH_SAVE, 'checkOut');
-                // if(this.$scope.component.lifecycleState !== 'NOT_CERTIFIED_CHECKIN') {
-                //     (<Resource>this.$scope.component).csarVersion = this.$stateParams.componentCsar.csarVersion;
-                // }
-            } else {
-                this.$scope.save();
-            }
+            this.$scope.save();
         }
 
-
         if (this.$scope.component.isResource() &&
             (this.$scope.component as Resource).resourceType === ResourceType.VF ||
-                (this.$scope.component as Resource).resourceType === ResourceType.PNF && (this.$scope.component as Resource).csarUUID) {
+            (this.$scope.component as Resource).resourceType === ResourceType.PNF && (this.$scope.component as Resource).csarUUID) {
             this.$scope.isShowOnboardingSelectionBrowse = true;
             this.setImportedFileText();
         } else {
@@ -386,11 +413,11 @@ export class GeneralViewModel {
         });
 
         return tempCategories;
-    };    
-   
+    };
+
     private initScopeMethods = ():void => {
 
-        this.$scope.initCategoreis = ():void => {
+        this.$scope.initCategories = ():void => {
             if (this.$scope.componentType === ComponentType.RESOURCE) {
                 this.$scope.categories = this.cacheService.get('resourceCategories');
 
@@ -404,7 +431,7 @@ export class GeneralViewModel {
                     //Flag to disbale category if service is created through External API
                     this.$scope.isHiddenCategorySelected = this.isHiddenCategory(this.$scope.component.selectedCategory);
                 }
-                
+
             }
         };
 
@@ -424,6 +451,84 @@ export class GeneralViewModel {
             }
         };
 
+        this.$scope.initBaseTypes = ():void => {
+            if (this.$scope.componentType === ComponentType.SERVICE && this.$scope.component && this.$scope.component.categories) {
+                if (!this.$scope.component.derivedFromGenericType) {
+                    this.$scope.component.derivedFromGenericVersion = undefined;
+                    this.$scope.showBaseTypeVersions = false;
+                    return;
+                }
+                let modelName = this.$scope.component.model ? this.$scope.component.model : null;
+                const categoryName = this.$scope.component.categories[0].name;
+                this.elementService.getCategoryBaseTypes(categoryName, modelName).subscribe((data: ListBaseTypesResponse) => {
+                    this.$scope.baseTypes = []
+                    this.$scope.baseTypeVersions = []
+                    this.$scope.isBaseTypeRequired = data.required;
+                    data.baseTypes.forEach(baseType => {
+                        this.$scope.baseTypes.push(baseType.toscaResourceName);
+                        if (baseType.toscaResourceName === this.$scope.component.derivedFromGenericType) {
+                            baseType.versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
+                        }
+                    });
+                    this.$scope.showBaseTypeVersions = true;
+                })
+            }
+        };
+
+        this.$scope.initModel = ():void => {
+            this.$scope.isModelRequired = false;
+            this.$scope.models = [];
+            this.$scope.defaultModelOption = DEFAULT_MODEL_NAME;
+            this.$scope.showDefaultModelOption = true;
+            if (this.$scope.componentType === ComponentType.SERVICE) {
+                this.filterCategoriesByModel(this.$scope.component.model);
+            }
+            if (this.$scope.isCreateMode() && this.$scope.isVspImport()) {
+                if (this.$scope.component.componentMetadata.models) {
+                    this.$scope.isModelRequired = true;
+                    const modelOptions = this.$scope.component.componentMetadata.models;
+                    if (modelOptions.length == 1) {
+                        this.$scope.models = modelOptions;
+                        this.$scope.component.model = modelOptions[0];
+                        this.$scope.showDefaultModelOption = false;
+                    } else {
+                        this.$scope.models = modelOptions.sort();
+                        this.$scope.defaultModelOption = 'Select';
+                    }
+                }
+                return;
+            }
+
+            if (!this.$scope.isCreateMode() && this.$scope.isVspImport()){
+                this.modelService.getModels().subscribe((modelsFound: Model[]) => {
+                    modelsFound.sort().forEach(model => {
+                        if (this.$scope.component.model != undefined) {
+                            if (model.modelType == "NORMATIVE_EXTENSION") {
+                                this.$scope.component.model = model.derivedFrom;
+                                this.$scope.models.push(model.derivedFrom)
+                            } else {
+                                this.$scope.component.model = model.name;
+                                this.$scope.models.push(model.name)
+                            }
+                        }
+                    });
+                });
+            } else {
+                this.modelService.getModelsOfType("normative").subscribe((modelsFound: Model[]) => {
+                    modelsFound.sort().forEach(model => {this.$scope.models.push(model.name)});
+                });
+            }
+        };
+
+        this.$scope.isVspImport = (): boolean => {
+            if (!this.$scope.component || !this.$scope.component.isResource()) {
+                return false;
+            }
+
+            const resource = <Resource>this.$scope.component;
+            return resource.isCsarComponent();
+        }
+
         this.$scope.initEnvironmentContext = ():void => {
             if (this.$scope.componentType === ComponentType.SERVICE) {
                 this.$scope.environmentContextObj = this.cacheService.get('UIConfiguration').environmentContext;
@@ -449,16 +554,16 @@ export class GeneralViewModel {
             this.importVSPService.openOnboardingModal(csarUUID, csarVersion).subscribe((result) => {
                 this.ComponentFactory.getComponentWithMetadataFromServer(result.type.toUpperCase(), result.previousComponent.uniqueId).then(
                     (component:Component)=> {
-                    if (result.componentCsar && component.isResource()){
-                        this.cacheService.set(PREVIOUS_CSAR_COMPONENT, angular.copy(component));
-                        component = this.ComponentFactory.updateComponentFromCsar(result.componentCsar, <Resource>component);
-                    }
-                    this.$scope.setComponent(component);
-                    this.$scope.save();
-                    this.setImportedFileText();
-                }, ()=> {
-                    // ERROR
-                });
+                        if (result.componentCsar && component.isResource()){
+                            this.cacheService.set(PREVIOUS_CSAR_COMPONENT, angular.copy(component));
+                            component = this.ComponentFactory.updateComponentFromCsar(result.componentCsar, <Resource>component);
+                        }
+                        this.$scope.setComponent(component);
+                        this.$scope.save();
+                        this.setImportedFileText();
+                    }, ()=> {
+                        // ERROR
+                    });
             })
         };
 
@@ -497,7 +602,11 @@ export class GeneralViewModel {
 
                 return;
             }
-            const subtype:string = ComponentType.RESOURCE == this.$scope.componentType ? this.$scope.component.getComponentSubType() : undefined;
+
+            let subtype:string = ComponentType.RESOURCE == this.$scope.componentType ? this.$scope.component.getComponentSubType() : undefined;
+            if (subtype == "SRVC") {
+                subtype = "VF"
+            }
 
             const onFailed = (response) => {
                 // console.info('onFaild', response);
@@ -615,8 +724,41 @@ export class GeneralViewModel {
 
         this.$scope.onCategoryChange = (): void => {
             this.$scope.component.selectedCategory = this.$scope.componentCategories.selectedCategory;
-            this.$scope.component.categories = this.convertCategoryStringToOneArray();
-            this.$scope.component.icon = DEFAULT_ICON;
+            if (this.$scope.component.selectedCategory) {
+                this.$scope.component.categories = this.convertCategoryStringToOneArray();
+                this.$scope.component.icon = DEFAULT_ICON;
+                if (this.$scope.component.categories[0].metadataKeys) {
+                    for (let metadataKey of this.$scope.component.categories[0].metadataKeys) {
+                        if (!this.$scope.component.categorySpecificMetadata[metadataKey.name]) {
+                            this.$scope.component.categorySpecificMetadata[metadataKey.name] = metadataKey.defaultValue ? metadataKey.defaultValue : "";
+                        }
+                    }
+                }
+                if (this.$scope.component.categories[0].subcategories && this.$scope.component.categories[0].subcategories[0].metadataKeys) {
+                    for (let metadataKey of this.$scope.component.categories[0].subcategories[0].metadataKeys) {
+                        if (!this.$scope.component.categorySpecificMetadata[metadataKey.name]) {
+                            this.$scope.component.categorySpecificMetadata[metadataKey.name] = metadataKey.defaultValue ? metadataKey.defaultValue : "";
+                        }
+                    }
+                }
+                if (this.$scope.componentType === ComponentType.SERVICE && this.$scope.component.categories[0]) {
+                    const modelName : string = this.$scope.component.model ? this.$scope.component.model : null;
+                    this.elementService.getCategoryBaseTypes(this.$scope.component.categories[0].name, modelName)
+                    .subscribe((data: ListBaseTypesResponse) => {
+                        if (this.$scope.isCreateMode()) {
+                            this.loadBaseTypes(data);
+                        } else {
+                            let isValidForBaseType:boolean = data.baseTypes.some(baseType => {
+                                return !this.$scope.component.derivedFromGenericType ||
+                                    baseType.toscaResourceName === this.$scope.component.derivedFromGenericType;
+                            });
+                            this.$scope.editForm['category'].$setValidity('validForBaseType', isValidForBaseType);
+                        }
+                    });
+                }
+            } else {
+                this.clearBaseTypes();
+            }
         };
 
         this.$scope.onEcompGeneratedNamingChange = (): void => {
@@ -625,18 +767,161 @@ export class GeneralViewModel {
             }
         };
 
+        this.$scope.onBaseTypeChange = (): void => {
+            if (!this.$scope.component.derivedFromGenericType) {
+                this.$scope.component.derivedFromGenericVersion = undefined;
+                this.$scope.showBaseTypeVersions = false;
+                return;
+            }
+
+            const modelName : string = this.$scope.component.model ? this.$scope.component.model : null;
+            const categoryName = this.$scope.component.categories[0].name;
+            this.elementService.getCategoryBaseTypes(categoryName, modelName).subscribe((baseTypeResponseList: ListBaseTypesResponse) => {
+                this.$scope.baseTypeVersions = []
+                baseTypeResponseList.baseTypes.forEach(baseType => {
+                    if (baseType.toscaResourceName === this.$scope.component.derivedFromGenericType) {
+                        baseType.versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
+                        this.$scope.component.derivedFromGenericVersion = baseType.versions[0];
+                    }
+                });
+                this.$scope.showBaseTypeVersions = true;
+            });
+        };
+
+        this.$scope.onModelChange = (): void => {
+            if (this.$scope.componentType === ComponentType.SERVICE && this.$scope.component && this.$scope.categories) {
+                let modelName = this.$scope.component.model ? this.$scope.component.model : null;
+                this.$scope.component.categories = undefined;
+                this.$scope.component.selectedCategory = undefined;
+                this.$scope.componentCategories.selectedCategory = undefined;
+                this.filterCategoriesByModel(modelName);
+                this.filterBaseTypesByModelAndCategory(modelName)
+            }
+        };
+
         this.$scope.onVendorNameChange = (oldVendorName: string): void => {
             if (this.$scope.component.icon === oldVendorName) {
                 this.$scope.component.icon = DEFAULT_ICON;
             }
         };
+
         this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.$scope.reload);
 
+        this.$scope.isMetadataKeyMandatory = (key: string): boolean => {
+            let metadataKey = this.getMetadataKey(key);
+            return metadataKey && metadataKey.mandatory;
+        }
+
+        this.$scope.getMetadataKeyValidValues = (key: string): string[] => {
+            let metadataKey = this.getMetadataKey(key);
+            if (metadataKey) {
+                return metadataKey.validValues;
+            }
+            return [];
+        }
+
+        this.$scope.getMetadataDisplayName = (key: string): string => {
+            let metadataKey = this.getMetadataKey(key);
+            if (metadataKey) {
+                return metadataKey.displayName ? metadataKey.displayName : metadataKey.name;
+            }
+            return "";
+        }
+
+        this.$scope.isMetadataKeyForComponentCategory = (key: string): boolean => {
+            return this.getMetadataKey(key) != null;
+        }
+
+        this.$scope.isCategoryServiceMetadataKey = (key: string): boolean => {
+            return this.isServiceMetadataKey(key);
+        }
+
+        this.$scope.isMetadataKeyForComponentCategoryService = (key: string, attribute: string): boolean => {
+            let metadatakey = this.getMetadataKey(key);
+            if (metadatakey && (!this.$scope.component[attribute] || !metadatakey.validValues.find(v => v === this.$scope.component[attribute]))) {
+                this.$scope.component[attribute] = metadatakey.defaultValue;
+            }
+            return metadatakey != null;
+        }
+    }
+
+    private filterCategoriesByModel(modelName:string) {
+        // reload categories
+        this.$scope.initCategories();
+        this.$scope.categories = this.$scope.categories.filter(category =>
+            !modelName ? category.models.indexOf(DEFAULT_MODEL_NAME) !== -1 : category.models !== null && category.models.indexOf(modelName) !== -1);
+    }
+
+
+    private filterBaseTypesByModelAndCategory(modelName:string) {
+        let categories = this.$scope.component.categories;
+        if (categories) {
+            this.elementService.getCategoryBaseTypes(categories[0].name, modelName).subscribe((data: ListBaseTypesResponse) => {
+                this.loadBaseTypes(data);
+            });
+            return;
+        }
+        this.clearBaseTypes();
+    }
+
+    private loadBaseTypes(baseTypeResponseList: ListBaseTypesResponse) {
+        this.$scope.isBaseTypeRequired = baseTypeResponseList.required;
+        this.$scope.baseTypes = [];
+        this.$scope.baseTypeVersions = [];
+        baseTypeResponseList.baseTypes.forEach(baseType => this.$scope.baseTypes.push(baseType.toscaResourceName));
+        if (this.$scope.isBaseTypeRequired) {
+            const baseType = baseTypeResponseList.baseTypes[0];
+            baseType.versions.reverse().forEach(version => this.$scope.baseTypeVersions.push(version));
+            this.$scope.component.derivedFromGenericType = baseType.toscaResourceName;
+            this.$scope.component.derivedFromGenericVersion = this.$scope.baseTypeVersions[0];
+            this.$scope.showBaseTypeVersions = true;
+            return
+        }
+        this.$scope.component.derivedFromGenericType = undefined;
+        this.$scope.component.derivedFromGenericVersion = undefined;
+        this.$scope.showBaseTypeVersions = false;
+    }
+
+    private clearBaseTypes() {
+        this.$scope.isBaseTypeRequired = false;
+        this.$scope.baseTypes = [];
+        this.$scope.baseTypeVersions = [];
+        this.$scope.component.derivedFromGenericType = undefined;
+        this.$scope.component.derivedFromGenericVersion = undefined;
+        this.$scope.showBaseTypeVersions = false;
     }
 
     private setUnsavedChanges = (hasChanges: boolean): void => {
         this.$state.current.data.unsavedChanges = hasChanges;
     }
 
-}
+    private getMetadataKey(key: string) : IMetadataKey {
+        if (this.$scope.component.categories) {
+            let metadataKey = this.getSubcategoryMetadataKey(this.$scope.component.categories, key);
+            if (!metadataKey){
+                return this.getCategoryMetadataKey(this.$scope.component.categories, key);
+            }
+            return metadataKey;
+        }
+        return null;
+    }
+
+    private getSubcategoryMetadataKey(categories: IMainCategory[], key: string) : IMetadataKey {
+        if (categories[0].subcategories && categories[0].subcategories[0].metadataKeys && categories[0].subcategories[0].metadataKeys.some(metadataKey => metadataKey.name == key)) {
+            return categories[0].subcategories[0].metadataKeys.find(metadataKey => metadataKey.name == key);
+        }
+        return null;
+    }
 
+    private getCategoryMetadataKey(categories: IMainCategory[], key: string) : IMetadataKey {
+        if (categories[0].metadataKeys && categories[0].metadataKeys.some(metadataKey => metadataKey.name == key)) {
+            return categories[0].metadataKeys.find(metadataKey => metadataKey.name == key);
+        }
+        return null;
+    }
+
+    private isServiceMetadataKey(key: string) : boolean {
+        return CATEGORY_SERVICE_METADATA_KEYS.indexOf(key) > -1;
+    }
+
+}