diff options
Diffstat (limited to 'catalog-ui/src/app/utils')
-rw-r--r-- | catalog-ui/src/app/utils/artifacts-utils.ts | 104 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/change-lifecycle-state-handler.ts | 163 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/common-utils.ts | 96 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/component-factory.ts | 161 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/component-instance-factory.ts | 81 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/constants.ts | 277 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/dictionary/dictionary.ts | 235 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/file-utils.ts | 51 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/functions.ts | 35 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/menu-handler.ts | 125 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/modals-handler.ts | 389 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/prototypes.ts | 64 | ||||
-rw-r--r-- | catalog-ui/src/app/utils/validation-utils.ts | 153 |
13 files changed, 1934 insertions, 0 deletions
diff --git a/catalog-ui/src/app/utils/artifacts-utils.ts b/catalog-ui/src/app/utils/artifacts-utils.ts new file mode 100644 index 0000000000..b52fe6f03e --- /dev/null +++ b/catalog-ui/src/app/utils/artifacts-utils.ts @@ -0,0 +1,104 @@ +import {ArtifactModel} from "../models/artifacts"; +import {IArtifactResourceFormViewModelScope} from "../view-models/forms/artifact-form/artifact-form-view-model"; +import {Component} from "../models/components/component"; +import {ArtifactGroupType, ArtifactType} from "./constants"; +export class ArtifactsUtils { + + static '$inject' = [ + '$filter' + ]; + + constructor(private $filter:ng.IFilterService) { + + } + + public getArtifactTypeByState(currentState:string):string { + switch (currentState) { + case "workspace.composition.lifecycle": + return "interface"; + case "workspace.composition.api": + return "api"; + case "workspace.deployment_artifacts": + case "workspace.composition.deployment": + return "deployment"; + case "workspace.composition.artifacts": + return "informational"; + default: + return "informational"; + } + } + + public getTitle(artifactType:string, selectedComponent:Component):string { + switch (artifactType) { + case "interface": + return "Lifecycle Management"; + case "api": + return "API Artifacts"; + case "deployment": + return "Deployment Artifacts"; + case "informational": + return "Informational Artifacts"; + default: + if (!selectedComponent) { + return ""; + } else { + return this.$filter("resourceName")(selectedComponent.name) + ' Artifacts'; + } + } + } + + public setArtifactType = (artifact:ArtifactModel, artifactType:string):void => { + switch (artifactType) { + case "api": + artifact.artifactGroupType = ArtifactGroupType.SERVICE_API; + break; + case "deployment": + artifact.artifactGroupType = ArtifactGroupType.DEPLOYMENT; + break; + default: + artifact.artifactGroupType = ArtifactGroupType.INFORMATION; + break; + } + }; + + public isLicenseType = (artifactType:string):boolean => { + let isLicense:boolean = false; + + if (ArtifactType.VENDOR_LICENSE === artifactType || ArtifactType.VF_LICENSE === artifactType) { + isLicense = true; + } + + return isLicense; + }; + + public removeArtifact = (artifact:ArtifactModel, artifactsArr:Array<ArtifactModel>):void => { + + if (!artifact.mandatory && (ArtifactGroupType.INFORMATION == artifact.artifactGroupType || + ArtifactGroupType.DEPLOYMENT == artifact.artifactGroupType)) { + _.remove(artifactsArr, {uniqueId: artifact.uniqueId}); + } + else { + let artifactToDelete = _.find(artifactsArr, {uniqueId: artifact.uniqueId}); + + delete artifactToDelete.esId; + delete artifactToDelete.description; + delete artifactToDelete.artifactName; + delete artifactToDelete.apiUrl; + } + }; + + public addAnotherAfterSave(scope:IArtifactResourceFormViewModelScope) { + let newArtifact = new ArtifactModel(); + this.setArtifactType(newArtifact, scope.artifactType); + scope.editArtifactResourceModel.artifactResource = newArtifact; + + scope.forms.editForm['description'].$setPristine(); + if (scope.forms.editForm['artifactLabel']) { + scope.forms.editForm['artifactLabel'].$setPristine(); + } + if (scope.forms.editForm['type']) { + scope.forms.editForm['type'].$setPristine(); + } + + } +} diff --git a/catalog-ui/src/app/utils/change-lifecycle-state-handler.ts b/catalog-ui/src/app/utils/change-lifecycle-state-handler.ts new file mode 100644 index 0000000000..dc59e3bb98 --- /dev/null +++ b/catalog-ui/src/app/utils/change-lifecycle-state-handler.ts @@ -0,0 +1,163 @@ +import {ComponentFactory} from "./component-factory"; +import {Component, Service,IAppMenu, IAppConfigurtaion} from "../models"; +import {IEmailModalModel, IEmailModalModel_Email, IEmailModalModel_Data} from "../view-models/modals/email-modal/email-modal-view-model"; +import {AsdcComment} from "../models/comments"; +import {ModalsHandler} from "./modals-handler"; +import {ServiceServiceNg2} from "../ng2/services/component-services/service.service"; + +/** + * Created by obarda on 2/11/2016. + */ + +export class ChangeLifecycleStateHandler { + + static '$inject' = [ + 'sdcConfig', + 'sdcMenu', + 'ComponentFactory', + '$filter', + 'ModalsHandler', + 'ServiceServiceNg2' + ]; + + constructor(private sdcConfig:IAppConfigurtaion, + private sdcMenu:IAppMenu, + private ComponentFactory:ComponentFactory, + private $filter:ng.IFilterService, + private ModalsHandler:ModalsHandler, + private ServiceServiceNg2:ServiceServiceNg2) { + + } + + private actualChangeLifecycleState = (component:Component, data:any, scope:any, onSuccessCallback?:Function, onErrorCallback?:Function):void => { + + let self = this; + + let getContacts = (component:Component):string => { + let testers = this.sdcConfig.testers; + let result:string = testers[component.componentType][component.categories[0].name] ? + testers[component.componentType][component.categories[0].name] : + testers[component.componentType]['default']; + return result; + }; + + let onSuccess = (newComponent:Component):void => { + //scope.isLoading = false; + console.info(component.componentType.toLowerCase + ' change state ', newComponent); + if (onSuccessCallback) { + onSuccessCallback(self.ComponentFactory.createComponent(newComponent), data.url); + } + }; + + let onError = (error):void => { + scope.isLoading = false; + console.info('Failed to changeLifecycleState to ', data.url); + if (onErrorCallback) { + onErrorCallback(error); + } + }; + + let comment:AsdcComment = new AsdcComment(); + if (data.alertModal) { + // Show alert dialog if defined in menu.json + //------------------------------------------------- + let onOk = (confirmationText):void => { + comment.userRemarks = confirmationText; + scope.isLoading = true; + component.changeLifecycleState(data.url, comment).then(onSuccess, onError); + }; + + let onCancel = ():void => { + console.info('Cancel pressed'); + scope.isLoading = false; + }; + + let modalTitle = this.sdcMenu.alertMessages[data.alertModal].title; + let modalMessage = this.sdcMenu.alertMessages[data.alertModal].message.format([component.componentType.toLowerCase()]); + this.ModalsHandler.openAlertModal(modalTitle, modalMessage).then(onOk, onCancel); + } else if (data.confirmationModal) { + // Show confirmation dialog if defined in menu.json + //------------------------------------------------- + let onOk = (confirmationText):void => { + comment.userRemarks = confirmationText; + scope.isLoading = true; + component.changeLifecycleState(data.url, comment).then(onSuccess, onError); + }; + + let onCancel = ():void => { + console.info('Cancel pressed'); + scope.isLoading = false; + }; + + let modalTitle = this.sdcMenu.confirmationMessages[data.confirmationModal].title; + let modalMessage = this.sdcMenu.confirmationMessages[data.confirmationModal].message.format([component.componentType.toLowerCase()]); + let modalShowComment = this.sdcMenu.confirmationMessages[data.confirmationModal].showComment; + this.ModalsHandler.openConfirmationModal(modalTitle, modalMessage, modalShowComment).then(onOk, onCancel); + + } else if (data.emailModal) { + // Show email dialog if defined in menu.json + //------------------------------------------------- + let onOk = (resource):void => { + if (resource) { + onSuccess(resource); + } else { + onError("Error changing life cycle state"); + } + }; + + let onCancel = ():void => { + scope.isLoading = false; + }; + + let emailModel:IEmailModalModel = <IEmailModalModel>{}; + emailModel.email = <IEmailModalModel_Email>{}; + emailModel.data = <IEmailModalModel_Data>{}; + emailModel.title = this.$filter('translate')("EMAIL_MODAL_TITLE"); + emailModel.email.to = getContacts(component); + emailModel.email.subject = this.$filter('translate')("EMAIL_MODAL_SUBJECT", "{'entityName': '" + this.$filter('resourceName')(component.name) + "','entityVersion': '" + component.version + "'}"); + emailModel.email.message = ''; + emailModel.data.component = component; + emailModel.data.stateUrl = data.url; + + this.ModalsHandler.openEmailModal(emailModel).then(onOk, onCancel); + + } else { + // Submit to server only (no modal is shown). + scope.isLoading = true; + component.changeLifecycleState(data.url, comment).then(onSuccess, onError); + } + + } + + public changeLifecycleState = (component:Component, data:any, scope:any, onSuccessCallback?:Function, onErrorCallback?:Function):void => { + + if (data.conformanceLevelModal) { + this.validateConformanceLevel(component, data, scope, onSuccessCallback, onErrorCallback); + } else { + this.actualChangeLifecycleState(component, data, scope, onSuccessCallback, onErrorCallback); + } + } + + private validateConformanceLevel = (component:Component, data:any, scope:any, onSuccessCallback?:Function, onErrorCallback?:Function):void => { + // Validate conformance level if defined in menu.json + //------------------------------------------------- + this.ServiceServiceNg2.validateConformanceLevel(<Service>component).subscribe((res:boolean) => { + if (res === true) { + //conformance level is ok - continue + this.actualChangeLifecycleState(component, data, scope, onSuccessCallback, onErrorCallback); + + } else { + //show warning modal + this.ModalsHandler.openConformanceLevelModal() + .then(() => { + //continue distribute + this.actualChangeLifecycleState(component, data, scope, onSuccessCallback, onErrorCallback); + + }).catch(() => { + //reject distribution + this.actualChangeLifecycleState(component, data.conformanceLevelModal, scope, onSuccessCallback, onErrorCallback); + }); + } + }); + } +} diff --git a/catalog-ui/src/app/utils/common-utils.ts b/catalog-ui/src/app/utils/common-utils.ts new file mode 100644 index 0000000000..d8019d2f96 --- /dev/null +++ b/catalog-ui/src/app/utils/common-utils.ts @@ -0,0 +1,96 @@ +import {Module, AttributeModel, ResourceInstance, PropertyModel, InputFEModel} from "../models"; +import {ComponentInstanceFactory} from "./component-instance-factory"; +import {PropertyBEModel, RelationshipModel} from "app/models"; + +export class CommonUtils { + + static initProperties(propertiesObj:Array<PropertyModel>, uniqueId?:string):Array<PropertyModel> { + + let properties = new Array<PropertyModel>(); + if (propertiesObj) { + _.forEach(propertiesObj, (property:PropertyModel):void => { + if (uniqueId) { + property.readonly = property.parentUniqueId != uniqueId; + } + properties.push(new PropertyModel(property)); + }); + } + return properties; + }; + + static initAttributes(attributesObj:Array<AttributeModel>, uniqueId?:string):Array<AttributeModel> { + + let attributes = new Array<AttributeModel>(); + if (attributesObj) { + _.forEach(attributesObj, (attribute:AttributeModel):void => { + if (uniqueId) { + attribute.readonly = attribute.parentUniqueId != uniqueId; + } + attributes.push(new AttributeModel(attribute)); + }); + } + return attributes; + }; + + static initComponentInstances(componentInstanceObj:Array<ResourceInstance>):Array<ResourceInstance> { + + let componentInstances = new Array<ResourceInstance>(); + if (componentInstanceObj) { + _.forEach(componentInstanceObj, (instance:ResourceInstance):void => { + componentInstances.push(ComponentInstanceFactory.createComponentInstance(instance)); + }); + } + return componentInstances; + }; + + static initModules(moduleArrayObj:Array<Module>):Array<Module> { + + let modules = new Array<Module>(); + + if (moduleArrayObj) { + _.forEach(moduleArrayObj, (module:Module):void => { + if (module.type === "org.openecomp.groups.VfModule") { + modules.push(new Module(module)); + } + }); + } + return modules; + }; + + static initInputs(inputsObj:Array<PropertyBEModel>):Array<PropertyBEModel> { + + let inputs = new Array<PropertyBEModel>(); + + if(inputsObj) { + _.forEach(inputsObj, (input:PropertyBEModel):void => { + inputs.push(new PropertyBEModel(input)); + }) + } + + return inputs; + } + + static initBeProperties(propertiesObj: Array<PropertyBEModel>): Array<PropertyBEModel> { + + let properties = new Array<PropertyBEModel>(); + + if (propertiesObj) { + _.forEach(propertiesObj, (property: PropertyBEModel): void => { + properties.push(new PropertyBEModel(property)); + }) + } + + return properties; + } + + static initComponentInstanceRelations = (componentInstanceRelationsObj:Array<RelationshipModel>):Array<RelationshipModel> => { + if (componentInstanceRelationsObj) { + let componentInstancesRelations: Array<RelationshipModel> = []; + _.forEach(componentInstanceRelationsObj, (instanceRelation:RelationshipModel):void => { + componentInstancesRelations.push(new RelationshipModel(instanceRelation)); + }); + return componentInstancesRelations; + } + }; +} + diff --git a/catalog-ui/src/app/utils/component-factory.ts b/catalog-ui/src/app/utils/component-factory.ts new file mode 100644 index 0000000000..18edbfb02c --- /dev/null +++ b/catalog-ui/src/app/utils/component-factory.ts @@ -0,0 +1,161 @@ +'use strict'; +import {DEFAULT_ICON, ResourceType, ComponentType} from "./constants"; +import {ServiceService, CacheService, ResourceService, ProductService} from "app/services"; +import {IMainCategory, ISubCategory, ICsarComponent, Component, Resource, Service, Product} from "app/models"; +import {ComponentMetadata} from "../models/component-metadata"; +import {ComponentServiceNg2} from "../ng2/services/component-services/component.service"; +import {ComponentGenericResponse} from "../ng2/services/responses/component-generic-response"; + + +export class ComponentFactory { + + static '$inject' = [ + 'Sdc.Services.Components.ResourceService', + 'Sdc.Services.Components.ServiceService', + 'Sdc.Services.Components.ProductService', + 'Sdc.Services.CacheService', + '$q', + 'ComponentServiceNg2' + ]; + + constructor(private ResourceService:ResourceService, + private ServiceService:ServiceService, + private ProductService:ProductService, + private cacheService:CacheService, + private $q:ng.IQService, + private ComponentServiceNg2: ComponentServiceNg2) { + } + + public createComponent = (component:Component):Component => { + let newComponent:Component; + switch (component.componentType) { + + case 'SERVICE': + newComponent = new Service(this.ServiceService, this.$q, <Service> component); + break; + + case 'RESOURCE': + newComponent = new Resource(this.ResourceService, this.$q, <Resource> component); + break; + + case 'PRODUCT': + newComponent = new Product(this.ProductService, this.$q, <Product> component); + break; + } + return newComponent; + }; + + public createProduct = (product:Product):Product => { + let newProduct:Product = new Product(this.ProductService, this.$q, <Product> product); + return newProduct; + }; + + public createService = (service:Service):Service => { + let newService:Service = new Service(this.ServiceService, this.$q, <Service> service); + return newService; + }; + + public createResource = (resource:Resource):Resource => { + let newResource:Resource = new Resource(this.ResourceService, this.$q, <Resource> resource); + return newResource; + }; + + public createFromCsarComponent = (csar:ICsarComponent):Component => { + let newResource:Resource = <Resource>this.createEmptyComponent(ComponentType.RESOURCE); + newResource.name = csar.vspName; + + /** + * Onboarding CSAR contains category and sub category that are uniqueId. + * Need to find the category and sub category and extract the name from them. + * First concat all sub categories to one array. + * Then find the selected sub category and category. + * @type {any} + */ + let availableCategories = angular.copy(this.cacheService.get('resourceCategories')); + let allSubs = []; + _.each(availableCategories, (main:IMainCategory)=> { + if (main.subcategories) { + allSubs = allSubs.concat(main.subcategories); + } + }); + + let selectedCategory:IMainCategory = _.find(availableCategories, function (main:IMainCategory) { + return main.uniqueId === csar.category; + }); + + let selectedSubCategory:ISubCategory = _.find(allSubs, (sub:ISubCategory)=> { + return sub.uniqueId === csar.subCategory; + }); + + // Build the categories and sub categories array (same format as component category) + let categories:Array<IMainCategory> = new Array(); + let subcategories:Array<ISubCategory> = new Array(); + if (selectedCategory && selectedSubCategory) { + subcategories.push(selectedSubCategory); + selectedCategory.subcategories = subcategories; + categories.push(selectedCategory); + } + + // Fill the component with details from CSAR + newResource.selectedCategory = selectedCategory && selectedSubCategory ? selectedCategory.name + "_#_" + selectedSubCategory.name : ''; + newResource.categories = categories; + newResource.vendorName = csar.vendorName; + newResource.vendorRelease = csar.vendorRelease; + newResource.csarUUID = csar.packageId; + newResource.csarPackageType = csar.packageType; + newResource.csarVersion = csar.version; + newResource.packageId = csar.packageId; + newResource.description = csar.description; + return newResource; + }; + + public createEmptyComponent = (componentType:string):Component => { + let newComponent:Component; + + switch (componentType) { + + case ComponentType.SERVICE: + newComponent = new Service(this.ServiceService, this.$q); + break; + + case ComponentType.RESOURCE: + case ResourceType.VF: + case ResourceType.VL: + case ResourceType.VFC: + case ResourceType.CP: + newComponent = new Resource(this.ResourceService, this.$q); + break; + + case ComponentType.PRODUCT: + newComponent = new Product(this.ProductService, this.$q); + break; + } + newComponent.componentType = componentType; + newComponent.tags = []; + newComponent.icon = DEFAULT_ICON; + return newComponent; + }; + + public getComponentFromServer = (componentType:string, componentId:string):ng.IPromise<Component> => { + let newComponent:Component = this.createEmptyComponent(componentType); + newComponent.setUniqueId(componentId); + return newComponent.getComponent(); + }; + + public createComponentOnServer = (componentObject:Component):ng.IPromise<Component> => { + let component:Component = this.createComponent(componentObject); + return component.createComponentOnServer(); + + }; + + public getComponentWithMetadataFromServer = (componentType:string, componentId:string):ng.IPromise<Component> => { + let deferred = this.$q.defer(); + let component = this.createEmptyComponent(componentType); + component.setUniqueId(componentId); + this.ComponentServiceNg2.getComponentMetadata(component).subscribe((response:ComponentGenericResponse) => { + component.setComponentMetadata(response.metadata); + deferred.resolve(component); + }); + return deferred.promise; + } +} diff --git a/catalog-ui/src/app/utils/component-instance-factory.ts b/catalog-ui/src/app/utils/component-instance-factory.ts new file mode 100644 index 0000000000..df92f20c90 --- /dev/null +++ b/catalog-ui/src/app/utils/component-instance-factory.ts @@ -0,0 +1,81 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +/** + * Created by obarda on 3/7/2016. + */ +'use strict'; +import {ComponentInstance, ServiceInstance, ProductInstance, ResourceInstance, Component} from "../models"; +import {LeftPaletteComponent} from "../models/components/displayComponent"; + +export class ComponentInstanceFactory { + + static createComponentInstance(componentInstance:ComponentInstance):ComponentInstance { + let newComponentInstance:ComponentInstance; + switch (componentInstance.originType) { + case 'SERVICE': + newComponentInstance = new ServiceInstance(componentInstance); + break; + + case 'PRODUCT': + newComponentInstance = new ProductInstance(componentInstance); + break; + + default : + newComponentInstance = new ResourceInstance(componentInstance); + break; + } + return newComponentInstance; + }; + + public createEmptyComponentInstance = (componentInstanceType?:string):ComponentInstance => { + let newComponentInstance:ComponentInstance; + switch (componentInstanceType) { + case 'SERVICE': + newComponentInstance = new ServiceInstance(); + break; + + case 'PRODUCT': + newComponentInstance = new ProductInstance(); + break; + + default : + newComponentInstance = new ResourceInstance(); + break; + } + return newComponentInstance; + }; + + public createComponentInstanceFromComponent = (component:LeftPaletteComponent):ComponentInstance => { + let newComponentInstance:ComponentInstance = this.createEmptyComponentInstance(component.componentType); + newComponentInstance.uniqueId = component.uniqueId + (new Date()).getTime(); + newComponentInstance.posX = 0; + newComponentInstance.posY = 0; + newComponentInstance.name = component.name; + newComponentInstance.componentVersion = component.version; + newComponentInstance.originType = component.getComponentSubType(); + //new component instance -> req. & cap. are added on successful instance creation + newComponentInstance.requirements = component.requirements; + newComponentInstance.capabilities = component.capabilities; + newComponentInstance.icon = component.icon; + newComponentInstance.componentUid = component.uniqueId; + return newComponentInstance; + }; + +} diff --git a/catalog-ui/src/app/utils/constants.ts b/catalog-ui/src/app/utils/constants.ts new file mode 100644 index 0000000000..d55079d662 --- /dev/null +++ b/catalog-ui/src/app/utils/constants.ts @@ -0,0 +1,277 @@ +/** + * Created by obarda on 2/18/2016. + */ + +export let DEFAULT_ICON = 'defaulticon'; +export let CP_END_POINT = 'CpEndPoint'; +export let CHANGE_COMPONENT_CSAR_VERSION_FLAG = 'changeComponentCsarVersion'; + +export class ComponentType { + static SERVICE = 'SERVICE'; + static RESOURCE = 'RESOURCE'; + static PRODUCT = 'PRODUCT'; +} + +export class ServerTypeUrl { + static RESOURCES = 'resources/'; + static SERVICES = 'services/'; + static PRODUCTS = 'product/'; +} + +export class ResourceType { + static VF = 'VF'; + static VL = 'VL'; + static CP = 'CP'; + static VFC = 'VFC'; + static VFCMT = 'VFCMT'; +} + +export class ComponentState { + static CERTIFICATION_IN_PROGRESS = 'CERTIFICATION_IN_PROGRESS'; + static CERTIFIED = 'CERTIFIED'; + static NOT_CERTIFIED_CHECKOUT = 'NOT_CERTIFIED_CHECKOUT'; + static NOT_CERTIFIED_CHECKIN = 'NOT_CERTIFIED_CHECKIN'; + static READY_FOR_CERTIFICATION = 'READY_FOR_CERTIFICATION'; +} + +export class DistributionStatus { + DISTRIBUTION_NOT_APPROVED = 'DISTRIBUTION_NOT_APPROVED'; + DISTRIBUTION_APPROVED = 'DISTRIBUTION_APPROVED'; + DISTRIBUTED = 'DISTRIBUTED'; + DISTRIBUTION_REJECTED = 'DISTRIBUTION_REJECTED'; +} + +export class ArtifactGroupType { + static DEPLOYMENT = "DEPLOYMENT"; + static INFORMATION = "INFORMATIONAL"; + static SERVICE_API = "SERVICE_API"; +} + +export class ArtifactType { + static HEAT = "HEAT"; + static HEAT_VOL = "HEAT_VOL"; + static HEAT_NET = "HEAT_NET"; + static VF_LICENSE = "VF_LICENSE"; + static VENDOR_LICENSE = "VENDOR_LICENSE"; + static THIRD_PARTY_RESERVED_TYPES = { + WORKFLOW: "WORKFLOW", + NETWORK_CALL_FLOW: "NETWORK_CALL_FLOW", + AAI_SERVICE_MODEL: "AAI_SERVICE_MODEL", + AAI_VF_MODEL: "AAI_VF_MODEL", + AAI_VF_MODULE_MODEL: "AAI_VF_MODULE_MODEL", + AAI_VF_INSTANCE_MODEL: "AAI_VF_INSTANCE_MODEL" + }; + static TOSCA = {TOSCA_TEMPLATE: "TOSCA_TEMPLATE", TOSCA_CSAR: "TOSCA_CSAR"}; +} + +export class SEVERITY { + public static DEBUG = 'DEBUG'; + public static INFO = 'INFO'; + public static WARNING = 'WARNING'; + public static ERROR = 'ERROR'; +} + +export class PROPERTY_TYPES { + public static STRING = 'string'; + public static INTEGER = 'integer'; + public static FLOAT = 'float'; + public static BOOLEAN = 'boolean'; + public static JSON = 'json'; + public static MAP = 'map'; + public static LIST = 'list'; +} + +export class SOURCES { + public static A_AND_AI = 'A&AI'; + public static ORDER = 'Order'; + public static RUNTIME = 'Runtime'; +} + +export class PROPERTY_DATA { + public static TYPES = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.FLOAT, PROPERTY_TYPES.BOOLEAN, PROPERTY_TYPES.JSON, PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP]; + public static SIMPLE_TYPES = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.FLOAT, PROPERTY_TYPES.BOOLEAN, PROPERTY_TYPES.JSON]; + public static ROOT_DATA_TYPE = "tosca.datatypes.Root"; + public static SOURCES = [SOURCES.A_AND_AI, SOURCES.ORDER, SOURCES.RUNTIME]; +} + +export class PROPERTY_VALUE_CONSTRAINTS { + public static MAX_LENGTH = 100; + public static JSON_MAX_LENGTH = 4096; +} + +export class Role { + public static ADMIN = 'ADMIN'; + public static DESIGNER = 'DESIGNER'; + public static PRODUCT_STRATEGIST = 'PRODUCT_STRATEGIST'; + public static PRODUCT_MANAGER = 'PRODUCT_MANAGER'; + public static TESTER = 'TESTER'; + public static OPS = 'OPS'; + public static GOVERNOR = 'GOVERNOR'; +} + +export enum FormState{ + CREATE, + UPDATE, + IMPORT, + VIEW +} + +export class WorkspaceMode { + public static CREATE = 'create'; + public static EDIT = 'edit'; + public static IMPORT = 'import'; + public static VIEW = 'view'; +} + +export class ImagesUrl { + public static RESOURCE_ICONS = '/assets/styles/images/resource-icons/'; + public static SERVICE_ICONS = '/assets/styles/images/service-icons/'; + public static SELECTED_UCPE_INSTANCE = '/assets/styles/images/resource-icons/selectedUcpeInstance.png'; + public static SELECTED_CP_INSTANCE = '/assets/styles/images/resource-icons/selectedCPInstance.png'; + public static SELECTED_VL_INSTANCE = '/assets/styles/images/resource-icons/selectedVLInstance.png'; + public static CANVAS_PLUS_ICON = '/assets/styles/images/resource-icons/canvasPlusIcon.png'; + public static MODULE_ICON = '/assets/styles/images/resource-icons/module.png'; + public static OPEN_MODULE_ICON = '/assets/styles/images/resource-icons/openModule.png'; + public static OPEN_MODULE_HOVER_ICON = '/assets/styles/images/resource-icons/openModuleHover.png'; + public static CLOSE_MODULE_ICON = '/assets/styles/images/resource-icons/closeModule.png'; + public static CLOSE_MODULE_HOVER_ICON = '/assets/styles/images/resource-icons/closeModuleHover.png'; +} + +export class ModalType { + static STANDARD = 'standard'; + static ERROR = 'error'; + static ALERT = 'alert'; +} + +export class GraphColors { + public static NOT_CERTIFIED_LINK = 'rgb(218,31,61)'; + public static VL_LINK = 'rgb(216,216,216)'; + public static ACTIVE_LINK = '#30bdf2'; + public static BASE_LINK = 'rgb(55,55,55)'; + public static NODE_BACKGROUND_COLOR = 'rgba(46, 162, 157, 0.24)'; + public static NODE_SHADOW_COLOR = 'rgba(198, 230, 228, 0.7)'; + public static NODE_OVERLAPPING_BACKGROUND_COLOR = 'rgba(179, 10, 60, 0.24)'; + public static NODE_OVERLAPPING_SHADOW_COLOR = 'rgba(236, 194, 206, 0.7)'; + public static NODE_UCPE_CP = '#9063cd'; + public static NODE_UCPE = '#fbfbfb'; + public static NODE_SELECTED_BORDER_COLOR = '#30bdf2'; +} +export class GraphTransactionLogText { + public static REMOVE_TEMP_LINK = "remove tempLink"; + public static DELETE_LINK = "delete link"; + public static ADD_LINK = "delete link"; + public static ADD_NODE = "adding node"; +} + +export class GraphUIObjects { + public static LINK_MENU_HEIGHT = 420; + public static TOP_HEADER_HEIGHT = 200; + public static TOOLTIP_OFFSET_X = 50; + public static TOOLTIP_OFFSET_Y = 145; + public static TOOLTIP_LINK_OFFSET_X = 35; + public static TOOLTIP_LINK_OFFSET_Y = 75; + public static MENU_LINK_VL_HEIGHT_OFFSET = 250; + public static MENU_LINK_VL_WIDTH_OFFSET = 200; + public static MENU_LINK_SIMPLE_HEIGHT_OFFSET = 180; + public static MENU_LINK_SIMPLE_WIDTH_OFFSET = 130; + public static DIAGRAM_RIGHT_WIDTH_OFFSET = 248; + public static DIAGRAM_HEADER_OFFSET = 103; + public static DIAGRAM_PALETTE_WIDTH_OFFSET = 247; + // public static COMPOSITION_HEADER_OFFSET = 50; + // public static COMPOSITION_NODE_MENU_WIDTH = 230; + // public static COMPOSITION_NODE_MENU_HEIGHT = 200; + // public static COMPOSITION_RIGHT_PANEL_OFFSET = 300; +} + + +export class States { + public static WORKSPACE_GENERAL = 'workspace.general'; + public static WORKSPACE_ICONS = 'workspace.icons'; + public static WORKSPACE_ACTIVITY_LOG = 'workspace.activity_log'; + public static WORKSPACE_DEPLOYMENT_ARTIFACTS = 'workspace.deployment_artifacts'; + public static WORKSPACE_PROPERTIES = 'workspace.properties'; + public static WORKSPACE_SERVICE_INPUTS = 'workspace.service_inputs'; + public static WORKSPACE_RESOURCE_INPUTS = 'workspace.resource_inputs'; + public static WORKSPACE_ATTRIBUTES = 'workspace.attributes'; + public static WORKSPACE_HIERARCHY = 'workspace.hierarchy'; + public static WORKSPACE_INFORMATION_ARTIFACTS = 'workspace.information_artifacts'; + public static WORKSPACE_TOSCA_ARTIFACTS = 'workspace.tosca_artifacts'; + public static WORKSPACE_COMPOSITION = 'workspace.composition'; + public static WORKSPACE_NETWORK_CALL_FLOW = 'workspace.network_call_flow'; + public static WORKSPACE_MANAGEMENT_WORKFLOW = 'workspace.management_workflow'; + public static WORKSPACE_DEPLOYMENT = 'workspace.deployment'; + public static WORKSPACE_DISTRIBUTION = 'workspace.distribution'; + public static WORKSPACE_PROPERTIES_ASSIGNMENT = 'workspace.properties_assignment'; + public static WORKSPACE_REQUIREMENTS_AND_CAPABILITIES = 'workspace.reqAndCap'; + public static WORKSPACE_NG2 = 'workspace.ng2'; +} + +export class EVENTS { + static RESOURCE_LEFT_PALETTE_UPDATE_EVENT = "resourceLeftPanelUpdateEvent"; + static SERVICE_LEFT_PALETTE_UPDATE_EVENT = "serviceLeftPanelUpdateEvent"; + static PRODUCT_LEFT_PALETTE_UPDATE_EVENT = "productLeftPanelUdateEvent"; + static VL_LEFT_PALETTE_UPDATE_EVENT = "vlLeftPanelUdateEvent"; + static ON_CSAR_LOADING = "onCsarLoading"; + static DOWNLOAD_ARTIFACT_FINISH_EVENT = "downloadArtifactFinishEvent"; + static ON_WORKSPACE_SAVE_BUTTON_CLICK = "onWorkspaceSaveButtonClick"; + static ON_WORKSPACE_SAVE_BUTTON_SUCCESS = "onWorkspaceSaveButtonSuccess"; + static ON_WORKSPACE_SAVE_BUTTON_ERROR = "onWorkspaceSaveButtonError"; + + //Loader events + static SHOW_LOADER_EVENT = "showLoaderEvent"; + static HIDE_LOADER_EVENT = "hideLoaderEvent"; +} + + +export class UNIQUE_GROUP_PROPERTIES_NAME { + public static MIN_VF_MODULE_INSTANCES = 'min_vf_module_instances'; + public static MAX_VF_MODULE_INSTANCES = 'max_vf_module_instances'; + public static INITIAL_COUNT = 'initial_count'; + public static IS_BASE = 'isBase'; + public static VF_MODULE_TYPE = 'vf_module_type'; + public static VF_MODULE_LABEL = 'vf_module_label'; + public static VF_MODULE_DESCRIPTION = 'vf_module_description'; + public static VOLUME_GROUP = 'volume_group'; +} + + +export class GRAPH_EVENTS { + static ON_COMPOSITION_GRAPH_DATA_LOADED = 'onCompositionGraphDataLoaded'; + static ON_DEPLOYMENT_GRAPH_DATA_LOADED = 'onDeploymentGraphDataLoaded'; + static ON_NODE_SELECTED = "onNodeSelected"; + static ON_GRAPH_BACKGROUND_CLICKED = "onGraphBackgroundClicked"; + static ON_PALETTE_COMPONENT_HOVER_IN = 'onPaletteComponentHoverIn'; + static ON_PALETTE_COMPONENT_HOVER_OUT = 'onPaletteComponentHoverOut'; + static ON_PALETTE_COMPONENT_DRAG_START = 'onPaletteComponentDragStart'; + static ON_PALETTE_COMPONENT_DRAG_ACTION = 'onPaletteComponentDragAction'; + static ON_COMPONENT_INSTANCE_NAME_CHANGED = 'onComponentInstanceNameChanged'; + static ON_DELETE_COMPONENT_INSTANCE = 'onDeleteComponentInstance'; + static ON_DELETE_MULTIPLE_COMPONENTS = 'onDeleteMultipleComponents'; + static ON_DELETE_EDGE = 'onDeleteEdge'; + static ON_INSERT_NODE_TO_UCPE = 'onInsertNodeToUCPE'; + static ON_REMOVE_NODE_FROM_UCPE = 'onRemoveNodeFromUCPE'; + static ON_VERSION_CHANGED = 'onVersionChanged'; + static ON_CREATE_COMPONENT_INSTANCE = 'onCreateComponentInstance'; +} + + +export class COMPONENT_FIELDS { + static COMPONENT_INSTANCES_PROPERTIES = "componentInstancesProperties"; + static COMPONENT_INSTANCES_ATTRIBUTES = "componentInstancesAttributes"; + static COMPONENT_ATTRIBUTES = "attributes"; + static COMPONENT_INSTANCES = "componentInstances"; + static COMPONENT_INSTANCES_RELATION = "componentInstancesRelations"; + static COMPONENT_INPUTS = "inputs"; + static COMPONENT_METADATA = "metadata"; + static COMPONENT_DEPLOYMENT_ARTIFACTS = "deploymentArtifacts"; + static COMPONENT_INFORMATIONAL_ARTIFACTS = "artifacts"; + static COMPONENT_PROPERTIES = "properties"; + static COMPONENT_CAPABILITIES = "capabilities"; + static COMPONENT_REQUIREMENTS = "requirements"; + static COMPONENT_TOSCA_ARTIFACTS = "toscaArtifacts"; + static COMPONENT_GROUPS = "groups"; + +} +export class API_QUERY_PARAMS { + static INCLUDE = "include"; +} diff --git a/catalog-ui/src/app/utils/dictionary/dictionary.ts b/catalog-ui/src/app/utils/dictionary/dictionary.ts new file mode 100644 index 0000000000..fd2a028c34 --- /dev/null +++ b/catalog-ui/src/app/utils/dictionary/dictionary.ts @@ -0,0 +1,235 @@ +/** + + This code was copy from collections.ts lib + https://github.com/basarat/typescript-collections + **/ +'use strict'; + +// Used internally by dictionary +interface IDictionaryPair<K, V> { + key:K; + value:V; +} + +export class Dictionary<K, V> { + + /** + * Object holding the key-value pairs. + * @type {Object} + * @private + */ + private table:{ [key:string]:IDictionaryPair<K, V> }; + //: [key: K] will not work since indices can only by strings in javascript and typescript enforces this. + + /** + * Number of elements in the list. + * @type {number} + * @private + */ + private nElements:number; + + /** + * Function used to convert keys to strings. + * @type {function(Object):string} + * @private + */ + private toStr:(key:K) => string; + + + /** + * Creates an empty dictionary. + * @class <p>Dictionaries map keys to values; each key can map to at most one value. + * This implementation accepts any kind of objects as keys.</p> + * + * <p>If the keys are custom objects a function which converts keys to unique + * strings must be provided. Example:</p> + * <pre> + * function petToString(pet) { + * return pet.name; + * } + * </pre> + * @constructor + * @param {function(Object):string=} toStrFunction optional function used + * to convert keys to strings. If the keys aren"t strings or if toString() + * is not appropriate, a custom function which receives a key and returns a + * unique string must be provided. + */ + constructor(toStrFunction?:(key:K) => string) { + this.table = {}; + this.nElements = 0; + this.toStr = toStrFunction || this.defaultToString; + } + + + /** + copy from angular.js isUndefined + */ + private isUndefined = (value:any):boolean => { + return typeof value === 'undefined'; + } + + defaultToString = (item:any):string => { + return item.toString(); + } + + /** + * Returns the value to which this dictionary maps the specified key. + * Returns undefined if this dictionary contains no mapping for this key. + * @param {Object} key key whose associated value is to be returned. + * @return {*} the value to which this dictionary maps the specified key or + * undefined if the map contains no mapping for this key. + */ + getValue = (key:K):V => { + let pair:IDictionaryPair<K, V> = this.table[this.toStr(key)]; + if (this.isUndefined(pair)) { + return undefined; + } + return pair.value; + } + + + /** + * Associates the specified value with the specified key in this dictionary. + * If the dictionary previously contained a mapping for this key, the old + * value is replaced by the specified value. + * @param {Object} key key with which the specified value is to be + * associated. + * @param {Object} value value to be associated with the specified key. + * @return {*} previous value associated with the specified key, or undefined if + * there was no mapping for the key or if the key/value are undefined. + */ + setValue = (key:K, value:V):V => { + + if (this.isUndefined(key) || this.isUndefined(value)) { + return undefined; + } + + let ret:V; + let k = this.toStr(key); + let previousElement:IDictionaryPair<K, V> = this.table[k]; + if (this.isUndefined(previousElement)) { + this.nElements++; + ret = undefined; + } else { + ret = previousElement.value; + } + this.table[k] = { + key: key, + value: value + }; + return ret; + } + + /** + * Removes the mapping for this key from this dictionary if it is present. + * @param {Object} key key whose mapping is to be removed from the + * dictionary. + * @return {*} previous value associated with specified key, or undefined if + * there was no mapping for key. + */ + remove = (key:K):V => { + let k = this.toStr(key); + let previousElement:IDictionaryPair<K, V> = this.table[k]; + if (!this.isUndefined(previousElement)) { + delete this.table[k]; + this.nElements--; + return previousElement.value; + } + return undefined; + } + + /** + * Returns an array containing all of the keys in this dictionary. + * @return {Array} an array containing all of the keys in this dictionary. + */ + keys = ():K[] => { + let array:K[] = []; + for (let name in this.table) { + if (this.table.hasOwnProperty(name)) { + let pair:IDictionaryPair<K, V> = this.table[name]; + array.push(pair.key); + } + } + return array; + } + + /** + * Returns an array containing all of the values in this dictionary. + * @return {Array} an array containing all of the values in this dictionary. + */ + values = ():V[] => { + let array:V[] = []; + for (let name in this.table) { + if (this.table.hasOwnProperty(name)) { + let pair:IDictionaryPair<K, V> = this.table[name]; + array.push(pair.value); + } + } + return array; + } + + /** + * Executes the provided function once for each key-value pair + * present in this dictionary. + * @param {function(Object,Object):*} callback function to execute, it is + * invoked with two arguments: key and value. To break the iteration you can + * optionally return false. + */ + forEach = (callback:(key:K, value:V) => any):void => { + for (let name in this.table) { + if (this.table.hasOwnProperty(name)) { + let pair:IDictionaryPair<K, V> = this.table[name]; + let ret = callback(pair.key, pair.value); + if (ret === false) { + return; + } + } + } + } + + /** + * Returns true if this dictionary contains a mapping for the specified key. + * @param {Object} key key whose presence in this dictionary is to be + * tested. + * @return {boolean} true if this dictionary contains a mapping for the + * specified key. + */ + containsKey = (key:K):boolean => { + return !this.isUndefined(this.getValue(key)); + } + + /** + * Removes all mappings from this dictionary. + * @this {Dictionary} + */ + clear = () => { + + this.table = {}; + this.nElements = 0; + } + + /** + * Returns the number of keys in this dictionary. + * @return {number} the number of key-value mappings in this dictionary. + */ + size = ():number => { + return this.nElements; + } + + /** + * Returns true if this dictionary contains no mappings. + * @return {boolean} true if this dictionary contains no mappings. + */ + isEmpty = ():boolean => { + return this.nElements <= 0; + } + + toString = ():string => { + let toret = "{"; + this.forEach((k, v) => { + toret = toret + "\n\t" + k.toString() + " : " + v.toString(); + }); + return toret + "\n}"; + } +} // End of dictionary + diff --git a/catalog-ui/src/app/utils/file-utils.ts b/catalog-ui/src/app/utils/file-utils.ts new file mode 100644 index 0000000000..d8c18229c3 --- /dev/null +++ b/catalog-ui/src/app/utils/file-utils.ts @@ -0,0 +1,51 @@ +export class FileUtils { + + static '$inject' = [ + '$window' + ]; + + constructor(private $window:any) { + } + + public byteCharactersToBlob = (byteCharacters, contentType):any => { + contentType = contentType || ''; + let sliceSize = 1024; + let bytesLength = byteCharacters.length; + let slicesCount = Math.ceil(bytesLength / sliceSize); + let byteArrays = new Array(slicesCount); + + for (let sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) { + let begin = sliceIndex * sliceSize; + let end = Math.min(begin + sliceSize, bytesLength); + + let bytes = new Array(end - begin); + for (let offset = begin, i = 0; offset < end; ++i, ++offset) { + bytes[i] = byteCharacters[offset].charCodeAt(0); + } + byteArrays[sliceIndex] = new Uint8Array(bytes); + } + return new Blob(byteArrays, {type: contentType}); + }; + + public base64toBlob = (base64Data, contentType):any => { + let byteCharacters = atob(base64Data); + return this.byteCharactersToBlob(byteCharacters, contentType); + }; + + public downloadFile = (blob, fileName):void=> { + let url = this.$window.URL.createObjectURL(blob); + let downloadLink = document.createElement("a"); + + downloadLink.setAttribute('href', url); + downloadLink.setAttribute('download', fileName); + document.body.appendChild(downloadLink); + + var clickEvent = new MouseEvent("click", { + "view": window, + "bubbles": true, + "cancelable": true + }); + downloadLink.dispatchEvent(clickEvent); + + } +} diff --git a/catalog-ui/src/app/utils/functions.ts b/catalog-ui/src/app/utils/functions.ts new file mode 100644 index 0000000000..24f8008393 --- /dev/null +++ b/catalog-ui/src/app/utils/functions.ts @@ -0,0 +1,35 @@ +export class QueueUtils { + + private executionQueue:any; + + constructor(private $q:ng.IQService) { + this.executionQueue = this.getDummyPromise(); + } + + + private getDummyPromise = ():ng.IPromise<boolean> => { + let deferred:ng.IDeferred<boolean> = this.$q.defer(); + deferred.resolve(true); + return deferred.promise; + }; + + + private addMethodToQueue = (runMe:Function):void => { + this.executionQueue = this.executionQueue.then(runMe, runMe); + }; + + addNonBlockingUIAction = (update:Function, releaseUIcallBack:Function):void => { + releaseUIcallBack(); + this.addMethodToQueue(update); + }; + + // The Method call is responsible for releasing the UI + addBlockingUIAction = (blockingServerRequest:Function):void => { + this.addMethodToQueue(blockingServerRequest); + }; + + addBlockingUIActionWithReleaseCallback = (blockingServerRequest:Function, releaseUIcallBack:Function):void=> { + this.addMethodToQueue(blockingServerRequest); + this.addMethodToQueue(releaseUIcallBack); + }; +} diff --git a/catalog-ui/src/app/utils/menu-handler.ts b/catalog-ui/src/app/utils/menu-handler.ts new file mode 100644 index 0000000000..1dc5a203e2 --- /dev/null +++ b/catalog-ui/src/app/utils/menu-handler.ts @@ -0,0 +1,125 @@ +'use strict'; +import {WorkspaceMode, ComponentState} from "./constants"; +import {IAppConfigurtaion, IAppMenu, Component} from "../models"; +import {ComponentFactory} from "./component-factory"; +import {ModalsHandler} from "./modals-handler"; + +export class MenuItem { + text:string; + callback:(...args:Array<any>) => ng.IPromise<boolean>; + state:string; + action:string; + params:Array<any>; + isDisabled:boolean; + disabledRoles:Array<string>; + blockedForTypes:Array<string>; // This item will not be shown for specific components types. + + //TODO check if needed + alertModal:string; + conformanceLevelModal: boolean; // Call validateConformanceLevel API and shows conformanceLevelModal if necessary, then continue with action or invokes another action + confirmationModal:string; // Open confirmation modal (user should select "OK" or "Cancel"), and continue with the action. + emailModal:string; // Open email modal (user should fill email details), and continue with the action. + url:string; // Data added to menu item, in case the function need to use it, example: for function "changeLifecycleState", I need to pass also the state "CHECKOUT" that I want the state to change to. + + + constructor(text:string, callback:(...args:Array<any>) => ng.IPromise<boolean>, state:string, action:string, params?:Array<any>, blockedForTypes?:Array<string>) { + this.text = text; + this.callback = callback; + this.state = state; + this.action = action; + this.params = params; + this.blockedForTypes = blockedForTypes; + } +} + +export class MenuItemGroup { + selectedIndex:number; + menuItems:Array<MenuItem>; + itemClick:boolean; + + constructor(selectedIndex?:number, menuItems?:Array<MenuItem>, itemClick?:boolean) { + this.selectedIndex = selectedIndex; + this.menuItems = menuItems; + this.itemClick = itemClick; + } + + public updateSelectedMenuItemText(newText:string) { + this.menuItems[this.selectedIndex].text = newText; + } +} + + +export class MenuHandler { + + static '$inject' = [ + 'sdcConfig', + 'sdcMenu', + 'ComponentFactory', + '$filter', + 'ModalsHandler', + '$state', + '$q' + ]; + + constructor(private sdcConfig:IAppConfigurtaion, + private sdcMenu:IAppMenu, + private ComponentFactory:ComponentFactory, + private $filter:ng.IFilterService, + private ModalsHandler:ModalsHandler, + private $state:ng.ui.IStateService, + private $q:ng.IQService) { + + } + + + generateBreadcrumbsModelFromComponents = (components:Array<Component>, selected:Component):MenuItemGroup => { + let result = new MenuItemGroup(0, [], false); + if (components) { + + // Search the component in all components by uuid (and not uniqueid, gives access to an assets's minor versions). + let selectedItem = _.find(components, (item:Component) => { + return item.uuid === selected.uuid; + }); + + // If not found search by invariantUUID + if (undefined == selectedItem) { + selectedItem = _.find(components, (item:Component) => { + //invariantUUID && Certified State matches between major versions + return item.invariantUUID === selected.invariantUUID && item.lifecycleState === ComponentState.CERTIFIED; + }); + } + + // If not found search by name (name is unique). + if (undefined == selectedItem) { + selectedItem = _.find(components, (item:Component) => { + return item.name === selected.name; + }); + } + + result.selectedIndex = components.indexOf(selectedItem); + components[result.selectedIndex] = selected; + let clickItemCallback = (component:Component):ng.IPromise<boolean> => { + this.$state.go('workspace.general', { + id: component.uniqueId, + type: component.componentType.toLowerCase(), + mode: WorkspaceMode.VIEW + }); + return this.$q.when(true); + }; + + components.forEach((component:Component) => { + let menuItem = new MenuItem( + // component.name, + component.getComponentSubType() + ': ' + this.$filter('resourceName')(component.name), + clickItemCallback, + null, + null, + [component] + ); + // menuItem.text = component.name; + result.menuItems.push(menuItem); + }); + } + return result; + }; +} diff --git a/catalog-ui/src/app/utils/modals-handler.ts b/catalog-ui/src/app/utils/modals-handler.ts new file mode 100644 index 0000000000..fe864cb658 --- /dev/null +++ b/catalog-ui/src/app/utils/modals-handler.ts @@ -0,0 +1,389 @@ +import {PropertyModel, Component, ArtifactModel, Distribution, InputModel, DisplayModule, InputPropertyBase} from "../models"; +import {IEmailModalModel} from "../view-models/modals/email-modal/email-modal-view-model"; +import {IClientMessageModalModel} from "../view-models/modals/message-modal/message-client-modal/client-message-modal-view-model"; +import {IServerMessageModalModel} from "../view-models/modals/message-modal/message-server-modal/server-message-modal-view-model"; +import {IConfirmationModalModel} from "../view-models/modals/confirmation-modal/confirmation-modal-view-model"; +import {ModalType} from "./constants"; +import {AttributeModel} from "../models/attributes"; + +export interface IModalsHandler { + + + openDistributionStatusModal (distribution:Distribution, status:string, component:Component):ng.IPromise<any>; + openConfirmationModal (title:string, message:string, showComment:boolean, size?:string):ng.IPromise<any>; + openAlertModal (title:string, message:string, size?:string):ng.IPromise<any>; + openEmailModal(emailModel:IEmailModalModel):ng.IPromise<any>; + openServerMessageModal(data:IServerMessageModalModel):ng.IPromise<any>; + openClientMessageModal(data:IClientMessageModalModel):ng.IPromise<ng.ui.bootstrap.IModalServiceInstance>; + openArtifactModal(artifact:ArtifactModel, component:Component):ng.IPromise<any>; + openEditPropertyModal(property:PropertyModel, component:Component, filteredProperties:Array<PropertyModel>, isPropertyOwnValue:boolean):ng.IPromise<any>; +} + +export class ModalsHandler implements IModalsHandler { + + static '$inject' = [ + '$uibModal', + '$q' + ]; + + constructor(private $uibModal:ng.ui.bootstrap.IModalService, + private $q:ng.IQService) { + } + + + + + openDistributionStatusModal = (distribution:Distribution, status:string, component:Component):ng.IPromise<any> => { + let deferred = this.$q.defer(); + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html', + controller: 'Sdc.ViewModels.DistributionStatusModalViewModel', + size: 'sdc-xl', + backdrop: 'static', + resolve: { + data: ():any => { + return { + 'distribution': distribution, + 'status': status, + 'component': component + }; + } + } + }; + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + + openAlertModal = (title:string, message:string, size?:string):ng.IPromise<any> => { + return this.openConfirmationModalBase(title, message, false, ModalType.ALERT, size); + }; + + openConfirmationModal = (title:string, message:string, showComment:boolean, size?:string):ng.IPromise<any> => { + return this.openConfirmationModalBase(title, message, showComment, ModalType.STANDARD, size); + }; + + private openConfirmationModalBase = (title:string, message:string, showComment:boolean, type:ModalType, size?:string):ng.IPromise<any> => { + let deferred = this.$q.defer(); + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/modals/confirmation-modal/confirmation-modal-view.html', + controller: 'Sdc.ViewModels.ConfirmationModalViewModel', + size: size ? size : 'sdc-sm', + backdrop: 'static', + resolve: { + confirmationModalModel: ():IConfirmationModalModel => { + let model:IConfirmationModalModel = { + title: title, + message: message, + showComment: showComment, + type: type + }; + return model; + } + } + }; + + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + openEmailModal = (emailModel:IEmailModalModel):ng.IPromise<any> => { + + let deferred = this.$q.defer(); + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/modals/email-modal/email-modal-view.html', + controller: 'Sdc.ViewModels.EmailModalViewModel', + size: 'sdc-sm', + backdrop: 'static', + resolve: { + emailModalModel: ():IEmailModalModel => { + return emailModel; + } + } + }; + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + + }; + + openServerMessageModal = (data:IServerMessageModalModel):ng.IPromise<any> => { + let deferred = this.$q.defer(); + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/modals/message-modal/message-server-modal/server-message-modal-view.html', + controller: 'Sdc.ViewModels.ServerMessageModalViewModel', + size: 'sdc-sm', + backdrop: 'static', + resolve: { + serverMessageModalModel: ():IServerMessageModalModel => { + return data; + } + } + }; + + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + openClientMessageModal = (data:IClientMessageModalModel):ng.IPromise<any> => { + let deferred = this.$q.defer(); + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/modals/message-modal/message-client-modal/client-message-modal-view.html', + controller: 'Sdc.ViewModels.ClientMessageModalViewModel', + size: 'sdc-sm', + backdrop: 'static', + resolve: { + clientMessageModalModel: ():IClientMessageModalModel => { + return data; + } + } + }; + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance); + return deferred.promise; + }; + + openOnboadrdingModal = (okButtonText:string, currentCsarUUID?:string):ng.IPromise<any> => { + let deferred = this.$q.defer(); + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/modals/onboarding-modal/onboarding-modal-view.html', + controller: 'Sdc.ViewModels.OnboardingModalViewModel', + size: 'sdc-xl', + backdrop: 'static', + resolve: { + okButtonText: ():string=> { + return okButtonText; + }, + currentCsarUUID: ():string=> { + return currentCsarUUID || null; + } + } + }; + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + openEditEnvParametersModal = (artifactResource:ArtifactModel, component?:Component):ng.IPromise<any> => { + let deferred = this.$q.defer(); + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/forms/env-parameters-form/env-parameters-form.html', + controller: 'Sdc.ViewModels.EnvParametersFormViewModel', + size: 'sdc-xl', + backdrop: 'static', + resolve: { + artifact: ():ArtifactModel => { + return artifactResource; + }, + component: ():Component => { + return component; + } + } + }; + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + openEditInputValueModal = (input:InputModel):ng.IPromise<any> => { + let deferred = this.$q.defer(); + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/forms/input-form/input-form-view.html', + controller: 'Sdc.ViewModels.InputFormViewModel', + size: 'sdc-md', + backdrop: 'static', + resolve: { + input: ():InputModel => { + return input; + } + } + }; + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + openArtifactModal = (artifact:ArtifactModel, component:Component):ng.IPromise<any> => { + let deferred = this.$q.defer(); + + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/forms/artifact-form/artifact-form-view.html', + controller: 'Sdc.ViewModels.ArtifactResourceFormViewModel', + size: 'sdc-md', + backdrop: 'static', + keyboard: false, + resolve: { + artifact: ():ArtifactModel => { + return artifact; + }, + component: ():Component => { + return component; + } + } + }; + + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + + /** + * + * This function openes up the edit property modal + * + * @param property - the property to edit + * @param component - the component who is the owner of the property + * @param filteredProperties - the filtered properties list to scroll between in the edit modal + * @param isPropertyValueOwner - boolean telling if the component is eligible of editing the property + * @returns {IPromise<T>} - Promise telling if the modal has opened or not + */ + openEditPropertyModal = (property:PropertyModel, component:Component, filteredProperties:Array<PropertyModel>, isPropertyValueOwner:boolean):ng.IPromise<any> => { + let deferred = this.$q.defer(); + + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/forms/property-forms/component-property-form/property-form-view.html', + controller: 'Sdc.ViewModels.PropertyFormViewModel', + size: 'sdc-l', + backdrop: 'static', + keyboard: false, + resolve: { + property: ():PropertyModel => { + return property; + }, + component: ():Component => { + return <Component> component; + }, + filteredProperties: ():Array<PropertyModel> => { + return filteredProperties; + }, + isPropertyValueOwner: ():boolean => { + return isPropertyValueOwner; + } + } + }; + + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + + openEditModulePropertyModal = (property:PropertyModel, component:Component, selectedModule:DisplayModule):ng.IPromise<any> => { + let deferred = this.$q.defer(); + + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/forms/property-forms/base-property-form/property-form-base-view.html', + controller: 'Sdc.ViewModels.ModulePropertyView', + size: 'sdc-l', + backdrop: 'static', + keyboard: false, + resolve: { + originalProperty: ():PropertyModel => { + return property; + }, + component: ():Component => { + return <Component> component; + }, + selectedModule: ():DisplayModule => { + return selectedModule; + } + } + }; + + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + openSelectDataTypeModal = (property:PropertyModel, component:Component, filteredProperties:Array<PropertyModel>, propertiesMap:Array<InputPropertyBase>):ng.IPromise<any> => { + let deferred = this.$q.defer(); + + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/forms/property-forms/base-property-form/property-form-base-view.html', + controller: 'Sdc.ViewModels.SelectDataTypeViewModel', + size: 'sdc-l', + backdrop: 'static', + keyboard: false, + resolve: { + originalProperty: ():PropertyModel => { + return property; + }, + component: ():Component => { + return <Component> component; + }, + filteredProperties: ():Array<PropertyModel> => { + return filteredProperties; + }, + propertiesMap: ():Array<InputPropertyBase>=> { + return propertiesMap; + } + } + }; + + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + public openEditAttributeModal = (attribute:AttributeModel, component: Component):void => { + + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/forms/attribute-form/attribute-form-view.html', + controller: 'Sdc.ViewModels.AttributeFormViewModel', + size: 'sdc-md', + backdrop: 'static', + keyboard: false, + resolve: { + attribute: ():AttributeModel => { + return attribute; + }, + component: ():Component => { + return component; + } + } + }; + this.$uibModal.open(modalOptions); + }; + + public openUpdateComponentInstanceNameModal = (currentComponent: Component):ng.IPromise<any> => { + let deferred = this.$q.defer(); + + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/forms/resource-instance-name-form/resource-instance-name-view.html', + controller: 'Sdc.ViewModels.ResourceInstanceNameViewModel', + size: 'sdc-sm', + backdrop: 'static', + resolve: { + component: ():Component => { + return currentComponent; + } + } + }; + + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + + public openConformanceLevelModal = ():ng.IPromise<any> => { + let deferred = this.$q.defer(); + let modalOptions:ng.ui.bootstrap.IModalSettings = { + templateUrl: '../view-models/workspace/conformance-level-modal/conformance-level-modal-view.html', + controller: 'Sdc.ViewModels.ConformanceLevelModalViewModel', + size: 'sdc-sm', + backdrop: 'static', + resolve: { + + } + }; + + let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$uibModal.open(modalOptions); + deferred.resolve(modalInstance.result); + return deferred.promise; + }; + +} diff --git a/catalog-ui/src/app/utils/prototypes.ts b/catalog-ui/src/app/utils/prototypes.ts new file mode 100644 index 0000000000..2aa718a06c --- /dev/null +++ b/catalog-ui/src/app/utils/prototypes.ts @@ -0,0 +1,64 @@ +interface String { + format(variables:Array<string>):string +} + +interface Array<T> { + clean(o:T):Array<T>; +} + + +/** + * This function will replace the %<number> with strings (from array). + * Example: "Requested '%1' resource was not found.".format(["MyResource"]); + * Note: in case the array contains empty string the function will also remove the '' or the "". + */ +if (!String.hasOwnProperty("format")) { + String.prototype["format"] = function (variables:Array<string>):string { + + if (variables === null || variables === undefined || variables.length === 0) { + variables = ['']; + } + + for (let i = 0; i < variables.length; i++) { + if (variables[i] === '' || variables[i] === null) { + variables[i] = '--DELETE--'; + } + } + + let res = this.replace(/%(\d+)/g, function (_, m) { + return variables[--m]; + }); + + res = res.replace(" '--DELETE--' ", " "); + res = res.replace(" \"--DELETE--\" ", " "); + res = res.replace("'--DELETE--'", ""); + res = res.replace("\"--DELETE--\"", ""); + res = res.replace("--DELETE--", ""); + + return res; + }; +} + +if (!String.hasOwnProperty("capitalizeFirstLetter")) { + String.prototype["capitalizeFirstLetter"] = function () { + return this.charAt(0).toUpperCase() + this.slice(1); + }; +} + +if (!String.hasOwnProperty("replaceAll")) { + String.prototype["replaceAll"] = function (find:string, replace:string):string { + return this.replace(new RegExp(find, 'g'), replace); + }; +} + +if (!Array.hasOwnProperty("clean")) { + Array.prototype.clean = function (deleteValue) { + for (let i = 0; i < this.length; i++) { + if (this[i] == deleteValue) { + this.splice(i, 1); + i--; + } + } + return this; + }; +} diff --git a/catalog-ui/src/app/utils/validation-utils.ts b/catalog-ui/src/app/utils/validation-utils.ts new file mode 100644 index 0000000000..9246d3350d --- /dev/null +++ b/catalog-ui/src/app/utils/validation-utils.ts @@ -0,0 +1,153 @@ +class basePattern { + pattern:RegExp; + base:number; + + constructor(pattern:RegExp, base:number) { + this.pattern = pattern; + this.base = base; + } +} + +export interface IMapRegex { + integer:RegExp; + boolean:RegExp; + float:RegExp; + string:RegExp; +} + +export class ValidationUtils { + + static '$inject' = [ + 'IntegerNoLeadingZeroValidationPattern', + 'FloatValidationPattern', + 'CommentValidationPattern', + 'BooleanValidationPattern', + 'NumberValidationPattern', + 'LabelValidationPattern', + ]; + private trueRegex:string = '[t][r][u][e]|[t]|[o][n]|[y]|[y][e][s]|[1]'; + private falseRegex:string = '[f][a][l][s][e]|[f]|[o][f][f]|[n]|[n][o]|[0]'; + private heatBooleanValidationPattern:RegExp = new RegExp('^(' + this.trueRegex + '|' + this.falseRegex + ')$'); + + + constructor(private IntegerNoLeadingZeroValidationPattern:RegExp, + private FloatValidationPattern:RegExp, + private CommentValidationPattern:RegExp, + private BooleanValidationPattern:RegExp, + private NumberValidationPattern:RegExp, + private LabelValidationPattern:RegExp) { + } + + public stripAndSanitize(text:string):string { + if (!text) { + return null; + } + return text.replace(/\s+/g, ' ').replace(/%[A-Fa-f0-9]{2}/g, '').trim(); + } + + public getValidationPattern = (validationType:string, parameterType?:string):RegExp => { + switch (validationType) { + case 'integer': + return this.IntegerNoLeadingZeroValidationPattern; + case 'float': + return this.FloatValidationPattern; + case 'number': + return this.NumberValidationPattern; + case 'string': + return this.CommentValidationPattern; + case 'boolean': + { + //Bug Fix DE197437 [Patch]Mismatch between BE to FE regarding supported characters in Boolean filed + if (parameterType && parameterType === 'heat') { + return this.heatBooleanValidationPattern; + } + else { + return this.BooleanValidationPattern; + } + } + + case 'label': + return this.LabelValidationPattern; + case 'category': + return this.LabelValidationPattern; + default : + return null; + } + }; + + public getPropertyListPatterns():IMapRegex { + return { + integer: /^(0|[-+]?[1-9][0-9]*|[-+]?0x[0-9a-fA-F]+|[-+]?0o[0-7]+)(,?(0|[-+]?[1-9][0-9]*|[-+]?0x[0-9a-fA-F]+|[-+]?0o[0-7]+))*$/, + string: /^"[\u0000-\u0021\u0023-\u00BF]+"(\s*,?\s*"[\u0000-\u0021\u0023-\u00BF]+")*$/, + boolean: /^([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])(,?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]))*$/, + float: /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?(,?[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f?)*$/ + }; + } + + public getPropertyMapPatterns():IMapRegex { + return { + integer: /^"\w+"\s*:\s?(0|[-+]?[1-9][0-9]*|[-+]?0x[0-9a-fA-F]+|[-+]?0o[0-7]+)+(\s*,?\s*"\w+"\s?:\s?(0|[-+]?[1-9][0-9]*|[-+]?0x[0-9a-fA-F]+|[-+]?0o[0-7]+)+)*$/, + string: /^"\w+"\s?:\s?"[\u0000-\u0021\u0023-\u00BF]*"(\s*,?\s*"\w+"\s?:\s?"[\u0000-\u0021\u0023-\u00BF]*")*$/, + boolean: /^"\w+"\s?:\s?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])(\s*,?\s*"\w+"\s?:\s?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]))*$/, + float: /^"\w+"\s?:\s?[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f?(\s*,?\s*"\w+"\s?:\s?[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f?)*$/ + }; + } + + public validateUniqueKeys(viewValue:string):boolean { + if (!viewValue) { + return true; //allow empty value + } + + let json:string = "{" + viewValue.replace(/\s\s+/g, ' ') + "}"; + try { + let obj:any = JSON.parse(json); + /* + //Method #1 : check json string length before & after parsing + let newJson:string = JSON.stringify(obj); + if (newJson.length < json.length) { + return false; + }*/ + + //Method #2 : check how many times we can find "KEY": in json string + let result:boolean = true; + Object.keys(obj).forEach((key:string) => { + result = result && json.split('"' + key + '":').length === 2; + }); + return result; + + } catch (e) { + return false; //not a valid JSON + } + + //return true; + } + + public validateJson = (json:string):boolean => { + try { + JSON.parse(json); + return true; + } catch (err) { + console.log('invalid json'); + return false; + } + }; + + public validateIntRange = (value:string):boolean => { + + let base8 = new basePattern(/^([-+]?0o[0-7]+)$/, 8); + let base10 = new basePattern(/^(0|[-+]?[1-9][0-9]*)$/, 10); + let base16 = new basePattern(/^([-+]?0x[0-9a-fA-F]+)$/, 16); + + let min:number = -0x80000000; + let max:number = 0x7fffffff; + let intPatterns:Array<basePattern> = [base8, base10, base16]; + let matchedBase = _.find(intPatterns, (item)=> { + return item.pattern.test(value); + }); + + let parsed:number = parseInt(value.replace('o', ''), matchedBase.base); + if (parsed) { + return min <= parsed && max >= parsed; + } + } +} |