X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=catalog-ui%2Fsrc%2Fapp%2Fview-models%2Fworkspace%2Ftabs%2Fgeneral%2Fgeneral-view-model.ts;h=a707e563afeae4b454720d969e844e331697a377;hb=c2fa1b7e8d43a236219d1f6ad2831de1af296cb4;hp=c60a490b4b2230364502d21a4ef782b547fb3466;hpb=67b4ec2d8d3c3db4758a73e81378d01787eb94a1;p=sdc.git diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts index c60a490b4b..a707e563af 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts @@ -7,9 +7,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -19,12 +19,26 @@ */ 'use strict'; -import {ModalsHandler, ValidationUtils, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, DEFAULT_ICON, - ResourceType, ComponentState} from "app/utils"; -import {CacheService, EventListenerService, ProgressService, OnboardingService} from "app/services"; -import {IAppConfigurtaion, IValidate, IMainCategory, Resource, ISubCategory,Service, ICsarComponent} from "app/models"; -import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; +import * as _ from "lodash"; import {Dictionary} from "lodash"; +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; @@ -34,18 +48,26 @@ export class Validation { VendorNameValidationPattern:RegExp; VendorModelNumberValidationPattern:RegExp; commentValidationPattern:RegExp; - projectCodeValidationPattern:RegExp; } export class componentCategories {//categories field bind to this obj in order to solve this bug: DE242059 selectedCategory:string; } +export class componentModel { + selectedModel:string; +} + +export interface IEnvironmentContext { + defaultValue:string; + validValues:Array; +} export interface IGeneralScope extends IWorkspaceViewModelScope { validation:Validation; editForm:ng.IFormController; categories:Array; + environmentContextObj:IEnvironmentContext; latestCategoryId:string; latestVendorName:string; importedFileExtension:any; @@ -53,11 +75,17 @@ export interface IGeneralScope extends IWorkspaceViewModelScope { isShowFileBrowse:boolean; isShowOnboardingSelectionBrowse:boolean; importedToscaBrowseFileText:string; - importCsarProgressKey:string; + importCsarProProgressKey:string; browseFileLabel:string; componentCategories:componentCategories; - - onToscaFileChange():void; + componentModel:componentModel; + instantiationTypes:Array; + isHiddenCategorySelected: boolean; + isModelRequired: boolean; + + save():Promise; + revert():void; + onImportFileChange():void; validateField(field:any):boolean; validateName(isInit:boolean):void; calculateUnique(mainCategory:string, subCategory:string):string; // Build unique string from main and sub category @@ -65,12 +93,21 @@ export interface IGeneralScope extends IWorkspaceViewModelScope { convertCategoryStringToOneArray(category:string, subcategory:string):Array; 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 export class GeneralViewModel { static '$inject' = [ @@ -85,7 +122,6 @@ export class GeneralViewModel { 'CommentValidationPattern', 'ValidationUtils', 'sdcConfig', - 'ProjectCodeValidationPattern', '$state', 'ModalsHandler', 'EventListenerService', @@ -94,7 +130,12 @@ export class GeneralViewModel { '$interval', '$filter', '$timeout', - 'Sdc.Services.OnboardingService' + 'OnboardingService', + 'ComponentFactory', + 'ImportVSPService', + 'ElementService', + 'ModelService', + '$stateParams' ]; constructor(private $scope:IGeneralScope, @@ -108,7 +149,6 @@ export class GeneralViewModel { private CommentValidationPattern:RegExp, private ValidationUtils:ValidationUtils, private sdcConfig:IAppConfigurtaion, - private ProjectCodeValidationPattern:RegExp, private $state:ng.ui.IStateService, private ModalsHandler:ModalsHandler, private EventListenerService:EventListenerService, @@ -117,7 +157,12 @@ export class GeneralViewModel { protected $interval:any, private $filter:ng.IFilterService, private $timeout:ng.ITimeoutService, - private onBoardingService:OnboardingService) { + private onBoardingService: OnboardingService, + private ComponentFactory:ComponentFactory, + private importVSPService: ImportVSPService, + private elementService: ElementService, + private modelService: ModelService, + private $stateParams: any) { this.initScopeValidation(); this.initScopeMethods(); @@ -136,55 +181,69 @@ export class GeneralViewModel { this.$scope.validation.VendorNameValidationPattern = this.VendorNameValidationPattern; this.$scope.validation.VendorModelNumberValidationPattern = this.VendorModelNumberValidationPattern; this.$scope.validation.commentValidationPattern = this.CommentValidationPattern; - this.$scope.validation.projectCodeValidationPattern = this.ProjectCodeValidationPattern; }; - private initImportedToscaBrowseFile = ():void =>{ - // Init the decision if to show onboarding - this.$scope.isShowOnboardingSelectionBrowse = false; - if (this.$scope.component.isResource() && - this.$scope.isEditMode() && - (this.$scope.component).resourceType == ResourceType.VF && - (this.$scope.component).csarUUID) { - this.$scope.isShowOnboardingSelectionBrowse = true; - let onboardCsarFilesMap:Dictionary> = this.cacheService.get('onboardCsarFilesMap'); - // The onboardCsarFilesMap in cache contains map of [packageId]:[vsp display name for brows] - // if the map is empty - Do request to BE - if(onboardCsarFilesMap) { - if (onboardCsarFilesMap[(this.$scope.component).csarUUID]){ - this.$scope.importedToscaBrowseFileText = onboardCsarFilesMap[(this.$scope.component).csarUUID][(this.$scope.component).csarVersion]; - } - } - if(!onboardCsarFilesMap || !this.$scope.importedToscaBrowseFileText){ + private loadOnboardingFileCache = (): Observable>> => { + let onboardCsarFilesMap:Dictionary>; + let onSuccess = (vsps:Array) => { + onboardCsarFilesMap = {}; + _.each(vsps, (vsp:ICsarComponent)=>{ + onboardCsarFilesMap[vsp.packageId] = onboardCsarFilesMap[vsp.packageId] || {}; + onboardCsarFilesMap[vsp.packageId][vsp.version] = vsp.vspName + " (" + vsp.version + ")"; + }); + this.cacheService.set('onboardCsarFilesMap', onboardCsarFilesMap); + return onboardCsarFilesMap; + }; + let onError = (): void =>{ + console.log("Error getting onboarding list"); + }; + return this.onBoardingService.getOnboardingVSPs().map(onSuccess, onError); + }; - let onSuccess = (vsps:Array): void =>{ - onboardCsarFilesMap = {}; - _.each(vsps, (vsp:ICsarComponent)=>{ - onboardCsarFilesMap[vsp.packageId] = onboardCsarFilesMap[vsp.packageId] || {}; - onboardCsarFilesMap[vsp.packageId][vsp.version] = vsp.vspName + " (" + vsp.version + ")"; - }); - this.cacheService.set('onboardCsarFilesMap', onboardCsarFilesMap); - this.$scope.importedToscaBrowseFileText = onboardCsarFilesMap[(this.$scope.component).csarUUID][(this.$scope.component).csarVersion]; - }; + private setImportedFileText = ():void => { - let onError = (): void =>{ - console.log("Error getting onboarding list"); - }; + if(!this.$scope.isShowOnboardingSelectionBrowse) return; - this.onBoardingService.getOnboardingVSPs().then(onSuccess, onError); + //these variables makes it easier to read this logic + let csarUUID:string = (this.$scope.component).csarUUID; + let csarVersion:string = (this.$scope.component).csarVersion; + + let onboardCsarFilesMap:Dictionary> = this.cacheService.get('onboardCsarFilesMap'); + let assignFileName = ():void => { + if(this.$scope.component.vspArchived){ + this.$scope.importedToscaBrowseFileText = 'VSP is archived'; + } else { + if(this.$stateParams.componentCsar && this.$scope.component.lifecycleState === 'NOT_CERTIFIED_CHECKIN' && !this.$scope.isCreateMode()) { + this.$scope.importedToscaBrowseFileText = this.$scope.originComponent.name + ' (' + (this.$scope.originComponent as Resource).csarVersion + ')'; + } else { + this.$scope.importedToscaBrowseFileText = onboardCsarFilesMap[csarUUID][csarVersion]; + } } } - }; - private initScope = ():void => { - // Work around to change the csar version - if (this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG)) { - (this.$scope.component).csarVersion = this.cacheService.get(CHANGE_COMPONENT_CSAR_VERSION_FLAG); + if(this.$scope.component.vspArchived || (onboardCsarFilesMap && onboardCsarFilesMap[csarUUID] && onboardCsarFilesMap[csarUUID][csarVersion])){ //check that the file name is already in cache + assignFileName(); + } else { + this.loadOnboardingFileCache().subscribe((onboardingFiles) => { + onboardCsarFilesMap = onboardingFiles; + this.cacheService.set('onboardCsarFilesMap', onboardingFiles); + assignFileName(); + }, ()=> {}); } + } + + isCreateModeAvailable(verifyObj:string): boolean { + var isCheckout:boolean = ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState; + return this.$scope.isCreateMode() || (isCheckout && !verifyObj) + } + + private initScope = ():void => { + this.$scope.importCsarProgressKey = "importCsarProgressKey"; - this.$scope.browseFileLabel = this.$scope.component.isResource() && (this.$scope.component).resourceType === ResourceType.VF ? "Upload file" : "Upload VFC"; + + this.$scope.browseFileLabel = (this.$scope.component.isResource() && ((this.$scope.component).resourceType === ResourceType.VF || (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; @@ -193,7 +252,13 @@ 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; @@ -203,12 +268,43 @@ export class GeneralViewModel { if (resource.importedFile) { // Component has imported file. this.$scope.isShowFileBrowse = true; } - if (this.$scope.isEditMode() && resource.resourceType == ResourceType.VF && !resource.csarUUID) { + if (resource.resourceType === ResourceType.VF && !resource.csarUUID) { + this.$scope.isShowFileBrowse = true; + } + } else if(this.$scope.component.isService()){ + let service: Service = this.$scope.component; + console.log(service.name + ": " + service.csarUUID); + if (service.importedFile) { // Component has imported file. + this.$scope.isShowFileBrowse = true; + (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 + this.$scope.setOriginComponent(this.cacheService.get(PREVIOUS_CSAR_COMPONENT)); + this.cacheService.remove(PREVIOUS_CSAR_COMPONENT); + } + + if (this.$stateParams.componentCsar && !this.$scope.isCreateMode()) { + this.$scope.updateUnsavedFileFlag(true); + 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.isShowOnboardingSelectionBrowse = true; + this.setImportedFileText(); + } else { + this.$scope.isShowOnboardingSelectionBrowse = false; } - this.initImportedToscaBrowseFile(); //init file extensions based on the file that was imported. if (this.$scope.component.isResource() && (this.$scope.component).importedFile) { @@ -221,11 +317,14 @@ export class GeneralViewModel { (this.$scope.component).importedFile.filetype = "yaml"; this.$scope.importedFileExtension = this.sdcConfig.toscaFileExtension; } + this.$scope.restoreFile = angular.copy((this.$scope.originComponent).importedFile); //create backup } else if (this.$scope.isEditMode() && (this.$scope.component).resourceType === ResourceType.VF) { this.$scope.importedFileExtension = this.sdcConfig.csarFileExtension; //(this.$scope.component).importedFile.filetype="csar"; } + + this.$scope.setValidState(true); this.$scope.calculateUnique = (mainCategory:string, subCategory:string):string => { @@ -239,12 +338,19 @@ export class GeneralViewModel { //TODO remove this after handling contact in UI if (this.$scope.isCreateMode()) { this.$scope.component.contactId = this.cacheService.get("user").userId; + this.$scope.originComponent.contactId = this.$scope.component.contactId; } + + this.$scope.$on('$destroy', () => { + this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE_WITH_SAVE); + this.EventListenerService.unRegisterObserver(EVENTS.ON_LIFECYCLE_CHANGE); + }); + }; // Convert category string MainCategory_#_SubCategory to Array with one item (like the server except) - private convertCategoryStringToOneArray = ():Array => { + private convertCategoryStringToOneArray = ():IMainCategory[] => { let tmp = this.$scope.component.selectedCategory.split("_#_"); let mainCategory = tmp[0]; let subCategory = tmp[1]; @@ -264,27 +370,173 @@ export class GeneralViewModel { } let tmpSelected = mainCategoryClone; - let result:Array = []; + let result:IMainCategory[] = []; result.push(tmpSelected); return result; }; private updateComponentNameInBreadcrumbs = ():void => { - //update breadcrum after changing name + // update breadcrum after changing name this.$scope.breadcrumbsModel[1].updateSelectedMenuItemText(this.$scope.component.getComponentSubType() + ': ' + this.$scope.component.name); this.$scope.updateMenuComponentName(this.$scope.component.name); }; + //Find if a category is applicable for External API or not + private isHiddenCategory = (category: string) => { + let items: Array = new Array(); + items = this.$scope.sdcMenu.component_workspace_menu_option[this.$scope.component.getComponentSubType()]; + for(let index = 0; index < items.length; ++index) { + if ((items[index].hiddenCategories && items[index].hiddenCategories.indexOf(category) > -1)) { + return true; + } + } + return false; + }; + + private filteredCategories = () => { + let tempCategories: Array = new Array(); + this.$scope.categories.forEach((category) => { + if (!this.isHiddenCategory(category.name) + && this.$scope.isCreateMode() + ) { + tempCategories.push(category); + } else if ((ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState) + && !this.isHiddenCategory(this.$scope.component.selectedCategory) + && !this.isHiddenCategory(category.name) + ) { + tempCategories.push(category); + } else if ((ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState) + && this.isHiddenCategory(this.$scope.component.selectedCategory)) { + tempCategories.push(category); + } + }); + + 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'); } if (this.$scope.componentType === ComponentType.SERVICE) { this.$scope.categories = this.cacheService.get('serviceCategories'); + + //Remove categories from dropdown applicable for External API + if (this.$scope.isCreateMode() || (ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState)) { + this.$scope.categories = this.filteredCategories(); + //Flag to disbale category if service is created through External API + this.$scope.isHiddenCategorySelected = this.isHiddenCategory(this.$scope.component.selectedCategory); + } + + } + }; + + this.$scope.initInstantiationTypes = ():void => { + if (this.$scope.componentType === ComponentType.SERVICE) { + this.$scope.instantiationTypes = new Array(); + this.$scope.instantiationTypes.push(instantiationType.A_LA_CARTE); + this.$scope.instantiationTypes.push(instantiationType.MACRO); + var instantiationTypeField:string =(this.$scope.component).instantiationType; + if (instantiationTypeField === ""){ + this.$scope.instantiationTypes.push(""); + } + else if (this.isCreateModeAvailable(instantiationTypeField)) { + (this.$scope.component).instantiationType = instantiationType.A_LA_CARTE; + + } + } + }; + + 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 = this.$scope.component; + return resource.isCsarComponent(); + } + + this.$scope.initEnvironmentContext = ():void => { + if (this.$scope.componentType === ComponentType.SERVICE) { + this.$scope.environmentContextObj = this.cacheService.get('UIConfiguration').environmentContext; + var environmentContext:string =(this.$scope.component).environmentContext; + // In creation new service OR check outing old service without environmentContext parameter - set default value + if(this.isCreateModeAvailable(environmentContext)){ + (this.$scope.component).environmentContext = this.$scope.environmentContextObj.defaultValue; + } } }; @@ -296,19 +548,29 @@ export class GeneralViewModel { }; this.$scope.openOnBoardingModal = ():void => { + if(this.$scope.component.vspArchived) return; let csarUUID = (this.$scope.component).csarUUID; - this.ModalsHandler.openOnboadrdingModal('Update', csarUUID).then(()=> { - // OK - this.$scope.uploadFileChangedInGeneralTab(); - }, ()=> { - // ERROR - }); + let csarVersion = (this.$scope.component).csarVersion; + 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, component); + } + this.$scope.setComponent(component); + this.$scope.save(); + this.setImportedFileText(); + }, ()=> { + // ERROR + }); + }) }; this.$scope.updateIcon = ():void => { this.ModalsHandler.openUpdateIconModal(this.$scope.component).then((isDirty:boolean)=> { - if(!this.$scope.isCreateMode()){ - this.$state.current.data.unsavedChanges = this.$state.current.data.unsavedChanges || isDirty; + if(isDirty && !this.$scope.isCreateMode()){ + this.setUnsavedChanges(true); } }, ()=> { // ERROR @@ -316,7 +578,7 @@ export class GeneralViewModel { }; this.$scope.possibleToUpdateIcon = ():boolean => { - if(this.$scope.componentCategories.selectedCategory && (!this.$scope.component.isResource() || this.$scope.component.vendorName)){ + if(this.$scope.componentCategories.selectedCategory && (!this.$scope.component.isResource() || this.$scope.component.vendorName) && !this.$scope.component.isAlreadyCertified()){ return true; }else{ return false; @@ -340,18 +602,21 @@ export class GeneralViewModel { return; } - //????????????????????????? + let subtype:string = ComponentType.RESOURCE == this.$scope.componentType ? this.$scope.component.getComponentSubType() : undefined; + if (subtype == "SRVC") { + subtype = "VF" + } - let onFailed = (response) => { - //console.info('onFaild', response); - //this.$scope.isLoading = false; + const onFailed = (response) => { + // console.info('onFaild', response); + // this.$scope.isLoading = false; }; - let onSuccess = (validation:IValidate) => { - this.$scope.editForm["componentName"].$setValidity('nameExist', validation.isValid); + const onSuccess = (validation:IValidate) => { + this.$scope.editForm['componentName'].$setValidity('nameExist', validation.isValid); if (validation.isValid) { - //update breadcrumb after changing name + // update breadcrumb after changing name this.updateComponentNameInBreadcrumbs(); } }; @@ -372,49 +637,291 @@ export class GeneralViewModel { && !this.$scope.editForm["componentName"].$error.pattern && (!this.$scope.originComponent.name || this.$scope.component.name.toUpperCase() !== this.$scope.originComponent.name.toUpperCase()) ) { - if (!(this.$scope.componentType === ComponentType.RESOURCE && (this.$scope.component).csarUUID !== undefined) + if (!(this.$scope.componentType === ComponentType.RESOURCE && (this.$scope.component as Resource).csarUUID !== undefined) ) { this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed); } - } else if (this.$scope.originComponent.name && this.$scope.component.name.toUpperCase() === this.$scope.originComponent.name.toUpperCase()) { + } else if (this.$scope.editForm && this.$scope.originComponent.name && this.$scope.component.name.toUpperCase() === this.$scope.originComponent.name.toUpperCase()) { // Clear the error - this.$scope.editForm["componentName"].$setValidity('nameExist', true); + this.$scope.editForm['componentName'].$setValidity('nameExist', true); } } }; - this.$scope.$watchCollection('component.name', (newData:any):void => { + + this.EventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE_WITH_SAVE, (nextState) => { + if (this.$state.current.data.unsavedChanges && this.$scope.isValidForm) { + this.$scope.save().then(() => { + this.$scope.handleChangeLifecycleState(nextState); + }, () => { + console.error('Save failed, unable to change lifecycle state to ' + nextState); + }); + } else if(!this.$scope.isValidForm){ + console.error('Form is not valid'); + } else { + let newCsarVersion:string; + if(this.$scope.unsavedFile) { + newCsarVersion = (this.$scope.component as Resource).csarVersion; + } + if(this.$stateParams.componentCsar && !this.$scope.isCreateMode()) { + const onError = (): void => { + if (this.$scope.component.lifecycleState === 'NOT_CERTIFIED_CHECKIN') { + this.$scope.revert(); + } + }; + this.$scope.handleChangeLifecycleState(nextState, newCsarVersion, onError); + + } else { + this.$scope.handleChangeLifecycleState(nextState, newCsarVersion); + } + } + }); + + this.$scope.revert = ():void => { + // in state of import file leave the file in place + + this.$scope.setComponent(this.ComponentFactory.createComponent(this.$scope.originComponent)); + + if (this.$scope.component.isResource() && this.$scope.restoreFile) { + (this.$scope.component as Resource).importedFile = angular.copy(this.$scope.restoreFile); + } + + this.setImportedFileText(); + this.$scope.updateBreadcrumbs(this.$scope.component); // update on workspace + + this.$scope.componentCategories.selectedCategory = this.$scope.originComponent.selectedCategory; + this.setUnsavedChanges(false); + this.$scope.updateUnsavedFileFlag(false); + this.$scope.editForm.$setPristine(); + }; + + this.$scope.onImportFileChange = () => { + + 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 + this.$scope.restoreFile && !angular.equals(this.$scope.restoreFile, this.$scope.editForm.fileElement.value)){ // or file was swapped for a new one + this.$scope.updateUnsavedFileFlag(true); + } else { + this.$scope.updateUnsavedFileFlag(false); + this.$scope.editForm.fileElement.$setPristine(); + } + }; + + this.$scope.$watchCollection('component.name', (newData: any): void => { this.$scope.validateName(false); }); // Notify the parent if this step valid or not. - this.$scope.$watch("editForm.$valid", (newVal, oldVal) => { + this.$scope.$watch('editForm.$valid', (newVal, oldVal) => { this.$scope.setValidState(newVal); }); - this.$scope.$watch("editForm.$dirty", (newVal, oldVal) => { - if (newVal !== oldVal) { - this.$state.current.data.unsavedChanges = newVal && !this.$scope.isCreateMode(); + this.$scope.$watch('editForm.$dirty', (newVal, oldVal) => { + if (newVal && !this.$scope.isCreateMode()) { + this.setUnsavedChanges(true); } + }); - this.$scope.onCategoryChange = ():void => { + 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 => { + if (!(this.$scope.component as Service).ecompGeneratedNaming) { + (this.$scope.component as Service).namingPolicy = ''; + } + }; + + 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.onEcompGeneratedNamingChange = ():void =>{ - if(!(this.$scope.component).ecompGeneratedNaming){ - (this.$scope.component).namingPolicy = ''; + 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 => { + this.$scope.onVendorNameChange = (oldVendorName: string): void => { if (this.$scope.component.icon === oldVendorName) { this.$scope.component.icon = DEFAULT_ICON; } }; - this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.$scope.reload); - }; + + 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; + } + }