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=96ce7d5fb4cc437f769f9a7db1fcf46404b405ad;hb=a762f63e5dc464dd160808615fce1cfa6c922c90;hp=1dc326a7c027210c624ba6404492ea6aa2694ef7;hpb=6789ffa5b9dad20c0b60f427f5b6fe432d689d51;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 1dc326a7c0..96ce7d5fb4 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,11 +19,29 @@ */ 'use strict'; -import {ModalsHandler, ValidationUtils, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, DEFAULT_ICON, - ResourceType} from "app/utils"; -import {CacheService, EventListenerService, ProgressService} from "app/services"; -import {IAppConfigurtaion, IValidate, IMainCategory, Resource, ISubCategory,Service} from "app/models"; +import * as _ from "lodash"; +import {Dictionary} from "lodash"; +import { + ComponentFactory, + ComponentState, + ComponentType, + DEFAULT_ICON, + EVENTS, + instantiationType, + ModalsHandler, + ResourceType, + ValidationUtils, + FileUtils, + ServiceCsarReader +} 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"; +import {SdcUiServices} from "onap-ui-angular/dist"; export class Validation { componentNameValidationPattern:RegExp; @@ -33,17 +51,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; @@ -51,11 +78,21 @@ export interface IGeneralScope extends IWorkspaceViewModelScope { isShowFileBrowse:boolean; isShowOnboardingSelectionBrowse:boolean; importedToscaBrowseFileText:string; - importCsarProgressKey:string; + importCsarProProgressKey:string; browseFileLabel:string; componentCategories:componentCategories; + componentModel:componentModel; + instantiationTypes:Array; + isHiddenCategorySelected: boolean; + isModelRequired: boolean; + othersFlag: boolean; + functionOption: string; + othersRoleFlag: boolean; + roleOption: string; - onToscaFileChange():void + 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 @@ -63,12 +100,23 @@ 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; + setServiceFunction(option:string):void; + setServiceRole(option:string):void; } +// tslint:disable-next-line:max-classes-per-file export class GeneralViewModel { static '$inject' = [ @@ -82,16 +130,23 @@ export class GeneralViewModel { 'VendorModelNumberValidationPattern', 'CommentValidationPattern', 'ValidationUtils', + 'FileUtils', 'sdcConfig', - 'ProjectCodeValidationPattern', '$state', 'ModalsHandler', + 'ModalServiceSdcUI', 'EventListenerService', 'Notification', 'Sdc.Services.ProgressService', '$interval', '$filter', - '$timeout' + '$timeout', + 'OnboardingService', + 'ComponentFactory', + 'ImportVSPService', + 'ElementService', + 'ModelService', + '$stateParams' ]; constructor(private $scope:IGeneralScope, @@ -104,26 +159,29 @@ export class GeneralViewModel { private VendorModelNumberValidationPattern:RegExp, private CommentValidationPattern:RegExp, private ValidationUtils:ValidationUtils, + private FileUtils: FileUtils, private sdcConfig:IAppConfigurtaion, - private ProjectCodeValidationPattern:RegExp, private $state:ng.ui.IStateService, private ModalsHandler:ModalsHandler, + private modalServiceSdcUI: SdcUiServices.ModalService, private EventListenerService:EventListenerService, private Notification:any, private progressService:ProgressService, protected $interval:any, private $filter:ng.IFilterService, - private $timeout:ng.ITimeoutService) { + private $timeout:ng.ITimeoutService, + private onBoardingService: OnboardingService, + private ComponentFactory:ComponentFactory, + private importVSPService: ImportVSPService, + private elementService: ElementService, + private modelService: ModelService, + private $stateParams: any) { this.initScopeValidation(); this.initScopeMethods(); this.initScope(); - this.$scope.updateSelectedMenuItem(); } - - - private initScopeValidation = ():void => { this.$scope.validation = new Validation(); this.$scope.validation.componentNameValidationPattern = this.ComponentNameValidationPattern; @@ -133,28 +191,88 @@ 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 initScope = ():void => { + 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); + }; + + private setImportedFileText = ():void => { + + if(!this.$scope.isShowOnboardingSelectionBrowse) return; - // 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); + //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 { + if (onboardCsarFilesMap && onboardCsarFilesMap[csarUUID]) { + this.$scope.importedToscaBrowseFileText = onboardCsarFilesMap[csarUUID][csarVersion]; + } + } + } } - this.$scope.importedToscaBrowseFileText = this.$scope.component.name + " (" + (this.$scope.component).csarVersion + ")"; + + 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; + this.$scope.othersFlag = false; + this.$scope.othersRoleFlag = false; // Init UIModel 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; @@ -164,19 +282,73 @@ 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) { + this.$scope.isShowFileBrowse = true; + (this.$scope.component).ecompGeneratedNaming = true; + let blob = this.FileUtils.base64toBlob(service.importedFile.base64, "zip"); + new ServiceCsarReader().read(blob).then( + (serviceCsar) => { + serviceCsar.serviceMetadata.contactId = this.cacheService.get("user").userId; + (this.$scope.component).setComponentMetadata(serviceCsar.serviceMetadata); + (this.$scope.component).model = serviceCsar.serviceMetadata.model; + this.$scope.onModelChange(); + this.$scope.componentCategories.selectedCategory = serviceCsar.serviceMetadata.selectedCategory; + this.$scope.onCategoryChange(); + serviceCsar.extraServiceMetadata.forEach((value: string, key: string) => { + if (this.getMetadataKey(key)) { + (this.$scope.component).categorySpecificMetadata[key] = value; + } + }); + (this.$scope.component).derivedFromGenericType = serviceCsar.substitutionNodeType; + this.$scope.onBaseTypeChange(); + this.setFunctionRole(service); + }, + (error) => { + const errorMsg = this.$filter('translate')('IMPORT_FAILURE_MESSAGE_TEXT'); + console.error(errorMsg, error); + const errorDetails = { + 'Error': this.capitalize(error.reason), + 'Details': this.capitalize(error.message) + }; + this.modalServiceSdcUI.openErrorDetailModal('Error', this.$filter('translate')('IMPORT_FAILURE_MESSAGE_TEXT'), + 'error-modal', errorDetails); + this.$state.go('dashboard'); + }); + } + + this.setFunctionRole(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(); } - ; - // 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.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; } //init file extensions based on the file that was imported. @@ -190,6 +362,7 @@ 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"; @@ -208,12 +381,44 @@ 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); + }); + }; + private capitalize(s) { + return s && s[0].toUpperCase() + s.slice(1); + } + + private setFunctionRole = (service : Service) : void => { + if (service.serviceFunction) { + const functionList : string[] = this.$scope.getMetadataKeyValidValues('Service Function'); + if (functionList.find(value => value == service.serviceFunction) != undefined) { + this.$scope.functionOption = service.serviceFunction; + } else { + this.$scope.functionOption = 'Others'; + this.$scope.othersFlag = true; + } + } + + if (service.serviceRole) { + const roleList : string[] = this.$scope.getMetadataKeyValidValues('Service Role'); + if (roleList.find(value => value == service.serviceRole) != undefined) { + this.$scope.roleOption = service.serviceRole; + } else { + this.$scope.roleOption = 'Others'; + this.$scope.othersRoleFlag = true; + } + } + } + // 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]; @@ -233,27 +438,176 @@ 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") { + if (this.$scope.component.model === model.name) { + this.$scope.component.model = model.derivedFrom; + } + this.$scope.models.push(model.derivedFrom) + } else { + 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; + } } }; @@ -265,19 +619,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 @@ -285,7 +649,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; @@ -309,19 +673,23 @@ 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 - this.updateComponentNameInBreadcrumbs(); + this.updateComponentNameInBreadcrumbs(); // update breadcrumb after changing name + } else { + this.$scope.editForm['componentName'].$setDirty(); } }; @@ -341,49 +709,360 @@ 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 => { + if (!this.$scope.component.selectedCategory) { + this.$scope.editForm['category'].$setDirty(); + } + if (!this.$scope.component.description) { + this.$scope.editForm['description'].$setDirty(); + } 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.roleOption = null; + (this.$scope.component).serviceRole = null; + this.$scope.othersFlag = false; + this.$scope.functionOption = null; + (this.$scope.component).serviceFunction = null; + this.$scope.othersRoleFlag = false; + + 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 (metadataKey.name === 'Service Role') { + this.$scope.roleOption = this.$scope.component.categorySpecificMetadata[metadataKey.name]; + (this.$scope.component).serviceRole = this.$scope.roleOption; + } + if (metadataKey.name === 'Service Function') { + this.$scope.functionOption = this.$scope.component.categorySpecificMetadata[metadataKey.name]; + } + } + } + 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.getCategoryDisplayNameOrName = (mainCategory: any): string => { + return mainCategory.displayName ? mainCategory.displayName : mainCategory.name ; + } + + 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.$scope.setServiceFunction = (option:string): void => { + if (option === 'Others') { + this.$scope.othersFlag = true; + (this.$scope.component).serviceFunction = ''; + } else { + this.$scope.othersFlag = false; + (this.$scope.component).serviceFunction = option; + } + + } + + this.$scope.setServiceRole = (option:string): void => { + if (option === 'Others') { + this.$scope.othersRoleFlag = true; + (this.$scope.component).serviceRole = ''; + } else { + this.$scope.othersRoleFlag = false; + (this.$scope.component).serviceRole = option; + } + + } + + 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) { + if (key == 'Service Function' || key == 'Service Role') { + return metadataKey.validValues.concat("Others"); + } else { + 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 (attribute != 'serviceFunction' && attribute != 'serviceRole') { + if (metadatakey && (!this.$scope.component[attribute] || !metadatakey.validValues.find(v => v === this.$scope.component[attribute]))) { + this.$scope.component[attribute] = metadatakey.defaultValue; + } + } + return metadatakey != null; + } + + this.$scope.isNotApplicableMetadataKeys = (key: string): boolean => { + return this.$scope.component.categories && this.$scope.component.categories[0].notApplicableMetadataKeys && this.$scope.component.categories[0].notApplicableMetadataKeys.some(item => item === key); + } + } + + private filterCategoriesByModel(modelName:string) { + // reload categories + this.$scope.initCategories(); + this.$scope.categories = this.$scope.categories.filter(category => + !modelName ? !category.models || 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 = []; + let defaultBaseType = baseTypeResponseList.defaultBaseType; + baseTypeResponseList.baseTypes.forEach(baseType => this.$scope.baseTypes.push(baseType.toscaResourceName)); + if (this.$scope.isBaseTypeRequired || defaultBaseType != null) { + let baseType = baseTypeResponseList.baseTypes[0]; + if(defaultBaseType != null){ + baseTypeResponseList.baseTypes.forEach(baseTypeObj => { + if(baseTypeObj.toscaResourceName == defaultBaseType) { + baseType = baseTypeObj; + } + }); + } + if((this.$scope.component).derivedFromGenericType) { + baseTypeResponseList.baseTypes.forEach(baseTypeObj => { + if(baseTypeObj.toscaResourceName == (this.$scope.component).derivedFromGenericType) { + baseType = baseTypeObj; + } + }); + } + 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; + } + }