- + + [fePropertiesMap]="instanceFePropertiesMap" + [readonly]="isReadonly" + [inputs]="inputs | searchFilter:'name':searchQuery" + [instanceNamesMap]="componentInstanceNamesMap" + [isLoading]="loadingInputs" + (deleteInput)="deleteInput($event)" + (inputChanged)="dataChanged($event)"> diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less index 855bdc5bcb..a1309aba61 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less @@ -133,13 +133,12 @@ flex-direction:column; margin: 0px 0 0 1em; overflow-x:auto; - .add-btn{ + .add-btn{ align-self: flex-end; margin-top: 10px; margin-bottom: 19px; } - /deep/ .tabs { border-bottom: solid 1px #d0d0d0; } diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts index 061439800f..4b84f0e66f 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts @@ -19,58 +19,34 @@ */ import * as _ from "lodash"; -import {Component, ViewChild, Inject, TemplateRef} from "@angular/core"; +import { Component, ViewChild, Inject, TemplateRef } from "@angular/core"; import { PropertiesService } from "../../services/properties.service"; -import { - PropertyFEModel, - InstanceFePropertiesMap, - InstanceBePropertiesMap, - InstancePropertiesAPIMap, - Component as ComponentData, - FilterPropertiesAssignmentData, - ModalModel, - ButtonModel, - Capability, - ToscaPresentationData -} from "app/models"; +import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData, ModalModel, ButtonModel } from "app/models"; import { ResourceType } from "app/utils"; -import {ComponentServiceNg2} from "../../services/component-services/component.service"; -import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service" -import { - InputBEModel, - InputFEModel, - ComponentInstance, - GroupInstance, - PolicyInstance, - PropertyBEModel, - DerivedFEProperty, - SimpleFlatProperty, - CapabilitiesGroup -} from "app/models"; +import { ComponentServiceNg2 } from "../../services/component-services/component.service"; +import { TopologyTemplateService } from "../../services/component-services/topology-template.service"; +import { ComponentInstanceServiceNg2 } from "../../services/component-instance-services/component-instance.service" +import { InputBEModel, InputFEModel, ComponentInstance, GroupInstance, PolicyInstance, PropertyBEModel, DerivedFEProperty, SimpleFlatProperty } from "app/models"; import { KeysPipe } from 'app/ng2/pipes/keys.pipe'; -import {WorkspaceMode, EVENTS} from "../../../utils/constants"; -import {EventListenerService} from "app/services/event-listener-service" -import {HierarchyDisplayOptions} from "../../components/logic/hierarchy-navigtion/hierarchy-display-options"; -import {FilterPropertiesAssignmentComponent} from "../../components/logic/filter-properties-assignment/filter-properties-assignment.component"; -import {PropertyRowSelectedEvent} from "../../components/logic/properties-table/properties-table.component"; -import {HierarchyNavService} from "./services/hierarchy-nav.service"; -import {PropertiesUtils} from "./services/properties.utils"; -import {ComponentModeService} from "../../services/component-services/component-mode.service"; -import {ModalService} from "../../services/modal.service"; -import {Tabs, Tab} from "../../components/ui/tabs/tabs.component"; -import {InputsUtils} from "./services/inputs.utils"; -import {PropertyCreatorComponent} from "./property-creator/property-creator.component"; -import {DeclareListComponent} from "./declare-list/declare-list.component"; +import { WorkspaceMode, EVENTS, PROPERTY_TYPES } from "../../../utils/constants"; +import { EventListenerService } from "app/services/event-listener-service" +import { HierarchyDisplayOptions } from "../../components/logic/hierarchy-navigtion/hierarchy-display-options"; +import { FilterPropertiesAssignmentComponent } from "../../components/logic/filter-properties-assignment/filter-properties-assignment.component"; +import { PropertyRowSelectedEvent } from "../../components/logic/properties-table/properties-table.component"; +import { HierarchyNavService } from "./services/hierarchy-nav.service"; +import { PropertiesUtils } from "./services/properties.utils"; +import { ComponentModeService } from "../../services/component-services/component-mode.service"; +import { Tabs, Tab } from "../../components/ui/tabs/tabs.component"; +import { InputsUtils } from "./services/inputs.utils"; import { InstanceFeDetails } from "../../../models/instance-fe-details"; -import { SdcUiComponents } from "sdc-ui/lib/angular"; -//import { ModalService as ModalServiceSdcUI} from "sdc-ui/lib/angular/modals/modal.service"; -import { IModalButtonComponent } from "sdc-ui/lib/angular/modals/models/modal-config"; +import { SdcUiServices, SdcUiCommon } from "onap-ui-angular"; import { UnsavedChangesComponent } from "app/ng2/components/ui/forms/unsaved-changes/unsaved-changes.component"; -import {Observable} from "rxjs"; -import { DataTypeService } from "app/ng2/services/data-type.service"; -import { DataTypeModel } from "app/models"; -import { PROPERTY_DATA, PROPERTY_TYPES } from "app/utils"; -import { PropertyDeclareAPIModel} from "app/models"; +import {PropertyCreatorComponent} from "./property-creator/property-creator.component"; +import {ModalService} from "../../services/modal.service"; +import { DeclareListComponent } from "./declare-list/declare-list.component"; +import { CapabilitiesGroup, Capability } from "../../../models/capability"; +import { ToscaPresentationData } from "../../../models/tosca-presentation"; +import { Observable } from "rxjs"; const SERVICE_SELF_TITLE = "SELF"; @Component({ @@ -119,7 +95,7 @@ export class PropertiesAssignmentComponent { stateChangeStartUnregister:Function; serviceBePropertiesMap: InstanceBePropertiesMap; serviceBeCapabilitiesPropertiesMap: InstanceBePropertiesMap; - selectedInstance_FlattenCapabilitiesList: Array; + selectedInstance_FlattenCapabilitiesList: Capability[]; @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs; @ViewChild('propertyInputTabs') propertyInputTabs: Tabs; @@ -136,12 +112,13 @@ export class PropertiesAssignmentComponent { @Inject("$state") private $state:ng.ui.IStateService, @Inject("Notification") private Notification:any, private componentModeService:ComponentModeService, - private ModalService:ModalService, private EventListenerService:EventListenerService, - private ModalServiceSdcUI: SdcUiComponents.ModalService) { + private ModalServiceSdcUI: SdcUiServices.ModalService, + private ModalService: ModalService, + private keysPipe:KeysPipe, + private topologyTemplateService: TopologyTemplateService) { this.instanceFePropertiesMap = new InstanceFePropertiesMap(); - /* This is the way you can access the component data, please do not use any data except metadata, all other data should be received from the new api calls on the first time than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/ this.component = _stateParams.component; @@ -159,8 +136,8 @@ export class PropertiesAssignmentComponent { this.loadingPolicies = true; this.loadingInstances = true; this.loadingProperties = true; - this.componentServiceNg2 - .getComponentInputsWithProperties(this.component) + this.topologyTemplateService + .getComponentInputsWithProperties(this.component.componentType, this.component.uniqueId) .subscribe(response => { _.forEach(response.inputs, (input: InputBEModel) => { const newInput: InputFEModel = new InputFEModel(input); @@ -169,7 +146,7 @@ export class PropertiesAssignmentComponent { }); this.loadingInputs = false; - }); + }, error => {}); //ignore error this.componentServiceNg2 .getComponentResourcePropertiesData(this.component) .subscribe(response => { @@ -177,6 +154,7 @@ export class PropertiesAssignmentComponent { this.instances = []; this.instances.push(...response.componentInstances); this.instances.push(...response.groupInstances); + this.instances.push(...response.policies); _.forEach(response.policies, (policy: any) => { const newPolicy: InputFEModel = new InputFEModel(policy); @@ -199,7 +177,7 @@ export class PropertiesAssignmentComponent { this.loadingProperties = false; } this.selectFirstInstanceByDefault(); - }); + }, error => { this.loadingInstances = false; }); //ignore error this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => { // stop if has changed properties @@ -238,14 +216,14 @@ export class PropertiesAssignmentComponent { getServiceProperties(){ this.loadingProperties = false; - this.componentServiceNg2 - .getServiceProperties(this.component) - .subscribe(response => { + this.topologyTemplateService + .getServiceProperties(this.component.uniqueId) + .subscribe((response) => { this.serviceBePropertiesMap = new InstanceBePropertiesMap(); this.serviceBePropertiesMap[this.component.uniqueId] = response; this.processInstancePropertiesResponse(this.serviceBePropertiesMap, false); this.loadingProperties = false; - }, error => { + }, (error) => { this.loadingProperties = false; }); } @@ -267,14 +245,12 @@ export class PropertiesAssignmentComponent { this.loadingProperties = true; if (instance instanceof ComponentInstance) { let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); - this.selectedInstance_FlattenCapabilitiesList = instance.capabilities ? CapabilitiesGroup.getFlattenedCapabilities(instance.capabilities) : []; if (this.isInput(instance.originType)) { this.componentInstanceServiceNg2 .getComponentInstanceInputs(this.component, instance) .subscribe(response => { instanceBePropertiesMap[instance.uniqueId] = response; this.processInstancePropertiesResponse(instanceBePropertiesMap, true); - this.processInstanceCapabilitiesPropertiesResponse(false); this.loadingProperties = false; }, error => { }); //ignore error @@ -286,7 +262,6 @@ export class PropertiesAssignmentComponent { .subscribe(response => { instanceBePropertiesMap[instance.uniqueId] = response; this.processInstancePropertiesResponse(instanceBePropertiesMap, false); - this.processInstanceCapabilitiesPropertiesResponse(false); this.loadingProperties = false; }, error => { }); //ignore error @@ -305,7 +280,7 @@ export class PropertiesAssignmentComponent { } else if (instance instanceof PolicyInstance) { let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); this.componentInstanceServiceNg2 - .getComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId) + .getComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId) .subscribe((response) => { instanceBePropertiesMap[instance.uniqueId] = response; this.processInstancePropertiesResponse(instanceBePropertiesMap, false); @@ -480,7 +455,7 @@ export class PropertiesAssignmentComponent { let selectedGroupInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap(); let selectedPolicyInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap(); let selectedComponentInstancesInputs: InstanceBePropertiesMap = new InstanceBePropertiesMap(); - let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []); + let instancesIds = this.keysPipe.transform(this.instanceFePropertiesMap, []); angular.forEach(instancesIds, (instanceId: string): void => { let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId); @@ -500,7 +475,7 @@ export class PropertiesAssignmentComponent { let inputsToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(selectedComponentInstancesInputs, selectedComponentInstancesProperties, selectedGroupInstancesProperties, selectedPolicyInstancesProperties); - //move changed capabilities properties from componentInstanceInputsMap obj to componentInstanceProperties + //move changed capabilities properties from componentInstanceInputsMap obj to componentInstanceProperties inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] = (inputsToCreate.componentInstanceProperties[this.selectedInstanceData.uniqueId] || []).concat( _.filter( @@ -526,15 +501,14 @@ export class PropertiesAssignmentComponent { } } ); - - this.componentServiceNg2 + this.topologyTemplateService .createInput(this.component, inputsToCreate, this.isSelf()) - .subscribe(response => { + .subscribe((response) => { this.setInputTabIndication(response.length); this.checkedPropertiesCount = 0; this.checkedChildPropertiesCount = 0; _.forEach(response, (input: InputBEModel) => { - let newInput: InputFEModel = new InputFEModel(input); + const newInput: InputFEModel = new InputFEModel(input); this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue); this.inputs.push(newInput); this.updatePropertyValueAfterDeclare(newInput); @@ -628,8 +602,8 @@ export class PropertiesAssignmentComponent { }; console.log("save button clicked. input=", input); - this.componentServiceNg2 - .createListInput(this.component, input, this.isSelf()) + this.topologyTemplateService + .createListInput(this.component.uniqueId, input, this.isSelf()) .subscribe(response => { this.setInputTabIndication(response.length); this.checkedPropertiesCount = 0; @@ -662,8 +636,8 @@ export class PropertiesAssignmentComponent { console.log('declareListProperties() - leave'); }; - /*** DECLARE PROPERTIES/POLICIES ***/ - declarePropertiesToPolicies = (): void => { + /*** DECLARE PROPERTIES/POLICIES ***/ + declarePropertiesToPolicies = (): void => { let selectedComponentInstancesProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap(); let instancesIds = new KeysPipe().transform(this.instanceFePropertiesMap, []); @@ -679,7 +653,7 @@ export class PropertiesAssignmentComponent { let policiesToCreate: InstancePropertiesAPIMap = new InstancePropertiesAPIMap(null, selectedComponentInstancesProperties, null, null); this.loadingPolicies = true; - this.componentServiceNg2 + this.topologyTemplateService .createPolicy(this.component, policiesToCreate, this.isSelf()) .subscribe(response => { this.setPolicyTabIndication(response.length); @@ -688,7 +662,7 @@ export class PropertiesAssignmentComponent { this.loadingPolicies = false; }); //ignore error - }; + } displayPoliciesAsDeclared = (policies) => { _.forEach(policies, (policy: any) => { @@ -699,8 +673,7 @@ export class PropertiesAssignmentComponent { this.updatePropertyValueAfterDeclare(newPolicy); this.policies.push(policy); }); - }; - + } saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => { return new Promise((resolve, reject) => { @@ -736,7 +709,8 @@ export class PropertiesAssignmentComponent { if (changedInputsProperties.length && changedCapabilitiesProperties.length) { request = Observable.forkJoin( this.componentInstanceServiceNg2.updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedInputsProperties), - this.componentInstanceServiceNg2.updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties) + this.componentInstanceServiceNg2.updateInstanceProperties(this.component.componentType, this.component.uniqueId, + this.selectedInstanceData.uniqueId, changedCapabilitiesProperties) ); } else if (changedInputsProperties.length) { @@ -745,7 +719,7 @@ export class PropertiesAssignmentComponent { } else if (changedCapabilitiesProperties.length) { request = this.componentInstanceServiceNg2 - .updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties); + .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedCapabilitiesProperties); } handleSuccess = (response) => { // reset each changed property with new value and remove it from changed properties list @@ -757,19 +731,18 @@ export class PropertiesAssignmentComponent { }; } else { if (this.isSelf()) { - request = this.componentServiceNg2.updateServiceProperties(this.component, _.map(changedProperties, cp => { + request = this.topologyTemplateService.updateServiceProperties(this.component.uniqueId, _.map(changedProperties, cp => { delete cp.constraints; return cp; })); } else { request = this.componentInstanceServiceNg2 - .updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties); + .updateInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties); } handleSuccess = (response) => { // reset each changed property with new value and remove it from changed properties list response.forEach((resProp) => { - const changedProp = _.find(this.changedData, changedDataObject => changedDataObject.uniqueId === resProp.uniqueId); - this.changedData = _.filter(this.changedData, changedDataObject => changedDataObject.uniqueId !== resProp.uniqueId); + const changedProp = this.changedData.shift(); this.propertiesUtils.resetPropertyValue(changedProp, resProp.value); }); resolve(response); @@ -778,7 +751,7 @@ export class PropertiesAssignmentComponent { } } else if (this.selectedInstanceData instanceof GroupInstance) { request = this.componentInstanceServiceNg2 - .updateComponentGroupInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties); + .updateComponentGroupInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties); handleSuccess = (response) => { // reset each changed property with new value and remove it from changed properties list response.forEach((resProp) => { @@ -790,7 +763,7 @@ export class PropertiesAssignmentComponent { }; } else if (this.selectedInstanceData instanceof PolicyInstance) { request = this.componentInstanceServiceNg2 - .updateComponentPolicyInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties); + .updateComponentPolicyInstanceProperties(this.component.componentType, this.component.uniqueId, this.selectedInstanceData.uniqueId, changedProperties); handleSuccess = (response) => { // reset each changed property with new value and remove it from changed properties list response.forEach((resProp) => { @@ -802,6 +775,7 @@ export class PropertiesAssignmentComponent { }; } } else if (this.isInputsTabSelected) { + const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => { changedInput = changedInput; const inputBE = new InputBEModel(changedInput); @@ -925,27 +899,27 @@ export class PropertiesAssignmentComponent { { title: modalTitle, size: 'sm', - type: 'custom', - testId: "id", - + type: SdcUiCommon.ModalType.custom, + testId: "navigate-modal", + buttons: [ - {id: 'cancelButton', text: 'Cancel', type: 'secondary', size: 'xsm', closeModal: true, callback: () => reject()}, - {id: 'discardButton', text: 'Discard', type: 'secondary', size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}}, - {id: 'saveButton', text: 'Save', type: 'primary', size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)} - ] as IModalButtonComponent[] - }, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData}); + {id: 'cancelButton', text: 'Cancel', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => reject()}, + {id: 'discardButton', text: 'Discard', type: SdcUiCommon.ButtonType.secondary, size: 'xsm', closeModal: true, callback: () => { this.reverseChangedData(); resolve()}}, + {id: 'saveButton', text: 'Save', type: SdcUiCommon.ButtonType.primary, size: 'xsm', closeModal: true, disabled: !this.isValidChangedData, callback: () => this.doSaveChangedData(resolve, reject)} + ] as SdcUiCommon.IModalButtonComponent[] + } as SdcUiCommon.IModalConfig, UnsavedChangesComponent, {isValidChangedData: this.isValidChangedData}); }); } updatePropertyValueAfterDeclare = (input: InputFEModel) => { if (this.instanceFePropertiesMap[input.instanceUniqueId]) { - let instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1); - let propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => { - return feProperty.uniqueId === input.propertyId && + const instanceName = input.instanceUniqueId.slice(input.instanceUniqueId.lastIndexOf('.') + 1); + const propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[input.instanceUniqueId], (feProperty: PropertyFEModel) => { + return feProperty.name == input.relatedPropertyName && (feProperty.name == input.relatedPropertyName || input.name === instanceName.concat('_').concat(feProperty.name.replace(/[.]/g, '_'))); }); - let inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined; + const inputPath = (input.inputPath && input.inputPath != propertyForUpdatindVal.name) ? input.inputPath : undefined; propertyForUpdatindVal.setAsDeclared(inputPath); //set prop as declared before assigning value this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, inputPath); this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, input.relatedPropertyValue, inputPath); @@ -968,7 +942,7 @@ export class PropertiesAssignmentComponent { setPolicyTabIndication = (numPolicies: number): void => { this.propertyInputTabs.setTabIndication('Policies', numPolicies); - }; + } resetUnsavedChangesForInput = (input:InputFEModel) => { this.inputsUtils.resetInputDefaultValue(input, input.defaultValue); @@ -1009,9 +983,9 @@ export class PropertiesAssignmentComponent { deletePolicy = (policy: PolicyInstance) => { this.loadingPolicies = true; - this.componentServiceNg2 + this.topologyTemplateService .deletePolicy(this.component, policy) - .subscribe(response => { + .subscribe((response) => { this.policies = this.policies.filter(policy => policy.uniqueId !== response.uniqueId); //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead! this.changeSelectedInstance(this.selectedInstanceData); @@ -1020,25 +994,25 @@ export class PropertiesAssignmentComponent { }; deleteProperty = (property: PropertyFEModel) => { - let propertyToDelete = new PropertyFEModel(property); + const propertyToDelete = new PropertyFEModel(property); this.loadingProperties = true; - let feMap = this.instanceFePropertiesMap; - this.componentServiceNg2 - .deleteServiceProperty(this.component, propertyToDelete) - .subscribe(response => { + const feMap = this.instanceFePropertiesMap; + this.topologyTemplateService + .deleteServiceProperty(this.component.uniqueId, propertyToDelete) + .subscribe((response) => { const props = feMap[this.component.uniqueId]; props.splice(props.findIndex(p => p.uniqueId === response),1); this.loadingProperties = false; - }, error => { + }, (error) => { this.loadingProperties = false; console.error(error); }); - }; + } /*** addProperty ***/ addProperty = () => { let modalTitle = 'Add Property'; - const modal = this.ModalService.createCustomModal(new ModalModel( + let modal = this.ModalService.createCustomModal(new ModalModel( 'sm', modalTitle, null, @@ -1046,10 +1020,10 @@ export class PropertiesAssignmentComponent { new ButtonModel('Save', 'blue', () => { modal.instance.dynamicContent.instance.isLoading = true; const newProperty: PropertyBEModel = modal.instance.dynamicContent.instance.propertyModel; - this.componentServiceNg2.createServiceProperty(this.component, newProperty) - .subscribe(response => { + this.topologyTemplateService.createServiceProperty(this.component.uniqueId, newProperty) + .subscribe((response) => { modal.instance.dynamicContent.instance.isLoading = false; - let newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response); + const newProp: PropertyFEModel = this.propertiesUtils.convertAddPropertyBAToPropertyFE(response); this.instanceFePropertiesMap[this.component.uniqueId].push(newProp); modal.instance.close(); }, (error) => { @@ -1059,7 +1033,6 @@ export class PropertiesAssignmentComponent { title: 'Failure' }); }); - }, () => !modal.instance.dynamicContent.instance.checkFormValidForSubmit()), new ButtonModel('Cancel', 'outline grey', () => { modal.instance.close(); @@ -1069,24 +1042,23 @@ export class PropertiesAssignmentComponent { )); this.ModalService.addDynamicContentToModal(modal, PropertyCreatorComponent, {}); modal.instance.open(); - }; + } /*** SEARCH RELATED FUNCTIONS ***/ searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => { let instanceBePropertiesMap:InstanceBePropertiesMap; this.componentServiceNg2 .filterComponentInstanceProperties(this.component, filterData) - .subscribe(response => { - + .subscribe((response) => { this.processInstancePropertiesResponse(response, false); this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree this.searchPropertyName = filterData.propertyName;//mark in table this.hierarchyNavTabs.triggerTabChange('Composition'); this.propertiesNavigationData = []; this.displayClearSearch = true; - }, error => {}); //ignore error + }, (error) => {}); //ignore error - }; + } clearSearch = () => { this.instancesNavigationData = this.instances; @@ -1106,5 +1078,6 @@ export class PropertiesAssignmentComponent { private isInput = (instanceType:string):boolean =>{ return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR; } + } diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.component.ts index 7d76904539..5053d52cc8 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.component.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.component.ts @@ -1,79 +1,78 @@ -import * as _ from "lodash"; -import {Component} from '@angular/core'; -import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component"; -import { DataTypeService } from "app/ng2/services/data-type.service"; -import {PropertyBEModel, DataTypesMap} from "app/models"; -import {PROPERTY_DATA} from "app/utils"; -import {PROPERTY_TYPES} from "../../../../utils"; - +import { Component } from '@angular/core'; +import { DataTypesMap, PropertyBEModel } from 'app/models'; +import { DropdownValue } from 'app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component'; +import { DataTypeService } from 'app/ng2/services/data-type.service'; +import { PROPERTY_DATA } from 'app/utils'; +import * as _ from 'lodash'; +import { PROPERTY_TYPES } from '../../../../utils'; @Component({ selector: 'property-creator', templateUrl: './property-creator.component.html', - styleUrls:['./property-creator.component.less'], + styleUrls: ['./property-creator.component.less'], }) export class PropertyCreatorComponent { - typesProperties: Array; - typesSchemaProperties: Array; + typesProperties: DropdownValue[]; + typesSchemaProperties: DropdownValue[]; propertyModel: PropertyBEModel; - //propertyNameValidationPattern:RegExp = /^[a-zA-Z0-9_:-]{1,50}$/; - //commentValidationPattern:RegExp = /^[\u0000-\u00BF]*$/; - //types:Array; - dataTypes:DataTypesMap; - isLoading:boolean; + // propertyNameValidationPattern:RegExp = /^[a-zA-Z0-9_:-]{1,50}$/; + // commentValidationPattern:RegExp = /^[\u0000-\u00BF]*$/; + // types:Array; + dataTypes: DataTypesMap; + isLoading: boolean; - constructor(protected dataTypeService:DataTypeService) {} + constructor(protected dataTypeService: DataTypeService) {} ngOnInit() { this.propertyModel = new PropertyBEModel(); this.propertyModel.type = ''; this.propertyModel.schema.property.type = ''; - const types: Array = PROPERTY_DATA.TYPES; //All types - simple type + map + list - this.dataTypes = this.dataTypeService.getAllDataTypes(); //Get all data types in service - const nonPrimitiveTypes :Array = _.filter(Object.keys(this.dataTypes), (type:string)=> { - return types.indexOf(type) == -1; + const types: string[] = PROPERTY_DATA.TYPES; // All types - simple type + map + list + this.dataTypes = this.dataTypeService.getAllDataTypes(); // Get all data types in service + const nonPrimitiveTypes: string[] = _.filter(Object.keys(this.dataTypes), (type: string) => { + return types.indexOf(type) === -1; }); this.typesProperties = _.map(PROPERTY_DATA.TYPES, (type: string) => new DropdownValue(type, type) ); - let typesSimpleProperties = _.map(PROPERTY_DATA.SIMPLE_TYPES, + const typesSimpleProperties = _.map(PROPERTY_DATA.SIMPLE_TYPES, (type: string) => new DropdownValue(type, type) ); - let nonPrimitiveTypesValues = _.map(nonPrimitiveTypes, + const nonPrimitiveTypesValues = _.map(nonPrimitiveTypes, (type: string) => new DropdownValue(type, - type.replace("org.openecomp.datatypes.heat.","")) + type.replace('org.openecomp.datatypes.heat.', '')) ) .sort((a, b) => a.label.localeCompare(b.label)); - this.typesProperties = _.concat(this.typesProperties,nonPrimitiveTypesValues); - this.typesSchemaProperties = _.concat(typesSimpleProperties,nonPrimitiveTypesValues); - this.typesProperties.unshift(new DropdownValue('','Select Type...')); - this.typesSchemaProperties.unshift(new DropdownValue('','Select Schema Type...')); + this.typesProperties = _.concat(this.typesProperties, nonPrimitiveTypesValues); + this.typesSchemaProperties = _.concat(typesSimpleProperties, nonPrimitiveTypesValues); + this.typesProperties.unshift(new DropdownValue('', 'Select Type...')); + this.typesSchemaProperties.unshift(new DropdownValue('', 'Select Schema Type...')); } - checkFormValidForSubmit(){ - const showSchema:boolean = this.showSchema(); - let isSchemaValid: boolean = (showSchema && !this.propertyModel.schema.property.type)? false : true; - if (!showSchema){ + checkFormValidForSubmit() { + const showSchema: boolean = this.showSchema(); + const isSchemaValid: boolean = (showSchema && !this.propertyModel.schema.property.type) ? false : true; + if (!showSchema) { this.propertyModel.schema.property.type = ''; } return this.propertyModel.name && this.propertyModel.type && isSchemaValid; } - showSchema():boolean { + showSchema(): boolean { return [PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP].indexOf(this.propertyModel.type) > -1; - }; + } - onSchemaTypeChange():void { - if (this.propertyModel.type == PROPERTY_TYPES.MAP) { + onSchemaTypeChange(): void { + if (this.propertyModel.type === PROPERTY_TYPES.MAP) { this.propertyModel.value = JSON.stringify({'': null}); - } else if (this.propertyModel.type == PROPERTY_TYPES.LIST) { + } else if (this.propertyModel.type === PROPERTY_TYPES.LIST) { this.propertyModel.value = JSON.stringify([]); } - }; + } } diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.module.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.module.ts index 92accb26b5..1cbb4e17ec 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.module.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/property-creator/property-creator.module.ts @@ -1,10 +1,10 @@ -import {NgModule} from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {PropertyCreatorComponent} from "./property-creator.component"; -import {FormsModule} from "@angular/forms"; -import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module"; -import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; -import {TranslateModule} from "../../../shared/translator/translate.module"; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { FormElementsModule } from 'app/ng2/components/ui/form-components/form-elements.module'; +import { UiElementsModule } from 'app/ng2/components/ui/ui-elements.module'; +import { TranslateModule } from '../../../shared/translator/translate.module'; +import { PropertyCreatorComponent } from './property-creator.component'; @NgModule({ declarations: [ diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts index 011be41611..bd7ccd1bfd 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts @@ -48,7 +48,7 @@ export class PropertiesUtils { let newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE - this.initValueObjectRef(newFEProp); //initialize valueObj. + this.initValueObjectRef(newFEProp); //initialize valueObj AND creates flattened children propertyFeArray.push(newFEProp); newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children this.dataTypeService.checkForCustomBehavior(newFEProp); @@ -79,8 +79,8 @@ export class PropertiesUtils { return instanceFePropertiesMap; } - public convertAddPropertyBAToPropertyFE = (property: PropertyBEModel):PropertyFEModel => { - let newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE + public convertAddPropertyBAToPropertyFE = (property: PropertyBEModel): PropertyFEModel => { + const newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE this.initValueObjectRef(newFEProp); newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children this.dataTypeService.checkForCustomBehavior(newFEProp); @@ -108,7 +108,7 @@ export class PropertiesUtils { let tempProps: Array = []; let dataTypeObj: DataTypeModel = this.dataTypeService.getDataTypeByTypeName(type); this.dataTypeService.getDerivedDataTypeProperties(dataTypeObj, tempProps, parentName); - return tempProps; + return _.sortBy(tempProps, ['propertiesName']); } /* Sets the valueObj of parent property and its children. diff --git a/catalog-ui/src/app/ng2/pages/req-and-capabilities-editor/capabilities-editor/capabilities-editor.module.ts b/catalog-ui/src/app/ng2/pages/req-and-capabilities-editor/capabilities-editor/capabilities-editor.module.ts index 1e767a5690..104a6d0579 100644 --- a/catalog-ui/src/app/ng2/pages/req-and-capabilities-editor/capabilities-editor/capabilities-editor.module.ts +++ b/catalog-ui/src/app/ng2/pages/req-and-capabilities-editor/capabilities-editor/capabilities-editor.module.ts @@ -5,7 +5,8 @@ import {FormsModule} from "@angular/forms"; import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module"; import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; import {TranslateModule} from 'app/ng2/shared/translator/translate.module'; -import {SdcUiComponentsModule} from "sdc-ui/lib/angular/index"; +import { SdcUiComponentsModule } from 'onap-ui-angular'; + @NgModule({ declarations: [ diff --git a/catalog-ui/src/app/ng2/pages/req-and-capabilities-editor/requirements-editor/requirements-editor.module.ts b/catalog-ui/src/app/ng2/pages/req-and-capabilities-editor/requirements-editor/requirements-editor.module.ts index 1be8be51af..d38790a8db 100644 --- a/catalog-ui/src/app/ng2/pages/req-and-capabilities-editor/requirements-editor/requirements-editor.module.ts +++ b/catalog-ui/src/app/ng2/pages/req-and-capabilities-editor/requirements-editor/requirements-editor.module.ts @@ -4,7 +4,7 @@ import {RequirementsEditorComponent} from "./requirements-editor.component"; import {FormsModule} from "@angular/forms"; import {FormElementsModule} from "../../../components/ui/form-components/form-elements.module"; import {TranslateModule} from 'app/ng2/shared/translator/translate.module'; -import {SdcUiComponentsModule} from "sdc-ui/lib/angular/index"; +import { SdcUiComponentsModule } from "onap-ui-angular"; @NgModule({ declarations: [ diff --git a/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.ts b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.ts index 2c86cc5c5c..8444c6261a 100644 --- a/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.ts +++ b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.ts @@ -14,29 +14,29 @@ * permissions and limitations under the License. */ -import * as _ from "lodash"; import { Component } from '@angular/core'; -import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; import { - Service, - ServiceInstanceObject, - InstanceFePropertiesMap, - InstanceBePropertiesMap, - PropertyBEModel, + Capability, InputBEModel, - OperationModel, + InstanceBePropertiesMap, + InstanceFePropertiesMap, InterfaceModel, - Capability + OperationModel, + PropertyBEModel, + Service } from 'app/models'; -import {ConsumptionInput, ConsumptionInputDetails, ServiceOperation} from 'app/ng2/components/logic/service-consumption/service-consumption.component'; -import {PropertiesUtils} from "app/ng2/pages/properties-assignment/services/properties.utils"; +import { ConsumptionInput, ConsumptionInputDetails, ServiceOperation } from 'app/ng2/components/logic/service-consumption/service-consumption.component'; +import { PropertiesUtils } from 'app/ng2/pages/properties-assignment/services/properties.utils'; +import { ServiceServiceNg2 } from 'app/ng2/services/component-services/service.service'; import { PROPERTY_DATA } from 'app/utils'; - +import * as _ from 'lodash'; +import { ServiceInstanceObject } from '../../../models/service-instance-properties-and-interfaces'; +import { TopologyTemplateService } from '../../services/component-services/topology-template.service'; @Component({ selector: 'service-consumption-editor', templateUrl: './service-consumption-editor.component.html', - styleUrls:['./service-consumption-editor.component.less'], + styleUrls: ['./service-consumption-editor.component.less'], providers: [] }) @@ -45,27 +45,27 @@ export class ServiceConsumptionCreatorComponent { input: { interfaceId: string, serviceOperationIndex: number, - serviceOperations: Array, + serviceOperations: ServiceOperation[], parentService: Service, selectedService: Service, - parentServiceInputs: Array, - selectedServiceProperties: Array, - selectedServiceInstanceId: String, - selectedInstanceSiblings: Array, - selectedInstanceCapabilitisList: Array, - siblingsCapabilitiesList: Map> + parentServiceInputs: InputBEModel[], + selectedServiceProperties: PropertyBEModel[], + selectedServiceInstanceId: string, + selectedInstanceSiblings: ServiceInstanceObject[], + selectedInstanceCapabilitisList: Capability[], + siblingsCapabilitiesList: Map }; - sourceTypes: Array = []; - serviceOperationsList: Array; + sourceTypes: any[] = []; + serviceOperationsList: ServiceOperation[]; serviceOperation: ServiceOperation; currentIndex: number; isLoading: boolean = false; parentService: Service; selectedService: Service; - selectedServiceInstanceId: String; - parentServiceInputs: Array; - selectedServiceProperties: Array; - changedData: Array = []; + selectedServiceInstanceId: string; + parentServiceInputs: InputBEModel[]; + selectedServiceProperties: PropertyBEModel[]; + changedData: ConsumptionInputDetails[] = []; inputFePropertiesMap: any = []; SOURCE_TYPES = { @@ -75,7 +75,7 @@ export class ServiceConsumptionCreatorComponent { SERVICE_INPUT_LABEL: 'Service Input' }; - constructor(private serviceServiceNg2: ServiceServiceNg2, private propertiesUtils:PropertiesUtils) {} + constructor(private topologyTemplateService: TopologyTemplateService, private propertiesUtils: PropertiesUtils) {} ngOnInit() { this.serviceOperationsList = this.input.serviceOperations; @@ -112,7 +112,7 @@ export class ServiceConsumptionCreatorComponent { capabilities: [] } ]; - _.forEach(this.input.selectedInstanceSiblings, sib => + _.forEach(this.input.selectedInstanceSiblings, (sib) => this.sourceTypes.push({ label: sib.name, value: sib.id, @@ -128,56 +128,84 @@ export class ServiceConsumptionCreatorComponent { } onExpandAll() { - _.forEach(this.serviceOperation.consumptionInputs, coInput => { + _.forEach(this.serviceOperation.consumptionInputs, (coInput) => { coInput.expanded = true; - }) + }); } onCollapseAll() { - _.forEach(this.serviceOperation.consumptionInputs, coInput => { + _.forEach(this.serviceOperation.consumptionInputs, (coInput) => { coInput.expanded = false; - }) + }); } isAllInputExpanded() { - return _.every(this.serviceOperation.consumptionInputs, coInput => coInput.expanded === true); + return _.every(this.serviceOperation.consumptionInputs, (coInput) => coInput.expanded === true); } isAllInputCollapsed() { - return _.every(this.serviceOperation.consumptionInputs, coInput => coInput.expanded === false); + return _.every(this.serviceOperation.consumptionInputs, (coInput) => coInput.expanded === false); } onChangePage(newIndex) { if (newIndex >= 0 && newIndex < this.serviceOperationsList.length) { this.currentIndex = newIndex; this.serviceOperation = this.serviceOperationsList[newIndex]; - if(!this.serviceOperation.consumptionInputs || this.serviceOperation.consumptionInputs.length === 0) { + if (!this.serviceOperation.consumptionInputs || this.serviceOperation.consumptionInputs.length === 0) { this.initConsumptionInputs(); } this.getComplexPropertiesForCurrentInputsOfOperation(this.serviceOperation.consumptionInputs); } } + checkFormValidForSubmit(): boolean { + return this.isValidInputsValues() && this.isMandatoryFieldsValid(); + } + + checkFormValidForNavigation(): boolean { + return this.isMandatoryFieldsValid() && (this.changedData.length === 0 || this.isValidInputsValues()); + } + + onChange(value: any, isValid: boolean, consumptionInput: ConsumptionInputDetails) { + consumptionInput.updateValidity(isValid); + const dataChangedIndex = this.changedData.findIndex((changedItem) => changedItem.inputId === consumptionInput.inputId); + if (value !== consumptionInput.origVal) { + if (dataChangedIndex === -1) { + this.changedData.push(consumptionInput); + } + } else { + if (dataChangedIndex !== -1) { + this.changedData.splice(dataChangedIndex, 1); + } + } + } + + onComplexPropertyChanged(property, consumptionInput) { + consumptionInput.value = JSON.stringify(property.valueObj); + this.onChange(property.valueObj, property.valueObjIsValid , consumptionInput); + } + private initConsumptionInputs() { this.isLoading = true; - this.serviceServiceNg2.getServiceConsumptionInputs(this.parentService, this.selectedServiceInstanceId, this.input.interfaceId, this.serviceOperation.operation).subscribe((result: Array) => { + this.topologyTemplateService.getServiceConsumptionInputs(this.parentService.uniqueId, this.selectedServiceInstanceId, + this.input.interfaceId, this.serviceOperation.operation).subscribe((result: ConsumptionInput[]) => { this.isLoading = false; this.serviceOperation.consumptionInputs = this.analyzeCurrentConsumptionInputs(result); this.getComplexPropertiesForCurrentInputsOfOperation(this.serviceOperation.consumptionInputs); - }, err=> { + }, (err) => { this.isLoading = false; }); } - private analyzeCurrentConsumptionInputs(result: Array): Array { - let inputsResult: Array = []; - let currentOp = this.serviceOperation.operation; - if(currentOp) { - inputsResult = _.map(result, input => { - let sourceVal = input.source || this.SOURCE_TYPES.STATIC; - let consumptionInputDetails: ConsumptionInputDetails = _.cloneDeep(input); + private analyzeCurrentConsumptionInputs(result: any[]): ConsumptionInputDetails[] { + let inputsResult: ConsumptionInputDetails[] = []; + const currentOp = this.serviceOperation.operation; + if (currentOp) { + inputsResult = _.map(result, (input) => { + const sourceVal = input.source || this.SOURCE_TYPES.STATIC; + const consumptionInputDetails: ConsumptionInputDetails = _.cloneDeep(input); consumptionInputDetails.source = sourceVal; consumptionInputDetails.isValid = true; consumptionInputDetails.expanded = false; - let filteredListsObj = this.getFilteredProps(sourceVal, input.type); + const filteredListsObj = this.getFilteredProps(sourceVal, input.type); consumptionInputDetails.assignValueLabel = this.getAssignValueLabel(sourceVal); consumptionInputDetails.associatedProps = filteredListsObj.associatedPropsList; consumptionInputDetails.associatedInterfaces = filteredListsObj.associatedInterfacesList; @@ -190,15 +218,14 @@ export class ServiceConsumptionCreatorComponent { private onSourceChanged(consumptionInput: ConsumptionInputDetails): void { consumptionInput.assignValueLabel = this.getAssignValueLabel(consumptionInput.source); - let filteredListsObj = this.getFilteredProps(consumptionInput.source, consumptionInput.type); + const filteredListsObj = this.getFilteredProps(consumptionInput.source, consumptionInput.type); consumptionInput.associatedProps = filteredListsObj.associatedPropsList; consumptionInput.associatedInterfaces = filteredListsObj.associatedInterfacesList; consumptionInput.associatedCapabilities = filteredListsObj.associatedCapabilitiesList; - if(consumptionInput.source === this.SOURCE_TYPES.STATIC) { - if(PROPERTY_DATA.SIMPLE_TYPES.indexOf(consumptionInput.type) !== -1) { - consumptionInput.value = consumptionInput.defaultValue || ""; - } - else { + if (consumptionInput.source === this.SOURCE_TYPES.STATIC) { + if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(consumptionInput.type) !== -1) { + consumptionInput.value = consumptionInput.defaultValue || ''; + } else { consumptionInput.value = null; Object.assign(this.inputFePropertiesMap, this.processPropertiesOfComplexTypeInput(consumptionInput)); } @@ -206,9 +233,11 @@ export class ServiceConsumptionCreatorComponent { } private getFilteredProps(sourceVal, inputType) { - let currentSourceObj = this.sourceTypes.find(s => s.value === sourceVal); - let associatedInterfacesList = [], associatedPropsList = [], associatedCapabilitiesPropsList: Array = []; - if(currentSourceObj) { + const currentSourceObj = this.sourceTypes.find((s) => s.value === sourceVal); + let associatedInterfacesList = []; + let associatedPropsList = []; + let associatedCapabilitiesPropsList: Capability[] = []; + if (currentSourceObj) { if (currentSourceObj.interfaces) { associatedInterfacesList = this.getFilteredInterfaceOutputs(currentSourceObj, inputType); } @@ -221,31 +250,31 @@ export class ServiceConsumptionCreatorComponent { associatedCapabilitiesPropsList = _.reduce(currentSourceObj.capabilities, (filteredCapsList, capability: Capability) => { - let filteredProps = _.filter(capability.properties, prop => prop.type === inputType); + const filteredProps = _.filter(capability.properties, (prop) => prop.type === inputType); if (filteredProps.length) { - let cap = new Capability(capability); + const cap = new Capability(capability); cap.properties = filteredProps; filteredCapsList.push(cap); } - return filteredCapsList + return filteredCapsList; }, []); } return { - associatedPropsList: associatedPropsList, - associatedInterfacesList: associatedInterfacesList, + associatedPropsList, + associatedInterfacesList, associatedCapabilitiesList: associatedCapabilitiesPropsList - } + }; } private getFilteredInterfaceOutputs(currentSourceObj, inputType) { - let currentServiceOperationId = this.serviceOperation.operation.uniqueId; - let filteredInterfacesList = []; - Object.keys(currentSourceObj.interfaces).map(interfId => { - let interfaceObj: InterfaceModel = new InterfaceModel(currentSourceObj.interfaces[interfId]); - Object.keys(interfaceObj.operations).map(opId => { - if(currentServiceOperationId !== opId) { - let operationObj: OperationModel = interfaceObj.operations[opId]; - let filteredOutputsList = _.filter(operationObj.outputs.listToscaDataDefinition, output => output.type === inputType); + const currentServiceOperationId = this.serviceOperation.operation.uniqueId; + const filteredInterfacesList = []; + Object.keys(currentSourceObj.interfaces).map((interfId) => { + const interfaceObj: InterfaceModel = new InterfaceModel(currentSourceObj.interfaces[interfId]); + Object.keys(interfaceObj.operations).map((opId) => { + if (currentServiceOperationId !== opId) { + const operationObj: OperationModel = interfaceObj.operations[opId]; + const filteredOutputsList = _.filter(operationObj.outputs.listToscaDataDefinition, (output) => output.type === inputType); if (filteredOutputsList.length) { filteredInterfacesList.push({ name: `${interfaceObj.type}.${operationObj.name}`, @@ -259,25 +288,23 @@ export class ServiceConsumptionCreatorComponent { return filteredInterfacesList; } - getAssignValueLabel(selectedSource: string): string { - if(selectedSource === this.SOURCE_TYPES.STATIC || selectedSource === "") { + private getAssignValueLabel(selectedSource: string): string { + if (selectedSource === this.SOURCE_TYPES.STATIC || selectedSource === '') { return this.SOURCE_TYPES.STATIC; - } - else { - if(selectedSource === this.parentService.uniqueId) { //parent is the source + } else { + if (selectedSource === this.parentService.uniqueId) { // parent is the source return this.SOURCE_TYPES.SERVICE_INPUT_LABEL; } return this.SOURCE_TYPES.SERVICE_PROPERTY_LABEL; } } - private isValidInputsValues(): boolean { return this.changedData.length > 0 && this.changedData.every((changedItem) => changedItem.isValid); } private isMandatoryFieldsValid(): boolean { - const invalid: Array = this.serviceOperation.consumptionInputs.filter(item => + const invalid: ConsumptionInputDetails[] = this.serviceOperation.consumptionInputs.filter((item) => item.required && (item.value === null || typeof item.value === 'undefined' || item.value === '')); if (invalid.length > 0) { return false; @@ -285,45 +312,19 @@ export class ServiceConsumptionCreatorComponent { return true; } - checkFormValidForSubmit(): boolean { - return this.isValidInputsValues() && this.isMandatoryFieldsValid(); - } - - checkFormValidForNavigation(): boolean { - return this.isMandatoryFieldsValid() && (this.changedData.length === 0 || this.isValidInputsValues()); - } - - onChange(value: any, isValid: boolean, consumptionInput: ConsumptionInputDetails) { - consumptionInput.updateValidity(isValid); - const dataChangedIndex = this.changedData.findIndex((changedItem) => changedItem.inputId === consumptionInput.inputId); - if (value !== consumptionInput.origVal) { - if (dataChangedIndex === -1) { - this.changedData.push(consumptionInput); - } - } else { - if (dataChangedIndex !== -1) { - this.changedData.splice(dataChangedIndex, 1); - } - } - } - - private getComplexPropertiesForCurrentInputsOfOperation(opInputs: Array) { - _.forEach(opInputs, input => { - if(PROPERTY_DATA.SIMPLE_TYPES.indexOf(input.type) === -1 && input.source === this.SOURCE_TYPES.STATIC) { + private getComplexPropertiesForCurrentInputsOfOperation(opInputs: ConsumptionInput[]) { + _.forEach(opInputs, (input) => { + if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(input.type) === -1 && input.source === this.SOURCE_TYPES.STATIC) { Object.assign(this.inputFePropertiesMap, this.processPropertiesOfComplexTypeInput(input)); } }); } private processPropertiesOfComplexTypeInput(input: ConsumptionInput): InstanceFePropertiesMap { - let inputBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); + const inputBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); inputBePropertiesMap[input.name] = [input]; - let originTypeIsVF = false; - return this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(inputBePropertiesMap, originTypeIsVF); //create flattened children and init values + const originTypeIsVF = false; + return this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(inputBePropertiesMap, originTypeIsVF); // create flattened children and init values } - onComplexPropertyChanged(property, consumptionInput) { - consumptionInput.value = JSON.stringify(property.valueObj); - this.onChange(property.valueObj, property.valueObjIsValid , consumptionInput); - } -} \ No newline at end of file +} diff --git a/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.module.ts b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.module.ts index e37cd76716..43e88eb0dc 100644 --- a/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.module.ts +++ b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.module.ts @@ -1,11 +1,11 @@ -import { NgModule } from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {ServiceConsumptionCreatorComponent} from "./service-consumption-editor.component"; -import {FormsModule} from "@angular/forms"; -import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module"; -import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; -import {PropertyTableModule} from 'app/ng2/components/logic/properties-table/property-table.module'; -import {TranslateModule} from 'app/ng2/shared/translator/translate.module'; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { PropertyTableModule } from 'app/ng2/components/logic/properties-table/property-table.module'; +import { FormElementsModule } from 'app/ng2/components/ui/form-components/form-elements.module'; +import { UiElementsModule } from 'app/ng2/components/ui/ui-elements.module'; +import { TranslateModule } from 'app/ng2/shared/translator/translate.module'; +import { ServiceConsumptionCreatorComponent } from './service-consumption-editor.component'; @NgModule({ declarations: [ @@ -25,4 +25,4 @@ import {TranslateModule} from 'app/ng2/shared/translator/translate.module'; providers: [] }) export class ServiceConsumptionCreatorModule { -} \ No newline at end of file +} diff --git a/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.ts b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.ts index 271dd4ada0..708742ae0c 100644 --- a/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.ts +++ b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.ts @@ -14,20 +14,23 @@ * permissions and limitations under the License. */ import { Component } from '@angular/core'; -import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; -import {ConstraintObjectUI, OPERATOR_TYPES} from 'app/ng2/components/logic/service-dependencies/service-dependencies.component'; -import {ServiceInstanceObject, PropertyBEModel, InputBEModel} from 'app/models'; +import { InputBEModel, PropertyBEModel } from 'app/models'; +import { ConstraintObjectUI, OPERATOR_TYPES } from 'app/ng2/components/logic/service-dependencies/service-dependencies.component'; +import { DropdownValue } from 'app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component'; +import { ServiceServiceNg2 } from 'app/ng2/services/component-services/service.service'; import { PROPERTY_DATA } from 'app/utils'; -import {DropdownValue} from 'app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component'; +import { ServiceInstanceObject } from '../../../models/service-instance-properties-and-interfaces'; -export class UIDropDownSourceTypesElement extends DropdownValue{ - options: Array; +export class UIDropDownSourceTypesElement extends DropdownValue { + options: any[]; assignedLabel: string; type: string; - constructor(input?: any){ - if(input) { - let value = input.value || ''; - let label = input.label || ''; + constructor(input?: any) { + if (input) { + const value = input.value || ''; + const label = input.label || ''; + // const hidden = input.hidden || ''; + // const selected = input.selected || ''; super(value, label); this.options = input.options; this.assignedLabel = input.assignedLabel; @@ -36,10 +39,11 @@ export class UIDropDownSourceTypesElement extends DropdownValue{ } } +// tslint:disable-next-line:max-classes-per-file @Component({ selector: 'service-dependencies-editor', templateUrl: './service-dependencies-editor.component.html', - styleUrls:['./service-dependencies-editor.component.less'], + styleUrls: ['./service-dependencies-editor.component.less'], providers: [ServiceServiceNg2] }) @@ -47,44 +51,42 @@ export class ServiceDependenciesEditorComponent { input: { serviceRuleIndex: number, - serviceRules: Array, + serviceRules: ConstraintObjectUI[], compositeServiceName: string, currentServiceName: string, - parentServiceInputs: Array, - selectedInstanceProperties: Array, - operatorTypes: Array, - selectedInstanceSiblings: Array + parentServiceInputs: InputBEModel[], + selectedInstanceProperties: PropertyBEModel[], + operatorTypes: DropdownValue[], + selectedInstanceSiblings: ServiceInstanceObject[] }; currentServiceName: string; - selectedServiceProperties: Array; + selectedServiceProperties: PropertyBEModel[]; selectedPropertyObj: PropertyBEModel; - ddValueSelectedServicePropertiesNames: Array; - operatorTypes: Array; - sourceTypes: Array = []; + ddValueSelectedServicePropertiesNames: DropdownValue[]; + operatorTypes: DropdownValue[]; + sourceTypes: UIDropDownSourceTypesElement[] = []; currentRule: ConstraintObjectUI; currentIndex: number; - listOfValuesToAssign: Array; - listOfSourceOptions: Array; + listOfValuesToAssign: DropdownValue[]; + listOfSourceOptions: PropertyBEModel[]; assignedValueLabel: string; - serviceRulesList: Array; - + serviceRulesList: ConstraintObjectUI[]; SOURCE_TYPES = { STATIC: {label: 'Static', value: 'static'}, SERVICE_PROPERTY: {label: 'Service Property', value: 'property'} }; - ngOnInit() { this.currentIndex = this.input.serviceRuleIndex; this.serviceRulesList = this.input.serviceRules; this.currentRule = this.serviceRulesList && this.input.serviceRuleIndex >= 0 ? - this.serviceRulesList[this.input.serviceRuleIndex]: - new ConstraintObjectUI({sourceName: this.SOURCE_TYPES.STATIC.value, sourceType: this.SOURCE_TYPES.STATIC.value, value: "", constraintOperator: OPERATOR_TYPES.EQUAL}); + this.serviceRulesList[this.input.serviceRuleIndex] : + new ConstraintObjectUI({sourceName: this.SOURCE_TYPES.STATIC.value, sourceType: this.SOURCE_TYPES.STATIC.value, value: '', constraintOperator: OPERATOR_TYPES.EQUAL}); this.currentServiceName = this.input.currentServiceName; this.operatorTypes = this.input.operatorTypes; this.selectedServiceProperties = this.input.selectedInstanceProperties; - this.ddValueSelectedServicePropertiesNames = _.map(this.input.selectedInstanceProperties, prop => new DropdownValue(prop.name, prop.name)); + this.ddValueSelectedServicePropertiesNames = _.map(this.input.selectedInstanceProperties, (prop) => new DropdownValue(prop.name, prop.name)); this.initSourceTypes(); this.syncRuleData(); this.updateSourceTypesRelatedValues(); @@ -100,7 +102,7 @@ export class ServiceDependenciesEditorComponent { type: this.SOURCE_TYPES.SERVICE_PROPERTY.value, options: this.input.parentServiceInputs }); - _.forEach(this.input.selectedInstanceSiblings, sib => + _.forEach(this.input.selectedInstanceSiblings, (sib) => this.sourceTypes.push({ label: sib.name, value: sib.name, @@ -112,28 +114,27 @@ export class ServiceDependenciesEditorComponent { } syncRuleData() { - if(!this.currentRule.sourceName && this.currentRule.sourceType === this.SOURCE_TYPES.STATIC.value) { + if (!this.currentRule.sourceName && this.currentRule.sourceType === this.SOURCE_TYPES.STATIC.value) { this.currentRule.sourceName = this.SOURCE_TYPES.STATIC.value; } - this.selectedPropertyObj = _.find(this.selectedServiceProperties, prop => prop.name === this.currentRule.servicePropertyName); + this.selectedPropertyObj = _.find(this.selectedServiceProperties, (prop) => prop.name === this.currentRule.servicePropertyName); this.updateOperatorTypesList(); this.updateSourceTypesRelatedValues(); } updateOperatorTypesList() { if (this.selectedPropertyObj && PROPERTY_DATA.SIMPLE_TYPES_COMPARABLE.indexOf(this.selectedPropertyObj.type) === -1) { - this.operatorTypes = [{label: "=", value: OPERATOR_TYPES.EQUAL}]; + this.operatorTypes = [{label: '=', value: OPERATOR_TYPES.EQUAL}]; this.currentRule.constraintOperator = OPERATOR_TYPES.EQUAL; - } - else { + } else { this.operatorTypes = this.input.operatorTypes; } } updateSourceTypesRelatedValues() { - if(this.currentRule.sourceName) { - let selectedSourceType: UIDropDownSourceTypesElement = this.sourceTypes.find( - t => t.value === this.currentRule.sourceName && t.type === this.currentRule.sourceType + if (this.currentRule.sourceName) { + const selectedSourceType: UIDropDownSourceTypesElement = this.sourceTypes.find( + (t) => t.value === this.currentRule.sourceName && t.type === this.currentRule.sourceType ); this.listOfSourceOptions = selectedSourceType.options || []; this.assignedValueLabel = selectedSourceType.assignedLabel || this.SOURCE_TYPES.STATIC.label; @@ -150,7 +151,7 @@ export class ServiceDependenciesEditorComponent { } onServicePropertyChanged() { - this.selectedPropertyObj = _.find(this.selectedServiceProperties, prop => prop.name === this.currentRule.servicePropertyName); + this.selectedPropertyObj = _.find(this.selectedServiceProperties, (prop) => prop.name === this.currentRule.servicePropertyName); this.updateOperatorTypesList(); this.filterOptionsByType(); this.currentRule.value = ''; @@ -165,11 +166,11 @@ export class ServiceDependenciesEditorComponent { } filterOptionsByType() { - if(!this.selectedPropertyObj) { + if (!this.selectedPropertyObj) { this.listOfValuesToAssign = []; return; } - this.listOfValuesToAssign = this.listOfSourceOptions.reduce((result, op:PropertyBEModel) => { + this.listOfValuesToAssign = this.listOfSourceOptions.reduce((result, op: PropertyBEModel) => { if (op.type === this.selectedPropertyObj.type && (!op.schemaType || op.schemaType === this.selectedPropertyObj.schemaType)) { result.push(new DropdownValue(op.name, op.name)); } @@ -182,11 +183,11 @@ export class ServiceDependenciesEditorComponent { } checkFormValidForSubmit() { - if(!this.serviceRulesList) { //for create modal - let isStatic = this.currentRule.sourceName === this.SOURCE_TYPES.STATIC.value; + if (!this.serviceRulesList) { // for create modal + const isStatic = this.currentRule.sourceName === this.SOURCE_TYPES.STATIC.value; return this.currentRule.isValidRule(isStatic); } - //for update all rules - return this.serviceRulesList.every(rule => rule.isValidRule(rule.sourceName === this.SOURCE_TYPES.STATIC.value)); + // for update all rules + return this.serviceRulesList.every((rule) => rule.isValidRule(rule.sourceName === this.SOURCE_TYPES.STATIC.value)); } -} \ No newline at end of file +} diff --git a/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.module.ts b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.module.ts index 98ac997bf7..7b128f4468 100644 --- a/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.module.ts +++ b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.module.ts @@ -1,9 +1,9 @@ -import { NgModule } from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {ServiceDependenciesEditorComponent} from "./service-dependencies-editor.component"; -import {FormsModule} from "@angular/forms"; -import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module"; -import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { FormElementsModule } from 'app/ng2/components/ui/form-components/form-elements.module'; +import { UiElementsModule } from 'app/ng2/components/ui/ui-elements.module'; +import { ServiceDependenciesEditorComponent } from './service-dependencies-editor.component'; @NgModule({ declarations: [ @@ -22,4 +22,4 @@ import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; providers: [] }) export class ServiceDependenciesEditorModule { -} \ No newline at end of file +} diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html deleted file mode 100644 index 0abdda1cc6..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - -
- - -
diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less deleted file mode 100644 index beec9bd567..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less +++ /dev/null @@ -1,21 +0,0 @@ -@import './../../../../../assets/styles/variables.less'; -.remove { - display: flex; - align-items: center; - justify-content: center; -} - -.cell { - padding: 0; -} - -/deep/ .link-selector { - select { - height: 30px; - border: none; - stroke: none; - } - -} - - diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts deleted file mode 100644 index e4fc1d4522..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts +++ /dev/null @@ -1,103 +0,0 @@ -import {Component, Input} from '@angular/core'; -import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component"; -import {Link} from './link.model'; -import {ServicePathMapItem} from "app/models/graph/nodes-and-links-map"; - -@Component({ - selector: 'link-row', - templateUrl: './link-row.component.html', - styleUrls: ['./link-row.component.less'] -}) - - -export class LinkRowComponent { - @Input() data:Array; - @Input() link:Link; - @Input() removeRow:Function; - source: Array = []; - target: Array = []; - srcCP: Array = []; - targetCP: Array = []; - - ngOnChanges() { - if (this.data) { - this.parseInitialData(this.data); - } - } - - parseInitialData(data: Array) { - this.source = this.convertValuesToDropDownOptions(data); - if (this.link.fromNode) { - let srcCPOptions = this.findOptions(data, this.link.fromNode); - if (!srcCPOptions) { return; } - this.srcCP = this.convertValuesToDropDownOptions(srcCPOptions); - if (this.link.fromCP) { - this.target = this.convertValuesToDropDownOptions(data); - if (this.link.toNode) { - let targetCPOptions = this.findOptions(data, this.link.toNode); - if (!targetCPOptions) { return; } - this.targetCP = this.convertValuesToDropDownOptions(targetCPOptions); - } - } - } - } - - private findOptions(items: Array, nodeOrCPId: string) { - let item = _.find(items, (dataItem) => nodeOrCPId === dataItem.id); - if (item && item.data && item.data.options) { - return item.data.options; - } - console.warn('no option was found to match selection of Node/CP with id:' + nodeOrCPId); - return null; - } - - private convertValuesToDropDownOptions(values: Array): Array { - let result:Array = []; - for (let i = 0; i < values.length ; i++) { - result[result.length] = new DropdownValue(values[i].id, values[i].data.name); - } - return result.sort((a, b) => a.label.localeCompare(b.label)); - } - - onSourceSelected(id) { - if (id) { - let srcCPOptions = this.findOptions(this.data, id); - this.srcCP = this.convertValuesToDropDownOptions(srcCPOptions); - this.link.fromCP = ''; - this.link.toNode = ''; - this.link.toCP = ''; - this.target = []; - this.targetCP = []; - } - } - - onSrcCPSelected (id) { - if (id) { - let srcCPOptions = this.findOptions(this.data, this.link.fromNode); - let srcCPData = srcCPOptions.find(option => id === option.id).data; - this.target = this.convertValuesToDropDownOptions(this.data); - this.link.fromCPOriginId = srcCPData.ownerId; - this.link.toNode = ''; - this.link.toCP = ''; - this.targetCP = []; - } - - } - - onTargetSelected(id) { - if (id) { - let targetCPOptions = this.findOptions(this.data, id); - this.targetCP = this.convertValuesToDropDownOptions(targetCPOptions); - this.link.toCP = ''; - } - - } - - onTargetCPSelected(id) { - if (id) { - let targetCPOptions = this.findOptions(this.data, this.link.toNode); - let targetCPDataObj = targetCPOptions.find(option => id === option.id).data; - this.link.toCPOriginId = targetCPDataObj.ownerId; - } - } -} diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts deleted file mode 100644 index 80128eb42e..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts +++ /dev/null @@ -1,36 +0,0 @@ -/*- - * ============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========================================================= - */ -'use strict'; -import {ForwardingPathLink} from "app/models/forwarding-path-link"; - -export class Link extends ForwardingPathLink { - public canEdit:boolean = false; - public canRemove:boolean = false; - public isFirst:boolean = false; - - constructor(link: ForwardingPathLink, canEdit: boolean, canRemove: boolean, isFirst: boolean) { - super(link.fromNode,link.fromCP, link.toNode, link.toCP, link.fromCPOriginId, link.toCPOriginId); - this.canEdit = canEdit; - this.canRemove = canRemove; - this.isFirst = isFirst; - } -} - - diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html deleted file mode 100644 index cc14b4961f..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html +++ /dev/null @@ -1,56 +0,0 @@ - - -
-
-
- - -
- -
-
- - -
-
- - -
-
- -
- Based On - Extend Flow -
- -
-
-
- {{header}} -
-
-
- There is no data to display -
-
- -
-
- - -
-
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less deleted file mode 100644 index 5c9e53e229..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less +++ /dev/null @@ -1,45 +0,0 @@ -@import './../../../../assets/styles/variables.less'; -.service-path-creator { - font-family: @font-opensans-regular; - .separator-buttons { - margin: 10px 0; - display: flex; - justify-content: space-between; - } - .i-sdc-form-label { - font-size: 12px; - } - .w-sdc-form .i-sdc-form-item { - margin-bottom: 15px; - } - - .side-by-side { - display: flex; - .i-sdc-form-item { - flex-basis: 100%; - &:first-child { - margin-right: 10px; - } - } - } - - .generic-table { - max-height: 233px; - .header-row .header-cell { - &:last-child { - padding: 0; - } - } - /deep/ .cell { - &:last-child { - min-width: 30px; - } - } - } - - .based-on-title { - text-transform: uppercase; - font-size: 18px; - font-family: @font-opensans-regular; - } -} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts deleted file mode 100644 index bffb1c5e7e..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts +++ /dev/null @@ -1,147 +0,0 @@ -/*- - * ============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========================================================= - */ - -import * as _ from "lodash"; -import { Component, ElementRef, forwardRef, Inject } from '@angular/core'; -import {Link} from './link-row/link.model'; -import {ForwardingPath} from 'app/models/forwarding-path'; -import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; -import {ForwardingPathLink} from "app/models/forwarding-path-link"; -import {ServicePathMapItem} from "app/models/graph/nodes-and-links-map"; - -@Component({ - selector: 'service-path-creator', - templateUrl: './service-path-creator.component.html', - styleUrls:['./service-path-creator.component.less'], - providers: [ServiceServiceNg2] -}) - -export class ServicePathCreatorComponent { - - linksMap:Array; - links:Array = []; - input:any; - headers: Array = []; - removeRow: Function; - forwardingPath:ForwardingPath; - //isExtendAllowed:boolean = false; - - constructor(private serviceService: ServiceServiceNg2) { - this.forwardingPath = new ForwardingPath(); - this.links = [new Link(new ForwardingPathLink('', '', '', '', '', ''), true, false, true)]; - this.headers = ['Source', 'Source Connection Point', 'Target', 'Target Connection Point', ' ']; - this.removeRow = () => { - if (this.links.length === 1) { - return; - } - this.links.splice(this.links.length-1, 1); - this.enableCurrentRow(); - }; - } - - ngOnInit() { - this.serviceService.getNodesAndLinksMap(this.input.service).subscribe((res:any) => { - this.linksMap = res; - }); - this.processExistingPath(); - - } - - private processExistingPath() { - if (this.input.pathId) { - let forwardingPath = {...this.input.service.forwardingPaths[this.input.pathId]}; - this.forwardingPath.name = forwardingPath.name; - this.forwardingPath.destinationPortNumber = forwardingPath.destinationPortNumber; - this.forwardingPath.protocol = forwardingPath.protocol; - this.forwardingPath.uniqueId = forwardingPath.uniqueId; - this.links = []; - _.forEach(forwardingPath.pathElements.listToscaDataDefinition, (link:ForwardingPathLink) => { - this.links[this.links.length] = new Link(link, false, false, false); - }); - this.links[this.links.length - 1].canEdit = true; - this.links[this.links.length - 1].canRemove = true; - this.links[0].isFirst = true; - } - } - - isExtendAllowed():boolean { - if (this.links[this.links.length-1].toCP) { - return true; - } - return false; - } - - enableCurrentRow() { - this.links[this.links.length-1].canEdit = true; - if (this.links.length !== 1) { - this.links[this.links.length-1].canRemove = true; - } - } - - addRow() { - this.disableRows(); - this.links[this.links.length] = new Link( - new ForwardingPathLink(this.links[this.links.length-1].toNode, - this.links[this.links.length-1].toCP, - '', - '', - this.links[this.links.length-1].toCPOriginId, - '' - ), - true, - true, - false - ); - } - - disableRows() { - for (let i = 0 ; i < this.links.length ; i++) { - this.links[i].canEdit = false; - this.links[i].canRemove = false; - } - } - - createPathLinksObject() { - for (let i = 0 ; i < this.links.length ; i++) { - let link = this.links[i]; - this.forwardingPath.addPathLink(link.fromNode, link.fromCP, link.toNode, link.toCP, link.fromCPOriginId, link.toCPOriginId); - } - } - - createServicePathData() { - this.createPathLinksObject(); - return this.forwardingPath; - } - - checkFormValidForSubmit():boolean { - if (this.forwardingPath.name && this.isPathValid() ) { - return true; - } - return false; - } - - isPathValid():boolean { - let lastLink = this.links[this.links.length -1] ; - if (lastLink.toNode && lastLink.toCP && lastLink.fromNode && lastLink.fromCP) { - return true; - } - return false; - } -} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts deleted file mode 100644 index 78005317a2..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NgModule } from "@angular/core"; -import {CommonModule} from "@angular/common"; -import {ServicePathCreatorComponent} from "./service-path-creator.component"; -import {FormsModule} from "@angular/forms"; -import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module"; -import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; -import {LinkRowComponent} from './link-row/link-row.component' -@NgModule({ - declarations: [ - ServicePathCreatorComponent, - LinkRowComponent - ], - imports: [CommonModule, - FormsModule, - FormElementsModule, - UiElementsModule - ], - exports: [], - entryComponents: [ - ServicePathCreatorComponent - ], - providers: [] -}) -export class ServicePathCreatorModule { -} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html deleted file mode 100644 index 33a0090372..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html +++ /dev/null @@ -1,37 +0,0 @@ - - -
- -
-
-
- {{header}} -
-
-
-
{{path.name}}
-
- - -
-
-
- No flows have been added yet. -
-
- -
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less deleted file mode 100644 index 291119f58c..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less +++ /dev/null @@ -1,24 +0,0 @@ -@import './../../../../assets/styles/variables.less'; - -.add-path-link { - display: flex; - align-items: flex-end; - flex-direction: column; - padding-bottom: 10px; -} - -.generic-table { - max-height: 233px; -} - -.path-action-buttons { - display: flex; - align-items: center; - justify-content: space-between; - .sprite-new { - cursor: pointer; - } - & > span:only-child { - margin: auto; -} -} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts deleted file mode 100644 index 1625ab4b66..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -/*- - * ============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========================================================= - */ - -import * as _ from "lodash"; -import {Component, ComponentRef} from '@angular/core'; -import {ForwardingPath} from "app/models/forwarding-path"; -import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; -import {ModalService} from "app/ng2/services/modal.service"; -import {ModalComponent} from "app/ng2/components/ui/modal/modal.component"; - -@Component({ - selector: 'service-paths-list', - templateUrl: './service-paths-list.component.html', - styleUrls:['service-paths-list.component.less'], - providers: [ServiceServiceNg2, ModalService] -}) -export default class ServicePathsListComponent { - modalInstance: ComponentRef; - headers: Array = []; - paths: Array = []; - input:any; - onAddServicePath: Function; - onEditServicePath: Function; - isViewOnly: boolean; - - constructor(private serviceService:ServiceServiceNg2) { - this.headers = ['Flow Name','Actions']; - } - - ngOnInit() { - _.forEach(this.input.service.forwardingPaths, (path: ForwardingPath)=> { - this.paths[this.paths.length] = path; - }); - this.paths.sort((a:ForwardingPath, b:ForwardingPath)=> { - return a.name.localeCompare(b.name); - }); - this.onAddServicePath = this.input.onCreateServicePath; - this.onEditServicePath = this.input.onEditServicePath; - this.isViewOnly = this.input.isViewOnly; - } - - deletePath = (id:string):void => { - this.serviceService.deleteServicePath(this.input.service, id).subscribe((res:any) => { - delete this.input.service.forwardingPaths[id]; - this.paths = this.paths.filter(function(path){ - return path.uniqueId !== id; - }); - }); - }; - -} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts deleted file mode 100644 index c236934002..0000000000 --- a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NgModule } from "@angular/core"; -import {CommonModule} from "@angular/common"; -import ServicePathsListComponent from "./service-paths-list.component"; - -@NgModule({ - declarations: [ - ServicePathsListComponent - ], - imports: [CommonModule], - exports: [], - entryComponents: [ - ServicePathsListComponent - ], - providers: [] -}) -export class ServicePathsListModule { -} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.html b/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.html new file mode 100644 index 0000000000..d7cf2f930a --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.html @@ -0,0 +1,68 @@ + +
+
+ + +
+ + + + + {{row.TIMESTAMP | date }} | {{row.TIMESTAMP | date:"HH:mm O"}} + + + + + {{row.ACTION}} + + + + + {{ row.COMMENT }} + + + + + {{ row.MODIFIER }} + + + + + + + + + + +
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.less b/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.less new file mode 100644 index 0000000000..4845f4f606 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.less @@ -0,0 +1,8 @@ +.sdc-filter-bar-wrapper { + sdc-filter-bar { + flex: 0 0 30%; + } + display: flex; + justify-content: flex-end; + margin-bottom: 10px; +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.spec.ts new file mode 100644 index 0000000000..25651e0c1f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.spec.ts @@ -0,0 +1,84 @@ +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture } from '@angular/core/testing'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; +import { SdcUiServices } from 'onap-ui-angular'; +import 'rxjs/add/observable/of'; +import { Observable } from 'rxjs/Observable'; +import { ConfigureFn, configureTests } from '../../../../../jest/test-config.helper'; +import { ComponentMetadata } from '../../../../models/component-metadata'; +import { ActivityLogService } from '../../../services/activity-log.service'; +import { WorkspaceService } from '../workspace.service'; +import { ActivityLogComponent } from './activity-log.component'; + +describe('activity log component', () => { + + let fixture: ComponentFixture; + let activityLogServiceMock: Partial; + let workspaceServiceMock: Partial; + let loaderServiceMock: Partial; + let componentMetadataMock: ComponentMetadata; + + const mockLogs = '[' + + '{"MODIFIER":"Carlos Santana(m08740)","COMMENT":"comment","STATUS":"200","ACTION":"Checkout","TIMESTAMP":"2018-11-19 13:00:02.388 UTC"},' + + '{"MODIFIER":"John Doe(m08741)","COMMENT":"comment","STATUS":"200","ACTION":"Checkin","TIMESTAMP":"2018-11-20 13:00:02.388 UTC"},' + + '{"MODIFIER":"Jane Doe(m08742)","COMMENT":"comment","STATUS":"200","ACTION":"Checkout","TIMESTAMP":"2018-11-21 13:00:02.388 UTC"}' + + ']'; + + beforeEach( + async(() => { + + componentMetadataMock = new ComponentMetadata(); + componentMetadataMock.uniqueId = 'fake'; + componentMetadataMock.componentType = 'SERVICE'; + + activityLogServiceMock = { + getActivityLog : jest.fn().mockImplementation((type, id) => Observable.of(JSON.parse(mockLogs)) ) + }; + + workspaceServiceMock = { + metadata : componentMetadataMock + }; + + loaderServiceMock = { + activate : jest.fn(), + deactivate: jest.fn() + }; + + const configure: ConfigureFn = (testBed) => { + testBed.configureTestingModule({ + declarations: [ActivityLogComponent], + imports: [NgxDatatableModule], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + { provide: WorkspaceService, useValue: workspaceServiceMock }, + { provide: ActivityLogService, useValue: activityLogServiceMock }, + { provide: SdcUiServices.LoaderService, useValue: loaderServiceMock } + ], + }); + }; + + configureTests(configure).then((testBed) => { + fixture = testBed.createComponent(ActivityLogComponent); + }); + }) + ); + + it('should see exactly 3 activity logs', () => { + fixture.componentInstance.ngOnInit(); + expect(fixture.componentInstance.activities.length).toBe(3); + }); + + it('should filter out 1 element when searching', () => { + fixture.componentInstance.ngOnInit(); + + const event = { + target : { + value : 'Checkin' + } + }; + + expect(fixture.componentInstance.activities.length).toBe(3); + fixture.componentInstance.updateFilter(event); + expect(fixture.componentInstance.activities.length).toBe(1); + }); +}); diff --git a/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.ts b/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.ts new file mode 100644 index 0000000000..84fb81a1ef --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { SdcUiServices } from 'onap-ui-angular'; +import { Activity } from '../../../../models/activity'; +import { ActivityLogService } from '../../../services/activity-log.service'; +import { WorkspaceService } from '../workspace.service'; + +@Component({ + selector: 'activity-log', + templateUrl: './activity-log.component.html', + styleUrls: ['./activity-log.component.less', '../../../../../assets/styles/table-style.less'] +}) +export class ActivityLogComponent implements OnInit { + + activities: Activity[] = []; + temp: Activity[] = []; + + constructor(private workspaceService: WorkspaceService, + private activityLogService: ActivityLogService, + private loaderService: SdcUiServices.LoaderService) { + } + + ngOnInit(): void { + this.loaderService.activate(); + const componentId: string = this.workspaceService.metadata.uniqueId; + const componentType: string = this.workspaceService.metadata.componentType; + this.activityLogService.getActivityLog(componentType, componentId).subscribe((logs) => { + this.activities = logs; + this.temp = [...logs]; + this.loaderService.deactivate(); + }, (error) => { this.loaderService.deactivate(); }); + } + + updateFilter(event) { + const val = event.target.value.toLowerCase(); + + // filter our data + const temp = this.temp.filter((activity: Activity) => { + return !val || + activity.COMMENT.toLowerCase().indexOf(val) !== -1 || + activity.STATUS.toLowerCase().indexOf(val) !== -1 || + activity.ACTION.toLowerCase().indexOf(val) !== -1 || + activity.MODIFIER.toLowerCase().indexOf(val) !== -1; + }); + + // update the rows + this.activities = temp; + } +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.module.ts b/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.module.ts new file mode 100644 index 0000000000..39334d8cde --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/activity-log/activity-log.module.ts @@ -0,0 +1,28 @@ +import {CommonModule} from "@angular/common"; +import {NgModule} from "@angular/core"; +import {SdcUiComponentsModule} from "onap-ui-angular"; +import {GlobalPipesModule} from "../../../pipes/global-pipes.module"; +import {ActivityLogComponent} from "./activity-log.component"; +import {ActivityLogService} from "../../../services/activity-log.service"; +import {NgxDatatableModule} from "@swimlane/ngx-datatable"; + +@NgModule({ + declarations: [ + ActivityLogComponent + ], + imports: [ + CommonModule, + SdcUiComponentsModule, + GlobalPipesModule, + NgxDatatableModule + ], + exports: [ + ActivityLogComponent + ], + entryComponents: [ + ActivityLogComponent + ], + providers: [ ActivityLogService ] +}) +export class ActivityLogModule { +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.html b/catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.html new file mode 100644 index 0000000000..bd30a469e0 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.html @@ -0,0 +1,104 @@ +
+
+ +
+ +
+ + + + + + +
+ + +
+ + +
+
+ +
+ +
+ + + + + + +
+ + +
+ + + + + + + +
+ + +
+ + + +
+ +
+ + + + + + +
+ + + + +
+
+ +
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.ts b/catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.ts new file mode 100644 index 0000000000..c703869ad2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.ts @@ -0,0 +1,138 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { IDropDownOption } from 'onap-ui-angular/dist/form-elements/dropdown/dropdown-models'; +import { InputComponent } from 'onap-ui-angular/dist/form-elements/text-elements/input/input.component'; +import { Subject } from 'rxjs/Subject'; +import { AttributeModel } from '../../../../models/attributes'; +import { ValidationUtils } from '../../../../utils/validation-utils'; +import { CacheService } from '../../../services/cache.service'; +import { TranslateService } from '../../../shared/translator/translate.service'; +import { AttributeOptions } from './attributes-options'; + +@Component({ + selector: 'attribute-modal', + templateUrl: './attribute-modal.component.html', + styleUrls: ['./attributes.component.less'] +}) +export class AttributeModalComponent implements OnInit { + + @ViewChild('defaultValue') validatedInput: InputComponent; + + public readonly types = AttributeOptions.types; // integer, string, boolean etc. + + public readonly booleanValues = AttributeOptions.booleanValues; // true / false + + public readonly entrySchemaValues = AttributeOptions.entrySchemaValues; // integer, string, boolean, float + + public onValidationChange: Subject = new Subject(); + + public validationPatterns: any; + public readonly listPattern = ValidationUtils.getPropertyListPatterns(); + public readonly mapPattern = ValidationUtils.getPropertyMapPatterns(); + + // The current effective default value pattern + public defaultValuePattern: string; + public defaultValueErrorMessage: string; + + // Attribute being Edited + public attributeToEdit: AttributeModel; + + constructor(private translateService: TranslateService, private cacheService: CacheService) { + this.validationPatterns = this.cacheService.get('validation').validationPatterns; + } + + ngOnInit() { + this.revalidateDefaultValue(); + } + + onHiddenCheckboxClicked(event: boolean) { + this.attributeToEdit.hidden = event; + } + + onTypeSelected(selectedElement: IDropDownOption) { + if (this.attributeToEdit.type !== selectedElement.value && selectedElement.value === 'boolean') { + this.attributeToEdit.defaultValue = ''; // Clean old value in case we choose change type to boolean + } + this.attributeToEdit.type = selectedElement.value; + this.revalidateDefaultValue(); + } + + onBooleanDefaultValueSelected(selectedElement: IDropDownOption) { + if (this.attributeToEdit.type === 'boolean') { + this.attributeToEdit.defaultValue = selectedElement.value; + } + } + + onEntrySchemaTypeSelected(selectedElement: IDropDownOption) { + this.attributeToEdit.schema.property.type = selectedElement.value; + this.revalidateDefaultValue(); + } + + onValidityChange(isValid: boolean, field: string) { + const typeIsValid = this.attributeToEdit.type && this.attributeToEdit.type.length > 0; // Make sure type is defined + + // Make sure name is defined when other fields are changed + let nameIsValid = true; + if (field !== 'name') { + nameIsValid = this.attributeToEdit.name && this.attributeToEdit.name.length > 0; + } + this.onValidationChange.next(isValid && nameIsValid && typeIsValid); + } + + defaultValueChanged() { + this.revalidateDefaultValue(); + } + + /** + * Utility function for UI that converts a simple value to IDropDownOption + * @param val + * @returns {{value: any; label: any}} + */ + toDropDownOption(val: string) { + return { value : val, label: val }; + } + + public isMapUnique = () => { + if (this.attributeToEdit && this.attributeToEdit.type === 'map' && this.attributeToEdit.defaultValue) { + return ValidationUtils.validateUniqueKeys(this.attributeToEdit.defaultValue); + } + return true; + } + + private revalidateDefaultValue() { + this.setDefaultValuePattern(this.attributeToEdit.type); + setTimeout(() => { + if (this.validatedInput) { + this.validatedInput.onKeyPress(this.attributeToEdit.defaultValue); + } }, 250); + } + + private setDefaultValuePattern(valueType: string) { + const selectedSchemaType = this.attributeToEdit.schema.property.type; + this.defaultValuePattern = '.*'; + switch (valueType) { + case 'float': + this.defaultValuePattern = this.validationPatterns.number; + this.defaultValueErrorMessage = this.translateService.translate('VALIDATION_ERROR_TYPE', { type : 'float' }); + break; + case 'integer': + this.defaultValuePattern = this.validationPatterns.integerNoLeadingZero; + this.defaultValueErrorMessage = this.translateService.translate('VALIDATION_ERROR_TYPE', { type : 'integer' }); + break; + case 'list': + if (selectedSchemaType != undefined) { + this.defaultValuePattern = this.listPattern[selectedSchemaType]; + const listTypeStr = `list of ${selectedSchemaType}s (v1, v2, ...) `; + this.defaultValueErrorMessage = this.translateService.translate('VALIDATION_ERROR_TYPE', { type : listTypeStr }); + } + break; + case 'map': + if (selectedSchemaType != undefined) { + this.defaultValuePattern = this.mapPattern[selectedSchemaType]; + const mapTypeStr = `map of ${selectedSchemaType}s (k1:v1, k2:v2, ...)`; + this.defaultValueErrorMessage = this.translateService.translate('VALIDATION_ERROR_TYPE', { type : mapTypeStr }); + } + break; + } + } + +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes-modal.component.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes-modal.component.spec.ts new file mode 100644 index 0000000000..99aa140dd1 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes-modal.component.spec.ts @@ -0,0 +1,128 @@ +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture } from '@angular/core/testing'; +import { ConfigureFn, configureTests } from '../../../../../jest/test-config.helper'; +import { AttributeModel } from '../../../../models/attributes'; +import { ValidationUtils } from '../../../../utils/validation-utils'; +import { CacheService } from '../../../services/cache.service'; +import { TranslatePipe } from '../../../shared/translator/translate.pipe'; +import { TranslateService } from '../../../shared/translator/translate.service'; +import { AttributeModalComponent } from './attribute-modal.component'; + +describe('attributes modal component', () => { + + let fixture: ComponentFixture; + + // Mocks + let translateServiceMock: Partial; + let cacheServiceMock: Partial; + + const validationPatterns = { + integerNoLeadingZero : 'int_regx', + number : 'number_regx' + }; + + const newAttribute = { + uniqueId: '1', name: 'attr1', description: 'description1', type: 'string', hidden: false, defaultValue: 'val1', schema: null + }; + + beforeEach( + async(() => { + + translateServiceMock = { + translate: jest.fn() + }; + + cacheServiceMock = { + get: jest.fn().mockImplementation((k) => { + return { validationPatterns}; + } ) + }; + + const configure: ConfigureFn = (testBed) => { + testBed.configureTestingModule({ + declarations: [AttributeModalComponent, TranslatePipe], + imports: [], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + {provide: TranslateService, useValue: translateServiceMock}, + {provide: CacheService, useValue: cacheServiceMock}, + ] + }); + }; + + configureTests(configure).then((testBed) => { + fixture = testBed.createComponent(AttributeModalComponent); + }); + }) + ); + + it('test that when hidden is clicked, hidden attribute is set', async () => { + fixture.componentInstance.attributeToEdit = new AttributeModel(); + const hidden = fixture.componentInstance.attributeToEdit.hidden; + fixture.componentInstance.ngOnInit(); + + expect(hidden).toBe(false); + fixture.componentInstance.onHiddenCheckboxClicked(true); + expect(fixture.componentInstance.attributeToEdit.hidden).toBe(true); + }); + + it('test that when type is set to boolean default value is cleared', async () => { + const component = fixture.componentInstance; + component.attributeToEdit = new AttributeModel(); + component.ngOnInit(); + + component.onTypeSelected({ value : 'string', label : 'string'}); + component.attributeToEdit.defaultValue = 'some_value'; + component.onTypeSelected({ value : 'boolean', label : 'boolean'}); + expect(component.attributeToEdit.defaultValue).toBe(''); + + component.onBooleanDefaultValueSelected({ value : 'true', label : 'true'}); + expect(component.attributeToEdit.defaultValue).toBe('true'); + }); + + it('test that when certain type is selected, the correct regex pattern is chosen', async () => { + const component = fixture.componentInstance; + component.attributeToEdit = new AttributeModel(); + component.ngOnInit(); + + // integer + component.onTypeSelected({ value : 'integer', label : 'integer'}); + expect(component.defaultValuePattern).toBe(validationPatterns.integerNoLeadingZero); + + // float + component.onTypeSelected({ value : 'float', label : 'float'}); + expect(component.defaultValuePattern).toBe(validationPatterns.number); + + // list is chosen with no schema, regex pattern is set to default + component.onTypeSelected({ value : 'list', label : 'list'}); + expect(component.defaultValuePattern).toEqual('.*'); + + // schema is set to list of int + component.onEntrySchemaTypeSelected({ value : 'integer', label : 'integer' }); + expect(component.defaultValuePattern).toEqual(ValidationUtils.getPropertyListPatterns().integer); + + // schema is set to list of float + component.onEntrySchemaTypeSelected({ value : 'float', label : 'float' }); + expect(component.defaultValuePattern).toEqual(ValidationUtils.getPropertyListPatterns().float); + + // map is selected (float schema is still selected from previous line) + component.onTypeSelected({ value : 'map', label : 'map'}); + expect(component.defaultValuePattern).toEqual(ValidationUtils.getPropertyMapPatterns().float); + + // change schema type to boolean + component.onEntrySchemaTypeSelected({ value : 'boolean', label : 'boolean' }); + }); + + it('should detect map with non-unique keys', async () => { + const component = fixture.componentInstance; + component.attributeToEdit = new AttributeModel(); + component.ngOnInit(); + expect(component.isMapUnique()).toBe(true); // map is not selected so return true by default + component.onTypeSelected({ value : 'map', label : 'map'}); + component.onEntrySchemaTypeSelected({ value : 'boolean', label : 'boolean' }); + component.attributeToEdit.defaultValue = '"1":true,"2":false'; + expect(component.isMapUnique()).toBe(true); + component.attributeToEdit.defaultValue = '"1":true,"1":false'; + expect(component.isMapUnique()).toBe(false); + }); +}); diff --git a/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes-options.ts b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes-options.ts new file mode 100644 index 0000000000..2a6924bc5e --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes-options.ts @@ -0,0 +1,60 @@ +import { IDropDownOption } from 'onap-ui-angular/dist/form-elements/dropdown/dropdown-models'; + +export class AttributeOptions { + public static readonly types: IDropDownOption[] = [ + { + label: 'integer', + value: 'integer', + }, + { + label: 'string', + value: 'string', + }, + { + label: 'float', + value: 'float' + }, + { + label: 'boolean', + value: 'boolean' + }, + { + label: 'list', + value: 'list' + }, + { + label: 'map', + value: 'map' + } + ]; + + public static readonly booleanValues: IDropDownOption[] = [ + { + label: 'true', + value: 'true', + }, + { + label: 'false', + value: 'false', + } + ]; + + public static readonly entrySchemaValues: IDropDownOption[] = [ + { + label: 'integer', + value: 'integer', + }, + { + label: 'string', + value: 'string', + }, + { + label: 'float', + value: 'float' + }, + { + label: 'boolean', + value: 'boolean' + } + ]; +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.html b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.html new file mode 100644 index 0000000000..00a7a5cec0 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.html @@ -0,0 +1,93 @@ + +
+ +
+ + +
+ + + + + +
{{row.description}}
+
+
+ + + + +
+ + {{ row.name }} +
+
+ +
+ + + + {{row.type}} + + + + + + {{row.defaultValue}} + + + + + +
+ + + + +
+
+
+ +
+
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.less b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.less new file mode 100644 index 0000000000..3e91ae4689 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.less @@ -0,0 +1,36 @@ +.action-bar-wrapper { + flex: 0 0 30%; + display: flex; + justify-content: flex-end; + margin-bottom: 10px; +} + +.add-attr-icon{ + cursor: pointer; +} + +.attr-container { + display: flex; + justify-content: space-between; + + .attr-col { + display: flex; + flex-direction: column; + max-width: 275px; + flex-grow: 1; + } + +} + +.attributeType { + margin-bottom: 10px; +} + +sdc-checkbox { + margin-top: 20px; +} + +.actionColumn { + text-align: center; + padding: 5px; +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.spec.ts new file mode 100644 index 0000000000..f676e2b4d9 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.spec.ts @@ -0,0 +1,182 @@ +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture } from '@angular/core/testing'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; +import { SdcUiCommon, SdcUiComponents, SdcUiServices } from 'onap-ui-angular'; +import 'rxjs/add/observable/of'; +import { Observable } from 'rxjs/Rx'; +import { ConfigureFn, configureTests } from '../../../../../jest/test-config.helper'; +import { ComponentMetadata } from '../../../../models/component-metadata'; +import { ModalsHandler } from '../../../../utils'; +import { TopologyTemplateService } from '../../../services/component-services/topology-template.service'; +import { TranslateService } from '../../../shared/translator/translate.service'; +import { WorkspaceService } from '../workspace.service'; +import { AttributesComponent } from './attributes.component'; + +describe('attributes component', () => { + + let fixture: ComponentFixture; + + // Mocks + let workspaceServiceMock: Partial; + let topologyTemplateServiceMock: Partial; + let loaderServiceMock: Partial; + let componentMetadataMock: ComponentMetadata; + let modalServiceMock: Partial; + + const mockAttributesList = [ + { uniqueId: '1', name: 'attr1', description: 'description1', type: 'string', hidden: false, defaultValue: 'val1', schema: null }, + { uniqueId : '2', name : 'attr2', description: 'description2', type : 'int', hidden : false, defaultValue : 1, schema : null}, + { uniqueId : '3', name : 'attr3', description: 'description3', type : 'double', hidden : false, defaultValue : 1.0, schema : null}, + { uniqueId : '4', name : 'attr4', description: 'description4', type : 'boolean', hidden : false, defaultValue : true, schema : null}, + ]; + + const newAttribute = { + uniqueId : '5', name : 'attr5', description: 'description5', type : 'string', hidden : false, defaultValue : 'val5', schema : null + }; + const updatedAttribute = { + uniqueId : '2', name : 'attr2', description: 'description_new', type : 'string', hidden : false, defaultValue : 'new_val2', schema : null + }; + const errorAttribute = { + uniqueId : '99', name : 'attr99', description: 'description_error', type : 'string', hidden : false, defaultValue : 'error', schema : null + }; + + beforeEach( + async(() => { + + componentMetadataMock = new ComponentMetadata(); + componentMetadataMock.uniqueId = 'fake'; + componentMetadataMock.componentType = 'VL'; + + topologyTemplateServiceMock = { + getComponentAttributes: jest.fn().mockResolvedValue({ attributes : mockAttributesList }), + addAttributeAsync: jest.fn().mockImplementation( + (compType, cUid, attr) => { + if (attr === errorAttribute) { + return Observable.throwError('add_error').toPromise(); + } else { + return Observable.of(newAttribute).toPromise(); + } + } + ), + updateAttributeAsync: jest.fn().mockImplementation( + (compType, cUid, attr) => { + if (attr === errorAttribute) { + return Observable.throwError('update_error').toPromise(); + } else { + return Observable.of(updatedAttribute).toPromise(); + } + } + ), + deleteAttributeAsync: jest.fn().mockImplementation((cid, ctype, attr) => Observable.of(attr)) + }; + + workspaceServiceMock = { + metadata: componentMetadataMock + }; + + const customModalInstance = { innerModalContent: { instance: { onValidationChange: { subscribe: jest.fn()}}}}; + + modalServiceMock = { + openInfoModal: jest.fn(), + openCustomModal: jest.fn().mockImplementation(() => customModalInstance) + }; + + loaderServiceMock = { + activate: jest.fn(), + deactivate: jest.fn() + }; + + const configure: ConfigureFn = (testBed) => { + testBed.configureTestingModule({ + declarations: [AttributesComponent], + imports: [NgxDatatableModule], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + {provide: WorkspaceService, useValue: workspaceServiceMock}, + {provide: TopologyTemplateService, useValue: topologyTemplateServiceMock}, + {provide: ModalsHandler, useValue: {}}, + {provide: TranslateService, useValue: { translate: jest.fn() }}, + {provide: SdcUiServices.ModalService, useValue: modalServiceMock }, + {provide: SdcUiServices.LoaderService, useValue: loaderServiceMock } + ], + }); + }; + + configureTests(configure).then((testBed) => { + fixture = testBed.createComponent(AttributesComponent); + }); + }) + ); + + it('should see exactly 1 attributes on init', async () => { + await fixture.componentInstance.asyncInitComponent(); + expect(fixture.componentInstance.getAttributes().length).toEqual(4); + }); + + it('should see exactly 5 attributes when adding', async () => { + await fixture.componentInstance.asyncInitComponent(); + expect(fixture.componentInstance.getAttributes().length).toEqual(4); + + await fixture.componentInstance.addOrUpdateAttribute(newAttribute, false); + expect(fixture.componentInstance.getAttributes().length).toEqual(5); + }); + + it('should see exactly 3 attributes when deleting', async () => { + await fixture.componentInstance.asyncInitComponent(); + expect(fixture.componentInstance.getAttributes().length).toEqual(4); + const attrToDelete = mockAttributesList[0]; + expect(fixture.componentInstance.getAttributes().filter((attr) => attr.uniqueId === attrToDelete.uniqueId).length).toEqual(1); + await fixture.componentInstance.deleteAttribute(attrToDelete); + expect(fixture.componentInstance.getAttributes().length).toEqual(3); + expect(fixture.componentInstance.getAttributes().filter((attr) => attr.uniqueId === attrToDelete.uniqueId).length).toEqual(0); + }); + + it('should see updated attribute', async () => { + await fixture.componentInstance.asyncInitComponent(); + + await fixture.componentInstance.addOrUpdateAttribute(updatedAttribute, true); + expect(fixture.componentInstance.getAttributes().length).toEqual(4); + const attribute = fixture.componentInstance.getAttributes().filter( (attr) => { + return attr.uniqueId === updatedAttribute.uniqueId; + })[0]; + expect(attribute.description).toEqual( 'description_new'); + }); + + it('Add fails, make sure loader is deactivated and attribute is not added', async () => { + await fixture.componentInstance.asyncInitComponent(); + const numAttributes = fixture.componentInstance.getAttributes().length; + await fixture.componentInstance.addOrUpdateAttribute(errorAttribute, false); // Add + expect(loaderServiceMock.deactivate).toHaveBeenCalled(); + expect(fixture.componentInstance.getAttributes().length).toEqual(numAttributes); + }); + + it('Update fails, make sure loader is deactivated', async () => { + await fixture.componentInstance.asyncInitComponent(); + const numAttributes = fixture.componentInstance.getAttributes().length; + await fixture.componentInstance.addOrUpdateAttribute(errorAttribute, true); // Add + expect(loaderServiceMock.deactivate).toHaveBeenCalled(); + expect(fixture.componentInstance.getAttributes().length).toEqual(numAttributes); + }); + + it('on delete modal shell be opened', async () => { + await fixture.componentInstance.asyncInitComponent(); + const event = { stopPropagation: jest.fn() }; + fixture.componentInstance.onDeleteAttribute(event, fixture.componentInstance.getAttributes()[0]); + expect(event.stopPropagation).toHaveBeenCalled(); + expect(modalServiceMock.openInfoModal).toHaveBeenCalled(); + }); + + it('on add modal shell be opened', async () => { + await fixture.componentInstance.asyncInitComponent(); + fixture.componentInstance.onAddAttribute(); + expect(modalServiceMock.openCustomModal).toHaveBeenCalled(); + }); + + it('on edit modal shell be opened', async () => { + await fixture.componentInstance.asyncInitComponent(); + const event = { stopPropagation: jest.fn() }; + fixture.componentInstance.onEditAttribute(event, fixture.componentInstance.getAttributes()[0]); + expect(event.stopPropagation).toHaveBeenCalled(); + expect(modalServiceMock.openCustomModal).toHaveBeenCalled(); + }); +}); diff --git a/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.ts b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.ts new file mode 100644 index 0000000000..bc47f1456b --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.component.ts @@ -0,0 +1,188 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { Select } from '@ngxs/store'; +import { IAttributeModel } from 'app/models'; +import * as _ from 'lodash'; +import { SdcUiCommon, SdcUiComponents, SdcUiServices } from 'onap-ui-angular'; +import { ModalComponent } from 'onap-ui-angular/dist/modals/modal.component'; +import { AttributeModel } from '../../../../models'; +import { Resource } from '../../../../models'; +import { ModalsHandler } from '../../../../utils'; +import { TopologyTemplateService } from '../../../services/component-services/topology-template.service'; +import { TranslateService } from '../../../shared/translator/translate.service'; +import { WorkspaceState } from '../../../store/states/workspace.state'; +import { WorkspaceService } from '../workspace.service'; +import { AttributeModalComponent } from './attribute-modal.component'; + +@Component({ + selector: 'attributes', + templateUrl: './attributes.component.html', + styleUrls: ['./attributes.component.less', '../../../../../assets/styles/table-style.less'] +}) +export class AttributesComponent implements OnInit { + + @Select(WorkspaceState.isViewOnly) + isViewOnly$: boolean; + + @ViewChild('componentAttributesTable') + private table: any; + + private componentType: string; + private componentUid: string; + + private attributes: IAttributeModel[] = []; + private temp: IAttributeModel[] = []; + private customModalInstance: ModalComponent; + + constructor(private workspaceService: WorkspaceService, + private topologyTemplateService: TopologyTemplateService, + private modalsHandler: ModalsHandler, + private modalService: SdcUiServices.ModalService, + private loaderService: SdcUiServices.LoaderService, + private translateService: TranslateService) { + + this.componentType = this.workspaceService.metadata.componentType; + this.componentUid = this.workspaceService.metadata.uniqueId; + } + + ngOnInit(): void { + this.asyncInitComponent(); + } + + async asyncInitComponent() { + this.loaderService.activate(); + const response = await this.topologyTemplateService.getComponentAttributes(this.componentType, this.componentUid); + this.attributes = response.attributes; + this.temp = [...response.attributes]; + this.loaderService.deactivate(); + } + + getAttributes(): IAttributeModel[] { + return this.attributes; + } + + addOrUpdateAttribute = async (attribute: AttributeModel, isEdit: boolean) => { + this.loaderService.activate(); + let attributeFromServer: AttributeModel; + this.temp = [...this.attributes]; + + const deactivateLoader = () => { + this.loaderService.deactivate(); + return undefined; + }; + + if (isEdit) { + attributeFromServer = await this.topologyTemplateService + .updateAttributeAsync(this.componentType, this.componentUid, attribute) + .catch(deactivateLoader); + if (attributeFromServer) { + const indexOfUpdatedAttribute = _.findIndex(this.temp, (e) => e.uniqueId === attributeFromServer.uniqueId); + this.temp[indexOfUpdatedAttribute] = attributeFromServer; + } + } else { + attributeFromServer = await this.topologyTemplateService + .addAttributeAsync(this.componentType, this.componentUid, attribute) + .catch(deactivateLoader); + if (attributeFromServer) { + this.temp.push(attributeFromServer); + } + } + this.attributes = this.temp; + this.loaderService.deactivate(); + } + + deleteAttribute = async (attributeToDelete: AttributeModel) => { + this.loaderService.activate(); + this.temp = [...this.attributes]; + const res = await this.topologyTemplateService.deleteAttributeAsync(this.componentType, this.componentUid, attributeToDelete); + _.remove(this.temp, (attr) => attr.uniqueId === attributeToDelete.uniqueId); + this.attributes = this.temp; + this.loaderService.deactivate(); + }; + + openAddEditModal(selectedRow: AttributeModel, isEdit: boolean) { + const component = new Resource(undefined, undefined, undefined); + component.componentType = this.componentType; + component.uniqueId = this.componentUid; + + const title: string = this.translateService.translate('ATTRIBUTE_DETAILS_MODAL_TITLE'); + const attributeModalConfig = { + title, + size: 'md', + type: SdcUiCommon.ModalType.custom, + buttons: [ + { + id: 'save', + text: 'Save', + // spinner_position: Placement.left, + size: 'sm', + callback: () => this.modalCallBack(isEdit), + closeModal: true, + disabled: false, + } + ] as SdcUiCommon.IModalButtonComponent[] + }; + + this.customModalInstance = this.modalService.openCustomModal(attributeModalConfig, AttributeModalComponent, { attributeToEdit: selectedRow }); + this.customModalInstance.innerModalContent.instance. + onValidationChange.subscribe((isValid) => this.customModalInstance.getButtonById('save').disabled = !isValid); + } + + /*********************** + * Call Backs from UI * + ***********************/ + + /** + * Called when 'Add' is clicked + */ + onAddAttribute() { + this.openAddEditModal(new AttributeModel(), false); + } + + /** + * Called when 'Edit' button is clicked + */ + onEditAttribute(event, row) { + event.stopPropagation(); + + const attributeToEdit: AttributeModel = new AttributeModel(row); + this.openAddEditModal(attributeToEdit, true); + } + + /** + * Called when 'Delete' button is clicked + */ + onDeleteAttribute(event, row: AttributeModel) { + event.stopPropagation(); + const onOk = () => { + this.deleteAttribute(row); + }; + + const title: string = this.translateService.translate('ATTRIBUTE_VIEW_DELETE_MODAL_TITLE'); + const message: string = this.translateService.translate('ATTRIBUTE_VIEW_DELETE_MODAL_TEXT'); + const okButton = new SdcUiComponents.ModalButtonComponent(); + okButton.testId = 'OK'; + okButton.text = 'OK'; + okButton.type = SdcUiCommon.ButtonType.info; + okButton.closeModal = true; + okButton.callback = onOk; + + this.modalService.openInfoModal(title, message, 'delete-modal', [okButton]); + } + + onExpandRow(event) { + if (event.type === 'click') { + this.table.rowDetail.toggleExpandRow(event.row); + } + } + + /** + * Callback from Modal after "Save" is clicked + * + * @param {boolean} isEdit - Whether modal is edit or add attribute + */ + modalCallBack = (isEdit: boolean) => { + const attribute: AttributeModel = this.customModalInstance.innerModalContent.instance.attributeToEdit; + this.addOrUpdateAttribute(attribute, isEdit); + } + +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.module.ts b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.module.ts new file mode 100644 index 0000000000..5abb952e37 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/attributes/attributes.module.ts @@ -0,0 +1,32 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { SdcUiComponentsModule } from 'onap-ui-angular'; +import { GlobalPipesModule } from '../../../pipes/global-pipes.module'; +import { AttributesComponent } from './attributes.component'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; +import { TopologyTemplateService } from '../../../services/component-services/topology-template.service'; +import { AttributeModalComponent } from './attribute-modal.component'; +import { TranslateModule } from '../../../shared/translator/translate.module'; + +@NgModule({ + declarations: [ + AttributesComponent, + AttributeModalComponent + ], + imports: [ + CommonModule, + SdcUiComponentsModule, + GlobalPipesModule, + NgxDatatableModule, + TranslateModule + ], + exports: [ + AttributesComponent + ], + entryComponents: [ + AttributesComponent, AttributeModalComponent + ], + providers: [TopologyTemplateService] +}) +export class AttributesModule { +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/__snapshots__/deployment-artifacts-page.spec.ts.snap b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/__snapshots__/deployment-artifacts-page.spec.ts.snap new file mode 100644 index 0000000000..b53674497c --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/__snapshots__/deployment-artifacts-page.spec.ts.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`deployment artifacts page should match current snapshot of informational artifact pages component 1`] = ` + + + +`; diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.component.html b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.component.html new file mode 100644 index 0000000000..35592d846a --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.component.html @@ -0,0 +1,73 @@ +
+ + + + +
+
+
+ {{row.artifactDisplayName}} + + + +
+
+ + + {{row.artifactType}} + + exactly 2 tosca artifacts + + + {{ row.artifactVersion }} + + + + + {{ row.artifactUUID }} + + + + +
+ + + + + + +
+
+
+ + + + + + +
+
diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.component.less b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.component.less new file mode 100644 index 0000000000..22ceb96653 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.component.less @@ -0,0 +1,55 @@ +.deployment-artifact-page { + + + .env-artifact-container { + margin-left: -25px; + margin-top: -21px; + padding-left: 10px; + position: absolute; + background-color: white; + .env-artifact { + border-left: 1px #848586 solid; + height: 33px; + + border-top: 1px #848586 solid; + border-bottom: 1px #848586 solid; + width: 10px; + float: left; + + } + } + .add-artifact-btn { + display: flex; + cursor: pointer; + justify-content: flex-end; + margin-bottom: 10px; + } + .download-artifact-button { + display: flex; + justify-content: center; + + .action-icon { + margin-right: 10px; + } + } + + .table-footer-container { + display: flex; + align-items: center; + width: 100%; + justify-content: center; + margin: 20px 0px; + } +} + +:host ::ng-deep { + + .ngx-datatable { + //border: 1px solid red; + .datatable-body-cell { + .info { + float: right; + } + } + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.component.ts b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.component.ts new file mode 100644 index 0000000000..53b21b34b6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.component.ts @@ -0,0 +1,155 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { Select, Store } from '@ngxs/store'; +import { ArtifactModel } from 'app/models'; +import * as _ from 'lodash'; +import { SdcUiCommon, SdcUiComponents, SdcUiServices } from 'onap-ui-angular'; +import { Observable } from 'rxjs/index'; +import { map } from 'rxjs/operators'; +import { GabConfig } from '../../../../models/gab-config'; +import { PathsAndNamesDefinition } from '../../../../models/paths-and-names'; +import { GenericArtifactBrowserComponent } from '../../../../ng2/components/logic/generic-artifact-browser/generic-artifact-browser.component'; +import { ArtifactGroupType, ArtifactType } from '../../../../utils/constants'; +import { ArtifactsService } from '../../../components/forms/artifacts-form/artifacts.service'; +import { PopoverContentComponent } from '../../../components/ui/popover/popover-content.component'; +import { CacheService } from '../../../services/cache.service'; +import { TranslateService } from '../../../shared/translator/translate.service'; +import { GetArtifactsByTypeAction } from '../../../store/actions/artifacts.action'; +import { ArtifactsState } from '../../../store/states/artifacts.state'; +import { WorkspaceState, WorkspaceStateModel } from '../../../store/states/workspace.state'; +import { WorkspaceService } from '../workspace.service'; +import { ModalService } from 'app/ng2/services/modal.service'; + +export interface IPoint { + x: number; + y: number; +} + +@Component({ + selector: 'deployment-artifact-page', + templateUrl: './deployment-artifacts-page.component.html', + styleUrls: ['./deployment-artifacts-page.component.less', '../../../../../assets/styles/table-style.less'] +}) +export class DeploymentArtifactsPageComponent implements OnInit { + + public componentId: string; + public componentType: string; + public deploymentArtifacts$: Observable; + public isComponentInstanceSelected: boolean; + + @Select(WorkspaceState) workspaceState$: Observable; + @ViewChild('informationArtifactsTable') table: any; + @ViewChild('popoverForm') popoverContentComponent: PopoverContentComponent; + + constructor(private workspaceService: WorkspaceService, + private artifactsService: ArtifactsService, + private store: Store, + private popoverService: SdcUiServices.PopoverService, + private cacheService: CacheService, + private modalService: SdcUiServices.ModalService, + private translateService: TranslateService) { + } + + private getEnvArtifact = (heatArtifact: ArtifactModel, artifacts: ArtifactModel[]): ArtifactModel => { + return _.find(artifacts, (item: ArtifactModel) => { + return item.generatedFromId === heatArtifact.uniqueId; + }); + }; + + // we need to sort the artifact in a way that the env artifact is always under the artifact he is connected to- this is cause of the way the ngx databale work + private sortArtifacts = ((artifacts) => { + const sortedArtifacts = []; + _.forEach(artifacts, (artifact: ArtifactModel): void => { + const envArtifact = this.getEnvArtifact(artifact, artifacts); + if (!artifact.generatedFromId) { + sortedArtifacts.push(artifact); + } + if (envArtifact) { + sortedArtifacts.push(envArtifact); + } + }); + return sortedArtifacts; + }) + + ngOnInit(): void { + this.componentId = this.workspaceService.metadata.uniqueId; + this.componentType = this.workspaceService.metadata.componentType; + + this.store.dispatch(new GetArtifactsByTypeAction({ + componentType: this.componentType, + componentId: this.componentId, + artifactType: ArtifactGroupType.DEPLOYMENT + })); + this.deploymentArtifacts$ = this.store.select(ArtifactsState.getArtifactsByType).pipe(map((filterFn) => filterFn(ArtifactType.DEPLOYMENT))).pipe(map(artifacts => { + return this.sortArtifacts(artifacts); + })); + } + + onActivate(event) { + if (event.type === 'click') { + this.table.rowDetail.toggleExpandRow(event.row); + } + } + + public addOrUpdateArtifact = (artifact: ArtifactModel, isViewOnly: boolean) => { + this.artifactsService.openArtifactModal(this.componentId, this.componentType, artifact, ArtifactGroupType.DEPLOYMENT, isViewOnly); + } + + public deleteArtifact = (artifactToDelete) => { + this.artifactsService.deleteArtifact(this.componentType, this.componentId, artifactToDelete); + } + + private openPopOver = (title: string, content: string, positionOnPage: IPoint, location: string) => { + this.popoverService.createPopOver(title, content, positionOnPage, location); + } + + public updateEnvParams = (artifact: ArtifactModel, isViewOnly: boolean) => { + this.artifactsService.openUpdateEnvParams(this.componentType, this.componentId, artifact ); + } + + private openGenericArtifactBrowserModal = (artifact: ArtifactModel): void => { + const titleStr = 'Generic Artifact Browser'; + const modalConfig = { + size: 'sdc-xl', + title: titleStr, + type: SdcUiCommon.ModalType.custom, + buttons: [{ + id: 'closeGABButton', + text: 'Close', + size: 'sm', + closeModal: true + }] as SdcUiCommon.IModalButtonComponent[] + }; + + const uiConfiguration: any = this.cacheService.get('UIConfiguration'); + let noConfig: boolean = false; + let pathsandnamesArr: PathsAndNamesDefinition[] = []; + + if (typeof uiConfiguration.gab === 'undefined') { + noConfig = true; + } else { + const gabConfig: GabConfig = uiConfiguration.gab + .find((config) => config.artifactType === artifact.artifactType); + if (typeof gabConfig === 'undefined') { + noConfig = true; + } else { + pathsandnamesArr = gabConfig.pathsAndNamesDefinitions; + } + } + + + if (noConfig) { + const msg = this.translateService.translate('DEPLOYMENT_ARTIFACT_GAB_NO_CONFIG'); + this.modalService.openAlertModal(titleStr, msg); + } + + const modalInputs = { + pathsandnames: pathsandnamesArr, + artifactid: artifact.esId, + resourceid: this.componentId + }; + + this.modalService.openCustomModal(modalConfig, GenericArtifactBrowserComponent, modalInputs); + + } + +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.module.ts b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.module.ts new file mode 100644 index 0000000000..398e9d3f4d --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.module.ts @@ -0,0 +1,35 @@ +import {CommonModule} from "@angular/common"; +import {NgModule} from "@angular/core"; +import {SdcUiComponentsModule} from "onap-ui-angular"; +import {NgxDatatableModule} from "@swimlane/ngx-datatable"; +import {UiElementsModule} from "../../../components/ui/ui-elements.module"; +import {ArtifactFormModule} from "../../../components/forms/artifacts-form/artifact-form.module"; +import {ArtifactsService} from "../../../components/forms/artifacts-form/artifacts.service"; +import {DeploymentArtifactsPageComponent} from "./deployment-artifacts-page.component"; +import {TranslatePipe} from "../../../shared/translator/translate.pipe"; +import {TranslateModule} from "../../../shared/translator/translate.module"; +import {GenericArtifactBrowserModule} from "../../../components/logic/generic-artifact-browser/generic-artifact-browser.module"; + +@NgModule({ + declarations: [ + DeploymentArtifactsPageComponent + ], + imports: [ + TranslateModule, + CommonModule, + SdcUiComponentsModule, + NgxDatatableModule, + UiElementsModule, + ArtifactFormModule, + GenericArtifactBrowserModule + ], + exports: [ + DeploymentArtifactsPageComponent + ], + entryComponents: [ + DeploymentArtifactsPageComponent + ], + providers:[ArtifactsService] +}) +export class DeploymentArtifactsPageModule { +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.spec.ts new file mode 100644 index 0000000000..056efdc5d4 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment-artifacts/deployment-artifacts-page.spec.ts @@ -0,0 +1,86 @@ +// import ' rxjs/add/observable/of'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture } from '@angular/core/testing'; +import { NgxsModule, Store } from '@ngxs/store'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; +import { SdcUiServices } from 'onap-ui-angular'; +import { Observable } from 'rxjs/Observable'; +import { deploymentArtifactMock } from '../../../../../jest/mocks/artifacts-mock'; +import { ConfigureFn, configureTests } from '../../../../../jest/test-config.helper'; +import { ComponentMetadata } from '../../../../models/component-metadata'; +import { ArtifactsService } from '../../../components/forms/artifacts-form/artifacts.service'; +import { CacheService } from '../../../services/cache.service'; +import { TopologyTemplateService } from '../../../services/component-services/topology-template.service'; +import { TranslateModule } from '../../../shared/translator/translate.module'; +import { TranslateService } from '../../../shared/translator/translate.service'; +import { ArtifactsState } from '../../../store/states/artifacts.state'; +import { WorkspaceService } from '../workspace.service'; +import { DeploymentArtifactsPageComponent } from './deployment-artifacts-page.component'; +import {ModalService} from "../../../services/modal.service"; + +describe('deployment artifacts page', () => { + + let fixture: ComponentFixture; + let topologyTemplateServiceMock: Partial; + let workspaceServiceMock: Partial; + let loaderServiceMock: Partial; + let store: Store; + + beforeEach( + async(() => { + + topologyTemplateServiceMock = { + getArtifactsByType: jest.fn().mockImplementation((componentType, id, artifactType) => Observable.of(deploymentArtifactMock)) + }; + workspaceServiceMock = { + metadata: { + uniqueId: 'service_unique_id', + componentType: 'SERVICE' + } + } + + loaderServiceMock = { + activate: jest.fn(), + deactivate: jest.fn() + } + const configure: ConfigureFn = (testBed) => { + testBed.configureTestingModule({ + declarations: [DeploymentArtifactsPageComponent], + imports: [NgxDatatableModule, TranslateModule, NgxsModule.forRoot([ArtifactsState])], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + {provide: WorkspaceService, useValue: workspaceServiceMock}, + {provide: TopologyTemplateService, useValue: topologyTemplateServiceMock}, + {provide: SdcUiServices.LoaderService, useValue: loaderServiceMock}, + {provide: ArtifactsService, useValue: {}}, + {provide: SdcUiServices.PopoverService, useValue: {}}, + {provide: CacheService, useValue: {}}, + {provide: SdcUiServices.ModalService, useValue: {}}, + {provide: ModalService, useValue: {}}, + {provide: TranslateService, useValue: {}} + ], + }); + }; + + configureTests(configure).then((testBed) => { + fixture = testBed.createComponent(DeploymentArtifactsPageComponent); + store = testBed.get(Store); + }); + }) + ); + + it('should match current snapshot of informational artifact pages component', () => { + expect(fixture).toMatchSnapshot(); + }); + + it('should see exactly 2 tosca artifacts', () => { + fixture.componentInstance.ngOnInit(); + fixture.componentInstance.deploymentArtifacts$.subscribe((artifacts) => { + expect(artifacts.length).toEqual(8); + }) + store.selectOnce((state) => state.artifacts.deploymentArtifacts).subscribe((artifacts) => { + expect(artifacts.length).toEqual(8); + }); + }); + +}); diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.component.html b/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.component.html new file mode 100644 index 0000000000..885277217d --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.component.html @@ -0,0 +1,11 @@ +
+ + + + + + + + +
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.component.less b/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.component.less new file mode 100644 index 0000000000..4b7a1e7e9f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.component.less @@ -0,0 +1,24 @@ +@import './../../../../../assets/styles/variables.less'; +@import './../../../../../assets/styles/override.less'; +.deployment-page { + width: 100%; + height: 100%; + + /deep/ .sdc-tabs { + height: 100%; + } + /deep/ .sdc-tabs-list { + position: absolute; + top: 22px; + right: 303px; + background-color: @sdcui_color_silver; + + svg-icon-label { + vertical-align: middle; + } + } + /deep/ .sdc-tab-content { + height: 100%; + } +} + diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.component.ts b/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.component.ts new file mode 100644 index 0000000000..12bd5369c7 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.component.ts @@ -0,0 +1,78 @@ +/*- + * ============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========================================================= + */ + +import {Component} from "@angular/core"; +import {HierarchyTabComponent} from "./panel/panel-tabs/hierarchy-tab/hierarchy-tab.component"; +import {ComponentGenericResponse} from "../../../services/responses/component-generic-response"; +import {TopologyTemplateService} from "../../../services/component-services/topology-template.service"; +import {WorkspaceService} from "../workspace.service"; +import {Module} from "app/models"; +import {SdcUiServices} from "onap-ui-angular"; +import {Select} from "@ngxs/store"; +import {WorkspaceState} from "../../../store/states/workspace.state"; +import {DeploymentGraphService} from "../../composition/deployment/deployment-graph.service"; + +const tabs = + { + hierarchyTab: { + titleIcon: 'composition-o', + component: HierarchyTabComponent, + input: {}, + isActive: true, + tooltipText: 'Hierarchy' + } + }; + +@Component({ + selector: 'deployment-page', + templateUrl: './deployment-page.component.html', + styleUrls: ['deployment-page.component.less'] +}) + +export class DeploymentPageComponent { + public tabs: Array; + public resourceType: string; + public modules: Array; + public isDataAvailable: boolean; + + @Select(WorkspaceState.isViewOnly) isViewOnly$: boolean; + + constructor(private topologyTemplateService: TopologyTemplateService, + private workspaceService: WorkspaceService, + private deploymentService: DeploymentGraphService, + private loaderService: SdcUiServices.LoaderService) { + this.tabs = []; + this.isDataAvailable = false; + } + + ngOnInit(): void { + this.topologyTemplateService.getDeploymentGraphData(this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId).subscribe((response: ComponentGenericResponse) => { + this.deploymentService.componentInstances = response.componentInstances; + this.deploymentService.componentInstancesRelations = response.componentInstancesRelations; + this.deploymentService.modules = response.modules; + this.isDataAvailable = true; + this.loaderService.deactivate(); + }); + + this.loaderService.activate(); + this.resourceType = this.workspaceService.getMetadataType(); + this.tabs.push(tabs.hierarchyTab); + } +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.module.ts b/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.module.ts new file mode 100644 index 0000000000..3635e8f2cf --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/deployment-page.module.ts @@ -0,0 +1,30 @@ +/** + * Created by ob0695 on 6/4/2018. + */ +import {NgModule} from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {DeploymentPageComponent} from "./deployment-page.component"; +import {SdcUiComponentsModule} from "onap-ui-angular"; +import {UiElementsModule} from "../../../components/ui/ui-elements.module"; +import {TranslateModule} from "../../../shared/translator/translate.module"; +import {GlobalPipesModule} from "../../../pipes/global-pipes.module"; +import {HierarchyTabModule} from "./panel/panel-tabs/hierarchy-tab/hierarchy-tab.module"; +import {DeploymentGraphService} from "../../composition/deployment/deployment-graph.service"; +import {DeploymentGraphModule} from "../../composition/deployment/deployment-graph.module"; + +@NgModule({ + declarations: [DeploymentPageComponent], + imports: [CommonModule, + DeploymentGraphModule, + SdcUiComponentsModule, + UiElementsModule, + TranslateModule, + GlobalPipesModule, + HierarchyTabModule + ], + exports: [DeploymentPageComponent], + entryComponents: [DeploymentPageComponent], + providers: [DeploymentGraphService] +}) +export class DeploymentPageModule { +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/edit-module-name/edit-module-name.component.html b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/edit-module-name/edit-module-name.component.html new file mode 100644 index 0000000000..d5b9d9e9b2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/edit-module-name/edit-module-name.component.html @@ -0,0 +1,29 @@ +
+
{{selectModule.vfInstanceName}}
+
+ + + + + +
+
{{selectModule.moduleName}}
+ + + + +
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/edit-module-name/edit-module-name.component.less b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/edit-module-name/edit-module-name.component.less new file mode 100644 index 0000000000..721ad53bc3 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/edit-module-name/edit-module-name.component.less @@ -0,0 +1,20 @@ +.edit-module-name-btn{ + float:right; + margin-left: 10px; + margin-bottom: 20px; +} +.save-button { + margin-left: 30px; +} +.cancel-button { + margin-left: 20px; +} +.edit-module-name-heatName { + margin-bottom: 15px; +} +.edit-module-name-label { + text-overflow: ellipsis; + display: block; + white-space: nowrap; + margin-bottom: 10px; +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/edit-module-name/edit-module-name.component.ts b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/edit-module-name/edit-module-name.component.ts new file mode 100644 index 0000000000..819182c75f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/edit-module-name/edit-module-name.component.ts @@ -0,0 +1,24 @@ +import { Component, Input, Output, OnInit } from "@angular/core"; +import { EventEmitter } from "@angular/core"; +import { DisplayModule } from "../../../../../../../models/modules/base-module"; +import { ValidationConfiguration } from "../../../../../../../models/validation-config"; + +@Component({ + selector: 'edit-module-name', + templateUrl: './edit-module-name.component.html', + styleUrls: ['edit-module-name.component.less'] +}) +export class EditModuleName implements OnInit{ + @Input() selectModule:DisplayModule; + @Output() clickButtonEvent: EventEmitter = new EventEmitter(); + private pattern = ValidationConfiguration.validation.validationPatterns.stringOrEmpty; + private originalName: string; + constructor(){} + public ngOnInit(): void { + this.originalName = this.selectModule.heatName; + } + + private clickButton(saveOrCancel: boolean) : void { + this.clickButtonEvent.emit(saveOrCancel ? this.selectModule.heatName : null); + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.html b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.html new file mode 100644 index 0000000000..7c0e60b814 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.html @@ -0,0 +1,119 @@ +
+ +
{{'DEPLOYMENT_TAB_TITLE' | translate }}
+
+
+
+
{{topologyTemplateName}}
+
+ +
+
{{memberId}}
+
+
+
+
+
+ + +
+
+ +
+ +
+
{{memberId}}
+
+
+
+
+
+
+
+ + +
+
+
+
{{selectedModule.name}}
+
+ +
+
+
+
Module ID:
+
{{selectedModule.groupUUID}}
+
+
+
Customization ID:
+
{{selectedModule.customizationUUID}}
+
+
+
Invariant UUID:
+
{{selectedModule.invariantUUID}}
+
+
+
Version:
+
{{selectedModule.version}}
+
+
+
IsBase:
+
{{selectedModule.isBase}}
+
+ +
+ +
+
+
+ {{property.name}} +
+
Type: {{property.type}}
+
+ Value: {{property.value}}
+
+
+
+ +
+
+
+
{{artifact.artifactName}}
+
UUID: {{artifact.artifactUUID}}
+
+ Version: {{artifact.artifactVersion}}
+
+
+
+
+
+
+
diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.less b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.less new file mode 100644 index 0000000000..269ca0aee0 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.less @@ -0,0 +1,222 @@ +@import './../../../../../../../../assets/styles/variables.less'; +.sdc-hierarchy-tab { + padding: 15px 0 0 0; + background-color: #f8f8f8; + height: 100%; + box-shadow: 0.3px 1px 3px rgba(24, 24, 25, 0.42); + display: flex; + flex-flow: column; + + .sdc-hierarchy-tab-title { + color: @main_color_a; + padding: 0 0 15px 20px; + border-bottom: 1px solid #d2d2d2; + } + + .sdc-hierarchy-tab-sub-title { + color: @main_color_a; + padding: 15px 20px 15px 20px; + } + + .scroll-module-list { + overflow-y: auto; + display: flex; + height: 100%; + flex-direction: column; + } + + /deep/ .expand-collapse-container { + margin-bottom: 0; + + .sdc-accordion-header { + white-space: nowrap; + line-height: 22px; + background-color: @tlv_color_u; + padding: 8px 20px 8px 8px; + box-shadow: inset 0px -1px 0px 0px rgba(255, 255, 255, 0.7); + height: 40px; + + .title { + overflow: hidden; + text-overflow: ellipsis; + max-width: 215px; + } + } + + .sdc-accordion-body.open { + padding: 0 0 5px 0; + } + + .sdc-accordion-header:hover { + background-color: @main_color_o; + } + + &.outer-container { + .sdc-accordion-body { + padding-left: 0; + } + } + + &.inner-container { + margin-bottom: 0; + + .sdc-accordion-header { + padding: 8px 20px 8px 30px; + background-color: @tlv_color_t + } + } + } + + sdc-accordion.selected { + /deep/ .expand-collapse-container { + .sdc-accordion-header { + background-color: @main_color_a; + color: @main_color_p; + + .svg-icon { + fill: @main_color_p; + } + } + } + } + + .expand-collapse-sub-title { + max-width: 225px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding: 10px 0 0 33px; + } + + .expand-collapse-content { + .expand-collapse-title { + padding: 0 10px 0 30px; + } + } + + .module-data-container { + width: 100%; + overflow-y: overlay; + background-color: @tlv_color_v; + border: 1px solid @main_color_a; + border-top: 4px solid @main_color_a; + box-shadow: 0.3px 1px 2px rgba(24, 24, 25, 0.32); + .module-data { + color: @main_color_a; + padding: 10px 0 10px 0; + margin: 0 20px 0 20px; + + .selected-module-property-header { + font-weight: bold; + } + + .selected-module-property-value { + font-family: @font-opensans-regular; + + &.small-font { + font-size: 12px; + } + } + + .module-name-container { + + display: flex; + flex-direction: row; + + .module-name { + font-size: 14px; + width: 75%; + max-width: 240px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .edit-name-container { + float: right; + border-left: 1px solid @main_color_a; + height: 20px; + padding-left: 12px; + + svg-icon { + padding-top: 3px; + fill: @main_color_s; + + &.hand-pointer { + cursor: pointer; + } + + } + } + } + } + + .selected-module-property-container { + flex-direction: row; + display: flex; + + .selected-module-property-value { + text-indent: 2px; + } + } + + /deep/ .expand-collapse-module-data-container { + margin-bottom: 0; + + .sdc-accordion-header { + white-space: nowrap; + line-height: 22px; + padding: 8px 20px 8px 16px; + height: 40px; + background-color: @tlv_color_w; + color: @main_color_l; + border-top: 1px solid @main_color_a; + border-bottom: 1px solid @main_color_a; + width: 100%; + } + + } + + .module-data-list-item { + padding-bottom: 10px; + margin: 0 20px 0 20px; + + .artifact-list-item { + color: @main_color_m; + } + + .module-data-list-item-value { + width: 100%; + max-width: 240px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + &.artifact-info { + font-family: @font-opensans-regular; + font-size: 12px; + } + + &.property-name { + font-weight: 400; + color: @main_color_a; + + .hand-pointer { + cursor: pointer; + } + } + + &.property-info { + color: @func_color_s; + font-family: @font-opensans-regular; + } + } + } + } +} + +.modules-list { + overflow-y: overlay; + flex-grow: 1; +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.spec.ts new file mode 100644 index 0000000000..ab88867cc0 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.spec.ts @@ -0,0 +1,133 @@ +import {async, ComponentFixture} from "@angular/core/testing"; +import {HierarchyTabComponent} from "./hierarchy-tab.component"; +import {ConfigureFn, configureTests} from "../../../../../../../../jest/test-config.helper"; +import {NO_ERRORS_SCHEMA} from "@angular/core"; +import {TranslateModule} from "../../../../../../shared/translator/translate.module"; +import {TopologyTemplateService} from "../../../../../../services/component-services/topology-template.service"; +import {WorkspaceService} from "../../../../workspace.service"; +import {ModulesService} from "../../../../../../services/modules.service"; +import {GlobalPipesModule} from "../../../../../../pipes/global-pipes.module"; +import {TranslateService} from "../../../../../../shared/translator/translate.service"; +import {ModalsHandler} from "../../../../../../../utils/modals-handler"; +import {ComponentFactory} from "../../../../../../../utils/component-factory"; +import {NgxsModule} from "@ngxs/store"; +import { SdcUiServices } from "onap-ui-angular"; +import {Observable} from "rxjs"; +import {DisplayModule, Module} from "../../../../../../../models/modules/base-module"; +import {DeploymentGraphService} from "../../../../../composition/deployment/deployment-graph.service"; +import {ComponentMetadata} from "../../../../../../../models/component-metadata"; + +describe('HierarchyTabComponent', () => { + + let fixture: ComponentFixture; + let workspaceService: Partial; + let popoverServiceMock: Partial; + let modulesServiceMock: Partial; + + let editModuleNameInstanceMock = {innerPopoverContent:{instance: { clickButtonEvent: Observable.of("new heat name")}}, + closePopover: jest.fn()}; + let eventMock = {x: 1650, y: 350}; + let moduleMock: Array = [{name: "NewVf2..base_vepdg..module-0", uniqueId: '1'}]; + let selectedModuleMock: DisplayModule = {name: "NewVf2..base_vepdg..module-0", vfInstanceName: "NewVf2", moduleName:"module-0", + heatName: "base_vepdg", uniqueId: '1', updateName: jest.fn().mockImplementation(() => { + selectedModuleMock.name = selectedModuleMock.vfInstanceName + '..' + selectedModuleMock.heatName + '..' + + selectedModuleMock.moduleName;})} + let updateSelectedModuleMock = () => { + selectedModuleMock.heatName = "base_vepdg"; + selectedModuleMock.name = "NewVf2..base_vepdg..module-0"; + fixture.componentInstance.selectedModule = selectedModuleMock; + fixture.componentInstance.modules = moduleMock; + } + beforeEach( + async(() => { + + workspaceService ={ + metadata: { + name: '', + componentType: '' + } + } + popoverServiceMock = { + createPopOverWithInnerComponent: jest.fn().mockImplementation(() => {return editModuleNameInstanceMock}) + } + modulesServiceMock = { + updateModuleMetadata: jest.fn().mockReturnValue(Observable.of({})) + } + + const configure: ConfigureFn = testBed => { + testBed.configureTestingModule({ + declarations: [HierarchyTabComponent], + schemas: [NO_ERRORS_SCHEMA], + imports: [TranslateModule, NgxsModule.forRoot([]), GlobalPipesModule], + providers: [ + {provide: DeploymentGraphService, useValue: {}}, + {provide: ComponentFactory, useValue: {}}, + {provide: TopologyTemplateService, useValue: {}}, + {provide: WorkspaceService, useValue: workspaceService}, + {provide: ModulesService, useValue: modulesServiceMock}, + {provide: TranslateService, useValue: {}}, + {provide: ModalsHandler, useValue: {}}, + {provide: SdcUiServices.PopoverService, useValue: popoverServiceMock} + ] + }); + }; + + configureTests(configure).then(testBed => { + fixture = testBed.createComponent(HierarchyTabComponent); + }); + }) + ); + + it('expected heirarchy component to be defined', () => { + expect(fixture).toBeDefined(); + }); + + it('Update heat name and name sucessfully', () => { + updateSelectedModuleMock(); + fixture.componentInstance.openEditModuleNamePopup(eventMock); + expect(fixture.componentInstance.selectedModule.updateName).toHaveBeenCalled(); + expect(modulesServiceMock.updateModuleMetadata).toHaveBeenCalled(); + expect(fixture.componentInstance.selectedModule.name).toEqual('NewVf2..new heat name..module-0'); + expect(fixture.componentInstance.modules[0].name).toEqual('NewVf2..new heat name..module-0'); + expect(fixture.componentInstance.selectedModule.heatName).toEqual('new heat name'); + }) + it('Try to update heat name and name and get error from server', () => { + updateSelectedModuleMock(); + modulesServiceMock.updateModuleMetadata.mockImplementation(() => Observable.throwError({})); + fixture.componentInstance.openEditModuleNamePopup(eventMock); + expect(fixture.componentInstance.selectedModule.updateName).toHaveBeenCalled(); + expect(modulesServiceMock.updateModuleMetadata).toHaveBeenCalled(); + expect(fixture.componentInstance.modules[0].name).toEqual('NewVf2..base_vepdg..module-0'); + expect(fixture.componentInstance.selectedModule.heatName).toEqual('base_vepdg'); + expect(fixture.componentInstance.selectedModule.name).toEqual('NewVf2..base_vepdg..module-0'); + }) + it('Try to update heat name and name but not find the module with the same uniqueId', () => { + selectedModuleMock.uniqueId = '2' + updateSelectedModuleMock(); + fixture.componentInstance.openEditModuleNamePopup(eventMock); + expect(fixture.componentInstance.selectedModule.updateName).toHaveBeenCalled(); + expect(modulesServiceMock.updateModuleMetadata).not.toHaveBeenCalled(); + expect(fixture.componentInstance.modules[0].name).toEqual('NewVf2..base_vepdg..module-0'); + expect(fixture.componentInstance.selectedModule.heatName).toEqual('base_vepdg'); + expect(fixture.componentInstance.selectedModule.name).toEqual('NewVf2..base_vepdg..module-0'); + selectedModuleMock.uniqueId = '1' + }) + it('Open edit module name popover and change the heat name', () => { + updateSelectedModuleMock(); + spyOn(fixture.componentInstance, 'updateHeatName'); + spyOn(fixture.componentInstance, 'updateOriginalHeatName'); + fixture.componentInstance.openEditModuleNamePopup(eventMock); + expect(popoverServiceMock.createPopOverWithInnerComponent).toHaveBeenCalled(); + expect(fixture.componentInstance.selectedModule.heatName).toEqual("new heat name"); + expect(fixture.componentInstance.updateHeatName).toHaveBeenCalled(); + }) + + + it('Open edit module name popover and not change the heat name', () => { + updateSelectedModuleMock(); + editModuleNameInstanceMock.innerPopoverContent.instance.clickButtonEvent = Observable.of(null); + fixture.componentInstance.openEditModuleNamePopup(eventMock); + expect(popoverServiceMock.createPopOverWithInnerComponent).toHaveBeenCalled(); + expect(fixture.componentInstance.selectedModule.heatName).toEqual("base_vepdg"); + }) +}); \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.ts b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.ts new file mode 100644 index 0000000000..604b194283 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.component.ts @@ -0,0 +1,139 @@ +import {Component, Input} from "@angular/core"; +import {Component as TopologyTemplate, ComponentInstance, DisplayModule, Module, PropertyModel} from "app/models"; +import {TranslateService} from "app/ng2/shared/translator/translate.service"; +import {ComponentType} from "app/utils/constants"; +import {WorkspaceService} from "../../../../workspace.service"; +import {ModulesService} from "../../../../../../services/modules.service"; +import * as _ from "lodash"; +import {ModalsHandler} from "../../../../../../../utils/modals-handler"; +import {ComponentFactory} from "../../../../../../../utils/component-factory"; +import {Select, Store} from "@ngxs/store"; +import { SdcUiServices } from "onap-ui-angular"; +import { EditModuleName } from "../edit-module-name/edit-module-name.component"; +import {GraphState} from "../../../../../composition/common/store/graph.state"; +import {DeploymentGraphService} from "../../../../../composition/deployment/deployment-graph.service"; +import {OnSidebarOpenOrCloseAction} from "../../../../../composition/common/store/graph.actions"; + +@ Component({ + selector: 'hierarchy-tab', + templateUrl: './hierarchy-tab.component.html', + styleUrls: ['./hierarchy-tab.component.less'], +}) +export class HierarchyTabComponent { + + @Select(GraphState.withSidebar) withSidebar$: boolean; + @Input() isViewOnly: boolean; + public selectedIndex: number; + public selectedModule: DisplayModule; + public isLoading: boolean; + public topologyTemplateName: string; + public topologyTemplateType: string; + public modules: Array = []; + public componentInstances: Array = []; + private editPropertyModalTopologyTemplate: TopologyTemplate; + + constructor(private translateService: TranslateService, + private workspaceService: WorkspaceService, + private deploymentService: DeploymentGraphService, + private modulesService: ModulesService, + private ModalsHandler: ModalsHandler, + private componentFactory: ComponentFactory, + private store: Store, + private popoverService: SdcUiServices.PopoverService) { + this.isLoading = false; + this.topologyTemplateName = this.workspaceService.metadata.name; + this.topologyTemplateType = this.workspaceService.metadata.componentType; + } + + ngOnInit() { + this.modules = this.deploymentService.modules; + this.componentInstances = this.deploymentService.componentInstances; + this.editPropertyModalTopologyTemplate = this.componentFactory.createEmptyComponent(this.topologyTemplateType); + this.editPropertyModalTopologyTemplate.componentInstances = this.deploymentService.componentInstances; + } + + onModuleSelected(module: Module, componentInstanceId?: string): void { + + let onSuccess = (module: DisplayModule) => { + console.log("Module Loaded: ", module); + this.selectedModule = module; + this.isLoading = false; + }; + + let onFailed = () => { + this.isLoading = false; + }; + + if (!this.selectedModule || (this.selectedModule && this.selectedModule.uniqueId != module.uniqueId)) { + this.isLoading = true; + if (this.topologyTemplateType == ComponentType.SERVICE) { + // this.selectedInstanceId = componentInstanceId; + this.modulesService.getComponentInstanceModule(this.topologyTemplateType, this.workspaceService.metadata.uniqueId, componentInstanceId, module.uniqueId).subscribe((resultModule: DisplayModule) => { + onSuccess(resultModule); + }, () => { + onFailed(); + }); + } else { + this.modulesService.getModuleForDisplay(this.topologyTemplateType, this.workspaceService.metadata.uniqueId, module.uniqueId).subscribe((resultModule: DisplayModule) => { + onSuccess(resultModule); + }, () => { + onFailed(); + }); + } + } + } + + updateHeatName(): void { + this.isLoading = true; + let originalName: string = this.selectedModule.name; + + this.selectedModule.updateName(); + let moduleIndex: number = _.indexOf(this.modules, _.find(this.modules, (module: Module) => { + return module.uniqueId === this.selectedModule.uniqueId; + })); + + if (moduleIndex !== -1) { + this.modules[moduleIndex].name = this.selectedModule.name; + this.modulesService.updateModuleMetadata(this.topologyTemplateType, this.workspaceService.metadata.uniqueId, this.modules[moduleIndex]).subscribe(() => { + this.isLoading = false; + }, () => { + this.updateOriginalHeatName(originalName, moduleIndex); + this.modules[moduleIndex].name = originalName; + }); + } else { + this.updateOriginalHeatName(originalName, moduleIndex); + } + }; + + private updateOriginalHeatName(originalName: string, moduleIndex: number){ + this.isLoading = false; + this.selectedModule.name = originalName; + this.selectedModule.heatName = this.selectedModule.name.split('..')[1]; + } + + openEditPropertyModal(property: PropertyModel): void { + this.editPropertyModalTopologyTemplate.setComponentMetadata(this.workspaceService.metadata); + this.ModalsHandler.openEditModulePropertyModal(property, this.editPropertyModalTopologyTemplate, this.selectedModule, this.selectedModule.properties).then(() => { + }); + } + + private getKeys(map: Map) { + return _.keys(map); + } + + private toggleSidebarDisplay = () => { + // this.withSidebar = !this.withSidebar; + this.store.dispatch(new OnSidebarOpenOrCloseAction()); + } + + public openEditModuleNamePopup($event) { + const editModuleNameInstance = this.popoverService.createPopOverWithInnerComponent('Edit Module Name', '', {x:$event.x , y:$event.y }, EditModuleName, {selectModule: _.cloneDeep(this.selectedModule)}, 'top'); + editModuleNameInstance.innerPopoverContent.instance.clickButtonEvent.subscribe((newHeatName) => { + if(newHeatName != null){ + this.selectedModule.heatName = newHeatName; + this.updateHeatName(); + } + editModuleNameInstance.closePopover(); + }) + } +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.module.ts b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.module.ts new file mode 100644 index 0000000000..048ca0c65f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/deployment/panel/panel-tabs/hierarchy-tab/hierarchy-tab.module.ts @@ -0,0 +1,24 @@ +/** + * Created by ob0695 on 6/4/2018. + */ +import {NgModule} from "@angular/core"; +import {SdcUiComponentsModule} from "onap-ui-angular"; +import {HierarchyTabComponent} from "./hierarchy-tab.component"; +import {UiElementsModule} from "../../../../../../components/ui/ui-elements.module"; +import {TranslateModule} from "../../../../../../shared/translator/translate.module"; +import {CommonModule} from "@angular/common"; +import {GlobalPipesModule} from "../../../../../../pipes/global-pipes.module"; +import { EditModuleName } from "../edit-module-name/edit-module-name.component"; + +@NgModule({ + declarations: [HierarchyTabComponent, EditModuleName], + imports: [CommonModule, + UiElementsModule, + SdcUiComponentsModule, + TranslateModule, + GlobalPipesModule], + entryComponents: [HierarchyTabComponent, EditModuleName], + exports: [HierarchyTabComponent, EditModuleName], +}) +export class HierarchyTabModule { +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.html b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.html new file mode 100644 index 0000000000..574f2d1bb4 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.html @@ -0,0 +1,62 @@ +
+ + + +
+ {{ status.timeStamp | date:'short':'UTC'}} + {{ status.status }} +
+
+
+ + +
+ + + + + {{ componentName }} + +
+
+
+ + +
{{ row.name }}
+
+
+ + +
+ {{ row.url }} + + + + +
+
+
+ + +
{{ getLatestArtifact(row.name).timeStamp | date:'short':'UTC'}}
+
+
+ + +
{{ getLatestArtifact(row.name).status }}
+
+
+
+
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.less b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.less new file mode 100644 index 0000000000..81b8805792 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.less @@ -0,0 +1,78 @@ +:host ::ng-deep { + .ngx-datatable { + > div { + min-height: 5px; + } + } +} + +.datatable-header-cell { + text-align: left; + color: red; +} + +.statusHeaderTable { + color: #000000; + font-family: OpenSans-Bold, sans-serif; + font-size: 12px; + font-weight: bold; + float: left; +} + +.status { + padding-right: 30px; + color: #5a5a5a; + font-family: OpenSans-Regular, sans-serif; + font-size: 12px; +} + +.distributionIDBlock { + display: inline-block; +} + +.distributionRowContainer{ + background-color: #eaeaea; + text-align: center; +} + +.distributionRowLabel { + overflow: hidden; + padding-top: 10px; + color: #000000; + font-family: OpenSans-Semibold, sans-serif; + font-size: 12px; + font-weight: bold; +} + +.distributionRowValue { + color: #263d4d; + font-family: OpenSans-Regular, sans-serif; + font-size: 14px; +} + +.urlValue { + float: left; + color: #263d4d; + font-family: OpenSans-Regular, sans-serif; + font-size: 14px; +} + +.urlCopyIcon { + float: right; + width: 8%; +} + +.ellipsisCell { + width: 92%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + + + + + + + + diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.spec.ts new file mode 100644 index 0000000000..72b930b6b8 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.spec.ts @@ -0,0 +1,90 @@ +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ComponentFixture } from '@angular/core/testing'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; +import { SdcUiServices } from 'onap-ui-angular'; +import { ConfigureFn, configureTests } from '../../../../../../../jest/test-config.helper'; +import { DistributionService } from '../../distribution.service'; +import { DistributionComponentArtifactTableComponent } from './distribution-component-artifact-table.component'; + +describe('DistributionComponentArtifactTableComponent', () => { + let fixture: ComponentFixture; + let distibutionServiceMock: Partial; + + const mockArtifactsForDistributionAndComponentName = [ + { + name: 'Artifact1', + statuses: [ + {timeStamp: '7/25/2019 12:48AM', status: 'DEPLOY_OK'}, + {timeStamp: '7/25/2019 12:48AM', status: 'DOWNLOAD_OK'}, + {timeStamp: '7/25/2019 12:48AM', status: 'NOTIFIED'} + ], + url: 'URL1', + }, + { + name: 'Artifact2', + statuses: [ + {timeStamp: '7/26/2019 12:48AM', status: 'STATUS_TO_DISPLAY'}, + {timeStamp: '7/25/2019 12:48AM', status: 'DOWNLOAD_OK'}, + {timeStamp: '7/25/2019 12:48AM', status: 'NOTIFIED'} + ], + url: 'URL2', + }, + { + name: 'ArtifactWithNoStatuses', + url: 'URL2', + } + ]; + + beforeEach(() => { + + distibutionServiceMock = { + getArtifactstByDistributionIDAndComponentsName: jest.fn().mockReturnValue(mockArtifactsForDistributionAndComponentName), + }; + + const configure: ConfigureFn = (testBed) => { + testBed.configureTestingModule({ + declarations: [DistributionComponentArtifactTableComponent], + imports: [NgxDatatableModule], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + {provide: DistributionService, useValue: distibutionServiceMock} + ], + }); + }; + + configureTests(configure).then((testBed) => { + fixture = testBed.createComponent(DistributionComponentArtifactTableComponent); + }); + + }); + + it('Get Latest Artifact (status and timeStamp) - So the Component Table will display the last time stamp of the notification', async () => { + await fixture.componentInstance.ngOnInit(); + expect(fixture.componentInstance.getLatestArtifact('Artifact2')).toEqual({status: 'STATUS_TO_DISPLAY', timeStamp: '7/26/2019 12:48AM'}); + expect(fixture.componentInstance.getLatestArtifact('ArtifactWithNoStatuses')).toEqual(null); + }); + + it('Once the Distribution Component Artifact Table Component is created - artifacts will keep the relevant artifacts for a specific distributionID and Component Name', async () => { + await fixture.componentInstance.ngOnInit(); + // tslint:disable:no-string-literal + expect(fixture.componentInstance.artifacts.length).toBe(3); + expect(fixture.componentInstance.artifacts[0].name).toBe('Artifact1'); + expect(fixture.componentInstance.artifacts[0].url).toBe('URL1'); + expect(fixture.componentInstance.artifacts[0].statuses.length).toBe(3); + + expect(fixture.componentInstance.artifacts[1].name).toBe('Artifact2'); + }); + + it('Once the Distribution Component Artifact Table Component is created for Modal- artifacts will keep the relevant artifacts for a ' + + 'specific distributionID and Component Name filtered by Status', async () => { + fixture.componentInstance.statusFilter = 'DOWNLOAD_OK'; + await fixture.componentInstance.ngOnInit(); + expect(fixture.componentInstance.artifacts.length).toBe(3); + expect(fixture.componentInstance.artifacts[0].name).toBe('Artifact1'); + expect(fixture.componentInstance.artifacts[0].url).toBe('URL1'); + + expect(fixture.componentInstance.artifacts[0].statuses.length).toBe(1); + expect(fixture.componentInstance.artifacts[0].statuses[0]).toEqual({status: 'DOWNLOAD_OK', timeStamp: '7/25/2019 12:48AM'}); + + }); +}); diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.ts b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.ts new file mode 100644 index 0000000000..af9aef5c64 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component.ts @@ -0,0 +1,68 @@ +import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import * as _ from 'lodash'; +import { DistributionService } from '../../distribution.service'; + +// tslint:disable:no-string-literal + +@Component({ + selector: 'app-distribution-component-artifact-table', + templateUrl: './distribution-component-artifact-table.component.html', + styleUrls: ['./distribution-component-artifact-table.component.less'] +}) +export class DistributionComponentArtifactTableComponent implements OnInit { + + @ViewChild('statusTable', {}) table: any; + + @Input() componentName: string; + @Input() rowDistributionID: string; + @Input() statusFilter: string; + + public artifacts = []; + + constructor(private distributionService: DistributionService) { + } + + ngOnInit() { + this.artifacts = this.distributionService.getArtifactstByDistributionIDAndComponentsName(this.rowDistributionID, this.componentName); + if (this.statusFilter) { + this.artifacts.forEach( + (artifact) => { + artifact.statuses = _.filter(artifact.statuses, {status: this.statusFilter}); + }); + } + } + + public getLatestArtifact(artifactName: string) { + const selectedArtifact = this.artifacts.filter((artifact) => artifact.name === artifactName); + if (selectedArtifact && selectedArtifact[0] && selectedArtifact[0]['statuses'] && selectedArtifact[0]['statuses'][0]) { + return selectedArtifact[0]['statuses'][0]; + } else { + return null; + } + } + + private copyToClipboard(urlToCopy: any) { + + const inputForCopyToClipboard = document.getElementById('inputForCopyToClipboard') as HTMLInputElement; + inputForCopyToClipboard.value = urlToCopy; + /* Select the text field */ + inputForCopyToClipboard.select(); + + /* Copy the text inside the text field */ + document.execCommand('copy'); + + } + + private generateDataTestID(preFix: string, componentName: string, artifactName: string, status?: string) { + if (!status) { + return preFix + componentName + '_' + artifactName; + } else { + return preFix + status + '_' + componentName + '_' + artifactName; + } + } + + private expandRow(row: any) { + this.table.rowDetail.toggleExpandRow(row); + } + +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.html b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.html new file mode 100644 index 0000000000..fa5a9ad7fb --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.html @@ -0,0 +1,47 @@ +
+
+ Total Artifacts {{ getTotalArtifactsForDistributionID(rowDistributionID) }} + Notified {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'NOTIFIED') }} + Downloaded {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'DOWNLOAD_OK') }} + Deployed {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'DEPLOY_OK') }} + Not Notified {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'NOT_NOTIFIED') }} + Deploy Errors {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'DEPLOY_ERROR') }} + Download Errors {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'DOWNLOAD_ERROR') }} +
+ + + + +
+
+ + + + {{ component }} {{ getTotalArtifactsForDistributionID(rowDistributionID, component) }} + Notified {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'NOTIFIED', component) }} + Downloaded {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'DOWNLOAD_OK', component) }} + Deployed {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'DEPLOY_OK', component) }} + Not Notified {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'NOT_NOTIFIED', component) }} + {{ getMSOStatus (rowDistributionID, component) }} + Deploy Errors {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'DEPLOY_ERROR', component) }} + Download Errors {{ getLengthArtifactsForDistributionIDByStatus(rowDistributionID, 'DOWNLOAD_ERROR', component) }} +
+ + + +
+ +
+
+
+ diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.less b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.less new file mode 100644 index 0000000000..3eab18ca14 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.less @@ -0,0 +1,66 @@ +.red { + color: red; +} + +.green { + color: green; +} + +.msoStatus { + padding-left: 5px; +} + +.blue { + color: #009fdb; + font-family: OpenSans-Semibold, sans-serif; + font-size: 14px; +} + +.rightVerticalSeperator { + border-right: 1px solid #d2d2d2; + padding-left: 5px; + padding-right: 5px; + cursor: pointer; +} + +.rightVerticalSeperatorComponent { + border-right: 1px solid #d2d2d2; + padding-left: 5px; + padding-right: 5px; +} + +.floatRight{ + float: right; +} + +.distributionSummary { + padding-top: 5px; + padding-bottom: 5px; + background-color: #eaeaea; + padding-left: 25px; + padding-right: 25px; +} + +.componentSummary { + margin-top: 5px; + margin-bottom: 5px; + padding-top: 5px; + padding-bottom: 5px; + background-color: #eaeaea; + padding-left: 25px; + padding-right: 25px; +} + +.componentShiftLeft { + margin-left: 15px; +} + +.titleSummaryFontSettings { + color: #191919; + font-family: OpenSans-Regular, sans-serif; + font-size: 14px; +} + + + + diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.spec.ts new file mode 100644 index 0000000000..ff89b92fd8 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.spec.ts @@ -0,0 +1,47 @@ +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ComponentFixture } from '@angular/core/testing'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; +import { SdcUiServices } from 'onap-ui-angular'; +import { ConfigureFn, configureTests } from '../../../../../../jest/test-config.helper'; +import { DistributionService } from '../distribution.service'; +import { DistributionComponentTableComponent } from './distribution-component-table.component'; + +describe('DistributionComponentTableComponent', () => { + let fixture: ComponentFixture; + let distibutionServiceMock: Partial; + + const mockComponentsForDistribution = ['Consumer1', 'Consumer2']; + + beforeEach(() => { + + distibutionServiceMock = { + getComponentsByDistributionID: jest.fn().mockReturnValue(mockComponentsForDistribution), + getArtifactstByDistributionIDAndComponentsName: jest.fn(), + getArtifactsForDistributionIDAndComponentByStatus: jest.fn() + }; + + const configure: ConfigureFn = (testBed) => { + testBed.configureTestingModule({ + declarations: [DistributionComponentTableComponent], + imports: [NgxDatatableModule], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + {provide: DistributionService, useValue: distibutionServiceMock}, + {provide: SdcUiServices.ModalService, useValue: {}} + ], + }); + }; + + configureTests(configure).then((testBed) => { + fixture = testBed.createComponent(DistributionComponentTableComponent); + }); + + }); + + it('Once the Distribution Component Table Component is created - components will keep the relevant components for a specific distributionID', async () => { + await fixture.componentInstance.ngOnInit(); + expect(fixture.componentInstance.components.length).toBe(2); + expect(fixture.componentInstance.components[0]).toBe('Consumer1'); + expect(fixture.componentInstance.components[1]).toBe('Consumer2'); + }); +}); diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.ts b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.ts new file mode 100644 index 0000000000..e3aaf9d639 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution-component-table/distribution-component-table.component.ts @@ -0,0 +1,104 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { SdcUiCommon, SdcUiComponents, SdcUiServices } from 'onap-ui-angular'; +import { ModalComponent } from 'onap-ui-angular/dist/modals/modal.component'; +import { DistributionComponent } from '../distribution.component'; +import { DistributionService } from '../distribution.service'; + +@Component({ + selector: 'app-distribution-component-table', + templateUrl: './distribution-component-table.component.html', + styleUrls: ['./distribution-component-table.component.less'] +}) +export class DistributionComponentTableComponent implements OnInit { + + @Input() rowDistributionID: string; + @Input() isModal: boolean = false; + @Input() statusFilter: string; + public components = []; + private customModalInstance: ModalComponent; + private expanded = []; + constructor(private distributionService: DistributionService, + private modalService: SdcUiServices.ModalService) { + } + + ngOnInit() { + this.initComponents(); + } + + private generateTotalComponentArtifactsLabel(componentName: any, status: string): string { + return 'total' + componentName + status + 'ArtifactsLabel'; + } + + private generateExpandDataTestID(componentName: string) { + return 'expandIcon_' + componentName; + } + + private initComponents() { + this.components = this.distributionService.getComponentsByDistributionID(this.rowDistributionID); + this.components.map((component) => this.expanded.push({componentName: component, expanded: false})); + } + + private getTotalArtifactsForDistributionID(distributionID: string, componentName?: string): number { + return this.distributionService.getArtifactstByDistributionIDAndComponentsName(distributionID, componentName).length; + } + + private getLengthArtifactsForDistributionIDByStatus(distributionID: string, statusToSerach: string, componentName?: string): number { + if (componentName) { + return this.distributionService.getArtifactsForDistributionIDAndComponentByStatus(distributionID, statusToSerach, componentName).length; + } else { + return this.distributionService.getArtifactsForDistributionIDAndComponentByStatus(distributionID, statusToSerach).length; + } + } + + private openModal(rowDistributionID: string, statusFilter: string) { + + const title: string = 'Distribution by Status'; + const attributeModalConfig = { + title, + size: 'sdc-xl', + type: SdcUiCommon.ModalType.custom, + buttons: [ + { + id: 'close', + text: 'Close', + size: 'sm', + closeModal: true, + disabled: false, + } + ] as SdcUiCommon.IModalButtonComponent[] + }; + + this.customModalInstance = this.modalService.openCustomModal(attributeModalConfig, DistributionComponent, { + // inputs + rowDistributionID, + statusFilter, + isModal: true, + }); + } + + private expandRow(componentName: string) { + console.log('Should expand componentSummary for componentName = ' + componentName); + const selectedComponent = this.expanded.find((component) => component.componentName === componentName); + // tslint:disable:no-string-literal + const selectedComponentExpandedVal = selectedComponent['expanded']; + // this.expanded = !this.expanded; + for (const i in this.expanded) { + if (this.expanded[i].componentName === componentName) { + this.expanded[i].expanded = !this.expanded[i].expanded; + break; //Stop this loop, we found it! + } + } + const selectedComponentAfter = this.expanded.find((component) => component.componentName === componentName); + const selectedComponentExpandedValAfter = selectedComponentAfter['expanded']; + } + + private isExpanded(componentName: string) { + const selectedComponent = this.expanded.find((component) => component.componentName === componentName); + return selectedComponent['expanded']; + } + + + private getMSOStatus(rowDistributionID: string, componentName: string): string { + return this.distributionService.getMSOStatus(rowDistributionID, componentName); + } +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.html b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.html new file mode 100644 index 0000000000..d0cacb054e --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.html @@ -0,0 +1,80 @@ +
+
+
DISTRIBUTION [{{distributions.length}}]
+
+ + +
+
+
No Distributions To Present
+
+ +
+ + + + + + + + +
+ + +
+
+
{{ row.distributionID }}
+
+
+
+ + +
{{ row.userId }}
+
+
+ + +
{{ row.timestamp }}
+
+
+ + +
+ + + + + {{ row.deployementStatus }} + + + + +
+
+
+
+
diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.less b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.less new file mode 100644 index 0000000000..b630881fdc --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.less @@ -0,0 +1,92 @@ +:host ::ng-deep { + .ngx-datatable { + > div { + min-height: 500px; + datatable-body { + max-height: max-content; + } + } + } +} + +.w-sdc-distribution-view-header { + display: flex; + -webkit-justify-content: space-between; + margin: 0 25px 5px 40px; + + .header-spacer { + flex-grow: 5; + } + + .w-sdc-distribution-view-title{ + color: #191919; + font-family: OpenSans-Regular, sans-serif; + font-size: 14px; + line-height: 30px; + + + .blue-font { + color: #009fdb; + font-family: OpenSans-Semibold, sans-serif; + font-size: 14px; + } + } + +} + +.distribution-page { + max-height: 150px; +} + + .distributionIDBlock { + display: inline-block; + } + + .expand-collapse-cell { + display: inline-block; + } + + .statusIcon { + display: inline-block; + margin-right: 10px; + } + + .btnMarkAsDistributed { + float: right; + background-color: #E5F3FF; + border: 1px solid #8DCCD5; + width: 55px; + height: 21px; + text-align: center; + } + + .distributionRowContainer{ + background-color: #eaeaea; + text-align: center; + } + + .distributionRowLabel { + overflow: hidden; + padding-top: 10px; + color: #000000; + font-family: OpenSans-Semibold, sans-serif; + font-size: 12px; + font-weight: bold; + } + + .distributionRowValue { + color: #263d4d; + font-family: OpenSans-Regular, sans-serif; + font-size: 14px; + } + +.ellipsisCell { + width: 92%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + + + + diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.spec.ts new file mode 100644 index 0000000000..e6c9c239e1 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.spec.ts @@ -0,0 +1,92 @@ +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ComponentFixture } from '@angular/core/testing'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; +import { SdcUiServices } from 'onap-ui-angular'; +import { ConfigureFn, configureTests } from '../../../../../jest/test-config.helper'; +import { ComponentMetadata } from '../../../../models/component-metadata'; +import { AuthenticationService } from '../../../services/authentication.service'; +import { WorkspaceService } from '../workspace.service'; +import { DistributionComponent } from './distribution.component'; +import { DistributionService } from './distribution.service'; +import {EventListenerService} from "../../../../services/event-listener-service"; + +describe('DistributionComponent', () => { + let fixture: ComponentFixture; + let distibutionServiceMock: Partial; + let workspaceServiceMock: Partial; + let loaderServiceMock: Partial; + let authenticationServiceMock: Partial ; + let eventListenerService: Partial ; + + const mockDistributionListFromService = [ + { + deployementStatus: 'Distributed', + distributionID: '1', + timestamp: '2019-07-21 08:37:02.834 UTC', + userId: 'Aretha Franklin(op0001)' + }, { + deployementStatus: 'Distributed', + distributionID: '2', + timestamp: '2019-07-21 09:37:02.834 UTC', + userId: 'Aretha Franklin(op0001)' + }]; + + beforeEach(() => { + + distibutionServiceMock = { + initDistributionsList: jest.fn(), + getDistributionList: jest.fn().mockReturnValue(mockDistributionListFromService), + initDistributionsStatusForDistributionID: jest.fn() + }; + + const componentMetadata = new ComponentMetadata(); + componentMetadata.uuid = '111'; + + workspaceServiceMock = { + metadata : componentMetadata + }; + + authenticationServiceMock = { + getLoggedinUser: jest.fn().mockReturnValue({role: 'designer'}) + }; + + eventListenerService = { + registerObserverCallback: jest.fn(), + unRegisterObserver: jest.fn() + } + + loaderServiceMock = { + activate: jest.fn(), + deactivate: jest.fn() + }; + + const configure: ConfigureFn = (testBed) => { + testBed.configureTestingModule({ + declarations: [DistributionComponent], + imports: [NgxDatatableModule], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + {provide: DistributionService, useValue: distibutionServiceMock}, + {provide: WorkspaceService, useValue: workspaceServiceMock}, + {provide: SdcUiServices.LoaderService, useValue: loaderServiceMock}, + {provide: AuthenticationService, useValue: authenticationServiceMock}, + {provide: EventListenerService, useValue: eventListenerService} + ], + }); + }; + + configureTests(configure).then((testBed) => { + fixture = testBed.createComponent(DistributionComponent); + }); + + }); + + it('Once the Distribution Component is created - distributionsResponseFromServer save all the distributions from the Service', async () => { + fixture.componentInstance.componentUuid = 'componentUid'; + fixture.componentInstance.rowDistributionID = null; + fixture.componentInstance.isModal = false; + + await fixture.componentInstance.ngOnInit(); + expect(fixture.componentInstance.distributions.length).toBe(2); + }); +}); diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.ts b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.ts new file mode 100644 index 0000000000..ca1b6292d3 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.component.ts @@ -0,0 +1,117 @@ +import { Component, Input, OnInit, ViewChild } from '@angular/core'; +import { SdcUiCommon, SdcUiServices } from 'onap-ui-angular'; +import { EventListenerService } from '../../../../services/event-listener-service'; +import { AuthenticationService } from '../../../services/authentication.service'; +import { WorkspaceService } from '../workspace.service'; +import { DistributionService } from './distribution.service'; +import { EVENTS } from '../../../../utils/constants'; + +@Component({ + selector: 'distribution', + templateUrl: './distribution.component.html', + styleUrls: ['../../../../../assets/styles/table-style.less', './distribution.component.less'] +}) +export class DistributionComponent implements OnInit { + + @ViewChild('distributionTable', { }) table: any; + + @Input() isModal: boolean = false; + @Input() statusFilter: string; + @Input() rowDistributionID: string; + public componentUuid: string; + public distributions = []; + private expanded: any = {}; + private serviceHasDistibutions: boolean = false; + private readonly uniqueId: string; + private userRole: string; + + constructor(private workspaceService: WorkspaceService, + private distributionService: DistributionService, + private loaderService: SdcUiServices.LoaderService, + private authService: AuthenticationService, + private eventListenerService: EventListenerService) { + this.componentUuid = this.workspaceService.metadata.uuid; + this.uniqueId = this.workspaceService.metadata.uniqueId; + } + + + + async ngOnInit() { + this.userRole = this.authService.getLoggedinUser().role; + this.eventListenerService.registerObserverCallback(EVENTS.ON_DISTRIBUTION_SUCCESS, async () => { + await this.refreshDistributions(); + }); + await this.initDistributions(this.componentUuid, this.rowDistributionID); + } + + ngOnDestroy(): void { + this.eventListenerService.unRegisterObserver(EVENTS.ON_DISTRIBUTION_SUCCESS); + } + + async initDistributions(componentUuid: string, specificDistributionID?: string) { + this.loaderService.activate(); + await this.distributionService.initDistributionsList(componentUuid); + this.distributions = this.distributionService.getDistributionList(); + this.distributions.length > 0 ? this.serviceHasDistibutions = true : this.serviceHasDistibutions = false; + if (specificDistributionID) { + this.distributions = this.distributionService.getDistributionList(specificDistributionID); + } + this.loaderService.deactivate(); + } + + getIconName(rowStatus: string ) { + if (rowStatus === 'Distributed') { + return 'distributed'; + } + if (rowStatus === 'Deployed') { + return 'v-circle'; + } + } + + getIconMode(rowStatus: string) { + if (rowStatus === 'Distributed') { + return 'primary'; + } + if (rowStatus === 'Deployed') { + return 'secondary'; + } + } + + private async markDeploy(distributionID: string, status: string) { + if (status === 'Distributed') { + console.log('Should send MarkDeploy POST Request ServiceID:' + this.uniqueId + ' DISTID:' + distributionID); + await this.distributionService.markDeploy(this.uniqueId, distributionID); + this.refreshDistributions(); + } + } + + private async refreshDistributions() { + await this.initDistributions(this.componentUuid); + } + + private updateFilter(event) { + const val = event.target.value.toLowerCase(); + + // filter our data + this.distributions = this.distributionService.getDistributionList().filter((distribution: any[]) => { + return !val || + // tslint:disable:no-string-literal + distribution['distributionID'].toLowerCase().indexOf(val) !== -1; + }); + } + + private generateDataTestID(preFix: string, distributionID: string, isModal?: boolean ): string { + if (isModal) { + return preFix + distributionID.substring(0, 5) + '_Modal'; + } else { + return preFix + distributionID.substring(0, 5); + } + } + + private async expandRow(row: any, expanded: boolean) { + if (!expanded) { + await this.distributionService.initDistributionsStatusForDistributionID(row.distributionID); + } + this.table.rowDetail.toggleExpandRow(row); + } +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.module.ts b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.module.ts new file mode 100644 index 0000000000..723a6d8c0a --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.module.ts @@ -0,0 +1,34 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { NgxDatatableModule } from '@swimlane/ngx-datatable'; +import { SdcUiComponentsModule } from 'onap-ui-angular'; +import { DistributionComponentArtifactTableComponent } from './distribution-component-table/distribution-component-artifact-table/distribution-component-artifact-table.component'; +import { DistributionComponentTableComponent } from './distribution-component-table/distribution-component-table.component'; +import { DistributionComponent } from './distribution.component'; +import { DistributionService } from './distribution.service'; + +@NgModule({ + declarations: [ + DistributionComponent, + DistributionComponentTableComponent, + DistributionComponentArtifactTableComponent, + ], + imports: [ + // TranslateModule, + CommonModule, + SdcUiComponentsModule, + NgxDatatableModule, + ], + exports: [ + DistributionComponent, + DistributionComponentTableComponent + ], + entryComponents: [ + DistributionComponent, + DistributionComponentTableComponent, + DistributionComponentArtifactTableComponent + ], + providers: [DistributionService] +}) +export class DistributionModule { +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.service.ts b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.service.ts new file mode 100644 index 0000000000..ed6791c5c1 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/disribution/distribution.service.ts @@ -0,0 +1,233 @@ +import { HttpClient } from '@angular/common/http'; +import { Inject, Injectable } from '@angular/core'; +import { tap } from 'rxjs/operators'; +import { Distribution } from '../../../../models/distribution'; +import { ISdcConfig, SdcConfigToken } from '../../../config/sdc-config.config'; + +@Injectable() +export class DistributionService { + protected baseUrl; + private distributionList = []; + private distributionStatusesMap = {}; + + // tslint:disable:no-string-literal + + constructor(protected http: HttpClient, @Inject(SdcConfigToken) sdcConfig: ISdcConfig) { + this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; + } + + // Once the distribution page is loaded or when the user wants to refresh the list + async initDistributionsList(componentUuid: string): Promise { + const distributionsListURL = this.baseUrl + 'services/' + componentUuid + '/distribution'; + const res = this.http.get(distributionsListURL).pipe(tap( (result) => { + this.distributionList = result['distributionStatusOfServiceList']; + this.insertDistrbutionsToMap(); + } )); + return res.toPromise(); + } + + // Once the user click on the relevant distribution ID in the distribution table (open and close) + async initDistributionsStatusForDistributionID(distributionID: string): Promise { + const distributionStatus = this.baseUrl + 'services/distribution/' + distributionID; + const res = this.http.get(distributionStatus).pipe(tap( (result) => { + this.insertDistributionStatusToDistributionsMap(distributionID, result['distributionStatusList']); + } )); + return res.toPromise(); + } + + public getDistributionList(specificDistributionID?: string) { + if (specificDistributionID) { + return this.distributionList.filter((distribution) => { + return distribution['distributionID'] === specificDistributionID; + }); + } else { + return this.distributionList; + } + } + + public getComponentsByDistributionID(distributionID: string) { + const components = []; + const distributionStatusMap = this.getStatusMapForDistributionID(distributionID); + if (distributionStatusMap) { + distributionStatusMap.forEach((component) => components.push(component.componentID)); + } + return components; + } + + // get array of artifacts per distributionID w/o componentName, sliced by artifact status + public getArtifactsForDistributionIDAndComponentByStatus(distributionID: string, statusToSearch: string, componentName?: string) { + const filteredArtifactsByStatus = []; + + if (componentName) { + this.getArtifactstByDistributionIDAndComponentsName(distributionID, componentName).forEach ( (artifact) => { + if (this.artifactStatusHasMatch(artifact, statusToSearch)) { + filteredArtifactsByStatus.push(artifact); + } + } ); + } else { + this.getArtifactstByDistributionIDAndComponentsName(distributionID).forEach ( (artifact) => { + if (this.artifactStatusHasMatch(artifact, statusToSearch)) { + filteredArtifactsByStatus.push(artifact); + } + } ); + } + return filteredArtifactsByStatus; + } + + public getArtifactstByDistributionIDAndComponentsName(distributionID: string, componentName?: string): any[] { + const artifacts = []; + if (this.getStatusMapForDistributionID(distributionID)) { + if (componentName) { + if (this.getStatusMapForDistributionID(distributionID).filter((component) => component.componentID === componentName).length > 0) { + const artifactsArr = this.getStatusMapForDistributionID(distributionID).filter((component) => component.componentID === componentName)[0]['artifacts'] + if (artifactsArr.length > 0) { + artifactsArr.forEach((artifact) => { + const artifactObj = { + url: artifact.artifactUrl, + name: artifact.artifactName, + statuses: artifact.statuses + }; + artifacts.push(artifactObj); + }); + } + } + } else { + const components = this.getComponentsByDistributionID(distributionID); + components.forEach((componentName) => { + if (this.getStatusMapForDistributionID(distributionID).filter((component) => component.componentID === componentName).length > 0) { + const artifactsArr = this.getStatusMapForDistributionID(distributionID).filter((component) => component.componentID === componentName)[0]['artifacts'] + if (artifactsArr.length > 0) { + artifactsArr.forEach((artifact) => { + const artifactObj = { + url: artifact.artifactUrl, + name: artifact.artifactName, + statuses: artifact.statuses + }; + artifacts.push(artifactObj); + }); + } + } + }); + } + } + return artifacts; + } + + public getStatusMapForDistributionID(distributionID: string) { + return this.distributionStatusesMap[distributionID]; + } + + public markDeploy(uniqueId: string, distributionID: string): Promise { + const distributionStatus = this.baseUrl + 'services/' + uniqueId + '/distribution/' + distributionID + '/markDeployed'; + const res = this.http.post(distributionStatus, {}).pipe(tap( (result) => { + console.log(result); + } )); + return res.toPromise(); + } + + public getMSOStatus(distributionID: string, componentName: string): string { + const msoStatus = this.distributionStatusesMap[distributionID].filter((component) => component.componentID === componentName)[0].msoStatus; + return msoStatus ? msoStatus : ''; + } + + private artifactStatusHasMatch(artifact: any, statusToSerach: string) { + for (let i = 0; i < artifact.statuses.length; i++) { + if (artifact.statuses[i].status === statusToSerach) { + return true; + } + } + return false; + } + + private insertDistributionStatusToDistributionsMap(distributionID: string, distributionStatusMapResponseFromServer: object[]) { + + // // Clear the Distribution ID array - to avoid statuses duplications + const distribution = this.distributionStatusesMap[distributionID]; + distribution.length = 0; + + // Sort the response of statuses from Server, so it will be easy to pop the latest status when it will be required + const sortedResponseByTimeStamp = distributionStatusMapResponseFromServer.sort((a, b) => b['timestamp'] - a['timestamp']) + + sortedResponseByTimeStamp.map((distributionStatus) => { + const formattedDate = this.formatDate(distributionStatus['timestamp']); + + // if (distributionStatus['url'] === null) { + // distributionStatus['url'] = ""; + // } + + const detailedArtifactStatus = { + componentID: distributionStatus['omfComponentID'], + artifactName: distributionStatus['url']? distributionStatus['url'].split('/').pop() : '', + url: distributionStatus['url'], + time: distributionStatus['timestamp'], + status: distributionStatus['status'], + }; + + + + // Add Component to this.distributionStatusesMap in case not exist. + let componentPosition = _.findIndex(distribution, {componentID: detailedArtifactStatus.componentID}) + + if (componentPosition === -1) { + this.addComponentIdToDistributionStatusMap(distributionID, detailedArtifactStatus.componentID); + componentPosition = distribution.length - 1; + } + + const component = distribution[componentPosition]; + + + // Add Artifact to this.distributionStatusesMap[componentID] in case not exist. + let artifactPosition = _.findIndex(component.artifacts, {artifactUrl: detailedArtifactStatus.url}) + + if (artifactPosition === -1) { + this.addArtifactToComponentId(distributionID, componentPosition, detailedArtifactStatus.artifactName, detailedArtifactStatus.url); + artifactPosition = component.artifacts.length - 1; + } + + + // Add status to relevat artifact in relevent componentID. + if (detailedArtifactStatus.url) { + // Case where there is a url -> should add its status + component.artifacts[artifactPosition].statuses.push({ + timeStamp: detailedArtifactStatus.time, + status: detailedArtifactStatus.status + }); + } else { + // Should update the Component -> status from MSO + this.distributionStatusesMap[distributionID][componentPosition].msoStatus = detailedArtifactStatus.status; + } + + + }); + } + + private addComponentIdToDistributionStatusMap(distributionID: string, componentIDValue: string) { + this.distributionStatusesMap[distributionID].push({ + componentID: componentIDValue, + msoStatus: null, + artifacts: [] + }); + } + + private addArtifactToComponentId(distributionID: string, componentPosition: number, artifactNameValue: string, artifactURLValue: any) { + if (artifactNameValue) { + this.distributionStatusesMap[distributionID][componentPosition].artifacts.push({ + artifactName: artifactNameValue, + artifactUrl: artifactURLValue, + statuses: [] + }); + } + } + + private insertDistrbutionsToMap() { + this.distributionList.map((distribution) => this.distributionStatusesMap[distribution.distributionID] = []); + } + + private formatDate(epochTime: string) { + const intEpochTime = new Date(parseInt(epochTime, 10)); + const amOrPm = (intEpochTime.getHours() + 24) % 24 > 12 ? 'PM' : 'AM'; + const formattedDate = (intEpochTime.getMonth() + 1) + '/' + intEpochTime.getDate() + '/' + intEpochTime.getFullYear() + ' ' + intEpochTime.getHours() + ':' + + intEpochTime.getMinutes() + amOrPm; + return formattedDate; + } +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/information-artifact/__snapshots__/informational-artifact-page.spec.ts.snap b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/__snapshots__/informational-artifact-page.spec.ts.snap new file mode 100644 index 0000000000..1a19b36cfb --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/__snapshots__/informational-artifact-page.spec.ts.snap @@ -0,0 +1,40 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`informational artifacts page should match current snapshot of informational artifact pages component 1`] = ` + +
+ + +
+ + + + + + + + + +
+
+
+
+`; diff --git a/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.component.html b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.component.html new file mode 100644 index 0000000000..cff33258ae --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.component.html @@ -0,0 +1,82 @@ +
+ + + + +
{{row.description}}
+
+
+ + +
+ + {{row.artifactDisplayName }} +
+
+
+ + + {{row.artifactType}} + + + + + {{ row.artifactVersion }} + + + + + {{ row.artifactUUID }} + + + + +
+ + + +
+
+
+ + + +
+ + +
+
+
+
+
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.component.less b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.component.less new file mode 100644 index 0000000000..b69e511f70 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.component.less @@ -0,0 +1,29 @@ +.information-artifact-page { + + .add-artifact-btn { + display: flex; + cursor: pointer; + justify-content: flex-end; + margin-bottom: 10px; + } + .download-artifact-button { + display: flex; + justify-content: center; + + .action-icon{ + margin-right: 10px; + } + } + + .add-artifacts-dynamic-btn-list { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + margin: 20px 0px; + .add-artifacts-dynamic-btn{ + width: 350px; + margin-top: 15px; + } + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.component.ts b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.component.ts new file mode 100644 index 0000000000..a6804a43c6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.component.ts @@ -0,0 +1,69 @@ +import {Component, OnInit, ViewChild} from "@angular/core"; +import {WorkspaceService} from "../workspace.service"; +import {SdcUiCommon, SdcUiComponents, SdcUiServices} from "onap-ui-angular"; +import {TopologyTemplateService} from "../../../services/component-services/topology-template.service"; +import * as _ from "lodash"; +import {ArtifactGroupType, ArtifactType} from "../../../../utils/constants"; +import {ArtifactsService} from "../../../components/forms/artifacts-form/artifacts.service"; +import {DeleteArtifactAction, GetArtifactsByTypeAction} from "../../../store/actions/artifacts.action"; +import {Select, Store} from "@ngxs/store"; +import {Observable} from "rxjs/index"; +import {ArtifactsState} from "../../../store/states/artifacts.state"; +import {map} from "rxjs/operators"; +import {WorkspaceState} from "../../../store/states/workspace.state"; +import {ArtifactModel} from "../../../../models/artifacts"; + +@Component({ + selector: 'information-artifact-page', + templateUrl: './information-artifact-page.component.html', + styleUrls: ['./information-artifact-page.component.less', '../../../../../assets/styles/table-style.less'] +}) +export class InformationArtifactPageComponent implements OnInit { + + public componentId: string; + public componentType: string; + public informationArtifacts$: Observable; + public informationArtifactsAsButtons$: Observable; + @Select(WorkspaceState.isViewOnly) isViewOnly$: boolean; + @ViewChild('informationArtifactsTable') table: any; + + constructor(private workspaceService: WorkspaceService, + private artifactsService: ArtifactsService, + private store: Store) { + } + + ngOnInit(): void { + this.componentId = this.workspaceService.metadata.uniqueId; + this.componentType = this.workspaceService.metadata.componentType; + + this.store.dispatch(new GetArtifactsByTypeAction({ + componentType: this.componentType, + componentId: this.componentId, + artifactType: ArtifactGroupType.INFORMATION + })); + + let artifacts = this.store.select(ArtifactsState.getArtifactsByType).pipe(map(filterFn => filterFn(ArtifactType.INFORMATION))); + this.informationArtifacts$ = artifacts.pipe(map(artifacts => _.filter(artifacts, (artifact) => { + return artifact.esId; + }))); + + this.informationArtifactsAsButtons$ = artifacts.pipe(map(artifacts => _.filter(artifacts, (artifact) => { + return !artifact.esId; + }))); + } + + onActivate(event) { + if (event.type === 'click') { + this.table.rowDetail.toggleExpandRow(event.row); + } + } + + public addOrUpdateArtifact = (artifact: ArtifactModel, isViewOnly?: boolean) => { + this.artifactsService.openArtifactModal(this.componentId, this.componentType, artifact, ArtifactGroupType.INFORMATION, isViewOnly); + } + + public deleteArtifact = (artifactToDelete) => { + this.artifactsService.deleteArtifact(this.componentType, this.componentId, artifactToDelete) + } + +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.module.ts b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.module.ts new file mode 100644 index 0000000000..5eb9e5851b --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/information-artifact-page.module.ts @@ -0,0 +1,30 @@ +import {CommonModule} from "@angular/common"; +import {NgModule} from "@angular/core"; +import {SdcUiComponentsModule} from "onap-ui-angular"; +import {NgxDatatableModule} from "@swimlane/ngx-datatable"; +import {UiElementsModule} from "../../../components/ui/ui-elements.module"; +import {InformationArtifactPageComponent} from "./information-artifact-page.component"; +import {ArtifactFormModule} from "../../../components/forms/artifacts-form/artifact-form.module"; +import {ArtifactsService} from "../../../components/forms/artifacts-form/artifacts.service"; + +@NgModule({ + declarations: [ + InformationArtifactPageComponent + ], + imports: [ + CommonModule, + SdcUiComponentsModule, + NgxDatatableModule, + UiElementsModule, + ArtifactFormModule + ], + exports: [ + InformationArtifactPageComponent + ], + entryComponents: [ + InformationArtifactPageComponent + ], + providers:[ArtifactsService] +}) +export class InformationArtifactPageModule { +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/information-artifact/informational-artifact-page.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/informational-artifact-page.spec.ts new file mode 100644 index 0000000000..10fd14739b --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/information-artifact/informational-artifact-page.spec.ts @@ -0,0 +1,77 @@ +import {async, ComponentFixture, TestBed} from "@angular/core/testing"; +import {NO_ERRORS_SCHEMA} from "@angular/core"; +import {ConfigureFn, configureTests} from "../../../../../jest/test-config.helper"; +import {NgxDatatableModule} from "@swimlane/ngx-datatable"; +import {WorkspaceService} from "../workspace.service"; +import {SdcUiServices} from "onap-ui-angular"; +import {TopologyTemplateService} from "../../../services/component-services/topology-template.service"; +import {Observable} from "rxjs/Observable"; +import {ComponentMetadata} from "../../../../models/component-metadata"; +import 'rxjs/add/observable/of'; +import {NgxsModule, Store} from "@ngxs/store"; +import {ArtifactsState} from "../../../store/states/artifacts.state"; +import {InformationArtifactPageComponent} from "./information-artifact-page.component"; +import { informationalArtifactsMock} from "../../../../../jest/mocks/artifacts-mock"; +import {ArtifactsService} from "../../../components/forms/artifacts-form/artifacts.service"; + +describe('informational artifacts page', () => { + + let fixture: ComponentFixture; + let topologyTemplateServiceMock: Partial; + let workspaceServiceMock: Partial; + let loaderServiceMock: Partial; + let store: Store; + + beforeEach( + async(() => { + + topologyTemplateServiceMock = { + getArtifactsByType: jest.fn().mockImplementation((componentType, id, artifactType) => Observable.of(informationalArtifactsMock)) + }; + workspaceServiceMock = {metadata: {uniqueId: 'service_unique_id', componentType: 'SERVICE'}} + + loaderServiceMock = { + activate : jest.fn(), + deactivate: jest.fn() + } + const configure: ConfigureFn = testBed => { + testBed.configureTestingModule({ + declarations: [InformationArtifactPageComponent], + imports: [NgxDatatableModule, NgxsModule.forRoot([ArtifactsState])], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + {provide: WorkspaceService, useValue: workspaceServiceMock}, + {provide: TopologyTemplateService, useValue: topologyTemplateServiceMock}, + {provide: SdcUiServices.LoaderService, useValue: loaderServiceMock }, + {provide: ArtifactsService, useValue: {}}, + ], + }); + }; + + configureTests(configure).then(testBed => { + fixture = testBed.createComponent(InformationArtifactPageComponent); + store = testBed.get(Store); + }); + }) + ); + + it('should match current snapshot of informational artifact pages component', () => { + expect(fixture).toMatchSnapshot(); + }); + + it('should see exactly 3 informational artifacts and six buttons to add artifact by template', () => { + fixture.componentInstance.ngOnInit(); + fixture.componentInstance.informationArtifacts$.subscribe((artifacts)=> { + expect(artifacts.length).toEqual(3); + }) + fixture.componentInstance.informationArtifactsAsButtons$.subscribe((artifacts)=> { + expect(artifacts.length).toEqual(6); + }) + + store.selectOnce(state => state.artifacts.artifacts).subscribe(artifacts => { + expect(artifacts.length).toEqual(9); + }); + }) + + +}); \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities-properties/capabilities-properties.html b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities-properties/capabilities-properties.html new file mode 100644 index 0000000000..f496e64c17 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities-properties/capabilities-properties.html @@ -0,0 +1,22 @@ +
+ + + + {{row[column.prop]}} + + + {{row[column.prop].property.type}} + + + {{row[column.prop]}} + + + +
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities-properties/capabilities-properties.less b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities-properties/capabilities-properties.less new file mode 100644 index 0000000000..007f509538 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities-properties/capabilities-properties.less @@ -0,0 +1,9 @@ + +:host ::ng-deep { + .ngx-datatable { + > div { + min-height: auto !important; + } + + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities-properties/capabilities-properties.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities-properties/capabilities-properties.ts new file mode 100644 index 0000000000..2a1a16e265 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities-properties/capabilities-properties.ts @@ -0,0 +1,33 @@ + +import { ViewChild, Input, OnInit, Component } from "@angular/core"; +import {SdcUiServices} from "onap-ui-angular"; +import { ModalsHandler } from "../../../../../../utils/modals-handler"; +import { WorkspaceService } from "../../../workspace.service"; +import { PropertyModel } from "../../../../../../models/properties"; + + +@Component({ + selector: 'capabilities-properties', + templateUrl: './capabilities-properties.html', + styleUrls: ['./capabilities-properties.less', '../../../../../../../assets/styles/table-style.less'] +}) +export class CapabilitiesPropertiesComponent { + @Input() public capabilitiesProperties: Array = []; + + private capabilityPropertiesColumns = [ + {name: 'Name', prop: 'name', flexGrow: 1}, + {name: 'Type', prop: 'type', flexGrow: 1}, + {name: 'Schema', prop: 'schema', flexGrow: 1}, + {name: 'Description', prop: 'description', flexGrow: 1}, + ]; + constructor(private modalsHandler: ModalsHandler, + private workspaceService: WorkspaceService) {} + + private updateProperty(property: PropertyModel): void { + _.forEach(this.capabilitiesProperties, (prop: PropertyModel) => { + prop.readonly = true; + }); + this.modalsHandler.openEditPropertyModal(property, this.workspaceService.metadata, this.capabilitiesProperties, false, 'component', + this.workspaceService.metadata.uniqueId); + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities.component.html b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities.component.html new file mode 100644 index 0000000000..819eb84849 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities.component.html @@ -0,0 +1,59 @@ +
+
+ + + + +
+ + + +
Properties
+ +
+
+ + +
+ + {{row.name}} +
+
+
+ + + {{row.type ? row.type.replace("tosca.capabilities.",""): ''}} + + + + + {{row.description}} + + + + + + {{row.validSourceTypes ? row.validSourceTypes.join(','): ''}} + + + + + + {{row.minOccurrences}},{{row.maxOccurrences}} + + +
+
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities.component.less b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities.component.less new file mode 100644 index 0000000000..0c520a8135 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities.component.less @@ -0,0 +1,16 @@ +:host ::ng-deep { + .datatable-row-detail { + width: 1260px; + } + .datatable-body-row { + cursor: pointer; + } +} +.expand-collapse-all-rows { + position: absolute; + top: 172px; + left: 890px; +} +.properties-title { + padding-bottom: 10px; +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities.component.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities.component.ts new file mode 100644 index 0000000000..02db5d3aee --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilities.component.ts @@ -0,0 +1,79 @@ +import {Capability, CapabilityUI} from "../../../../../models/capability"; +import { ViewChild, Input, OnInit, Component } from "@angular/core"; +import {SdcUiServices} from "onap-ui-angular"; +import {CapabilitiesEditorComponent} from "./capabilityEditor/capabilities-editor.component"; +import {WorkspaceService} from "../../workspace.service"; +import {TopologyTemplateService} from "../../../../services/component-services/topology-template.service"; +import {ReqAndCapabilitiesService} from "../req-and-capabilities.service"; +import {ModalComponent} from "onap-ui-angular/dist/modals/modal.component"; +import {EventListenerService} from "../../../../../services/event-listener-service"; + + +@Component({ + selector: 'capabilities', + templateUrl: './capabilities.component.html', + styleUrls: ['./capabilities.component.less','../../../../../../assets/styles/table-style.less'] +}) +export class CapabilitiesComponent { + @Input() public capabilities: Array; + @ViewChild('capabilitiesTable') capabilitiesTable: any; + private customModalInstance: ModalComponent; + + constructor( + private workspaceService: WorkspaceService, + private loaderService: SdcUiServices.LoaderService, + private topologyTemplateService: TopologyTemplateService, + private reqAndCapabilitiesService : ReqAndCapabilitiesService, + private modalService: SdcUiServices.ModalService, + private eventListenerService: EventListenerService) { + } + + private onSelectCapabilities({ selected }) { + } + + editCapability(cap: CapabilityUI) { + let modalConfig = { + size: 'md', + title: 'Update Capability', + type: 'custom', + buttons: [ + { + id: 'saveButton', + text: ('Update'), + size: "'x-small'", + callback: () => this.updateCapability(), + closeModal: true + }, + {text: "Cancel", size: "'x-small'", closeModal: true}] + }; + let modalInputs = { + capability: cap, + capabilityTypesList: this.reqAndCapabilitiesService.getCapabilityTypesList(), + }; + + this.customModalInstance = this.modalService.openCustomModal(modalConfig, CapabilitiesEditorComponent, {input: modalInputs}); + this.customModalInstance.innerModalContent.instance. + onValidationChange.subscribe((isValid) => this.customModalInstance.getButtonById('saveButton').disabled = !isValid); + } + + expendRow(row) { + this.capabilitiesTable.rowDetail.toggleExpandRow(row); + } + + private updateCapability() { + const capability = this.customModalInstance.innerModalContent.instance.capabilityData; + this.loaderService.activate(); + if (capability.uniqueId) { + this.topologyTemplateService.updateCapability(this.workspaceService.metadata.getTypeUrl(), this.workspaceService.metadata.uniqueId, capability).subscribe((result) => { + let index = this.capabilities.findIndex((cap) => result[0].uniqueId === cap.uniqueId); + this.capabilities[index] = new CapabilityUI(result[0], this.workspaceService.metadata.uniqueId); + this.loaderService.deactivate(); + this.eventListenerService.notifyObservers('CAPABILITIES_UPDATED'); + }, () => { + this.loaderService.deactivate(); + }); + } + } + + +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.component.html b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.component.html new file mode 100644 index 0000000000..bc15d4d228 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.component.html @@ -0,0 +1,93 @@ +
+
+
+
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + + + +
+
+
+
+
+
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.component.less b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.component.less new file mode 100644 index 0000000000..324dc6c4d2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.component.less @@ -0,0 +1,38 @@ +@import '../../../../../../../assets/styles/variables.less'; + +.capability-editor { + .i-sdc-form-content-capability-content { + padding: 10px 25px; + .group-with-border { + margin: 25px 0; + padding: 15px 0; + border-top: 1px solid @tlv_color_u; + border-bottom: 1px solid @tlv_color_u; + .content-row:not(:last-of-type) { + padding-bottom: 13px; + } + } + + .occurrences-label { + font-family: @font-opensans-bold; + margin-bottom: 19px; + } + .occurrences-section, /deep/ .max-occurrences-value { + display: flex; + .min-occurrences-value { + padding-right: 30px; + } + .unbounded-value { + padding-top: 7px; + padding-right: 20px; + .sdc-checkbox__label { + text-transform: capitalize; + } + } + } + textarea { + min-height: unset; + height: unset; + } + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.component.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.component.ts new file mode 100644 index 0000000000..3bafa42e0f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.component.ts @@ -0,0 +1,81 @@ +import {Component} from '@angular/core'; +import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; +import {Capability, CapabilityTypeModel} from 'app/models'; +import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component"; +import {TranslateService} from 'app/ng2/shared/translator/translate.service'; +import {Subject} from "rxjs"; + +@Component({ + selector: 'capabilities-editor', + templateUrl: './capabilities-editor.component.html', + styleUrls: ['./capabilities-editor.component.less'], + providers: [ServiceServiceNg2] +}) + +export class CapabilitiesEditorComponent { + input: { + test: string, + capability: Capability, + capabilityTypesList: Array, + isReadonly: boolean; + }; + capabilityData: Capability; + capabilityTypesMappedList: Array; + isUnboundedChecked: boolean; + isReadonly: boolean; + translatedUnboundTxt: string; + + public onValidationChange: Subject = new Subject(); + + constructor(private translateService: TranslateService) { + } + + ngOnInit() { + this.capabilityData = new Capability(this.input.capability); + this.translatedUnboundTxt = ''; + this.capabilityData.minOccurrences = this.capabilityData.minOccurrences || 0; + this.translateService.languageChangedObservable.subscribe(lang => { + this.translatedUnboundTxt = this.translateService.translate('REQ_CAP_OCCURRENCES_UNBOUNDED'); + this.capabilityData.maxOccurrences = this.capabilityData.maxOccurrences || this.translatedUnboundTxt; + this.isUnboundedChecked = this.capabilityData.maxOccurrences === this.translatedUnboundTxt; + }); + this.capabilityTypesMappedList = _.map(this.input.capabilityTypesList, capType => new DropdownValue(capType.toscaPresentation.type, capType.toscaPresentation.type)); + this.isReadonly = this.input.isReadonly; + this.validityChanged(); + } + + onUnboundedChanged() { + this.isUnboundedChecked = !this.isUnboundedChecked; + this.capabilityData.maxOccurrences = this.isUnboundedChecked ? this.translatedUnboundTxt : null; + this.validityChanged(); + } + + checkFormValidForSubmit() { + return this.capabilityData.name && this.capabilityData.name.length && + this.capabilityData.type && this.capabilityData.type.length && !_.isEqual(this.capabilityData.minOccurrences, "") && this.capabilityData.minOccurrences >= 0 && + ( + this.isUnboundedChecked || + (this.capabilityData.maxOccurrences && (this.capabilityData.minOccurrences <= parseInt(this.capabilityData.maxOccurrences))) + ); + } + + onSelectCapabilityType(selectedCapType: DropdownValue) { + this.capabilityData.type = selectedCapType && selectedCapType.value; + if (selectedCapType && selectedCapType.value) { + let selectedCapabilityTypeObj: CapabilityTypeModel = this.input.capabilityTypesList.find(capType => capType.toscaPresentation.type === selectedCapType.value); + this.capabilityData.description = selectedCapabilityTypeObj.toscaPresentation.description; + this.capabilityData.validSourceTypes = selectedCapabilityTypeObj.toscaPresentation.validTargetTypes; + this.capabilityData.properties = _.forEach( + _.toArray(selectedCapabilityTypeObj.properties), + prop => prop.uniqueId = null //a requirement for the BE + ); + } + this.validityChanged(); + } + + validityChanged = () => { + let validState = this.checkFormValidForSubmit(); + this.onValidationChange.next(validState); + } + +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.module.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.module.ts new file mode 100644 index 0000000000..38b104a0f6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/capabilities/capabilityEditor/capabilities-editor.module.ts @@ -0,0 +1,29 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {CapabilitiesEditorComponent} from './capabilities-editor.component'; +import {FormsModule} from '@angular/forms'; +import {FormElementsModule} from 'app/ng2/components/ui/form-components/form-elements.module'; +import {UiElementsModule} from 'app/ng2/components/ui/ui-elements.module'; +import {TranslateModule} from 'app/ng2/shared/translator/translate.module'; +import {SdcUiComponentsModule} from 'onap-ui-angular'; +// import {SdcUiComponentsModule} from "sdc-ui/lib/angular/index"; + +@NgModule({ + declarations: [ + CapabilitiesEditorComponent + ], + imports: [CommonModule, + FormsModule, + FormElementsModule, + UiElementsModule, + TranslateModule, + SdcUiComponentsModule + ], + exports: [], + entryComponents: [ + CapabilitiesEditorComponent + ], + providers: [] +}) +export class CapabilitiesEditorModule { +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.html b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.html new file mode 100644 index 0000000000..73e0ae52ae --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.html @@ -0,0 +1,21 @@ +
+
+ Add Requirement + Add Capability + + + + +
+ + +
+
+ +
+
+
+
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.less b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.less new file mode 100644 index 0000000000..f3d39cacd6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.less @@ -0,0 +1,19 @@ +.req-and-cap-filter { + width: 336px; + float: right; + margin-right: 10px; +} + +.addTitle { + float: right; + text-transform: uppercase; + font-family: OpenSans-Semibold, sans-serif; + color: #009fdb; + cursor: pointer; +} + +:host ::ng-deep .sdc-tabs { + .sdc-tab-content { + margin-top: 0; + } +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.spec.ts new file mode 100644 index 0000000000..b7fad045d3 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.spec.ts @@ -0,0 +1,127 @@ +import {async, ComponentFixture, TestBed} from "@angular/core/testing"; +import { NO_ERRORS_SCHEMA} from "@angular/core"; +import {ConfigureFn, configureTests} from "../../../../../jest/test-config.helper"; + +import {Observable} from "rxjs/Observable"; +import {NgxDatatableModule} from "@swimlane/ngx-datatable"; +import {SdcUiServices, SdcUiCommon} from "onap-ui-angular"; +import 'rxjs/add/observable/of'; +import {ReqAndCapabilitiesComponent} from "./req-and-capabilities.component"; +import {ReqAndCapabilitiesService} from "./req-and-capabilities.service"; +import {WorkspaceService} from "../workspace.service"; +import { + capabilitiesMock, + filterRequirmentsMock, + requirementMock +} from "../../../../../jest/mocks/req-and-capabilities.mock"; +import {ComponentMetadata} from "../../../../models/component-metadata"; +import { TopologyTemplateService } from "../../../services/component-services/topology-template.service"; +import {EventListenerService} from "../../../../services/event-listener-service"; + +describe('req and capabilities component', () => { + + let fixture: ComponentFixture; + let workspaceServiceMock: Partial; + let loaderServiceMock: Partial; + let topologyTemplateServiceMock: Partial; + let createDynamicComponentServiceMock: Partial + let reqAndCapabilitiesService: Partial; + let modalService: Partial; + let eventListenerService: Partial; + + + + beforeEach( + async(() => { + + workspaceServiceMock = { + metadata: new ComponentMetadata() + }; + + topologyTemplateServiceMock = { + getRequirementsAndCapabilitiesWithProperties: jest.fn().mockImplementation(() => + Observable.of({requirements: {'tosca.requirements.Node': requirementMock}, + capabilities: {'tosca.capabilities.Node': capabilitiesMock}})) + }; + + loaderServiceMock = { + activate : jest.fn(), + deactivate: jest.fn() + } + createDynamicComponentServiceMock = { + insertComponentDynamically: jest.fn() + } + + const configure: ConfigureFn = testBed => { + testBed.configureTestingModule({ + declarations: [ReqAndCapabilitiesComponent], + imports: [NgxDatatableModule], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + { provide: WorkspaceService, useValue: workspaceServiceMock }, + { provide: SdcUiServices.LoaderService, useValue: loaderServiceMock }, + { provide: TopologyTemplateService, useValue: topologyTemplateServiceMock }, + { provide: SdcUiServices.CreateDynamicComponentService, useValue: createDynamicComponentServiceMock }, + { provide: ReqAndCapabilitiesService, useValue: reqAndCapabilitiesService }, + { provide: SdcUiServices.ModalService, useValue: modalService }, + { provide: EventListenerService, useValue: eventListenerService } + ], + }); + }; + configureTests(configure).then(testBed => { + fixture = testBed.createComponent(ReqAndCapabilitiesComponent); + }); + }) + ); + + it('should see exactly 2 requirement in requirements table when call initCapabilitiesAndRequirements and meta data requirements null', () => { + workspaceServiceMock.metadata.requirements = null; + fixture.componentInstance.initCapabilitiesAndRequirements(); + expect(workspaceServiceMock.metadata.requirements["tosca.requirements.Node"].length).toBe(3); + }); + it('should see exactly 2 capabilities in capabilities table when call initCapabilitiesAndRequirements and meta data capabilities null', () => { + workspaceServiceMock.metadata.capabilities = null; + fixture.componentInstance.initCapabilitiesAndRequirements(); + expect(workspaceServiceMock.metadata.capabilities["tosca.capabilities.Node"].length).toBe(2); + }); + + it('capabilities array papulated when call populateReqOrCap with capabilities', () => { + workspaceServiceMock.metadata.capabilities = {"tosca.capabilities.Node": capabilitiesMock, "tosca.capabilities.Scalable": capabilitiesMock}; + fixture.componentInstance.populateReqOrCap("capabilities"); + expect(fixture.componentInstance.capabilities.length).toBe(4); + }); + + it('create requirements component when call loadReqOrCap with true', () => { + createDynamicComponentServiceMock.insertComponentDynamically.mockImplementation(() => { return {instance: {requirements: requirementMock}}}); + fixture.componentInstance.requirements = requirementMock; + fixture.componentInstance.loadReqOrCap(true); + expect(fixture.componentInstance.instanceRef.instance.requirements.length).toEqual(3); + }); + + it('create capabilities component when call loadReqOrCap with false', () => { + fixture.componentInstance.instanceRef = {instance: {requirements: null}}; + createDynamicComponentServiceMock.insertComponentDynamically.mockImplementation(() => { return {instance: {capabilities: capabilitiesMock}}}); + fixture.componentInstance.capabilities = capabilitiesMock; + fixture.componentInstance.requirementsUI = filterRequirmentsMock; + let event = { + target : { + value : 'root' + } + } + fixture.componentInstance.updateFilter(event); + expect(fixture.componentInstance.instanceRef.instance.requirements.length).toBe(1); + }); + + it('should filter 1 capabilities when searching and call updateFilter function and instanceRef is capabilities component', () => { + fixture.componentInstance.instanceRef = {instance: {capabilities: null}}; + fixture.componentInstance.capabilities = capabilitiesMock; + fixture.componentInstance.selectTabName = 'CAPABILITIES'; + let event = { + target : { + value : '1source' + } + } + fixture.componentInstance.updateFilter(event); + expect(fixture.componentInstance.instanceRef.instance.capabilities[0].type).toBe("tosca.capabilities.Node"); + }); +}); diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.ts new file mode 100644 index 0000000000..69999bfb86 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component.ts @@ -0,0 +1,229 @@ +import { Component, ComponentRef, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; +import * as _ from 'lodash'; +import { SdcUiServices } from 'onap-ui-angular'; +import { Capability, CapabilityUI } from '../../../../models/capability'; +import { Requirement, RequirementUI } from '../../../../models/requirement'; +import { TopologyTemplateService } from '../../../services/component-services/topology-template.service'; +import { ComponentGenericResponse } from '../../../services/responses/component-generic-response'; +import { WorkspaceService } from '../workspace.service'; +import { CapabilitiesComponent } from './capabilities/capabilities.component'; +import { RequirmentsComponent } from './requirements/requirments.components'; +import {ReqAndCapabilitiesService} from "./req-and-capabilities.service"; +import {CapabilitiesEditorComponent} from "./capabilities/capabilityEditor/capabilities-editor.component"; +import {ModalComponent} from "onap-ui-angular/dist/modals/modal.component"; +import {EventListenerService} from "../../../../services/event-listener-service"; +import {RequirementsEditorComponent} from "./requirements/requirementEditor/requirements-editor.component"; + +@Component({ + selector: 'req-and-capabilities', + templateUrl: './req-and-capabilities.component.html', + styleUrls: ['./req-and-capabilities.component.less'] +}) +export class ReqAndCapabilitiesComponent implements OnInit { + + @ViewChild('requirmentsContainer', { read: ViewContainerRef }) requirmentsContainer: ViewContainerRef; + @ViewChild('capabilitiesContainer', { read: ViewContainerRef }) capabilitiesContainer: ViewContainerRef; + private requirements: Requirement[] = []; + private requirementsUI: RequirementUI[] = []; + private capabilities: Capability[] = []; + private selectTabName: string = 'REQUIREMENTS'; + private notEmptyTable: boolean = true; + private instanceRef: ComponentRef; + private customModalInstance: ModalComponent; + readonly INPUTS_FOR_CAPABILITIES: string = 'INPUTS_FOR_CAPABILITIES'; + readonly INPUTS_FOR_REQUIREMENTS: string = 'INPUTS_FOR_REQUIREMENTS'; + + constructor(private workspaceService: WorkspaceService, + private loaderService: SdcUiServices.LoaderService, + private topologyTemplateService: TopologyTemplateService, + private createDynamicComponentService: SdcUiServices.CreateDynamicComponentService, + private reqAndCapabilitiesService : ReqAndCapabilitiesService, + private modalService: SdcUiServices.ModalService, + private eventListenerService: EventListenerService) { + } + + ngOnInit(): void { + this.initCapabilitiesAndRequirements(); + + this.eventListenerService.registerObserverCallback('CAPABILITIES_UPDATED', () => { + this.loadReqOrCap(); + }); + + this.eventListenerService.registerObserverCallback('REQUIREMENTS_UPDATED', () => { + this.loadReqOrCap(); + }); + } + + + + private initCapabilitiesAndRequirements(): void { + if (!this.workspaceService.metadata.capabilities || !this.workspaceService.metadata.requirements) { + this.loaderService.activate(); + this.topologyTemplateService.getRequirementsAndCapabilitiesWithProperties + (this.workspaceService.metadata.componentType, this.workspaceService.metadata.uniqueId) + .subscribe((response: ComponentGenericResponse) => { + this.workspaceService.metadata.capabilities = response.capabilities; + this.workspaceService.metadata.requirements = response.requirements; + this.initReqOrCap(); + this.loaderService.deactivate(); + }, (error) => { + this.loaderService.deactivate(); + }); + } else { + this.initReqOrCap(); + } + } + + private initReqOrCap() { + this.populateReqOrCap('requirements'); + this.extendRequirementsToRequiremnetsUI(this.requirements); + this.populateReqOrCap('capabilities'); + this.loadReqOrCap(); + } + + private populateReqOrCap(instanceName: string) { + _.forEach(this.workspaceService.metadata[instanceName], (concatArray: any[], name) => { + this[instanceName] = this[instanceName].concat(concatArray); + }); + } + + private updateFilter(event) { + const val = event.target.value.toLowerCase(); + if (this.selectTabName === 'REQUIREMENTS') { + this.instanceRef.instance.requirements = this.requirementsUI.filter((req: Requirement) => { + return !val || this.filterRequirments(req, val); + }); + } else { + this.instanceRef.instance.capabilities = this.capabilities.filter((cap: Capability) => { + return !val || this.filterCapabilities(cap, val); + }); + } + + } + + private selectTab($event) { + this.selectTabName = $event.title.contains('Requirement') ? 'REQUIREMENTS' : 'CATPABILITIES'; + this.loadReqOrCap(); + } + + private async loadReqOrCap() { + if (this.instanceRef) { + this.instanceRef.destroy(); + } + + if (this.selectTabName === 'REQUIREMENTS') { + this.notEmptyTable = this.requirementsUI.length !== 0; + this.instanceRef = this.createDynamicComponentService. + insertComponentDynamically(RequirmentsComponent, {requirements: this.requirementsUI}, this.requirmentsContainer); + // TODO - Keep the initInputs, so it will be called only for the first time - no need to wait to thse API's every time that a user switches tab + await this.reqAndCapabilitiesService.initInputs(this.INPUTS_FOR_REQUIREMENTS); + } else { + this.notEmptyTable = this.capabilities.length !== 0; + this.instanceRef = this.createDynamicComponentService. + insertComponentDynamically(CapabilitiesComponent, {capabilities: this.capabilities}, this.capabilitiesContainer); + // TODO - Keep the initInputs, so it will be called only for the first time - no need to wait to thse API's every time that a user switches tab + await this.reqAndCapabilitiesService.initInputs(this.INPUTS_FOR_CAPABILITIES); + } + } + + private filterCapabilities(capability: Capability, val: string): boolean { + return _.includes([capability.name, capability.description, capability.validSourceTypes.join(), + capability.minOccurrences, capability.maxOccurrences].join('').toLowerCase(), val) || + (capability.type && capability.type.replace('tosca.capabilities.', '').toLowerCase().indexOf(val) !== -1); + } + + private filterRequirments(requirement: Requirement, val: string): boolean { + return _.includes([requirement.name, requirement.minOccurrences, requirement.maxOccurrences].join('').toLowerCase(), val) || + (requirement.capability && requirement.capability.substring('tosca.capabilities.'.length).toLowerCase().indexOf(val) !== -1) || + (requirement.node && requirement.node.substring('tosca.node.'.length).toLowerCase().indexOf(val) !== -1) || + (requirement.relationship && requirement.relationship.substring('tosca.relationship.'.length) + .toLowerCase().indexOf(val) !== -1); + } + + private addCapability() { + let modalConfig = { + size: 'md', + title: 'Add Capability', + type: 'custom', + buttons: [ + { + id: 'saveButton', + text: ('Create'), + size: "'x-small'", + callback: () => this.createCapability(), + closeModal: true + }, + {text: "Cancel", size: "'x-small'", closeModal: true}] + }; + let modalInputs = { + capabilityTypesList: this.reqAndCapabilitiesService.getCapabilityTypesList(), + }; + + this.customModalInstance = this.modalService.openCustomModal(modalConfig, CapabilitiesEditorComponent, {input: modalInputs}); + this.customModalInstance.innerModalContent.instance. + onValidationChange.subscribe((isValid) => this.customModalInstance.getButtonById('saveButton').disabled = !isValid); + } + + private createCapability() { + const capability = this.customModalInstance.innerModalContent.instance.capabilityData; + this.loaderService.activate(); + if (!capability.uniqueId) { + this.topologyTemplateService.createCapability(this.workspaceService.metadata.getTypeUrl(), this.workspaceService.metadata.uniqueId, capability).subscribe((result) => { + this.capabilities.unshift(new CapabilityUI(result[0], this.workspaceService.metadata.uniqueId)); + this.loadReqOrCap(); + this.loaderService.deactivate(); + }, () => { + this.loaderService.deactivate(); + }); + } + } + + private addRequiremnet () { + let modalConfig = { + size: 'md', + title: 'Add Requirement', + type: 'custom', + buttons: [ + { + id: 'saveButton', + text: ('Create'), + size: "'x-small'", + callback: () => this.createRequirement(), + closeModal: true + }, + {text: "Cancel", size: "'x-small'", closeModal: true}] + }; + let modalInputs = { + // requirement: req, + relationshipTypesList: this.reqAndCapabilitiesService.getRelationsShipeTypeList(), + nodeTypesList: this.reqAndCapabilitiesService.getNodeTypesList(), + capabilityTypesList: this.reqAndCapabilitiesService.getCapabilityTypesList(), + // isReadonly: this.$scope.isViewMode() || !this.$scope.isDesigner(), + }; + + this.customModalInstance = this.modalService.openCustomModal(modalConfig, RequirementsEditorComponent, {input: modalInputs}); + this.customModalInstance.innerModalContent.instance. + onValidationChange.subscribe((isValid) => this.customModalInstance.getButtonById('saveButton').disabled = !isValid); + } + + + private createRequirement() { + const requirement = this.customModalInstance.innerModalContent.instance.requirementData; + this.loaderService.activate(); + if (!requirement.uniqueId) { + this.topologyTemplateService.createRequirement(this.workspaceService.metadata.getTypeUrl(), this.workspaceService.metadata.uniqueId, requirement).subscribe(result => { + this.requirementsUI.unshift(new RequirementUI(result[0], this.workspaceService.metadata.uniqueId)); + this.loadReqOrCap(); + this.loaderService.deactivate(); + }, () => { + this.loaderService.deactivate(); + }); + } + } + + private extendRequirementsToRequiremnetsUI(requirements: Requirement[]) { + this.requirements.map((requirement) => { + this.requirementsUI.push(new RequirementUI(requirement, this.workspaceService.metadata.uniqueId)); + }); + } +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.module.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.module.ts new file mode 100644 index 0000000000..aacb3a5bd1 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.module.ts @@ -0,0 +1,49 @@ +import {NgModule} from "@angular/core"; +import {SdcUiComponentsModule} from "onap-ui-angular"; + +import {NgxDatatableModule} from "@swimlane/ngx-datatable"; +import { ReqAndCapabilitiesComponent } from "./req-and-capabilities.component"; +import { CommonModule } from "@angular/common"; + +import {RequirmentsComponent } from "./requirements/requirments.components"; +import { CapabilitiesComponent } from "./capabilities/capabilities.component"; +import { CapabilitiesPropertiesComponent } from "./capabilities/capabilities-properties/capabilities-properties"; +import {ReqAndCapabilitiesService} from "./req-and-capabilities.service"; +import {RequirementsEditorComponent} from "./requirements/requirementEditor/requirements-editor.component"; +import {CapabilitiesEditorComponent} from "./capabilities/capabilityEditor/capabilities-editor.component"; +import {TranslateModule} from "../../../shared/translator/translate.module"; +import {ToscaTypesServiceNg2} from "../../../services/tosca-types.service"; + +@NgModule({ + declarations: [ + ReqAndCapabilitiesComponent, + CapabilitiesComponent, + RequirmentsComponent, + CapabilitiesPropertiesComponent, + RequirementsEditorComponent, + CapabilitiesEditorComponent + ], + imports: [ + CommonModule, + SdcUiComponentsModule, + NgxDatatableModule, + TranslateModule + ], + exports: [ + ReqAndCapabilitiesComponent, + CapabilitiesComponent, + RequirmentsComponent, + CapabilitiesPropertiesComponent + ], + entryComponents: [ + ReqAndCapabilitiesComponent, + CapabilitiesComponent, + RequirmentsComponent, + CapabilitiesPropertiesComponent, + RequirementsEditorComponent, + CapabilitiesEditorComponent + ], + providers: [ ReqAndCapabilitiesService] +}) +export class reqAndCapabilitiesModule { +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.service.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.service.ts new file mode 100644 index 0000000000..470aac75a6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/req-and-capabilities.service.ts @@ -0,0 +1,80 @@ +import { Injectable } from "@angular/core"; +import { TopologyTemplateService } from "../../../services/component-services/topology-template.service"; +import { Store } from "@ngxs/store"; +import { SdcUiServices } from "onap-ui-angular"; +import { CapabilityTypeModel } from "../../../../models/capability-types"; +import { RelationshipTypeModel } from "../../../../models/relationship-types"; +import { NodeTypeModel } from "../../../../models/node-types"; +import { WorkspaceService } from "../workspace.service"; +import { ToscaTypesServiceNg2 } from "../../../services/tosca-types.service"; + + + +@Injectable() +export class ReqAndCapabilitiesService { + + private capabilityTypesList: CapabilityTypeModel[]; + private relationshipTypesList: RelationshipTypeModel[]; + private nodeTypesList: NodeTypeModel[]; + private capabilitiesListUpdated: boolean = false; + private requirementsListUpdated: boolean = false; + private nodeTypeListUpdated: boolean = false; + + readonly INPUTS_FOR_REQUIREMENTS: string = 'INPUTS_FOR_REQUIREMENTS'; + readonly INPUTS_FOR_CAPABILITIES: string = 'INPUTS_FOR_CAPABILITIES'; + + constructor( + private workspaceService: WorkspaceService, + private modalService: SdcUiServices.ModalService, + private loaderService: SdcUiServices.LoaderService, + private topologyTemplateService: TopologyTemplateService, + private store: Store, + private toscaTypesServiceNg2: ToscaTypesServiceNg2){} + + public isViewOnly = (): boolean => { + return this.store.selectSnapshot((state) => state.workspace.isViewOnly); + } + + public isDesigner = (): boolean => { + return this.store.selectSnapshot((state) => state.workspace.isDesigner); + } + + public async initInputs(initInputsFor: string) { + + if (!this.capabilitiesListUpdated){ + // -- COMMON for both -- + this.capabilityTypesList = []; + let capabilityTypesResult = await this.toscaTypesServiceNg2.fetchCapabilityTypes(); + Object.keys(capabilityTypesResult).forEach(key => {this.capabilityTypesList.push(capabilityTypesResult[key])}) + this.capabilitiesListUpdated = true; + } + + if (initInputsFor === 'INPUTS_FOR_REQUIREMENTS') { + if (!this.requirementsListUpdated){ + this.relationshipTypesList = []; + let relationshipTypesResult = await this.toscaTypesServiceNg2.fetchRelationshipTypes(); + Object.keys(relationshipTypesResult).forEach(key => {this.relationshipTypesList.push(relationshipTypesResult[key])}); + this.requirementsListUpdated = true; + } + + if (!this.nodeTypeListUpdated){ + this.nodeTypesList = []; + let nodeTypesResult = await this.toscaTypesServiceNg2.fetchNodeTypes(); + Object.keys(nodeTypesResult).forEach(key => {this.nodeTypesList.push(nodeTypesResult[key])}) + this.nodeTypeListUpdated = true; + } + } + } + + getCapabilityTypesList() { + return this.capabilityTypesList; + } + + getRelationsShipeTypeList() { + return this.relationshipTypesList; + } + + getNodeTypesList() { + return this.nodeTypesList; + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.component.html b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.component.html new file mode 100644 index 0000000000..330680d3ed --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.component.html @@ -0,0 +1,91 @@ +
+
+
+
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + + +
+ +
+
+
+
+
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.component.less b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.component.less new file mode 100644 index 0000000000..6e50eb79f5 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.component.less @@ -0,0 +1,35 @@ +@import '../../../../../../../assets/styles/variables.less'; + +.requirement-editor { + .i-sdc-form-content-requirement-content { + padding: 10px 25px; + + .group-with-border { + margin: 25px 0; + padding: 15px 0; + border-top: 1px solid @tlv_color_u; + border-bottom: 1px solid @tlv_color_u; + .content-row:not(:last-of-type) { + padding-bottom: 13px; + } + } + + .occurrences-label { + font-family: @font-opensans-bold; + margin-bottom: 19px; + } + .occurrences-section, /deep/ .max-occurrences-value { + display: flex; + .min-occurrences-value { + padding-right: 30px; + } + .unbounded-value { + padding-top: 7px; + padding-right: 20px; + .sdc-checkbox__label { + text-transform: capitalize; + } + } + } + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.component.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.component.ts new file mode 100644 index 0000000000..2c5c96f3da --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.component.ts @@ -0,0 +1,90 @@ +import {Component} from '@angular/core'; +import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; +import {Requirement, RelationshipTypeModel, NodeTypeModel, CapabilityTypeModel} from 'app/models'; +import {TranslateService} from 'app/ng2/shared/translator/translate.service'; +import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component"; +import {Subject} from "rxjs"; + +@Component({ + selector: 'requirements-editor', + templateUrl: 'requirements-editor.component.html', + styleUrls: ['requirements-editor.component.less'], + providers: [ServiceServiceNg2, TranslateService] +}) + +export class RequirementsEditorComponent { + + input: { + requirement: Requirement, + relationshipTypesList: Array; + nodeTypesList: Array; + capabilityTypesList: Array; + isReadonly: boolean; + }; + requirementData: Requirement; + capabilityTypesMappedList: Array; + relationshipTypesMappedList: Array; + nodeTypesMappedList: Array; + isUnboundedChecked: boolean; + isReadonly: boolean; + translatedUnboundTxt: string; + + public onValidationChange: Subject = new Subject(); + + constructor(private translateService: TranslateService) { + } + + ngOnInit() { + this.requirementData = new Requirement(this.input.requirement); + this.requirementData.minOccurrences = this.requirementData.minOccurrences || 0; + this.translatedUnboundTxt = ''; + this.capabilityTypesMappedList = _.map(this.input.capabilityTypesList, capType => new DropdownValue(capType.toscaPresentation.type, capType.toscaPresentation.type)); + this.relationshipTypesMappedList = _.map(this.input.relationshipTypesList, rType => new DropdownValue(rType.toscaPresentation.type, rType.toscaPresentation.type)); + this.nodeTypesMappedList = _.map(this.input.nodeTypesList, nodeType => { + return new DropdownValue( + nodeType.componentMetadataDefinition.componentMetadataDataDefinition.toscaResourceName, + nodeType.componentMetadataDefinition.componentMetadataDataDefinition.toscaResourceName) + }); + this.translateService.languageChangedObservable.subscribe(lang => { + this.translatedUnboundTxt = this.translateService.translate('REQ_CAP_OCCURRENCES_UNBOUNDED'); + this.requirementData.maxOccurrences = this.requirementData.maxOccurrences || this.translatedUnboundTxt; + this.isUnboundedChecked = this.requirementData.maxOccurrences === this.translatedUnboundTxt; + }); + this.isReadonly = this.input.isReadonly; + this.validityChanged(); + } + + onUnboundedChanged() { + this.isUnboundedChecked = !this.isUnboundedChecked; + this.requirementData.maxOccurrences = this.isUnboundedChecked ? this.translatedUnboundTxt : null; + this.validityChanged(); + } + + onCapabilityChanged(selectedCapability: DropdownValue) { + this.requirementData.capability = selectedCapability && selectedCapability.value; + this.validityChanged(); + } + + onNodeChanged(selectedNode: DropdownValue) { + this.requirementData.node = selectedNode && selectedNode.value; + } + + onRelationshipChanged(selectedRelationship: DropdownValue) { + this.requirementData.relationship = selectedRelationship && selectedRelationship.value; + } + + checkFormValidForSubmit() { + return this.requirementData.name && this.requirementData.name.length && + this.requirementData.capability && this.requirementData.capability.length && !_.isEqual(this.requirementData.minOccurrences, "") && this.requirementData.minOccurrences >= 0 && + ( + this.isUnboundedChecked || + (this.requirementData.maxOccurrences && (this.requirementData.minOccurrences <= parseInt(this.requirementData.maxOccurrences))) + ); + } + + validityChanged = () => { + let validState = this.checkFormValidForSubmit(); + this.onValidationChange.next(validState); + } + +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.module.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.module.ts new file mode 100644 index 0000000000..b1d8db54aa --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirementEditor/requirements-editor.module.ts @@ -0,0 +1,28 @@ +import {NgModule} from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {RequirementsEditorComponent} from "./requirements-editor.component"; +import {FormsModule} from "@angular/forms"; +// import {FormElementsModule} from "../../../components/ui/form-components/form-elements.module"; +import {TranslateModule} from 'app/ng2/shared/translator/translate.module'; +import {SdcUiComponentsModule} from "onap-ui-angular/"; +import {FormElementsModule} from 'app/ng2/components/ui/form-components/form-elements.module'; +// import {SdcUiComponentsModule} from "sdc-ui/lib/angular/index"; + +@NgModule({ + declarations: [ + RequirementsEditorComponent + ], + imports: [CommonModule, + FormsModule, + FormElementsModule, + TranslateModule, + SdcUiComponentsModule + ], + exports: [], + entryComponents: [ + RequirementsEditorComponent + ], + providers: [] +}) +export class RequirementsEditorModule { +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirements.component.less b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirements.component.less new file mode 100644 index 0000000000..19f1c9b55a --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirements.component.less @@ -0,0 +1,4 @@ +/deep/ .importedFromFile { + background-color: #f8f8f8; + color: #959595; + } \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirments.components.html b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirments.components.html new file mode 100644 index 0000000000..7606ed189a --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirments.components.html @@ -0,0 +1,38 @@ +
+ + + + {{row.name}} + + + + + {{row.capability ? row.capability.substring("tosca.capabilities.".length) : ''}} + + + + + {{row.node ? row.node.substring("tosca.nodes.".length) : ''}} + + + + + {{row.relationship ? row.relationship.substring("tosca.relationships.".length): ''}} + + + + + + + + {{row.minOccurrences}},{{row.maxOccurrences}} + + + +
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirments.components.ts b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirments.components.ts new file mode 100644 index 0000000000..b65489ce4e --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/req-and-capabilities/requirements/requirments.components.ts @@ -0,0 +1,103 @@ +import {Input, Component, OnInit} from "@angular/core"; +import {Requirement, RequirementUI} from "../../../../../models/requirement"; +import {RequirementsEditorComponent} from "./requirementEditor/requirements-editor.component"; +import {WorkspaceService} from "../../workspace.service"; +import {TopologyTemplateService} from "../../../../services/component-services/topology-template.service"; +import {ReqAndCapabilitiesService} from "../req-and-capabilities.service"; +import {EventListenerService} from "../../../../../services/event-listener-service"; +import {ModalComponent} from "onap-ui-angular/dist/modals/modal.component"; +import {SdcUiServices} from "onap-ui-angular"; +import sortedIndexBy = require("lodash/sortedIndexBy"); + +@Component({ + selector: 'requirments', + templateUrl: './requirments.components.html', + styleUrls: ['../../../../../../assets/styles/table-style.less', './requirements.component.less'] +}) + + + +export class RequirmentsComponent implements OnInit { + @Input() public requirements: Array; + private customModalInstance: ModalComponent; + + constructor( + private workspaceService: WorkspaceService, + private loaderService: SdcUiServices.LoaderService, + private topologyTemplateService: TopologyTemplateService, + private reqAndCapabilitiesService : ReqAndCapabilitiesService, + private modalService: SdcUiServices.ModalService, + private eventListenerService: EventListenerService) { + } + + + ngOnInit(): void { + let isCreatedManually: RequirementUI[] = []; + let isImportedFromFile: RequirementUI[] = []; + + isCreatedManually = this.requirements.filter((requirement) => requirement.isCreatedManually); + isImportedFromFile = this.requirements.filter((requirement) => !requirement.isCreatedManually); + + this.requirements = []; + + isCreatedManually.map((requirement) => this.requirements.push(requirement)); + isImportedFromFile.map((requirement) => this.requirements.push(requirement)); + + } + + + + editRequirement(req) { + + let modalConfig = { + size: 'md', + title: 'Update Requirement', + type: 'custom', + buttons: [ + { + id: 'saveButton', + text: ('Update'), + size: "'x-small'", + callback: () => this.updateRequirement(), + closeModal: true + }, + {text: "Cancel", size: "'x-small'", closeModal: true}] + }; + let modalInputs = { + requirement: req, + relationshipTypesList: this.reqAndCapabilitiesService.getRelationsShipeTypeList(), + nodeTypesList: this.reqAndCapabilitiesService.getNodeTypesList(), + capabilityTypesList: this.reqAndCapabilitiesService.getCapabilityTypesList(), + // isReadonly: this.$scope.isViewMode() || !this.$scope.isDesigner(), + }; + + this.customModalInstance = this.modalService.openCustomModal(modalConfig, RequirementsEditorComponent, {input: modalInputs}); + this.customModalInstance.innerModalContent.instance. + onValidationChange.subscribe((isValid) => this.customModalInstance.getButtonById('saveButton').disabled = !isValid); + + } + + private updateRequirement() { + const requirement = this.customModalInstance.innerModalContent.instance.requirementData; + this.loaderService.activate(); + if (requirement.uniqueId) { + this.topologyTemplateService.updateRequirement(this.workspaceService.metadata.getTypeUrl(), this.workspaceService.metadata.uniqueId, requirement).subscribe(result => { + let index = this.requirements.findIndex(req => result[0].uniqueId === req.uniqueId); + this.requirements[index] = new RequirementUI(result[0], this.workspaceService.metadata.uniqueId); + this.eventListenerService.notifyObservers('REQUIREMENTS_UPDATED'); + this.loaderService.deactivate(); + }, () => { + this.loaderService.deactivate(); + }); + } + } + + getRowClass(row) { + if (!row.isCreatedManually) { + return { + 'importedFromFile': true + }; + } + } + +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/__snapshots__/tosca-artifact-page.spec.ts.snap b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/__snapshots__/tosca-artifact-page.spec.ts.snap new file mode 100644 index 0000000000..14146d51d2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/__snapshots__/tosca-artifact-page.spec.ts.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`tosca artifacts page should match current snapshot of tosca artifact pages component 1`] = ` + +
+ +
+ + + + + + + + + +
+
+
+
+`; diff --git a/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.html b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.html new file mode 100644 index 0000000000..fece92ee37 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.html @@ -0,0 +1,50 @@ +
+ + + +
Label: {{row.artifactLabel}}
+
UUID: {{row.artifactUUID}}
+
Description: {{row.description}}
+
+
+ + +
+ + {{row.artifactDisplayName }} +
+
+
+ + + {{row.artifactType}} + + + + + {{ row.artifactVersion }} + + + + +
+ +
+
+
+
+
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.less b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.less new file mode 100644 index 0000000000..9c5dd47585 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.less @@ -0,0 +1,7 @@ +.tosca-artifact-page { + .download-artifact-button { + text-align: center; + padding-top: 4px; + + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.ts b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.ts new file mode 100644 index 0000000000..e74e5db668 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.component.ts @@ -0,0 +1,46 @@ +import {Component, OnInit, ViewChild} from "@angular/core"; +import {WorkspaceService} from "../workspace.service"; +import {SdcUiServices} from "onap-ui-angular"; +import {ArtifactModel} from "../../../../models"; +import {Select, Store} from "@ngxs/store"; +import {WorkspaceState} from "../../../store/states/workspace.state"; +import * as _ from "lodash"; +import {ArtifactGroupType, COMPONENT_FIELDS} from "../../../../utils"; +import {GetArtifactsByTypeAction} from "../../../store/actions/artifacts.action"; +import {Observable} from "rxjs/index"; +import {ArtifactsState} from "../../../store/states/artifacts.state"; +import {ArtifactType} from "../../../../utils/constants"; +import {map} from "rxjs/operators"; + +@Component({ + selector: 'tosca-artifact-page', + + templateUrl: './tosca-artifact-page.component.html', + styleUrls: ['./tosca-artifact-page.component.less', '../../../../../assets/styles/table-style.less'] +}) +export class ToscaArtifactPageComponent implements OnInit { + + @Select(WorkspaceState.isViewOnly) isViewOnly$: boolean; + @ViewChild('toscaArtifactsTable') table: any; + public toscaArtifacts$: Observable; + public componentId: string; + public componentType:string; + + constructor(private serviceLoader: SdcUiServices.LoaderService, private workspaceService: WorkspaceService, private store: Store) { + } + + + ngOnInit(): void { + this.componentId = this.workspaceService.metadata.uniqueId; + this.componentType = this.workspaceService.metadata.componentType; + + this.store.dispatch(new GetArtifactsByTypeAction({componentType:this.componentType, componentId:this.componentId, artifactType:ArtifactGroupType.TOSCA})); + this.toscaArtifacts$ = this.store.select(ArtifactsState.getArtifactsByType).pipe(map(filterFn => filterFn(ArtifactGroupType.TOSCA))); + } + + onActivate(event) { + if(event.type === 'click'){ + this.table.rowDetail.toggleExpandRow(event.row); + } + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.module.ts b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.module.ts new file mode 100644 index 0000000000..00c7b0b371 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.module.ts @@ -0,0 +1,28 @@ +import {CommonModule} from "@angular/common"; +import {NgModule} from "@angular/core"; +import {SdcUiComponentsModule} from "onap-ui-angular"; +import {GlobalPipesModule} from "../../../pipes/global-pipes.module"; +import {NgxDatatableModule} from "@swimlane/ngx-datatable"; +import {ToscaArtifactPageComponent} from "./tosca-artifact-page.component"; +import {UiElementsModule} from "../../../components/ui/ui-elements.module"; + +@NgModule({ + declarations: [ + ToscaArtifactPageComponent + ], + imports: [ + CommonModule, + SdcUiComponentsModule, + NgxDatatableModule, + UiElementsModule + ], + exports: [ + ToscaArtifactPageComponent + ], + entryComponents: [ + ToscaArtifactPageComponent + ], + +}) +export class ToscaArtifactPageModule { +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.spec.ts b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.spec.ts new file mode 100644 index 0000000000..af3558e15b --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/tosca-artifacts/tosca-artifact-page.spec.ts @@ -0,0 +1,71 @@ +import {async, ComponentFixture, TestBed} from "@angular/core/testing"; +import {NO_ERRORS_SCHEMA} from "@angular/core"; +import {ToscaArtifactPageComponent} from "./tosca-artifact-page.component"; +import {ConfigureFn, configureTests} from "../../../../../jest/test-config.helper"; +import {NgxDatatableModule} from "@swimlane/ngx-datatable"; +import {WorkspaceService} from "../workspace.service"; +import {SdcUiServices} from "onap-ui-angular"; +import {TopologyTemplateService} from "../../../services/component-services/topology-template.service"; +import {Observable} from "rxjs/Observable"; +import {ComponentMetadata} from "../../../../models/component-metadata"; +import 'rxjs/add/observable/of'; +import {NgxsModule, Store} from "@ngxs/store"; +import {ArtifactsState} from "../../../store/states/artifacts.state"; +import {toscaArtifactMock} from "../../../../../jest/mocks/artifacts-mock"; + +describe('tosca artifacts page', () => { + + let fixture: ComponentFixture; + let topologyTemplateServiceMock: Partial; + let workspaceServiceMock: Partial; + let loaderServiceMock: Partial; + let store: Store; + + + beforeEach( + async(() => { + + topologyTemplateServiceMock = { + getArtifactsByType: jest.fn().mockImplementation((componentType, id, artifactType) => Observable.of(toscaArtifactMock)) + }; + workspaceServiceMock = {metadata: {uniqueId: 'service_unique_id', componentType: 'SERVICE'}} + + loaderServiceMock = { + activate : jest.fn(), + deactivate: jest.fn() + } + const configure: ConfigureFn = testBed => { + testBed.configureTestingModule({ + declarations: [ToscaArtifactPageComponent], + imports: [NgxDatatableModule, NgxsModule.forRoot([ArtifactsState])], + schemas: [NO_ERRORS_SCHEMA], + providers: [ + {provide: WorkspaceService, useValue: workspaceServiceMock}, + {provide: TopologyTemplateService, useValue: topologyTemplateServiceMock}, + {provide: SdcUiServices.LoaderService, useValue: loaderServiceMock } + ], + }); + }; + + configureTests(configure).then(testBed => { + fixture = testBed.createComponent(ToscaArtifactPageComponent); + store = testBed.get(Store); + }); + }) + ); + + it('should match current snapshot of tosca artifact pages component', () => { + expect(fixture).toMatchSnapshot(); + }); + + it('should see exactly 2 tosca artifacts', () => { + fixture.componentInstance.ngOnInit(); + fixture.componentInstance.toscaArtifacts$.subscribe((artifacts)=> { + expect(artifacts.length).toEqual(2); + }) + store.selectOnce(state => state.artifacts.toscaArtifacts).subscribe(artifacts => { + expect(artifacts.length).toEqual(9); + }); + }) + +}); \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/workspace/workspace-ng1-bridge-service.ts b/catalog-ui/src/app/ng2/pages/workspace/workspace-ng1-bridge-service.ts new file mode 100644 index 0000000000..3d93b459a2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/workspace-ng1-bridge-service.ts @@ -0,0 +1,37 @@ +/** + * Created by ob0695 on 6/24/2018. + */ +/*- + * ============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========================================================= + */ +import {Store} from "@ngxs/store"; +import {Injectable} from "@angular/core"; +import {UpdateIsViewOnly} from "../../store/actions/workspace.action"; + +@Injectable() +export class WorkspaceNg1BridgeService { + + constructor(private store: Store) { + }; + + public updateIsViewOnly = (isViewOnly: boolean):void => { + this.store.dispatch(new UpdateIsViewOnly(isViewOnly)); + } + +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/workspace.component.ts b/catalog-ui/src/app/ng2/pages/workspace/workspace.component.ts new file mode 100644 index 0000000000..a209406a53 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/workspace.component.ts @@ -0,0 +1,3 @@ +/** + * Created by ob0695 on 6/11/2018. + */ diff --git a/catalog-ui/src/app/ng2/pages/workspace/workspace.module.ts b/catalog-ui/src/app/ng2/pages/workspace/workspace.module.ts new file mode 100644 index 0000000000..cb646379d2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/workspace.module.ts @@ -0,0 +1,50 @@ +/** + * Created by ob0695 on 6/4/2018. + */ +/** + * Created by ob0695 on 6/4/2018. + */ +import {NgModule} from "@angular/core"; +import {CompositionPageModule} from "../composition/composition-page.module"; + +import {NgxsModule} from "@ngxs/store"; +import {TopologyTemplateService} from "../../services/component-services/topology-template.service"; +import {WorkspaceState} from "../../store/states/workspace.state"; +import {WorkspaceService} from "./workspace.service"; +import {DeploymentPageModule} from "./deployment/deployment-page.module"; +import {ToscaArtifactPageModule} from "./tosca-artifacts/tosca-artifact-page.module"; +import {InformationArtifactPageModule} from "./information-artifact/information-artifact-page.module"; +import { reqAndCapabilitiesModule } from "./req-and-capabilities/req-and-capabilities.module"; +import {AttributesModule} from "./attributes/attributes.module"; +import {ArtifactsState} from "../../store/states/artifacts.state"; +import {InstanceArtifactsState} from "../../store/states/instance-artifacts.state"; +import {DeploymentArtifactsPageModule} from "./deployment-artifacts/deployment-artifacts-page.module"; +import { DistributionModule } from './disribution/distribution.module'; +import { ActivityLogModule } from './activity-log/activity-log.module'; + +@NgModule({ + declarations: [], + imports: [ + DeploymentPageModule, + CompositionPageModule, + AttributesModule, + reqAndCapabilitiesModule, + ToscaArtifactPageModule, + DeploymentArtifactsPageModule, + InformationArtifactPageModule, + DistributionModule, + ActivityLogModule, + NgxsModule.forFeature([WorkspaceState, ArtifactsState, InstanceArtifactsState]) + ], + + exports: [], + entryComponents: [], + providers: [TopologyTemplateService, WorkspaceService] +}) + +export class WorkspaceModule { + + constructor() { + + } +} diff --git a/catalog-ui/src/app/ng2/pages/workspace/workspace.service.ts b/catalog-ui/src/app/ng2/pages/workspace/workspace.service.ts new file mode 100644 index 0000000000..9f985016ec --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/workspace/workspace.service.ts @@ -0,0 +1,70 @@ +/** + * Created by ob0695 on 6/5/2018. + */ +/*- + * ============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 rc2122 on 5/23/2017. + */ +import { Injectable } from '@angular/core'; +import {WorkspaceMode, ComponentState, Role} from "../../../utils/constants"; +import {Component as TopologyTemplate, ComponentMetadata} from "app/models"; +import {CacheService} from "../../services/cache.service"; +import {IComponentMetadata} from "../../../models/component-metadata"; +import {ComponentType} from "../../../utils"; + +@Injectable() +export class WorkspaceService { + + public metadata:ComponentMetadata; + + constructor(private cacheService:CacheService) { + + } + + public setComponentMetadata = (metadata: ComponentMetadata) => { + this.metadata = metadata; + } + + public getMetadataType(): string { + switch (this.metadata.componentType) { + case ComponentType.SERVICE: + return ComponentType.SERVICE; + default: + return this.metadata.resourceType; + } + } + + public getComponentMode = (component:TopologyTemplate):WorkspaceMode => {//return if is edit or view for resource or service + let mode = WorkspaceMode.VIEW; + + let user = this.cacheService.get("user"); + if (component.lifecycleState === ComponentState.NOT_CERTIFIED_CHECKOUT && + component.lastUpdaterUserId === user.userId) { + if ((component.isService() || component.isResource()) && user.role == Role.DESIGNER) { + mode = WorkspaceMode.EDIT; + } + } + return mode; + } +} + + \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pipes/entity-filter.pipe.ts b/catalog-ui/src/app/ng2/pipes/entity-filter.pipe.ts new file mode 100644 index 0000000000..af107ed006 --- /dev/null +++ b/catalog-ui/src/app/ng2/pipes/entity-filter.pipe.ts @@ -0,0 +1,151 @@ +/*- + * ============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========================================================= + */ + +import {Pipe, PipeTransform} from "@angular/core"; +import {Component, Resource} from "app/models"; +import {ComponentType} from "app/utils/constants"; + +export interface ISearchFilter { + [key:string]: string; +} + +export interface IEntityFilterObject { + // Types + selectedComponentTypes?:Array; + selectedResourceSubTypes?:Array; + // Categories + selectedCategoriesModel?:Array; + // Statuses + selectedStatuses?:Array; + // distributed + distributed?:Array; + // search + search?:ISearchFilter; + +} + +@Pipe({name: 'entity-filter'}) +export class EntityFilterPipe implements PipeTransform{ + constructor() { + } + + public static transform(components:Array, filter:IEntityFilterObject) { + let filteredComponents:Array = components; + + // filter by type + // -------------------------------------------------------------------------- + if ((filter.selectedComponentTypes && filter.selectedComponentTypes.length > 0) || (filter.selectedResourceSubTypes && filter.selectedResourceSubTypes.length > 0)) { + let filteredTypes = []; + angular.forEach(components, (component:Component):void => { + // Filter by component type + let typeLower:string = component.componentType.toLowerCase(); + let typeFirstCapital:string = typeLower.charAt(0).toUpperCase() + typeLower.slice(1); + if (filter.selectedComponentTypes.indexOf(typeFirstCapital) !== -1) { + filteredTypes.push(component); + } + + // Filter by resource sub type, only in case the resource checkbox was not selected (because in this case we already added all the components in above section). + if (component.isResource() && filter.selectedComponentTypes.indexOf("Resource") === -1 && filter.selectedResourceSubTypes.length > 0) { + //filteredComponents.pop(); // Remove the last inserted component. + let resource:Resource = component; + if (filter.selectedResourceSubTypes.indexOf(resource.getComponentSubType()) !== -1) { + filteredTypes.push(component); + } + } + }); + filteredComponents = filteredTypes; + } + + // filter by categories & subcategories & groupings + // -------------------------------------------------------------------------- + if (filter.selectedCategoriesModel && filter.selectedCategoriesModel.length > 0) { + let filteredCategories = []; + angular.forEach(filteredComponents, (component:Component):void => { + let componentCategory = component.categoryNormalizedName + + ((component.subCategoryNormalizedName) ? '.' + component.subCategoryNormalizedName : ''); + if (component.componentType === ComponentType.RESOURCE) { + componentCategory = 'resourceNewCategory.' + componentCategory; + } else if (component.componentType === ComponentType.SERVICE) { + componentCategory = 'serviceNewCategory.' + componentCategory; + } + if (filter.selectedCategoriesModel.indexOf(componentCategory) !== -1) { + filteredCategories.push(component); + } + }); + filteredComponents = filteredCategories; + } + + // filter by statuses + // -------------------------------------------------------------------------- + if (filter.selectedStatuses && filter.selectedStatuses.length > 0) { + + let filteredStatuses = []; + angular.forEach(filteredComponents, (component:Component):void => { + if (filter.selectedStatuses.indexOf(component.lifecycleState) > -1) { + filteredStatuses.push(component); + } + //if status DISTRIBUTED && CERTIFIED are selected the component will added in CERTIFIED status , not need to add twice + if (filter.selectedStatuses.indexOf('DISTRIBUTED') > -1 && !(filter.selectedStatuses.indexOf('CERTIFIED') > -1)) { + if (component.distributionStatus && component.distributionStatus.indexOf('DISTRIBUTED') > -1 && component.lifecycleState.indexOf('CERTIFIED') > -1) { + filteredStatuses.push(component); + } + } + }); + filteredComponents = filteredStatuses; + } + + // filter by statuses and distributed + // -------------------------------------------------------------------------- + if (filter.distributed != undefined && filter.distributed.length > 0) { + let filterDistributed:Array = filter.distributed; + let filteredDistributed = []; + angular.forEach(filteredComponents, (entity) => { + filterDistributed.forEach((distribute) => { + let distributeItem = distribute.split(','); + distributeItem.forEach((item) => { + if (item !== undefined && entity.distributionStatus === item) { + filteredDistributed.push(entity); + } + }) + }); + }); + filteredComponents = filteredDistributed; + } + + // filter by search + // -------------------------------------------------------------------------- + if (filter.search != undefined) { + Object.keys(filter.search).forEach((searchKey) => { + let searchVal = filter.search[searchKey]; + if (searchVal) { + searchVal = searchVal.toLowerCase(); + filteredComponents = filteredComponents.filter((component:Component) => + component[searchKey].toLowerCase().indexOf(searchVal) !== -1); + } + }); + } + + return filteredComponents; + } + + public transform(components:Array, filter:IEntityFilterObject) { + return EntityFilterPipe.transform(components, filter); + } +} diff --git a/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts b/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts index c44d71b30d..66f9518a47 100644 --- a/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts +++ b/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts @@ -26,7 +26,10 @@ import {GroupByPipe} from "./groupBy.pipe"; import {ResourceNamePipe} from "./resource-name.pipe"; import {NgModule} from "@angular/core"; import {SafeUrlSanitizerPipe} from "./safeUrlSanitizer.pipe"; -import {OrderByPipe} from "./orderBy.pipe"; +import {EntityFilterPipe} from "./entity-filter.pipe"; +import {KeyValuePipe} from "./key-value.pipe"; +import {PropertiesOrderByPipe} from "./properties-order-by.pipe"; +import {OrderByPipe} from "./order-by.pipe"; @NgModule({ declarations: [ @@ -36,6 +39,9 @@ import {OrderByPipe} from "./orderBy.pipe"; SafeUrlSanitizerPipe, SearchFilterPipe, ResourceNamePipe, + EntityFilterPipe, + KeyValuePipe, + PropertiesOrderByPipe, OrderByPipe ], exports: [ @@ -45,7 +51,20 @@ import {OrderByPipe} from "./orderBy.pipe"; SafeUrlSanitizerPipe, SearchFilterPipe, ResourceNamePipe, - OrderByPipe + EntityFilterPipe, + PropertiesOrderByPipe, + OrderByPipe, + KeyValuePipe + ], + providers: [ + ContentAfterLastDotPipe, + GroupByPipe, + KeysPipe, + SafeUrlSanitizerPipe, + SearchFilterPipe, + ResourceNamePipe, + EntityFilterPipe, + KeyValuePipe ] }) diff --git a/catalog-ui/src/app/ng2/pipes/groupBy.pipe.ts b/catalog-ui/src/app/ng2/pipes/groupBy.pipe.ts index 90dce23352..19811f2f08 100644 --- a/catalog-ui/src/app/ng2/pipes/groupBy.pipe.ts +++ b/catalog-ui/src/app/ng2/pipes/groupBy.pipe.ts @@ -26,6 +26,9 @@ import {Pipe, PipeTransform} from '@angular/core'; @Pipe({name: 'groupBy'}) export class GroupByPipe implements PipeTransform { transform(value: Array, field: string): Array { + if(!value) { + return null; + } const groupedObj = value.reduce((prev, cur)=> { if(!prev[cur[field]]) { prev[cur[field]] = [cur]; diff --git a/catalog-ui/src/app/ng2/pipes/key-value.pipe.ts b/catalog-ui/src/app/ng2/pipes/key-value.pipe.ts new file mode 100644 index 0000000000..4adb0b12f7 --- /dev/null +++ b/catalog-ui/src/app/ng2/pipes/key-value.pipe.ts @@ -0,0 +1,41 @@ +/** + * Created by ob0695 on 7/3/2018. + */ +/*- + * ============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 rc2122 on 5/17/2017. + */ +import {Pipe, PipeTransform} from '@angular/core'; + +@Pipe({name: 'keyValue'}) +export class KeyValuePipe implements PipeTransform { + transform(value, field: string): Array { + if(!value) { + return null; + } + let keyValueObject = []; + for (let key in value) { + keyValueObject.push({key:key, value: value[key]}); + } + return keyValueObject; + } +} diff --git a/catalog-ui/src/app/ng2/pipes/keys.pipe.ts b/catalog-ui/src/app/ng2/pipes/keys.pipe.ts index 349e9334f7..f8663aca2e 100644 --- a/catalog-ui/src/app/ng2/pipes/keys.pipe.ts +++ b/catalog-ui/src/app/ng2/pipes/keys.pipe.ts @@ -23,6 +23,9 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({name: 'keys'}) export class KeysPipe implements PipeTransform { transform(value, args:string[]) : any { + if(!value) { + return null; + } let keys = []; for (let key in value) { keys.push(key); diff --git a/catalog-ui/src/app/ng2/pipes/order-by.pipe.ts b/catalog-ui/src/app/ng2/pipes/order-by.pipe.ts new file mode 100644 index 0000000000..6dc5d47863 --- /dev/null +++ b/catalog-ui/src/app/ng2/pipes/order-by.pipe.ts @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 Huawei 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========================================================= + */ + +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({name: 'orderBy'}) +export class OrderByPipe implements PipeTransform { + transform(records: Array, args?: any): any { + if (!records || !args.path) { return records; } + let len = args.path.length; + return records.sort((itemIdx1, itemIdx2) => { + var i = 0; + while (i < len) { itemIdx1 = itemIdx1[args.path[i]]; itemIdx2 = itemIdx2[args.path[i]]; i++; } + // Order * (-1): We change our order + return itemIdx1 + "" > itemIdx2 + "" ? args.direction : args.direction * (- 1); + }) + }; +} diff --git a/catalog-ui/src/app/ng2/pipes/orderBy.pipe.ts b/catalog-ui/src/app/ng2/pipes/orderBy.pipe.ts deleted file mode 100644 index 4edbd60f60..0000000000 --- a/catalog-ui/src/app/ng2/pipes/orderBy.pipe.ts +++ /dev/null @@ -1,35 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 Huawei 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========================================================= - */ - -import { Pipe, PipeTransform } from '@angular/core'; - -@Pipe({ name: 'orderBy' }) -export class OrderByPipe implements PipeTransform { - transform(records: Array, args?: any): any { - if (!records || !args.path) return records; - let len = args.path.length; - return records.sort((itemIdx1, itemIdx2) => { - var i = 0; - while (i < len) { itemIdx1 = itemIdx1[args.path[i]]; itemIdx2 = itemIdx2[args.path[i]]; i++; } - // Order * (-1): We change our order - return itemIdx1 + "" > itemIdx2 + "" ? args.direction : args.direction * (- 1); - }) - }; -} diff --git a/catalog-ui/src/app/ng2/pipes/properties-order-by.pipe.ts b/catalog-ui/src/app/ng2/pipes/properties-order-by.pipe.ts new file mode 100644 index 0000000000..98debd28d9 --- /dev/null +++ b/catalog-ui/src/app/ng2/pipes/properties-order-by.pipe.ts @@ -0,0 +1,35 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 Huawei 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========================================================= + */ + +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ name: 'propertiesOrderBy' }) +export class PropertiesOrderByPipe implements PipeTransform { + transform(records: Array, args?: any): any { + if (!records || !args.path) return records; + let len = args.path.length; + return records.sort((itemIdx1, itemIdx2) => { + var i = 0; + while (i < len) { itemIdx1 = itemIdx1[args.path[i]]; itemIdx2 = itemIdx2[args.path[i]]; i++; } + // Order * (-1): We change our order + return itemIdx1 + "" > itemIdx2 + "" ? args.direction : args.direction * (- 1); + }) + }; +} diff --git a/catalog-ui/src/app/ng2/pipes/resource-name.pipe.ts b/catalog-ui/src/app/ng2/pipes/resource-name.pipe.ts index fdf9526375..0fdbbcaade 100644 --- a/catalog-ui/src/app/ng2/pipes/resource-name.pipe.ts +++ b/catalog-ui/src/app/ng2/pipes/resource-name.pipe.ts @@ -20,15 +20,20 @@ import { Pipe, PipeTransform } from '@angular/core'; +import * as _ from 'lodash'; @Pipe({name: 'resourceName'}) export class ResourceNamePipe implements PipeTransform { + + public static getDisplayName (value:string): string { + const newName:string = + _.last(value.split(/tosca\.nodes\..*network\..*relationships\..*org\.openecomp\..*resource\.nfv\..*nodes\.module\..*cp\..*vl\./)); + return (newName) ? newName : value; + } + transform(value) : any { if (value) { - //newName = _.last(newName.split('.')); - const newName:string = - _.last(value.split(/tosca\.nodes\..*network\..*relationships\..*org\.openecomp\..*resource\.nfv\..*nodes\.module\..*cp\..*vl\./)); - return (newName) ? newName : value; + return ResourceNamePipe.getDisplayName(value); } } } diff --git a/catalog-ui/src/app/ng2/services/activity-log.service.ts b/catalog-ui/src/app/ng2/services/activity-log.service.ts new file mode 100644 index 0000000000..deabbee0bd --- /dev/null +++ b/catalog-ui/src/app/ng2/services/activity-log.service.ts @@ -0,0 +1,48 @@ +/*- + * ============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========================================================= + */ + +import { HttpClient } from '@angular/common/http'; +import { Inject, Injectable} from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { Activity } from '../../models/activity'; +import { ServerTypeUrl } from '../../utils/constants'; +import { ISdcConfig, SdcConfigToken } from '../config/sdc-config.config'; +import { HttpHelperService } from './http-hepler.service'; + +@Injectable() +export class ActivityLogService { + url: string; + + constructor(private httpClient: HttpClient, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig) { + this.url = this.sdcConfig.api.root + this.sdcConfig.api.GET_activity_log; + } + + public getActivityLog(componentType: string, uid: string): Observable { + + // Compose URL: audit-records/services_or_resources/uid + const url = HttpHelperService.replaceUrlParams(this.url, { + type: ServerTypeUrl.toServerTypeUrl(componentType), + id: uid + }); + + return this.httpClient.get(url); + } + +} diff --git a/catalog-ui/src/app/ng2/services/archive.service.ts b/catalog-ui/src/app/ng2/services/archive.service.ts deleted file mode 100644 index 83f1c502c2..0000000000 --- a/catalog-ui/src/app/ng2/services/archive.service.ts +++ /dev/null @@ -1,67 +0,0 @@ -/*- - * ============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========================================================= - */ - -import { Injectable, Inject } from "@angular/core"; -import { Observable } from "rxjs/Observable"; -import { HttpService } from "./http.service"; -import { SdcConfigToken, ISdcConfig } from "../config/sdc-config.config"; -import { Component } from "../../models"; -import { ComponentFactory } from 'app/utils/component-factory'; - - -@Injectable() -export class ArchiveService { - protected baseUrl; - - constructor(private http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig, private componentFactory:ComponentFactory/*, @Inject(ComponentFactory) componentFactory */) { - this.baseUrl = sdcConfig.api.root ; - } - - public getArchiveCatalog() { - let archiveCatalogItems:Component[] = []; - let archiveCatalogResourceItems:Component[] = []; - let archiveCatalogServiceItems:Component[] = []; - - return this.http.get(this.baseUrl + '/v1/catalog/archive/', {}).map(res => { - let archiveCatalogObject = res.json(); - if (archiveCatalogObject.resources) archiveCatalogResourceItems = this.getResourceItems(archiveCatalogObject.resources); - if (archiveCatalogObject.services) archiveCatalogServiceItems = this.getServiceItems(archiveCatalogObject.services); - archiveCatalogItems = [].concat(archiveCatalogResourceItems, archiveCatalogServiceItems); - - return archiveCatalogItems; - }); - } - - - private getResourceItems(resources){ - let resourceItems = resources.map((resource)=>{ - return this.componentFactory.createResource(resource) - }) - return resourceItems; - } - - private getServiceItems(services){ - let serviceItems = services.map((service)=>{ - return this.componentFactory.createService(service) - }) - return serviceItems; - } - -} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/authentication.service.ts b/catalog-ui/src/app/ng2/services/authentication.service.ts index 1c6502dd0d..52ca643833 100644 --- a/catalog-ui/src/app/ng2/services/authentication.service.ts +++ b/catalog-ui/src/app/ng2/services/authentication.service.ts @@ -20,38 +20,44 @@ import {Injectable, Inject} from '@angular/core'; import {IAppConfigurtaion, ICookie} from "../../models/app-config"; -import {Response, Headers, RequestOptions, Http} from '@angular/http'; import {Cookie2Service} from "./cookie.service"; import { Observable } from 'rxjs/Observable'; import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; +import { IUserProperties } from "app/models"; +import { CacheService } from "app/ng2/services/cache.service"; +import { HttpClient, HttpHeaders } from '@angular/common/http'; @Injectable() export class AuthenticationService { - constructor(private cookieService:Cookie2Service, private http: Http, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig) { + private _loggedinUser:IUserProperties; + + constructor(private cookieService:Cookie2Service, private http: HttpClient, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig, private cacheService: CacheService) { this.cookieService = cookieService; - this.http = http; } - private getAuthHeaders():any { + private getAuthHeaders():HttpHeaders { let cookie:ICookie = this.sdcConfig.cookie; - let authHeaders:any = {}; - authHeaders[cookie.userFirstName] = this.cookieService.getFirstName(); - authHeaders[cookie.userLastName] = this.cookieService.getLastName(); - authHeaders[cookie.userEmail] = this.cookieService.getEmail(); - authHeaders[cookie.userIdSuffix] = this.cookieService.getUserId(); + let authHeaders: HttpHeaders = new HttpHeaders(); + authHeaders = authHeaders.set(cookie.userFirstName, this.cookieService.getFirstName()) + .set(cookie.userLastName, this.cookieService.getLastName()) + .set(cookie.userEmail, this.cookieService.getEmail()) + .set(cookie.userIdSuffix, this.cookieService.getUserId()) return authHeaders; } - public authenticate(): Observable { - let options = new RequestOptions({ - headers: new Headers(this.getAuthHeaders()) - }); - + public authenticate(): Observable { let authUrl = this.sdcConfig.api.root + this.sdcConfig.api.GET_user_authorize; - return this.http - .get(authUrl, options) - .map((res: Response) => res.json()); + return this.http.get(authUrl, {headers: this.getAuthHeaders()}); + } + + public getLoggedinUser():IUserProperties { + return this._loggedinUser; } + public setLoggedinUser(loggedinUser:IUserProperties) { + this._loggedinUser = loggedinUser; + this.cacheService.set('user', loggedinUser); + }; + } diff --git a/catalog-ui/src/app/ng2/services/cache.service.ts b/catalog-ui/src/app/ng2/services/cache.service.ts new file mode 100644 index 0000000000..e876ec1098 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/cache.service.ts @@ -0,0 +1,49 @@ +/*- + * ============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========================================================= + */ + +import {Injectable} from "@angular/core"; +import {Dictionary} from "app/utils"; + +@Injectable() +export class CacheService { + private storage:Dictionary; + + constructor() { + this.storage = new Dictionary(); + }; + + public get(key:string): any { + return this.storage.getValue(key); + } + + public set(key:string, value:any): void { + this.storage.setValue(key, value); + } + + public remove(key:string): void { + if (this.storage.containsKey(key)) { + this.storage.remove(key); + } + } + + public contains(key:string): boolean { + return this.storage.containsKey(key); + } +} diff --git a/catalog-ui/src/app/ng2/services/catalog.service.ts b/catalog-ui/src/app/ng2/services/catalog.service.ts new file mode 100644 index 0000000000..bbdfa1b420 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/catalog.service.ts @@ -0,0 +1,85 @@ +/*- + * ============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========================================================= + */ + +import { Injectable, Inject } from "@angular/core"; +import { Observable } from "rxjs/Observable"; +import { SdcConfigToken, ISdcConfig } from "../config/sdc-config.config"; +import { Component, IApi, IComponentsArray } from "app/models"; +import { ComponentFactory } from 'app/utils/component-factory'; +import {ResourceType} from "../../utils/constants"; +import {SharingService} from "./sharing.service"; +import { HttpClient, HttpParams } from "@angular/common/http"; + +@Injectable() +export class CatalogService { + protected api:IApi; + protected baseUrl:string; + protected baseMicroServiceUrl:string; + + constructor(private http: HttpClient, + @Inject(SdcConfigToken) sdcConfig:ISdcConfig, + private componentFactory:ComponentFactory, + private sharingService:SharingService) { + this.api = sdcConfig.api; + this.baseUrl = sdcConfig.api.root ; + this.baseMicroServiceUrl = sdcConfig.api.uicache_root; + } + + public getCatalog(): Observable> { + let searchParams = new HttpParams(); + searchParams = searchParams.append('excludeTypes', ResourceType.VFCMT).append('excludeTypes', ResourceType.CONFIGURATION); + return this.http.get(this.baseMicroServiceUrl + this.api.GET_uicache_catalog, {params: searchParams}) + .map(res => this.processComponentsResponse(res, true)); + } + + public getArchiveCatalog() { + return this.http.get(this.baseUrl + '/v1/catalog/archive/', {}) + .map(res => this.processComponentsResponse(res)); + } + + private processComponentsResponse(componentsArr: IComponentsArray, addSharing:boolean = false) { + const componentsList: Component[] = []; + if (componentsArr.resources) { + componentsList.push(...this.getResourceItems(componentsArr.resources)); + } + if (componentsArr.services) { + componentsList.push(...this.getServiceItems(componentsArr.services)); + } + if (addSharing) { + componentsList.forEach((item) => this.sharingService.addUuidValue(item.uniqueId, item.uuid)); + } + return componentsList; + } + + private getResourceItems(resources){ + let resourceItems = resources.map((resource)=>{ + return this.componentFactory.createResource(resource) + }) + return resourceItems; + } + + private getServiceItems(services){ + let serviceItems = services.map((service)=>{ + return this.componentFactory.createService(service) + }) + return serviceItems; + } + +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts b/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts index 15750020dc..cc382a3df0 100644 --- a/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts +++ b/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts @@ -19,62 +19,102 @@ */ import {Injectable, Inject} from '@angular/core'; -import {Response, RequestOptions, Headers} from '@angular/http'; import { Observable } from 'rxjs/Observable'; import {PropertyFEModel, PropertyBEModel} from "app/models"; -import {CommonUtils} from "app/utils"; -import {Component, ComponentInstance, Capability, PropertyModel} from "app/models"; -import { HttpService } from '../http.service'; +import {CommonUtils, ComponentType, ServerTypeUrl, ComponentInstanceFactory} from "app/utils"; +import {Component, ComponentInstance, Capability, PropertyModel, ArtifactGroupModel, ArtifactModel, AttributeModel, IFileDownload} from "app/models"; import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { InputBEModel } from '../../../models/properties-inputs/input-be-model'; +import { HttpHelperService } from '../http-hepler.service'; @Injectable() export class ComponentInstanceServiceNg2 { protected baseUrl; - constructor(private http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + constructor(private http: HttpClient, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; } + private getServerTypeUrl = (componentType:string):string => { + switch (componentType) { + case ComponentType.SERVICE: + return ServerTypeUrl.SERVICES; + default: + return ServerTypeUrl.RESOURCES; + } + } getComponentInstanceProperties(component: Component, componentInstanceId: string): Observable> { - - return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/properties') - .map((res: Response) => { - return CommonUtils.initBeProperties(res.json()); + return this.http.get>(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/properties') + .map(res => { + return CommonUtils.initBeProperties(res); }) } getComponentInstanceInputs(component: Component, componentInstance: ComponentInstance): Observable> { - return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstance.uniqueId + '/' + componentInstance.componentUid + '/inputs') - .map((res: Response) => { - return CommonUtils.initInputs(res.json()); + return this.http.get>(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstance.uniqueId + '/' + componentInstance.componentUid + '/inputs') + .map(res => { + return CommonUtils.initInputs(res); }) } - updateInstanceProperties(component: Component, componentInstanceId: string, properties: PropertyBEModel[]) { + getComponentInstanceArtifactsByGroupType = (componentType:string, componentId:string, componentInstanceId:string, artifactGroupType:string):Observable => { + + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + "/resourceInstances/" + componentInstanceId + "/artifactsByType/" + artifactGroupType) + .map(res => { + return new ArtifactGroupModel(res); + }); + }; + + getArtifactByGroupType = (componentType:string, componentId:string, artifactGroupType:string):Observable => { + + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + "/artifactsByType/" + artifactGroupType) + .map(response => new ArtifactGroupModel(response)); + }; + + changeResourceInstanceVersion = (componentType:string, componentId:string, componentInstanceId:string, componentUid:string):Observable => { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/' + componentInstanceId + '/changeVersion', {'componentUid': componentUid}) + .map((res) => { + return ComponentInstanceFactory.createComponentInstance(res); + }) + }; - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/properties', properties) - .map((res: Response) => { - return res.json().map((resProperty) => new PropertyBEModel(resProperty)); + updateComponentInstance = (componentType:string, componentId:string, componentInstance:ComponentInstance):Observable => { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/' + componentInstance.uniqueId, componentInstance.toJSON()) + .map((response) => { + return ComponentInstanceFactory.createComponentInstance(response); + }); + }; + + updateInstanceProperties(componentType:string, componentId:string, componentInstanceId: string, properties: PropertyBEModel[]) { + + return this.http.post>(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/' + componentInstanceId + '/properties', properties) + .map((res) => { + return res.map((resProperty) => { + let newProp = new PropertyModel(resProperty); + newProp.resourceInstanceUniqueId = componentInstanceId + return newProp; + }); }); } - getInstanceCapabilityProperties(component: Component, componentInstanceId: string, capability: Capability): Observable> { + getInstanceCapabilityProperties(componentType: string, componentId: string, componentInstanceId: string, capability: Capability): Observable> { - return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capability.type + + return this.http.get>(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/componentInstances/' + componentInstanceId + '/capability/' + capability.type + '/capabilityName/' + capability.name + '/ownerId/' + capability.ownerId + '/properties') - .map((res: Response) => { - capability.properties = res.json().map((capProp) => new PropertyModel(capProp)); // update capability properties + .map((res) => { + capability.properties = res.map((capProp) => new PropertyModel(capProp)); // update capability properties return capability.properties; }) } updateInstanceCapabilityProperties(component: Component, componentInstanceId: string, capability: Capability, properties: PropertyBEModel[]): Observable> { - return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capability.type + + return this.http.put>(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capability.type + '/capabilityName/' + capability.name + '/ownerId/' + capability.ownerId + '/properties', properties) - .map((res: Response) => { - const savedProperties: PropertyModel[] = res.json().map((resProperty) => new PropertyModel(resProperty)); + .map((res) => { + const savedProperties: PropertyModel[] = res.map((resProperty) => new PropertyModel(resProperty)); savedProperties.forEach((savedProperty) => { const propIdx = capability.properties.findIndex((p) => p.uniqueId === savedProperty.uniqueId); if (propIdx !== -1) { @@ -87,37 +127,87 @@ export class ComponentInstanceServiceNg2 { updateInstanceInputs(component: Component, componentInstanceId: string, inputs: PropertyBEModel[]): Observable { - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/inputs', inputs) - .map((res: Response) => { - return res.json().map((resInput) => new PropertyBEModel(resInput)); + return this.http.post>(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/inputs', inputs) + .map((res) => { + return res.map((resInput) => new PropertyBEModel(resInput)); }); } getComponentGroupInstanceProperties(component: Component, groupInstanceId: string): Observable> { - return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/groups/' + groupInstanceId + '/properties') - .map((res: Response) => { - return CommonUtils.initBeProperties(res.json()); + return this.http.get>(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/groups/' + groupInstanceId + '/properties') + .map((res) => { + return CommonUtils.initBeProperties(res); + }); + } + + updateComponentGroupInstanceProperties(componentType:string, componentId:string, groupInstanceId: string, properties: PropertyBEModel[]): Observable> { + return this.http.put>(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/groups/' + groupInstanceId + '/properties', properties) + .map((res) => { + return res.map((resProperty) => new PropertyBEModel(resProperty)); }); } - updateComponentGroupInstanceProperties(component: Component, groupInstanceId: string, properties: PropertyBEModel[]): Observable> { - return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/groups/' + groupInstanceId + '/properties', properties) - .map((res: Response) => { - return res.json().map((resProperty) => new PropertyBEModel(resProperty)); + getComponentPolicyInstanceProperties(componentType:string, componentId:string, policyInstanceId: string): Observable> { + return this.http.get>(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/policies/' + policyInstanceId + '/properties') + .map((res) => { + return CommonUtils.initBeProperties(res); }); } - getComponentPolicyInstanceProperties(component: Component, policyInstanceId: string): Observable> { - return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/policies/' + policyInstanceId + '/properties') - .map((res: Response) => { - return CommonUtils.initBeProperties(res.json()); + updateComponentPolicyInstanceProperties(componentType:string, componentId:string, policyInstanceId: string, properties: PropertyBEModel[]): Observable> { + return this.http.put>(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/policies/' + policyInstanceId + '/properties', properties) + .map((res) => { + return res.map((resProperty) => new PropertyBEModel(resProperty)); }); } - updateComponentPolicyInstanceProperties(component: Component, policyInstanceId: string, properties: PropertyBEModel[]): Observable> { - return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/policies/' + policyInstanceId + '/properties', properties) - .map((res: Response) => { - return res.json().map((resProperty) => new PropertyBEModel(resProperty)); + addInstanceArtifact = (componentType:string, componentId:string, instanceId:string, artifact:ArtifactModel):Observable => { + // let deferred = this.$q.defer(); + let headerObj = new HttpHeaders(); + if (artifact.payloadData) { + headerObj = headerObj.set('Content-MD5', HttpHelperService.getHeaderMd5(artifact)); + } + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/' + instanceId + '/artifacts', JSON.stringify(artifact), {headers: headerObj}) + .map((res) => { + return new ArtifactModel(res); + }); + }; + + updateInstanceArtifact = (componentType:string, componentId:string, instanceId:string, artifact:ArtifactModel):Observable => { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/' + instanceId + '/artifacts/' + artifact.uniqueId, artifact).map((res) => { + return new ArtifactModel(res); + });; + }; + + deleteInstanceArtifact = (componentId:string, componentType:string, instanceId:string, artifactId:string, artifactLabel:string):Observable => { + return this.http.delete(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + "/resourceInstance/" + instanceId + '/artifacts/' + artifactId + '?operation=' + artifactLabel) + .map((res) => { + return new ArtifactModel(res); }); } + + downloadInstanceArtifact = (componentType:string, componentId:string, instanceId:string, artifactId:string):Observable => { + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + "/resourceInstances/" + instanceId + "/artifacts/" + artifactId); + }; + + uploadInstanceEnvFile = (componentType:string, componentId:string, instanceId:string, artifact:ArtifactModel):Observable => { + let headerObj = new HttpHeaders(); + if (artifact.payloadData) { + headerObj = headerObj.set('Content-MD5', HttpHelperService.getHeaderMd5(artifact)); + } + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/' + instanceId + '/artifacts/' + artifact.uniqueId, JSON.stringify(artifact), {headers: headerObj}); + }; + + + updateInstanceAttribute = (componentType:string, componentId:string, attribute:AttributeModel):Observable => { + let instanceId = attribute.resourceInstanceUniqueId; + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + "/resourceInstance/" + instanceId + "/attribute", attribute) + .map((response) => { + let newAttribute = new AttributeModel(response); + newAttribute.readonly = true; + newAttribute.resourceInstanceUniqueId = instanceId; + return newAttribute; + }); + }; + } diff --git a/catalog-ui/src/app/ng2/services/component-services/component-mode.service.ts b/catalog-ui/src/app/ng2/services/component-services/component-mode.service.ts index b0cc1b8f1a..9952b03c7c 100644 --- a/catalog-ui/src/app/ng2/services/component-services/component-mode.service.ts +++ b/catalog-ui/src/app/ng2/services/component-services/component-mode.service.ts @@ -24,7 +24,7 @@ import { Injectable } from '@angular/core'; import {WorkspaceMode, ComponentState, Role} from "../../../utils/constants"; import { Component as ComponentData } from "app/models"; -import { CacheService } from "app/services/cache-service" +import { CacheService } from "app/services-ng2"; @Injectable() diff --git a/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts b/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts index 6e9d0e8031..15e5a7960a 100644 --- a/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts +++ b/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts @@ -20,15 +20,18 @@ import {Injectable} from "@angular/core"; -import {Component} from "../../../models/components/component"; +import {Component} from "app/models"; import {ComponentServiceNg2} from "./component.service"; import {ServiceServiceNg2} from "./service.service"; +import {CacheService} from "app/services-ng2"; @Injectable() export class ComponentServiceFactoryNg2 { + componentService: ComponentServiceNg2; serviceService: ServiceServiceNg2; - constructor(componentService: ComponentServiceNg2, serviceService: ServiceServiceNg2) { + + constructor(componentService: ComponentServiceNg2, serviceService: ServiceServiceNg2, private cacheService:CacheService) { this.serviceService = serviceService; this.componentService = componentService; } diff --git a/catalog-ui/src/app/ng2/services/component-services/component.service.ts b/catalog-ui/src/app/ng2/services/component-services/component.service.ts index 445e1231f9..760bfc591b 100644 --- a/catalog-ui/src/app/ng2/services/component-services/component.service.ts +++ b/catalog-ui/src/app/ng2/services/component-services/component.service.ts @@ -23,51 +23,52 @@ import {Injectable, Inject} from '@angular/core'; import {Observable} from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/toPromise'; -import {Response, URLSearchParams, Headers} from '@angular/http'; -import { Component, ComponentInstance, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData, - PropertyBEModel, InterfaceModel, OperationModel, BEOperationModel, Capability, Requirement, PolicyInstance} from "app/models"; -import {COMPONENT_FIELDS, CommonUtils, SERVICE_FIELDS} from "app/utils"; -import {downgradeInjectable} from '@angular/upgrade/static'; +import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData, OperationModel, CreateOperationResponse, ArtifactModel} from "app/models"; +import {COMPONENT_FIELDS} from "app/utils"; import {ComponentGenericResponse} from "../responses/component-generic-response"; import {InstanceBePropertiesMap} from "../../../models/properties-inputs/property-fe-map"; import {API_QUERY_PARAMS} from "app/utils"; -import { ComponentType, ServerTypeUrl } from "../../../utils/constants"; -import { HttpService } from '../http.service'; +import {ComponentType, ServerTypeUrl, SERVICE_FIELDS} from "../../../utils/constants"; import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; -import {ConstraintObject} from 'app/ng2/components/logic/service-dependencies/service-dependencies.component'; import {IDependenciesServerResponse} from "../responses/dependencies-server-response"; import {AutomatedUpgradeGenericResponse} from "../responses/automated-upgrade-response"; import {IAutomatedUpgradeRequestObj} from "../../pages/automated-upgrade/automated-upgrade.service"; - -declare var angular:angular.IAngularStatic; - +import {ComponentInstance} from "../../../models/componentsInstances/componentInstance"; +import {CommonUtils} from "../../../utils/common-utils"; +import {RelationshipModel} from "../../../models/graph/relationship"; +import { HttpClient, HttpParams, HttpHeaders } from "@angular/common/http"; +import { BEOperationModel, InterfaceModel } from "../../../models/operation"; +import { PropertyBEModel } from "../../../models/properties-inputs/property-be-model"; +import { PolicyInstance } from "../../../models/graph/zones/policy-instance"; +import { ConstraintObject } from "../../components/logic/service-dependencies/service-dependencies.component"; +import { Requirement } from "../../../models/requirement"; +import { Capability } from "../../../models/capability"; + +/* +PLEASE DO NOT USE THIS SERVICE IN ANGULAR2! Use the topology-template.service instead + */ @Injectable() export class ComponentServiceNg2 { protected baseUrl; - constructor(protected http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + constructor(protected http: HttpClient, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; } - protected getComponentDataByFieldsName(componentType:string, componentId: string, fields:Array):Observable { + protected getComponentDataByFieldsName(componentType:string, componentId:string, fields:Array):Observable { - let params:URLSearchParams = new URLSearchParams(); + let params: HttpParams = new HttpParams(); _.forEach(fields, (field:string):void => { - params.append(API_QUERY_PARAMS.INCLUDE, field); + params = params.append(API_QUERY_PARAMS.INCLUDE, field); }); - return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/filteredDataByParams', {search: params}) - .map((res:Response) => { - return this.analyzeComponentDataResponse(res); + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/filteredDataByParams', {params: params}) + .map((res) => { + return new ComponentGenericResponse().deserialize(res); }); } - - protected analyzeComponentDataResponse(res: Response):ComponentGenericResponse { - return new ComponentGenericResponse().deserialize(res.json()); - } - - private getServerTypeUrl = (componentType:string):string => { + protected getServerTypeUrl = (componentType:string):string => { switch (componentType) { case ComponentType.SERVICE: return ServerTypeUrl.SERVICES; @@ -76,15 +77,23 @@ export class ComponentServiceNg2 { } } - getComponentMetadata(component:Component):Observable { - return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_METADATA]); + getFullComponent(uniqueId:string):Observable { + return this.http.get(this.baseUrl + uniqueId) + .map((res) => { + return new ComponentGenericResponse().deserialize(res); + }); } + getComponentMetadata(uniqueId:string, type:string):Observable { + return this.getComponentDataByFieldsName(type, uniqueId, [COMPONENT_FIELDS.COMPONENT_METADATA]); + } + + getComponentInstanceAttributesAndProperties(component:Component):Observable { return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_PROPERTIES, COMPONENT_FIELDS.COMPONENT_INSTANCES_ATTRIBUTES]); } - getComponentInstanceProperties(component:Component):Observable { + getComponentInstanceProperties(component:Component): Observable { return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_PROPERTIES]); } @@ -132,17 +141,15 @@ export class ComponentServiceNg2 { return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_PROPERTIES]); } - getInterfaces(component:Component):Observable { + getInterfaceOperations(component:Component):Observable { return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INTERFACE_OPERATIONS]); } getInterfaceOperation(component:Component, operation:OperationModel):Observable { - return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaces/' + operation.interfaceId + '/operations/' + operation.uniqueId) - .map((res:Response) => { - return res.json(); - }); + return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations/' + operation.uniqueId); } + // tslint:disable-next-line:member-ordering createInterfaceOperation(component:Component, operation:OperationModel):Observable { const operationList = { 'interfaces': { @@ -154,9 +161,9 @@ export class ComponentServiceNg2 { } } }; - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations', operationList) - .map((res:Response) => { - const interf:InterfaceModel = _.find(res.json().interfaces, interf => interf.type === operation.interfaceType); + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations', operationList) + .map((res:any) => { + const interf:InterfaceModel = _.find(res.interfaces, interf => interf.type === operation.interfaceType); const newOperation:OperationModel = _.find(interf.operations, op => op.name === operation.name); return new OperationModel({ ...newOperation, @@ -167,21 +174,22 @@ export class ComponentServiceNg2 { }); } + // tslint:disable-next-line:member-ordering updateInterfaceOperation(component:Component, operation:OperationModel):Observable { const operationList = { - 'interfaces': { + interfaces: { [operation.interfaceType]: { - 'type': operation.interfaceType, - 'operations': { + type: operation.interfaceType, + operations: { [operation.name]: new BEOperationModel(operation) } } } }; - return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations', operationList) - .map((res:Response) => { - const interf:InterfaceModel = _.find(res.json().interfaces, interf => interf.type === operation.interfaceType); - const newOperation:OperationModel = _.find(interf.operations, op => op.name === operation.name); + return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations', operationList) + .map((res: any) => { + const interf: InterfaceModel = _.find(res.interfaces, interf => interf.type === operation.interfaceType); + const newOperation: OperationModel = _.find(interf.operations, op => op.name === operation.name); return new OperationModel({ ...newOperation, interfaceType: interf.type, @@ -191,18 +199,16 @@ export class ComponentServiceNg2 { }); } - deleteInterfaceOperation(component:Component, operation:OperationModel):Observable { - return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaces/' + operation.interfaceId + '/operations/' + operation.uniqueId) - .map((res:Response) => { - return res.json(); - }); + + deleteInterfaceOperation(component: Component, operation:OperationModel):Observable { + return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaces/' + operation.interfaceId + '/operations/' + operation.uniqueId); } getInterfaceTypes(component:Component):Observable<{[id:string]: Array}> { - return this.http.get(this.baseUrl + 'interfaceLifecycleTypes') - .map((res:Response) => { + return this.http.get(this.baseUrl + 'interfaceLifecycleTypes') + .map((res: any) => { const interfaceMap = {}; - _.forEach(res.json(), (interf:any) => { + _.forEach(res, (interf: any) => { interfaceMap[interf.toscaPresentation.type] = _.keys(interf.toscaPresentation.operations); }); return interfaceMap; @@ -217,19 +223,18 @@ export class ComponentServiceNg2 { payloadData: oldOperation.artifactData }; - const headers = new Headers(); + const headers = new HttpHeaders(); JSON.stringify(payload); const payloadString = JSON.stringify(payload, null, ' '); const md5Result = md5(payloadString).toLowerCase(); headers.append('Content-MD5', btoa(md5Result)); return this.http.post(this.baseUrl + component.getTypeUrl() + component.uuid + '/interfaces/' + newOperation.interfaceId + '/operations/' + newOperation.uniqueId + '/artifacts/' + newOperation.implementation.artifactUUID, - payload, - {headers} - ).map((res: Response) => { - const fileName = res.json().artifactDisplayName || res.json().artifactName; + payload, {headers} + ).map((res: any) => { + const fileName = res.artifactDisplayName || res.artifactName; newOperation.artifactFileName = fileName; - return res.json(); + return res; }); } @@ -237,78 +242,17 @@ export class ComponentServiceNg2 { return this.getComponentDataByFieldsName(componentType, componentId, [COMPONENT_FIELDS.COMPONENT_REQUIREMENTS, COMPONENT_FIELDS.COMPONENT_CAPABILITIES]); } - createCapability(component: Component, capabilityData: Capability): Observable> { - let capBEObj = { - 'capabilities': { - [capabilityData.type]: [capabilityData] - } - }; - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/capabilities', capBEObj) - .map((res: Response) => { - return res.json(); - }); - } - updateCapability(component: Component, capabilityData: Capability): Observable> { - let capBEObj = { - 'capabilities': { - [capabilityData.type]: [capabilityData] - } - }; - return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/capabilities', capBEObj) - .map((res: Response) => { - return res.json(); - }); - } - deleteCapability(component: Component, capId: string): Observable { - return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/capabilities/' + capId) - .map((res: Response) => { - return res.json(); - }); - } - createRequirement(component: Component, requirementData: Requirement): Observable { - let reqBEObj = { - 'requirements': { - [requirementData.capability]: [requirementData] - } - }; - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/requirements', reqBEObj) - .map((res: Response) => { - return res.json(); - }); - } - - updateRequirement(component: Component, requirementData: Requirement): Observable { - let reqBEObj = { - 'requirements': { - [requirementData.capability]: [requirementData] - } - }; - return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/requirements', reqBEObj) - .map((res: Response) => { - return res.json(); - }); - } - - deleteRequirement(component: Component, reqId: string): Observable { - return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/requirements/' + reqId) - .map((res: Response) => { - return res.json(); - }); - } getDeploymentGraphData(component:Component):Observable { return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_GROUPS]); } createInput(component:Component, inputsToCreate:InstancePropertiesAPIMap, isSelf:boolean):Observable { - let inputs = isSelf ? { serviceProperties: inputsToCreate.componentInstanceProperties } : inputsToCreate; - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/create/inputs', inputs) - .map(res => { - return res.json(); - }) + const inputs = isSelf ? { serviceProperties: inputsToCreate.componentInstanceProperties } : inputsToCreate; + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/create/inputs', inputs); } createListInput(component:Component, input:any, isSelf:boolean):Observable { @@ -324,10 +268,7 @@ export class ComponentServiceNg2 { } else { inputs = input; } - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/create/listInput', inputs) - .map(res => { - return res.json(); - }) + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/create/listInput', inputs); } createPolicy(component:Component, policiesToCreate:InstancePropertiesAPIMap, isSelf:boolean):Observable { @@ -341,78 +282,71 @@ export class ComponentServiceNg2 { ...policiesToCreate.componentInstanceProperties } }; - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/create/policies', policiesList) - .map(res => { - return res.json(); - }); + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/create/policies', policiesList); } deletePolicy(component:Component, policy: PolicyInstance) { - return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/policies/' + policy.uniqueId + '/undeclare', policy) - .map(res => { - return res.json(); - }); + return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/policies/' + policy.uniqueId + '/undeclare', policy); } - restoreComponent(componentType:string, componentId:string){ + restoreComponent(componentType:string, componentId:string) { return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/restore', {}) } - archiveComponent(componentType:string, componentId:string){ + archiveComponent(componentType:string, componentId:string) { return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/archive', {}) } deleteInput(component:Component, input:InputBEModel):Observable { - return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/delete/' + input.uniqueId + '/input') - .map((res:Response) => { - return new InputBEModel(res.json()); - }); + + return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/delete/' + input.uniqueId + '/input') + .map((res) => { + return new InputBEModel(res); + }) } updateComponentInputs(component:Component, inputs:InputBEModel[]):Observable { - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/update/inputs', inputs) - .map((res:Response) => { - return res.json().map((input) => new InputBEModel(input)); + + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/update/inputs', inputs) + .map((res) => { + return res.map((input) => new InputBEModel(input)); }) } - filterComponentInstanceProperties(component: Component, filterData:FilterPropertiesAssignmentData): Observable {//instance-property-be-map - let params: URLSearchParams = new URLSearchParams(); + filterComponentInstanceProperties(component:Component, filterData:FilterPropertiesAssignmentData):Observable {//instance-property-be-map + let params: HttpParams = new HttpParams(); _.forEach(filterData.selectedTypes, (type:string) => { - params.append('resourceType', type); + params = params.append('resourceType', type); }); - return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/filteredproperties/' + filterData.propertyName, {search: params}) - .map((res: Response) => { - return res.json(); - }); + return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/filteredproperties/' + filterData.propertyName, {params: params}); } createServiceProperty(component: Component, propertyModel:PropertyBEModel): Observable { let serverObject = {}; serverObject[propertyModel.name] = propertyModel; return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/properties', serverObject) - .map(res => { - let property:PropertyBEModel = new PropertyBEModel(res.json()); + .map((res: PropertyBEModel) => { + const property: PropertyBEModel = new PropertyBEModel(res); return property; }) } - getServiceProperties(component: Component): Observable> { + getServiceProperties(component: Component): Observable { return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/properties') - .map((res: Response) => { - if (!res.text()){ + .map((res: PropertyBEModel[]) => { + if (!res) { return new Array(); } - return CommonUtils.initBeProperties(res.json()); + return CommonUtils.initBeProperties(res); }); } updateServiceProperties(component: Component, properties: PropertyBEModel[]) { - return this.http.put( this.baseUrl + component.getTypeUrl() + component.uniqueId + '/properties', properties) - .map((res: Response) => { - const resJson = res.json(); + return this.http.put( this.baseUrl + component.getTypeUrl() + component.uniqueId + '/properties', properties) + .map((res) => { + const resJson = res; return _.map(resJson, (resValue:PropertyBEModel) => new PropertyBEModel(resValue)); }); @@ -425,25 +359,16 @@ export class ComponentServiceNg2 { }) } - getDependencies(componentType:string, componentId: string):Observable> { - return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/dependencies') - .map((res:Response) => { - return res.json(); - }); + getDependencies(componentType:string, componentId: string):Observable { + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/dependencies'); } - automatedUpgrade(componentType:string, componentId: string, componentsIdsToUpgrade:Array):Observable { - return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/automatedupgrade', componentsIdsToUpgrade) - .map((res:Response) => { - return res.json(); - }); + automatedUpgrade(componentType:string, componentId:string, componentsIdsToUpgrade:IAutomatedUpgradeRequestObj[]):Observable { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/automatedupgrade', componentsIdsToUpgrade); } - updateComponentInstance(component:Component, componentInstance:ComponentInstance):Observable { - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstance.uniqueId, componentInstance) - .map((res:Response) => { - return res.json(); - }); + updateMultipleComponentInstances(componentId:string, instances:ComponentInstance[]):Observable { + return this.http.post(this.baseUrl + componentId + '/resourceInstance/multipleComponentInstance', instances); } getServiceFilterConstraints(component:Component):Observable { @@ -451,24 +376,18 @@ export class ComponentServiceNg2 { } createServiceFilterConstraints(component:Component, componentInstance:ComponentInstance, constraint:ConstraintObject):Observable { - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstances/' + componentInstance.uniqueId + '/nodeFilter', constraint) - .map((res:Response) => { - return res.json(); - }); + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstances/' + componentInstance.uniqueId + '/nodeFilter', constraint); } - updateServiceFilterConstraints(component:Component, componentInstance:ComponentInstance, constraints:Array):Observable { - return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstances/' + componentInstance.uniqueId + '/nodeFilter/', constraints) - .map((res:Response) => { - return res.json(); - }); + updateServiceFilterConstraints(component:Component, componentInstance:ComponentInstance, constraints:ConstraintObject[]):Observable { + return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstances/' + componentInstance.uniqueId + '/nodeFilter/', constraints); } deleteServiceFilterConstraints(component:Component, componentInstance:ComponentInstance, constraintIndex:number) { - return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstances/' + componentInstance.uniqueId + '/nodeFilter/' + constraintIndex) - .map((res:Response) => { - return res.json(); - }); + return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstances/' + componentInstance.uniqueId + '/nodeFilter/' + constraintIndex); } -} + protected analyzeComponentDataResponse(res: Response):ComponentGenericResponse { + return new ComponentGenericResponse().deserialize(res); + } +} diff --git a/catalog-ui/src/app/ng2/services/component-services/resource.service.ts b/catalog-ui/src/app/ng2/services/component-services/resource.service.ts index 699e762a60..d20f5415bc 100644 --- a/catalog-ui/src/app/ng2/services/component-services/resource.service.ts +++ b/catalog-ui/src/app/ng2/services/component-services/resource.service.ts @@ -21,14 +21,14 @@ import { Injectable } from '@angular/core'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/toPromise'; -import { Http, Response, Headers, RequestOptions } from '@angular/http'; +import { HttpClient } from '@angular/common/http'; @Injectable() export class ResourceServiceNg2 { protected baseUrl = ""; - constructor(private http: Http) { + constructor(private http: HttpClient) { } diff --git a/catalog-ui/src/app/ng2/services/component-services/service.service.ts b/catalog-ui/src/app/ng2/services/component-services/service.service.ts index dce4e814ec..9460a32323 100644 --- a/catalog-ui/src/app/ng2/services/component-services/service.service.ts +++ b/catalog-ui/src/app/ng2/services/component-services/service.service.ts @@ -22,9 +22,7 @@ import { Injectable, Inject } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/toPromise'; -import { Response, URLSearchParams } from '@angular/http'; -import {Service, OperationModel} from "app/models"; -import { HttpService } from '../http.service'; +import {Service} from "app/models"; import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; import {ForwardingPath} from "app/models/forwarding-path"; @@ -34,77 +32,53 @@ import {Component} from "app/models/components/component"; import {ComponentGenericResponse} from "app/ng2/services/responses/component-generic-response"; import {COMPONENT_FIELDS, SERVICE_FIELDS} from "app/utils/constants"; import {ComponentServiceNg2} from "./component.service"; -import {ServiceGenericResponse} from "app/ng2/services/responses/service-generic-response"; import {ServicePathMapItem} from "app/models/graph/nodes-and-links-map"; -import {ConsumptionInput} from 'app/ng2/components/logic/service-consumption/service-consumption.component'; - +import { HttpClient, HttpParams } from '@angular/common/http'; +import { OperationModel } from '../../../models/operation'; +import { ConsumptionInput } from '../../components/logic/service-consumption/service-consumption.component'; @Injectable() export class ServiceServiceNg2 extends ComponentServiceNg2 { protected baseUrl = ""; - constructor(protected http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + constructor(protected http: HttpClient, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { super(http, sdcConfig); this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; } validateConformanceLevel(service: Service): Observable { - return this.http.get(this.baseUrl + service.getTypeUrl() + service.uuid + '/conformanceLevelValidation') - .map((res: Response) => { - return res.json(); - }); - } - - getNodesAndLinksMap(service: Service):Observable> { - return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/linksMap').map(res => { - return >res.json(); - }); - } - - getServicePath(service: Service, id: string):Observable { - return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths/' + id) - .map(res => { - return res.json(); - }) + return this.http.get(this.baseUrl + service.getTypeUrl() + service.uuid + '/conformanceLevelValidation'); } - getServicePaths(service: Service):Observable { - return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths') - .map(res => { - return res.json(); - }) + getNodesAndLinksMap(serviceId: string):Observable> { + return this.http.get>(this.baseUrl + 'services/' + serviceId + '/linksMap'); } - createOrUpdateServicePath(service: Service, inputsToCreate: ForwardingPath):Observable { + createOrUpdateServicePath(serviceId: string, inputsToCreate: ForwardingPath):Observable { if (inputsToCreate.uniqueId) { - return this.updateServicePath(service, inputsToCreate); + return this.updateServicePath(serviceId, inputsToCreate); } else { - return this.createServicePath(service, inputsToCreate); + return this.createServicePath(serviceId, inputsToCreate); } } - createServicePath(service: Service, inputsToCreate: ForwardingPath):Observable { + createServicePath(serviceId: string, inputsToCreate: ForwardingPath):Observable { let input = new ServicePathRequestData(inputsToCreate); - - return this.http.post(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths', input) - .map(res => { - return this.parseServicePathResponse(res); - }); + return this.http.post(this.baseUrl + 'services/' + serviceId + '/paths', input).map((res:any) => { + return this.parseServicePathResponse(res); + }); } - deleteServicePath(service: Service, id: string):Observable { - return this.http.delete(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths/' + id ) - .map((res) => { - return res.json(); - }); + deleteServicePath(serviceId: string, id: string):Observable { + return this.http.delete(this.baseUrl + 'services/' + serviceId + '/paths/' + id); } - updateServicePath(service: Service, inputsToUpdate:ForwardingPath):Observable { + updateServicePath(serviceId: string, inputsToUpdate:ForwardingPath):Observable { let input = new ServicePathRequestData(inputsToUpdate); - return this.http.put(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths', input) + return this.http.put<{[key:string]:ForwardingPath}>(this.baseUrl + 'services/' + serviceId + '/paths', input) .map((res) => { return this.parseServicePathResponse(res); }); @@ -122,28 +96,25 @@ export class ServiceServiceNg2 extends ComponentServiceNg2 { } getServiceConsumptionInputs(service: Service, serviceInstanceId: String, interfaceId: string, operation: OperationModel): Observable { - return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/consumption/' + serviceInstanceId + '/interfaces/' + interfaceId + '/operations/' + operation.uniqueId + '/inputs') - .map(res => { - return res.json(); - }); + return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/consumption/' + serviceInstanceId + '/interfaces/' + interfaceId + + '/operations/' + operation.uniqueId + '/inputs'); } createOrUpdateServiceConsumptionInputs(service: Service, serviceInstanceId: String, consumptionInputsList: Array<{[id: string]: Array}>): Observable { return this.http.post(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/consumption/' + serviceInstanceId, consumptionInputsList); } - checkComponentInstanceVersionChange(service: Service, newVersionId: string):Observable> { - let instanceId = service.selectedInstance.uniqueId; - let queries = {componentInstanceId: instanceId, newComponentInstanceId: newVersionId}; + checkComponentInstanceVersionChange(componentType:string, componentId:string, instanceId:string, newInstanceId:string):Observable> { + let queries = {componentInstanceId: instanceId, newComponentInstanceId: newInstanceId}; - let params:URLSearchParams = new URLSearchParams(); + let params:HttpParams = new HttpParams(); _.map(_.keys(queries), (key:string):void => { - params.append(key, queries[key]); + params = params.append(key, queries[key]); }); - let url = this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths-to-delete'; - return this.http.get(url, {search: params}).map((res: Response) => { - return res.json().forwardingPathToDelete; + let url = this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/paths-to-delete'; + return this.http.get(url, {params: params}).map((res) => { + return res.forwardingPathToDelete; }); } @@ -151,12 +122,8 @@ export class ServiceServiceNg2 extends ComponentServiceNg2 { return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, SERVICE_FIELDS.FORWARDING_PATHS, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_POLICIES, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_GROUPS]); } - protected analyzeComponentDataResponse(res: Response):ComponentGenericResponse { - return new ServiceGenericResponse().deserialize(res.json()); - } - - private parseServicePathResponse(res: Response):ForwardingPath { - let resJSON = res.json(); + private parseServicePathResponse(res: { [key:string]:ForwardingPath }):ForwardingPath { + let resJSON = res; let pathId = Object.keys(resJSON.forwardingPaths)[0]; let forwardingPath = resJSON.forwardingPaths[pathId]; let path:ForwardingPath = new ForwardingPath(); diff --git a/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts b/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts new file mode 100644 index 0000000000..0abb163404 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts @@ -0,0 +1,515 @@ +/** + * Created by ob0695 on 6/26/2018. + */ +/*- + * ============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========================================================= + */ + +import * as _ from "lodash"; +import {Injectable, Inject} from '@angular/core'; +import {Observable} from 'rxjs/Observable'; +import 'rxjs/add/operator/map'; +import 'rxjs/add/operator/toPromise'; +import { + Component, + InputBEModel, + InstancePropertiesAPIMap, + FilterPropertiesAssignmentData, + ArtifactModel, + PropertyModel, + IFileDownload, + AttributeModel, + IAttributeModel, Capability, Requirement +} from "app/models"; +import {ArtifactGroupType, COMPONENT_FIELDS} from "app/utils"; +import {ComponentGenericResponse} from "../responses/component-generic-response"; +import {InstanceBePropertiesMap} from "../../../models/properties-inputs/property-fe-map"; +import {API_QUERY_PARAMS} from "app/utils"; +import {ComponentType, ServerTypeUrl, SERVICE_FIELDS} from "../../../utils/constants"; +import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; +import {IDependenciesServerResponse} from "../responses/dependencies-server-response"; +import {AutomatedUpgradeGenericResponse} from "../responses/automated-upgrade-response"; +import {IAutomatedUpgradeRequestObj} from "../../pages/automated-upgrade/automated-upgrade.service"; +import {ComponentInstance} from "../../../models/componentsInstances/componentInstance"; +import {CommonUtils} from "../../../utils/common-utils"; +import {RelationshipModel} from "../../../models/graph/relationship"; +import {ServiceGenericResponse} from "../responses/service-generic-response"; +import { HttpClient, HttpParams, HttpHeaders } from "@angular/common/http"; +import { HttpHelperService } from "../http-hepler.service"; +import { + Component as TopologyTemplate, + FullComponentInstance, + Service, + OperationModel, +} from 'app/models'; +import { ConsumptionInput } from "../../components/logic/service-consumption/service-consumption.component"; +import { ConstraintObject } from "../../components/logic/service-dependencies/service-dependencies.component"; +import { ComponentMetadata } from "../../../models/component-metadata"; +import { PolicyInstance } from "../../../models/graph/zones/policy-instance"; +import { PropertyBEModel } from "../../../models/properties-inputs/property-be-model"; + +/* we need to use this service from now, we will remove component.service when we finish remove the angular1. + The service is duplicated since we can not use downgrades service with NGXS*/ + +@Injectable() +export class TopologyTemplateService { + + protected baseUrl; + + constructor(protected http: HttpClient, @Inject(SdcConfigToken) sdcConfig: ISdcConfig) { + this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; + } + + getFullComponent(componentType: string, uniqueId: string): Observable { + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + uniqueId); + } + + getComponentMetadata(uniqueId: string, type: string): Observable { + return this.getComponentDataByFieldsName(type, uniqueId, [COMPONENT_FIELDS.COMPONENT_METADATA]); + } + + getComponentInstanceAttributesAndProperties(uniqueId: string, type: string): Observable { + return this.getComponentDataByFieldsName(type, uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_PROPERTIES, COMPONENT_FIELDS.COMPONENT_INSTANCES_ATTRIBUTES]); + } + + async getComponentAttributes(componentType: string, componentId: string): Promise { + return this.getComponentDataByFieldsName(componentType, componentId, [COMPONENT_FIELDS.COMPONENT_ATTRIBUTES]).toPromise(); + } + + getComponentCompositionData(componentUniqueId: string, componentType: string): Observable { + const params: string[] = [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, + COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_POLICIES, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_GROUPS]; + if (componentType === ComponentType.SERVICE) { + params.push(COMPONENT_FIELDS.FORWARDING_PATHS); + } + return this.getComponentDataByFieldsName(componentType, componentUniqueId, params); + } + + getComponentResourcePropertiesData(component: Component): Observable { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, + [COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_POLICIES, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_GROUPS]); + } + + getComponentResourceInstances(component: Component): Observable { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES]); + } + + getComponentInputs(component: Component): Observable { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INPUTS]); + } + + getComponentInputsWithProperties(componentType: string, componentId: string): Observable { + return this.getComponentDataByFieldsName(componentType, componentId, + [COMPONENT_FIELDS.COMPONENT_INPUTS, COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_INSTANCES_PROPERTIES, COMPONENT_FIELDS.COMPONENT_PROPERTIES]); + } + + getComponentDeploymentArtifacts(component: Component): Observable { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_DEPLOYMENT_ARTIFACTS]); + } + + getComponentInformationalArtifacts(component: Component): Observable { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INFORMATIONAL_ARTIFACTS]); + } + + getComponentInformationalArtifactsAndInstances(component: Component): Observable { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INFORMATIONAL_ARTIFACTS, COMPONENT_FIELDS.COMPONENT_INSTANCES]); + } + + getComponentToscaArtifacts(componentType: string, componentId: string): Observable { + return this.getComponentDataByFieldsName(componentType, componentId, [COMPONENT_FIELDS.COMPONENT_TOSCA_ARTIFACTS]); + } + + getComponentProperties(component: Component): Observable { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_PROPERTIES]); + } + + getCapabilitiesAndRequirements(componentType: string, componentId: string): Observable { + return this.getComponentDataByFieldsName(componentType, componentId, [COMPONENT_FIELDS.COMPONENT_REQUIREMENTS, COMPONENT_FIELDS.COMPONENT_CAPABILITIES]); + } + + getRequirementsAndCapabilitiesWithProperties(componentType: string, componentId: string): Observable { + return this.getComponentDataByFieldsName(componentType, componentId, + [COMPONENT_FIELDS.COMPONENT_REQUIREMENTS, COMPONENT_FIELDS.COMPONENT_CAPABILITIES, COMPONENT_FIELDS.COMPONENT_CAPABILITIES_PROPERTIES]); + } + + getDeploymentGraphData(componentType: string, componentId: string): Observable { + return this.getComponentDataByFieldsName(componentType, componentId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_GROUPS]); + } + + createInput(component: Component, inputsToCreate: InstancePropertiesAPIMap, isSelf: boolean): Observable { + const inputs = isSelf ? { serviceProperties: inputsToCreate.componentInstanceProperties } : inputsToCreate; + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/create/inputs', inputs); + } + + restoreComponent(componentType: string, componentId: string) { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/restore', {}); + } + + archiveComponent(componentType: string, componentId: string) { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/archive', {}); + } + + deleteInput(component: Component, input: InputBEModel): Observable { + return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/delete/' + input.uniqueId + '/input') + .map((res) => { + return new InputBEModel(res); + }); + } + + updateComponentInputs(component: Component, inputs: InputBEModel[]): Observable { + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/update/inputs', inputs) + .map((res) => { + return res.map((input) => new InputBEModel(input)); + }); + } + + filterComponentInstanceProperties(component: Component, filterData: FilterPropertiesAssignmentData): Observable {// instance-property-be-map + let params: HttpParams = new HttpParams(); + _.forEach(filterData.selectedTypes, (type: string) => { + params = params.append('resourceType', type); + }); + + // tslint:disable-next-line:object-literal-shorthand + return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/filteredproperties/' + filterData.propertyName, {params: params}); + } + + createServiceProperty(componentId: string, propertyModel: PropertyBEModel): Observable { + const serverObject = {}; + serverObject[propertyModel.name] = propertyModel; + return this.http.post(this.baseUrl + 'services/' + componentId + '/properties', serverObject) + .map((res) => { + const property: PropertyBEModel = new PropertyBEModel(res); + return property; + }); + } + + getServiceProperties(componentId: string): Observable { + return this.http.get(this.baseUrl + 'services/' + componentId + '/properties') + .map((res) => { + if (!res) { + return new Array(); + } + return CommonUtils.initBeProperties(res); + }); + } + + updateServiceProperties(componentId: string, properties: PropertyBEModel[]) { + return this.http.put( this.baseUrl + 'services/' + componentId + '/properties', properties) + .map((res) => { + const resJson = res; + return _.map(resJson, + (resValue: PropertyBEModel) => new PropertyBEModel(resValue)); + }); + } + + deleteServiceProperty(componentId: string, property: PropertyBEModel): Observable { + return this.http.delete(this.baseUrl + 'services/' + componentId + '/properties/' + property.uniqueId ) + .map((res: Response) => { + return property.uniqueId; + }); + } + + getDependencies(componentType: string, componentId: string): Observable { + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/dependencies'); + } + + automatedUpgrade(componentType: string, componentId: string, componentsIdsToUpgrade: IAutomatedUpgradeRequestObj[]): Observable { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/automatedupgrade', componentsIdsToUpgrade); + } + + updateComponentInstance(componentMetaDataId: string, componentInstance:ComponentInstance): Observable { + return this.http.post(this.baseUrl + 'services/' + componentMetaDataId + '/resourceInstance/' + componentInstance.uniqueId, componentInstance); + } + + updateMultipleComponentInstances(componentId: string, componentType: string, instances: ComponentInstance[]): Observable { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/multipleComponentInstance', instances) + .map((res) => { + return CommonUtils.initComponentInstances(res); + }); + } + + createRelation(componentId: string, componentType: string, link: RelationshipModel): Observable { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/associate', link) + .map((res) => { + return new RelationshipModel(res); + }); + } + + deleteRelation(componentId: string, componentType: string, link: RelationshipModel): Observable { + return this.http.put(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/dissociate', link) + .map((res) => { + return new RelationshipModel(res); + }); + } + + createComponentInstance(componentType: string, componentId: string, componentInstance: ComponentInstance): Observable { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance', componentInstance) + .map((res) => { + return new ComponentInstance(res); + }); + } + + deleteComponentInstance(componentType: string, componentId: string, componentInstanceId: string): Observable { + return this.http.delete(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/' + componentInstanceId) + .map((res) => { + return new ComponentInstance(res); + }); + } + + fetchRelation(componentType: string, componentId: string, linkId: string): Observable { + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/relationId/' + linkId) + .map((res) => { + return new RelationshipModel(res); + }); + } + + addOrUpdateArtifact = (componentType: string, componentId: string, artifact: ArtifactModel): Observable => { + let headerObj: HttpHeaders = new HttpHeaders(); + if (artifact.payloadData) { + headerObj = headerObj.append('Content-MD5', HttpHelperService.getHeaderMd5(artifact)); + } + + let artifactID: string = ''; + if (artifact.uniqueId) { + artifactID = '/' + artifact.uniqueId; + } + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/artifacts' + artifactID, JSON.stringify(artifact), {headers: headerObj}).map( + (res) => new ArtifactModel(res) + ); + } + + deleteArtifact = (componentId: string, componentType: string, artifactId: string, artifactLabel: string): Observable => { + return this.http.delete(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/artifacts/' + artifactId + '?operation=' + artifactLabel) + .map((res) => new ArtifactModel(res)); + } + + downloadArtifact = (componentType: string, componentId: string, artifactId: string): Observable => { + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/artifacts/' + artifactId); + } + + // ------------------------------------------------ Properties API --------------------------------------------------// + addProperty = (componentType: string, componentId: string, property: PropertyModel):Observable => { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/properties', property.convertToServerObject()).map((response) => { + return new PropertyModel(response[Object.keys(response)[0]]); + }); + } + + updateProperty = (componentType: string, componentId: string, property: PropertyModel): Observable => { + var propertiesList:PropertyBEModel[] = [property]; + return this.http.put(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/properties', propertiesList) + .map((response) => { + return new PropertyModel(response[Object.keys(response)[0]]); + }); + } + + deleteProperty = (componentType: string, componentId: string, propertyId: string): Observable => { + return this.http.delete(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/properties/' + propertyId); + } + + // ------------------------------------------------ Attributes API --------------------------------------------------// + addAttribute = (componentType: string, componentId: string, attribute: AttributeModel): Observable => { + return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/attributes', attribute.convertToServerObject()) + .map((response) => { + return new AttributeModel(response); + }); + } + + updateAttribute = (componentType: string, componentId: string, attribute: AttributeModel): Observable => { + const payload = attribute.convertToServerObject(); + + return this.http.put(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/attributes/' + attribute.uniqueId, payload) + .map((response) => { + return new AttributeModel(response); + }); + } + + // Async Methods + addAttributeAsync = async (componentType: string, componentId: string, attribute: AttributeModel): Promise => { + return this.addAttribute(componentType, componentId, attribute).toPromise(); + } + + updateAttributeAsync = async (componentType: string, componentId: string, attribute: AttributeModel): Promise => { + return this.updateAttribute(componentType, componentId, attribute).toPromise(); + } + + deleteAttributeAsync = async (componentType: string, componentId: string, attribute: AttributeModel): Promise => { + return this.http.delete(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/attributes/' + attribute.uniqueId, {}).toPromise(); + } + + getArtifactsByType(componentType: string, componentId: string, artifactsType: ArtifactGroupType) { + return this.getComponentDataByFieldsName(componentType, componentId, [this.convertArtifactTypeToUrl(artifactsType)]); + } + + getServiceConsumptionData(componentType: string, componentId: string): Observable { + return this.getComponentDataByFieldsName(componentType, componentId, [ + // COMPONENT_FIELDS.COMPONENT_INSTANCES_INTERFACES, + COMPONENT_FIELDS.COMPONENT_INSTANCES_PROPERTIES, + // COMPONENT_FIELDS.COMPONENT_INSTANCES_INPUTS, + COMPONENT_FIELDS.COMPONENT_INPUTS, + COMPONENT_FIELDS.COMPONENT_INSTANCES, + COMPONENT_FIELDS.COMPONENT_CAPABILITIES + ]); + } + + getServiceConsumptionInputs(componentMetaDataId: string, serviceInstanceId: string, interfaceId: string, operation: OperationModel): Observable { + return this.http.get + (this.baseUrl + 'services/' + componentMetaDataId + '/consumption/' + serviceInstanceId + '/interfaces/' + interfaceId + '/operations/' + operation.uniqueId + '/inputs'); + } + + createOrUpdateServiceConsumptionInputs(componentMetaDataId: string, serviceInstanceId: string, consumptionInputsList: Array<{[id: string]: ConsumptionInput[]}>): Observable { + return this.http.post(this.baseUrl + 'services/' + componentMetaDataId + '/consumption/' + serviceInstanceId, consumptionInputsList); + } + + getServiceFilterConstraints(componentType: string, componentId: string): Observable { + return this.getComponentDataByFieldsName(componentType, componentId, [SERVICE_FIELDS.NODE_FILTER]); + } + + getComponentInstanceProperties(componentType: string, componentId: string): Observable { + return this.getComponentDataByFieldsName(componentType, componentId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_PROPERTIES]); + } + + createServiceFilterConstraints(componentMetaDataId: string, componentInstanceId: string, constraint: ConstraintObject): Observable { + return this.http.post(this.baseUrl + 'services/' + componentMetaDataId + '/resourceInstances/' + componentInstanceId + '/nodeFilter', constraint); + } + + updateServiceFilterConstraints(componentMetaDataId: string, componentInstanceId: string, constraints: ConstraintObject[]):Observable { + return this.http.put(this.baseUrl + 'services/' + componentMetaDataId + '/resourceInstances/' + componentInstanceId + '/nodeFilter', constraints) + } + + deleteServiceFilterConstraints(componentMetaDataId: string, componentInstanceId: string, constraintIndex: number): Observable{ + return this.http.delete(this.baseUrl + 'services/' + componentMetaDataId + '/resourceInstances/' + componentInstanceId + '/nodeFilter/' + constraintIndex) + } + + deletePolicy(component: Component, policy: PolicyInstance): Observable { + return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/policies/' + policy.uniqueId + '/undeclare', policy) + } + + createListInput(componentId: string, input: any, isSelf: boolean): Observable { + let inputs: any; + if (isSelf) { + // change componentInstanceProperties -> serviceProperties + inputs = { + componentInstInputsMap: { + serviceProperties: input.componentInstInputsMap.componentInstanceProperties + }, + listInput: input.listInput + }; + } else { + inputs = input; + } + return this.http.post(this.baseUrl + 'services/' + componentId + '/create/listInput', inputs); + } + + createPolicy(component: Component, policiesToCreate: InstancePropertiesAPIMap, isSelf: boolean): Observable { + const policiesList = + isSelf ? + // tslint:disable-next-line:object-literal-key-quotes + {'componentPropertiesToPolicies': { + ...policiesToCreate.componentInstanceProperties + } + } : + // tslint:disable-next-line:object-literal-key-quotes + {'componentInstancePropertiesToPolicies': { + ...policiesToCreate.componentInstanceProperties + } + }; + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/create/policies', policiesList); + } + + protected getComponentDataByFieldsName(componentType: string, componentId: string, fields: string[]): Observable { + let params: HttpParams = new HttpParams(); + _.forEach(fields, (field: string): void => { + params = params.append(API_QUERY_PARAMS.INCLUDE, field); + }); + // tslint:disable-next-line:object-literal-shorthand + return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/filteredDataByParams', {params: params}) + .map((res) => { + return componentType === ComponentType.SERVICE ? new ServiceGenericResponse().deserialize(res) : + new ComponentGenericResponse().deserialize(res); + }); + } + + private getServerTypeUrl = (componentType: string): string => { + switch (componentType) { + case ComponentType.SERVICE: + case ComponentType.SERVICE_PROXY: + return ServerTypeUrl.SERVICES; + default: + return ServerTypeUrl.RESOURCES; + } + } + + private convertArtifactTypeToUrl = (artifactType: ArtifactGroupType): string => { + switch (artifactType) { + case ArtifactGroupType.TOSCA: + return COMPONENT_FIELDS.COMPONENT_TOSCA_ARTIFACTS; + case ArtifactGroupType.INFORMATION: + return COMPONENT_FIELDS.COMPONENT_INFORMATIONAL_ARTIFACTS; + case ArtifactGroupType.DEPLOYMENT: + return COMPONENT_FIELDS.COMPONENT_DEPLOYMENT_ARTIFACTS; + case ArtifactGroupType.SERVICE_API: + return COMPONENT_FIELDS.SERVICE_API_ARTIFACT; + } + } + + // createCapability(component: Component, capabilityData: Capability): Observable { + createCapability(type: string, uniqueId: string, capabilityData: Capability): Observable { + let capBEObj = { + 'capabilities': { + [capabilityData.type]: [capabilityData] + } + }; + return this.http.post(this.baseUrl + type + uniqueId + '/capabilities', capBEObj); + } + + updateCapability(type: string, uniqueId: string, capabilityData: Capability): Observable { + let capBEObj = { + 'capabilities': { + [capabilityData.type]: [capabilityData] + } + }; + return this.http.put(this.baseUrl + type + uniqueId + '/capabilities', capBEObj); + } + + deleteCapability(component: Component, capId: string): Observable { + return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/capabilities/' + capId); + } + + createRequirement(type: string, uniqueId: string, requirementData: Requirement): Observable { + let reqBEObj = { + 'requirements': { + [requirementData.capability]: [requirementData] + } + }; + return this.http.post(this.baseUrl + type + uniqueId + '/requirements', reqBEObj); + } + + updateRequirement(type: string, uniqueId: string, requirementData: Requirement): Observable { + let reqBEObj = { + 'requirements': { + [requirementData.capability]: [requirementData] + } + }; + return this.http.put(this.baseUrl + type + uniqueId + '/requirements', reqBEObj); + } + + deleteRequirement(component: Component, reqId: string): Observable { + return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/requirements/' + reqId); + } +} diff --git a/catalog-ui/src/app/ng2/services/config.service.ts b/catalog-ui/src/app/ng2/services/config.service.ts index 11fe395811..2b9b49cf62 100644 --- a/catalog-ui/src/app/ng2/services/config.service.ts +++ b/catalog-ui/src/app/ng2/services/config.service.ts @@ -16,62 +16,80 @@ * */ -import { Injectable, Inject } from '@angular/core'; -import { Http, Response } from '@angular/http'; +import { HttpClient } from '@angular/common/http'; +import { Inject, Injectable, Injector } from '@angular/core'; +import { IAppConfigurtaion, Plugins, PluginsConfiguration, ValidationConfiguration, Validations } from 'app/models'; +import { IApi } from 'app/models/app-config'; import 'rxjs/add/operator/toPromise'; -import {IAppConfigurtaion, ValidationConfiguration, Validations, Plugins, PluginsConfiguration} from "app/models"; -import {IApi} from "app/models/app-config"; -import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; +import { ISdcConfig, SdcConfigToken } from '../config/sdc-config.config'; +import { CacheService } from './cache.service'; @Injectable() export class ConfigService { - private baseUrl; public configuration: IAppConfigurtaion; - public api:IApi; + public api: IApi; + private baseUrl; + + constructor( + @Inject(SdcConfigToken) private sdcConfig: ISdcConfig, + private cacheService: CacheService, + private injector: Injector, + private http: HttpClient + ) { + this.api = this.sdcConfig.api; + this.baseUrl = this.api.root + this.sdcConfig.api.component_api_root; + } - constructor(private http: Http, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig) { - this.api = this.sdcConfig.api; - this.baseUrl = this.api.root + this.sdcConfig.api.component_api_root; + loadSdcSetupData = (): Promise => { + const url: string = this.api.root + this.api.GET_SDC_Setup_Data; + const promise: Promise = this.http.get(url).toPromise(); + promise.then((response) => { + this.cacheService.set('version', response.version); + this.cacheService.set('serviceCategories', response.categories.serviceCategories); + this.cacheService.set('resourceCategories', response.categories.resourceCategories); + this.cacheService.set('UIConfiguration', response.configuration); + }); + return promise; } loadValidationConfiguration(): Promise { - let url: string = this.sdcConfig.validationConfigPath; - let promise: Promise = this.http.get(url).map((res: Response) => res.json()).toPromise(); + const url: string = this.sdcConfig.validationConfigPath; + const promise: Promise = this.http.get(url).toPromise(); promise.then((validationData: Validations) => { ValidationConfiguration.validation = validationData; + this.cacheService.set('validation', validationData); }).catch((ex) => { console.error('Error loading validation.json configuration file, using fallback data', ex); - let fallback:Validations = { - "propertyValue": { - "max": 2500, - "min": 0 + const fallback = { + propertyValue: { + max: 2500, + min: 0 }, - - "validationPatterns": { - "string": "^[\\sa-zA-Z0-9+-]+$", - "comment": "^[\\sa-zA-Z0-9+-_\\{\\}\"]+$", - "integer": "^(([-+]?\\d+)|([-+]?0x[0-9a-fA-F]+))$" + validationPatterns: { + string: '^[\\sa-zA-Z0-9+-]+$', + stringOrEmpty: '^[\\sa-zA-Z0-9&-]*$', + comment: '^[\\sa-zA-Z0-9+-_\\{\\}"]+$', + integer: '^(([-+]?\\d+)|([-+]?0x[0-9a-fA-F]+))$' } }; - ValidationConfiguration.validation = fallback; - + this.cacheService.set('validation', fallback); }); return promise; } - loadPluginsConfiguration(): Promise { - let url:string = this.api.no_proxy_root + this.api.GET_plugins_configuration; - let promise: Promise = this.http.get(url).map((res: Response) => res.json()).toPromise(); + loadPluginsConfiguration = (): Promise => { + const url: string = this.api.no_proxy_root + this.api.GET_plugins_configuration; + const promise: Promise = this.http.get(url).toPromise(); return new Promise((resolve) => { promise.then((pluginsData: Plugins) => { PluginsConfiguration.plugins = pluginsData; resolve(); }).catch((ex) => { - console.error("Error loading plugins configuration from FE", ex); + console.error('Error loading plugins configuration from FE', ex); PluginsConfiguration.plugins = [] as Plugins; resolve(); diff --git a/catalog-ui/src/app/ng2/services/cookie.service.ts b/catalog-ui/src/app/ng2/services/cookie.service.ts index 2a783fdd48..61d13186fa 100644 --- a/catalog-ui/src/app/ng2/services/cookie.service.ts +++ b/catalog-ui/src/app/ng2/services/cookie.service.ts @@ -19,7 +19,7 @@ */ import {Injectable, Inject} from '@angular/core'; -import {IAppConfigurtaion, ICookie} from "../../models/app-config"; +import {ICookie} from "../../models/app-config"; import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; @Injectable() diff --git a/catalog-ui/src/app/ng2/services/data-type.service.ts b/catalog-ui/src/app/ng2/services/data-type.service.ts index cabaccd1d5..0559f35ae2 100644 --- a/catalog-ui/src/app/ng2/services/data-type.service.ts +++ b/catalog-ui/src/app/ng2/services/data-type.service.ts @@ -20,9 +20,9 @@ import * as _ from "lodash"; import { Injectable } from '@angular/core'; -import { DataTypeModel, DataTypesMap, PropertyBEModel, PropertyFEModel, DerivedFEProperty, DerivedFEPropertyMap } from "app/models"; +import { DataTypeModel, DataTypesMap, PropertyFEModel, DerivedFEProperty} from "app/models"; import { DataTypesService } from "app/services/data-types-service"; -import { PROPERTY_DATA, PROPERTY_TYPES } from "app/utils"; +import { PROPERTY_DATA } from "app/utils"; /** This is a new service for NG2, to eventually replace app/services/data-types-service.ts * @@ -32,13 +32,16 @@ import { PROPERTY_DATA, PROPERTY_TYPES } from "app/utils"; @Injectable() export class DataTypeService { - private dataTypes: DataTypesMap; + public dataTypes: DataTypesMap; constructor(private dataTypeService: DataTypesService) { this.dataTypes = dataTypeService.getAllDataTypes(); //This should eventually be replaced by an NG2 call to the backend instead of utilizing Angular1 downgraded component. } public getDataTypeByTypeName(typeName: string): DataTypeModel { + if(!this.dataTypes){ + this.dataTypes = this.dataTypeService.getAllDataTypes(); + } if (!this.dataTypes[typeName]) console.log("MISSING Datatype: " + typeName); return this.dataTypes[typeName]; } @@ -47,6 +50,13 @@ export class DataTypeService { return this.dataTypes; } + public getConstraintsByParentTypeAndUniqueID(rootPropertyType, propertyName){ + // const property = this.dataTypes[rootPropertyType].properties.filter(property => + // property.name == propertyName); + // return property[0] && property[0].constraints ? property[0].constraints[0].validValues : null; + return null; + } + public getDerivedDataTypeProperties(dataTypeObj: DataTypeModel, propertiesArray: Array, parentName: string) { //push all child properties to array diff --git a/catalog-ui/src/app/ng2/services/event-bus.service.ts b/catalog-ui/src/app/ng2/services/event-bus.service.ts index cc53d7978b..2a15ca25db 100644 --- a/catalog-ui/src/app/ng2/services/event-bus.service.ts +++ b/catalog-ui/src/app/ng2/services/event-bus.service.ts @@ -1,5 +1,5 @@ import {Injectable} from '@angular/core'; -import {BasePubSub, IPubSubEvent} from 'sdc-pubsub'; +import {BasePubSub, IPubSubEvent} from "sdc-pubsub"; @Injectable() export class EventBusService extends BasePubSub { diff --git a/catalog-ui/src/app/ng2/services/file-utils.service.ts b/catalog-ui/src/app/ng2/services/file-utils.service.ts new file mode 100644 index 0000000000..57897492b0 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/file-utils.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from "@angular/core"; +import { WindowRef } from "./window.service"; + +@Injectable() +export class FileUtilsService { + constructor(private windowRef: WindowRef){} + + 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.windowRef.nativeWindow.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); + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/gab.service.ts b/catalog-ui/src/app/ng2/services/gab.service.ts index d903d20ade..b62c566dd6 100644 --- a/catalog-ui/src/app/ng2/services/gab.service.ts +++ b/catalog-ui/src/app/ng2/services/gab.service.ts @@ -18,12 +18,13 @@ * ============LICENSE_END========================================================= */ -import {Injectable, Inject} from "@angular/core"; -import {Response} from '@angular/http'; -import {HttpService} from "./http.service"; -import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; -import {Observable} from "rxjs"; +import { HttpClient } from '@angular/common/http'; +import { Inject, Injectable } from '@angular/core'; +import { Response } from '@angular/http'; +import { Observable } from 'rxjs'; +import { ISdcConfig, SdcConfigToken } from '../config/sdc-config.config'; +// tslint:disable-next-line:interface-name export interface IServerResponse { data: [{ [key: string]: string }]; } @@ -36,24 +37,25 @@ export class GabRequest { } } +// tslint:disable-next-line:max-classes-per-file @Injectable() export class GabService { baseUrl: string; gabUrl: string; - constructor(@Inject(SdcConfigToken) sdcConfig: ISdcConfig, private http: HttpService) { + constructor(@Inject(SdcConfigToken) sdcConfig: ISdcConfig, protected http: HttpClient) { this.baseUrl = sdcConfig.api.root; this.gabUrl = sdcConfig.api.POST_GAB_Search; } public getArtifact(artifactUniqueId: string, resourceId: string, columns: string[]): Observable { - let finalUrl: string = this.baseUrl + this.gabUrl; - let request: GabRequest = { + const finalUrl: string = this.baseUrl + this.gabUrl; + const request: GabRequest = { fields: columns, parentId: resourceId, - artifactUniqueId: artifactUniqueId + artifactUniqueId }; - return this.http.post(finalUrl, request); + return this.http.post(finalUrl, request); } } \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/groups.service.ts b/catalog-ui/src/app/ng2/services/groups.service.ts index e3b3d85b50..8b2bcd3415 100644 --- a/catalog-ui/src/app/ng2/services/groups.service.ts +++ b/catalog-ui/src/app/ng2/services/groups.service.ts @@ -1,11 +1,11 @@ import {IZoneInstanceAssignment} from '../../models/graph/zones/zone-instance'; import {Injectable, Inject} from "@angular/core"; import {Observable} from "rxjs/Observable"; -import {HttpService} from "./http.service"; import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; import {GroupInstance} from '../../models/graph/zones/group-instance'; import {UiBaseObject} from "../../models/ui-models/ui-base-object"; import {IZoneService} from "../../models/graph/zones/zone"; +import { HttpClient } from '@angular/common/http'; @Injectable() export class GroupsService implements IZoneService { @@ -17,13 +17,13 @@ export class GroupsService implements IZoneService { 'SERVICE': 'services' } - constructor(private http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + constructor(private http: HttpClient, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { this.baseUrl = sdcConfig.api.root; } - public createGroupInstance(componentType:string, componentUniqueId:string, groupType:string) { - return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[componentType.toUpperCase()] + '/' + componentUniqueId + '/groups/' + groupType, {}).map(resp => { - return resp.json(); + public createGroupInstance(componentType:string, componentUniqueId:string, groupType:string): Observable{ + return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[componentType.toUpperCase()] + '/' + componentUniqueId + '/groups/' + groupType, {}).map(resp => { + return new GroupInstance(resp); }); }; @@ -40,8 +40,7 @@ export class GroupsService implements IZoneService { } public updateGroupMembers(topologyTemplateType:string, topologyTemplateId:string, groupId:string, members:Array):Observable> { - return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId + '/members', members) - .map(response => response.json()); + return this.http.post>(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId + '/members', members); } public updateMembers(topologyTemplateType:string, topologyTemplateId:string, groupId:string, members:Array):Observable> { @@ -50,22 +49,18 @@ export class GroupsService implements IZoneService { } public getSpecificGroup(topologyTemplateType:string, topologyTemplateId:string, groupId:string):Observable { - return this.http.get(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId) + return this.http.get(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId) .map(res => { - return new GroupInstance(res.json()); + return new GroupInstance(res); }); } public updateName(topologyTemplateType:string, topologyTemplateId:string, groupId:string, newName:string):Observable { - return this.http.put(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId, {name: newName}).map(resp => { - return resp.json(); - }); + return this.http.put(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId, {name: newName}); }; public deleteGroup(topologyTemplateType:string, topologyTemplateId:string, groupId:string) { - return this.http.delete(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId).map(resp => { - return resp.json(); - }); + return this.http.delete(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/groups/' + groupId); }; public updateZoneInstanceAssignments(topologyTemplateType:string, topologyTemplateId:string, policyId:string, members:Array):Observable { @@ -75,4 +70,6 @@ export class GroupsService implements IZoneService { public deleteZoneInstance(topologyTemplateType:string, topologyTemplateId:string, policyId:string):Observable { return this.deleteGroup(topologyTemplateType, topologyTemplateId, policyId); }; -} \ No newline at end of file + + +} diff --git a/catalog-ui/src/app/ng2/services/home.service.ts b/catalog-ui/src/app/ng2/services/home.service.ts new file mode 100644 index 0000000000..c472aa80c1 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/home.service.ts @@ -0,0 +1,74 @@ +/*- + * ============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========================================================= + */ +import { HttpClient } from '@angular/common/http'; +import { Inject, Injectable } from '@angular/core'; +import { Component, IApi, Resource, Service } from 'app/models'; +import { ComponentFactory } from 'app/utils/component-factory'; +import { Observable } from 'rxjs'; +import { ISdcConfig, SdcConfigToken } from '../config/sdc-config.config'; +import { SharingService } from './sharing.service'; + +// tslint:disable-next-line:interface-name +interface IComponentsArray { + services: Service[]; + resources: Resource[]; +} + +@Injectable() +export class HomeService { + private api: IApi; + private smallObjectAttributes = [ + 'uniqueId', 'name', 'componentType', 'resourceType', 'lastUpdateDate', 'lifecycleState', 'distributionStatus', + 'icon', 'version' + ]; + + constructor(private http: HttpClient, + @Inject(SdcConfigToken) private sdcConfig: ISdcConfig, + private sharingService: SharingService, + private componentFactory: ComponentFactory) { + this.api = sdcConfig.api; + } + + public getAllComponents(smallObjects?: boolean): Observable { + return this.http.get(this.api.root + this.api.GET_element) + .map((response) => { + const componentResponse: IComponentsArray = response; + let componentsList: Component[] = []; + + componentResponse.services && componentResponse.services.forEach((serviceResponse: Service) => { + serviceResponse = (smallObjects) ? _.pick(serviceResponse, this.smallObjectAttributes) : serviceResponse; + const component: Service = this.componentFactory.createService(serviceResponse); + componentsList.push(component); + this.sharingService.addUuidValue(component.uniqueId, component.uuid); + }); + + componentResponse.resources && componentResponse.resources.forEach((resourceResponse: Resource) => { + resourceResponse = (smallObjects) ? _.pick(resourceResponse, this.smallObjectAttributes) : resourceResponse; + const component: Resource = this.componentFactory.createResource(resourceResponse); + componentsList.push(component); + this.sharingService.addUuidValue(component.uniqueId, component.uuid); + }); + + componentsList = _.orderBy(componentsList, ['lastUpdateDate'], ['desc']); + + return componentsList; + }); + } +} diff --git a/catalog-ui/src/app/ng2/services/http-hepler.service.ts b/catalog-ui/src/app/ng2/services/http-hepler.service.ts new file mode 100644 index 0000000000..2b11067a24 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/http-hepler.service.ts @@ -0,0 +1,31 @@ +import { Injectable, Inject } from "@angular/core"; +import { Dictionary } from "../../utils/dictionary/dictionary"; +import { SharingService } from "../services/sharing.service"; +import { SdcConfigToken, ISdcConfig } from "../config/sdc-config.config"; + + +@Injectable() +export class HttpHelperService { + constructor( private sharingService: SharingService, + @Inject(SdcConfigToken) private sdcConfig: ISdcConfig){} + + public getUuidValue = (url: string): string => { + let map: Dictionary = this.sharingService.getUuidMap(); + if (map && url.indexOf(this.sdcConfig.api.root) > 0) { + map.forEach((key: string) => { + if (url.indexOf(key) !== -1) { + return this.sharingService.getUuidValue(key); + } + }); + } + return ''; + } + public static replaceUrlParams(url: string, urlParams: { [index: string]: any }): string { + return url.replace(/:(\w+)/g, (m, p1): string => urlParams[p1] || ''); + } + public static getHeaderMd5 = (object:any):string => { + let componentString:string = JSON.stringify(object); + let md5Result = md5(componentString).toLowerCase(); + return btoa(md5Result); + }; +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/http.service.ts b/catalog-ui/src/app/ng2/services/http.service.ts deleted file mode 100644 index 2785688ace..0000000000 --- a/catalog-ui/src/app/ng2/services/http.service.ts +++ /dev/null @@ -1,100 +0,0 @@ -/*- - * ============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========================================================= - */ - -import {Injectable, Inject} from '@angular/core'; -import {Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers} from '@angular/http'; -import {Observable} from 'rxjs/Observable'; -import {UUID} from 'angular2-uuid'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/catch'; -import 'rxjs/add/observable/throw'; -import {Dictionary} from "../../utils/dictionary/dictionary"; -import {SharingService, CookieService} from "app/services"; -import { ModalService } from "app/ng2/services/modal.service"; -import { ServerErrorResponse } from "app/models"; -import {ErrorMessageComponent} from "../components/ui/modal/error-message/error-message.component"; -import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; - -@Injectable() -export class HttpService extends Http { - - constructor(backend: XHRBackend, options: RequestOptions, private sharingService: SharingService, private cookieService: CookieService, private modalService: ModalService, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig) { - super(backend, options); - this._defaultOptions.withCredentials = true; - this._defaultOptions.headers.append(cookieService.getUserIdSuffix(), cookieService.getUserId()); - } - - request(request:string|Request, options?:RequestOptionsArgs):Observable { - /** - * For every request to the server, that the service id, or resource id is sent in the URL, need to pass UUID in the header. - * Check if the unique id exists in uuidMap, and if so get the UUID and add it to the header. - */ - if (typeof request === 'string') { // meaning we have to add the token to the options, not in url - if (!options) { - // make option object - options = {headers: new Headers()}; - } - - var uuidValue = this.getUuidValue(request); - if(uuidValue!= ''){ - options.headers['X-ECOMP-ServiceID'] = uuidValue; - - } - options.headers.set('X-ECOMP-RequestID', UUID.UUID()); - - } else { - // we have to add the token to the url object - var uuidValue = this.getUuidValue((request).url); - if(uuidValue!= ''){ - request.headers.set('X-ECOMP-ServiceID',uuidValue); - - } - request.headers.set('X-ECOMP-RequestID', UUID.UUID()); - } - return super.request(request, options).catch((err) => this.catchError(err)); - } - - private getUuidValue = (url: string) :string => { - let map:Dictionary = this.sharingService.getUuidMap(); - if (map && url.indexOf(this.sdcConfig.api.root) > 0) { - map.forEach((key:string) => { - if (url.indexOf(key) !== -1) { - return this.sharingService.getUuidValue(key); - } - }); - } - return ''; - } - - private catchError = (response: Response): Observable => { - - let modalInstance = this.modalService.createErrorModal("OK"); - let errorResponse: ServerErrorResponse = new ServerErrorResponse(response); - this.modalService.addDynamicContentToModal(modalInstance, ErrorMessageComponent, errorResponse); - modalInstance.instance.open(); - - return Observable.throw(response); - }; - - public static replaceUrlParams(url:string, urlParams:{[index:string]:any}):string { - return url.replace(/:(\w+)/g, (m, p1):string => urlParams[p1] || ''); - } - -} diff --git a/catalog-ui/src/app/ng2/services/modules.service.ts b/catalog-ui/src/app/ng2/services/modules.service.ts new file mode 100644 index 0000000000..857f0718d6 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/modules.service.ts @@ -0,0 +1,34 @@ +import {Inject, Injectable} from "@angular/core"; +import {HttpClient} from "@angular/common/http"; +import {ISdcConfig, SdcConfigToken} from "../config/sdc-config.config"; +import {DisplayModule, Module} from "../../models/modules/base-module"; +import {Observable} from "rxjs/Observable"; +import {ServerTypeUrl} from "../../utils/constants"; + +@Injectable() +export class ModulesService { + + protected baseUrl; + + constructor(private http: HttpClient, @Inject(SdcConfigToken) sdcConfig: ISdcConfig) { + this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; + } + + getComponentInstanceModule = (topologyTemplateType: string, topologyTemplateId: string, componentInstanceId: string, moduleId: string):Observable => { + return this.http.get(this.baseUrl + ServerTypeUrl.toServerTypeUrl(topologyTemplateType) + "/" + topologyTemplateId + "/resourceInstance/" + componentInstanceId + "/groupInstance/" + moduleId) + .map((response) => { + return new DisplayModule(response); + }) + }; + + getModuleForDisplay = (topologyTemplateType: string, topologyTemplateId: string, moduleId: string):Observable => { + return this.http.get(this.baseUrl + ServerTypeUrl.toServerTypeUrl(topologyTemplateType) + "/" + topologyTemplateId + "/groups/" + moduleId) + .map((response) => { + return new DisplayModule(response); + }) + }; + + public updateModuleMetadata = (topologyTemplateType: string, topologyTemplateId: string, module: Module):Observable => { + return this.http.put(this.baseUrl + ServerTypeUrl.toServerTypeUrl(topologyTemplateType) + "/" + topologyTemplateId + "/groups/" + module.uniqueId + "/metadata", module) + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/onboarding.service.ts b/catalog-ui/src/app/ng2/services/onboarding.service.ts new file mode 100644 index 0000000000..0ec4875d91 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/onboarding.service.ts @@ -0,0 +1,151 @@ +/** + * Created by rc2122 on 6/4/2018. + */ +/*- + * ============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========================================================= + */ + +'use strict'; +import {Inject, Injectable} from "@angular/core"; +import {SdcConfigToken, ISdcConfig} from "app/ng2/config/sdc-config.config"; +import {Observable} from "rxjs/Observable"; +import { HttpClient, HttpResponse } from "@angular/common/http"; +import { ComponentFactory } from "../../utils/component-factory"; +import { DEFAULT_ICON, ComponentType } from "../../utils/constants"; +import { ICsarComponent } from "../../models/csar-component"; +import { IApi } from "../../models/app-config"; +import { CacheService } from "./cache.service"; +import { IComponentMetadata, ComponentMetadata } from "../../models/component-metadata"; +import { IMainCategory, ISubCategory } from "../../models/category"; +import { Resource } from "../../models/components/resource"; + +export interface OnboardingComponents { + listCount: number; + results: Array +} + +@Injectable() +export class OnboardingService { + private api:IApi; + + constructor(protected http: HttpClient, + private cacheService:CacheService, + @Inject(SdcConfigToken) sdcConfig:ISdcConfig, + private componentFactory: ComponentFactory) { + this.api = sdcConfig.api; + } + + getOnboardingVSPs = (): Observable> =>{ + return this.http.get(this.api.GET_onboarding).map((onboardingVSPs) =>{ + return onboardingVSPs.results + }); + } + + getOnboardingComponents = ():Observable> => { + return this.getOnboardingVSPs().map((onboardingComponents) => { + let componentsMetadataList: Array = new Array(); + onboardingComponents.forEach((obc:ICsarComponent) => { + let componentMetaData: ComponentMetadata = this.createFromCsarComponent(obc); + componentsMetadataList.push(componentMetaData); + }); + return componentsMetadataList; + }); + }; + + public createFromCsarComponent = (csar:ICsarComponent): ComponentMetadata => { + let newMetadata = new ComponentMetadata(); + newMetadata.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 = new Array(); + let subcategories:Array = new Array(); + if (selectedCategory && selectedSubCategory) { + subcategories.push(selectedSubCategory); + selectedCategory.subcategories = subcategories; + categories.push(selectedCategory); + } + + // Fill the component with details from CSAR + + newMetadata.categories = categories; + newMetadata.vendorName = csar.vendorName; + newMetadata.vendorRelease = csar.vendorRelease; + newMetadata.csarUUID = csar.packageId; + newMetadata.csarPackageType = csar.packageType; + newMetadata.csarVersion = csar.version; + newMetadata.packageId = csar.packageId; + newMetadata.description = csar.description; + newMetadata.selectedCategory = selectedCategory && selectedSubCategory ? selectedCategory.name + "_#_" + selectedSubCategory.name : ''; + newMetadata.filterTerm = newMetadata.name + ' ' + newMetadata.description + ' ' + newMetadata.vendorName + ' ' + newMetadata.csarVersion; + newMetadata.resourceType = "VF"; + newMetadata.componentType = ComponentType.RESOURCE; + newMetadata.tags = []; + newMetadata.icon = DEFAULT_ICON; + newMetadata.iconSprite = "sprite-resource-icons"; + return newMetadata; + }; + + downloadOnboardingCsar = (packageId:string):Observable> => { + return this.http.get(this.api.GET_onboarding + "/" + packageId, {observe: 'response', responseType: 'blob'}); + }; + + getComponentFromCsarUuid = (csarUuid:string):Observable => { + return this.http.get(this.api.root + this.api.GET_component_from_csar_uuid.replace(':csar_uuid', csarUuid)) + .map((response: any) => { + // If the status is 400, this means that the component not found. + // I do not want to return error from server, because a popup will appear in client with the error. + // So returning success (200) with status 400. + if (response.status !== 400) { + let componentMetadata = new ComponentMetadata(); + componentMetadata = response; + return componentMetadata; + } + }); + }; + + //TODO remove when workspace page convert to angular5 + convertMetaDataToComponent(componentMetadata: ComponentMetadata) { + let newResource: Resource = this.componentFactory.createEmptyComponent(ComponentType.RESOURCE); + newResource.setComponentMetadata(componentMetadata); + return newResource; + } +} diff --git a/catalog-ui/src/app/ng2/services/plugins.service.ts b/catalog-ui/src/app/ng2/services/plugins.service.ts index 2a3b68fe25..0f71eee1f0 100644 --- a/catalog-ui/src/app/ng2/services/plugins.service.ts +++ b/catalog-ui/src/app/ng2/services/plugins.service.ts @@ -1,40 +1,40 @@ -import { Injectable, Inject } from '@angular/core'; +import {Inject, Injectable} from '@angular/core'; import {Observable} from 'rxjs/Observable'; import {Http, Response} from '@angular/http'; -import {IApi, IAppConfigurtaion, Plugin, Plugins, PluginsConfiguration} from "app/models"; +import {IApi, IAppConfigurtaion, Plugin, PluginsConfiguration} from "app/models"; import {ISdcConfig, SdcConfigToken} from "../config/sdc-config.config"; @Injectable() export class PluginsService { - private baseUrl; public configuration: IAppConfigurtaion; public api: IApi; + private baseUrl; - constructor(private http: Http, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig) { + constructor(private http: Http, @Inject(SdcConfigToken) private sdcConfig: ISdcConfig) { this.api = this.sdcConfig.api; this.baseUrl = this.api.root + this.sdcConfig.api.component_api_root; } public getPluginByStateUrl = (stateUrl: string) => { - let pluginKey: any = _.findKey(PluginsConfiguration.plugins, (pluginConfig: Plugin) =>{ - return pluginConfig.pluginStateUrl === stateUrl; + let pluginKey: any = _.findKey(PluginsConfiguration.plugins, (pluginConfig: Plugin) => { + return pluginConfig.pluginStateUrl === stateUrl; }); return PluginsConfiguration.plugins[pluginKey]; }; - public isPluginDisplayedInContext = (plugin: Plugin ,userRole: string, contextType: string) => { + public isPluginDisplayedInContext = (plugin: Plugin, userRole: string, contextType: string) => { return plugin.pluginDisplayOptions["context"] && - plugin.pluginDisplayOptions["context"].displayRoles.includes(userRole) && - plugin.pluginDisplayOptions["context"].displayContext.indexOf(contextType) !== -1 + plugin.pluginDisplayOptions["context"].displayRoles.includes(userRole) && + plugin.pluginDisplayOptions["context"].displayContext.indexOf(contextType) !== -1 }; public isPluginOnline = (pluginId: string): Observable => { - let url:string = this.api.no_proxy_root + this.api.GET_plugin_online_state.replace(':pluginId', pluginId); + let url: string = this.api.no_proxy_root + this.api.GET_plugin_online_state.replace(':pluginId', pluginId); return this.http.get(url).map((res: Response) => { return res.json() }) - .catch(error => Observable.of(false)); + .catch(error => Observable.of(false)); } } diff --git a/catalog-ui/src/app/ng2/services/policies.service.ts b/catalog-ui/src/app/ng2/services/policies.service.ts index 3675a7b9ab..a1a9013303 100644 --- a/catalog-ui/src/app/ng2/services/policies.service.ts +++ b/catalog-ui/src/app/ng2/services/policies.service.ts @@ -20,13 +20,13 @@ import {Injectable, Inject} from "@angular/core"; import {Observable} from "rxjs/Observable"; -import {HttpService} from "./http.service"; import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; import {PolicyInstance, PolicyTargetsRequest} from '../../models/graph/zones/policy-instance'; import {IZoneInstanceAssignment} from "../../models/graph/zones/zone-instance"; import {IZoneService} from "../../models/graph/zones/zone"; import {TargetUiObject} from "../../models/ui-models/ui-target-object"; import {TargetOrMemberType} from "../../utils/constants"; +import { HttpClient } from "@angular/common/http"; @Injectable() @@ -38,14 +38,12 @@ export class PoliciesService implements IZoneService { 'SERVICE': 'services' } - constructor(private http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + constructor(private http: HttpClient, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { this.baseUrl = sdcConfig.api.root; } - public createPolicyInstance(topologyTemplateType:string, topologyTemplateId:string, policyType:string) { - return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyType, {}).map(resp => { - return resp.json(); - }); + public createPolicyInstance(topologyTemplateType:string, topologyTemplateId:string, policyType:string): Observable { + return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyType, {}); } public addPolicyTarget(topologyTemplateType:string, topologyTemplateId:string, policy:PolicyInstance, targetId:string, targetType:TargetOrMemberType) { @@ -76,8 +74,8 @@ export class PoliciesService implements IZoneService { } public updatePolicyTargets(topologyTemplateType:string, topologyTemplateId:string, policyId:string, targets:PolicyTargetsRequest): Observable { - return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId + '/targets', targets.requestItems) - .map(response => new PolicyInstance(response.json())); + return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId + '/targets', targets.requestItems) + .map(response => new PolicyInstance(response)); } public updateTargets(topologyTemplateType:string, topologyTemplateId:string, policyId:string, targets:Array):Observable { @@ -94,22 +92,18 @@ export class PoliciesService implements IZoneService { } public getSpecificPolicy(topologyTemplateType:string, topologyTemplateId:string, policyId:string):Observable { - return this.http.get(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId) + return this.http.get(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId) .map(res => { - return new PolicyInstance(res.json()); + return new PolicyInstance(res); }); } public updateName(topologyTemplateType:string, topologyTemplateId:string, policyId:string, newName:string):Observable { - return this.http.put(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId, {name: newName}).map(res => { - return res.json(); - }); + return this.http.put(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId, {name: newName}); }; public deletePolicy(topologyTemplateType:string, topologyTemplateId:string, policyId:string) { - return this.http.delete(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId).map(resp => { - return resp.json(); - }); + return this.http.delete(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[topologyTemplateType.toUpperCase()] + '/' + topologyTemplateId + '/policies/' + policyId); }; public updateZoneInstanceAssignments(topologyTemplateType:string, topologyTemplateId:string, policyId:string, targets:Array):Observable{ diff --git a/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts b/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts index d297ea0874..f161babfa6 100644 --- a/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts +++ b/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts @@ -22,12 +22,15 @@ * Created by ob0695 on 4/18/2017. */ -import { ArtifactGroupModel, PropertyModel, PropertiesGroup, InputsGroup, AttributeModel, AttributesGroup, ComponentInstance, OperationModel, - InputBEModel, Module, ComponentMetadata, RelationshipModel, RequirementsGroup, CapabilitiesGroup, InterfaceModel} from "app/models"; +import { ArtifactGroupModel, PropertyModel, PropertiesGroup, AttributeModel, AttributesGroup, ComponentInstance, OperationModel, + InputBEModel, Module, ComponentMetadata, RelationshipModel, RequirementsGroup, CapabilitiesGroup} from "app/models"; import {CommonUtils} from "app/utils"; import {Serializable} from "../utils/serializable"; +import {PropertyBEModel} from "../../../models/properties-inputs/property-be-model"; import { PolicyInstance } from "app/models/graph/zones/policy-instance"; import { GroupInstance } from "../../../models/graph/zones/group-instance"; +import { InputsGroup } from "../../../models/inputs"; +import { InterfaceModel } from "../../../models/operation"; export class ComponentGenericResponse implements Serializable { @@ -40,7 +43,7 @@ export class ComponentGenericResponse implements Serializable; public componentInstances:Array; - public componentInstancesInterfaces:Map>; + public componentInstancesInterfaces: Map>; public inputs:Array; public capabilities:CapabilitiesGroup; public requirements:RequirementsGroup; @@ -75,7 +78,7 @@ export class ComponentGenericResponse implements Serializable { public forwardingPaths: { [key:string]:ForwardingPath } = {}; + public serviceApiArtifacts: ArtifactGroupModel; deserialize (response): ServiceGenericResponse { super.deserialize(response); + this.serviceApiArtifacts = new ArtifactGroupModel(response.serviceApiArtifacts); if(response.forwardingPaths) { _.forEach(response.forwardingPaths, (pathResponse, id) => { let pathId = id; diff --git a/catalog-ui/src/app/ng2/services/sharing.service.ts b/catalog-ui/src/app/ng2/services/sharing.service.ts new file mode 100644 index 0000000000..0a6b8cb1af --- /dev/null +++ b/catalog-ui/src/app/ng2/services/sharing.service.ts @@ -0,0 +1,42 @@ +/*- + * ============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========================================================= + */ + +import {Injectable} from "@angular/core"; +import {Dictionary} from "app/utils"; + +@Injectable() +export class SharingService { + private uuidMap:Dictionary = new Dictionary(); + + constructor() { + } + + public getUuidValue(uniqueId:string): string { + return this.uuidMap.getValue(uniqueId); + } + + public addUuidValue(uniqueId:string, uuid:string): void { + this.uuidMap.setValue(uniqueId, uuid); + } + + public getUuidMap() :Dictionary { + return this.uuidMap; + } +} diff --git a/catalog-ui/src/app/ng2/services/tosca-types.service.ts b/catalog-ui/src/app/ng2/services/tosca-types.service.ts index 66826c0fef..83b833b1ab 100644 --- a/catalog-ui/src/app/ng2/services/tosca-types.service.ts +++ b/catalog-ui/src/app/ng2/services/tosca-types.service.ts @@ -14,12 +14,19 @@ * permissions and limitations under the License. */ -import {Injectable, Inject} from '@angular/core'; -import {Observable} from 'rxjs/Observable'; -import {HttpService} from './http.service'; -import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; -import {CapabilityTypesMap, NodeTypesMap, RelationshipTypesMap} from "app/models"; -import {Response} from '@angular/http'; +import { HttpClient } from '@angular/common/http'; +import { Inject, Injectable } from '@angular/core'; +import { Response } from '@angular/http'; +import { + CapabilityTypeModel, + CapabilityTypesMap, + IComponentsArray, + NodeTypesMap, + RelationshipTypesMap +} from 'app/models'; +import { Observable } from 'rxjs/Observable'; +import { ISdcConfig, SdcConfigToken } from '../config/sdc-config.config'; +import 'rxjs/add/operator/toPromise'; declare var angular: angular.IAngularStatic; @@ -28,28 +35,20 @@ export class ToscaTypesServiceNg2 { protected baseUrl; - constructor(protected http: HttpService, @Inject(SdcConfigToken) sdcConfig: ISdcConfig) { + constructor(protected http: HttpClient, @Inject(SdcConfigToken) sdcConfig: ISdcConfig) { this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; } - fetchRelationshipTypes(): Observable { - return this.http.get(this.baseUrl + 'relationshipTypes') - .map((res: Response) => { - return res.json(); - }); + async fetchRelationshipTypes(): Promise { + return this.http.get(this.baseUrl + 'relationshipTypes').toPromise(); } - fetchNodeTypes(): Observable { - return this.http.get(this.baseUrl + 'nodeTypes') - .map((res: Response) => { - return res.json(); - }); + async fetchNodeTypes(): Promise { + return this.http.get(this.baseUrl + 'nodeTypes').toPromise(); } - fetchCapabilityTypes(): Observable { - return this.http.get(this.baseUrl + 'capabilityTypes') - .map((res: Response) => { - return res.json(); - }); + async fetchCapabilityTypes(): Promise{ + return this.http.get(this.baseUrl + 'capabilityTypes').toPromise(); } } + diff --git a/catalog-ui/src/app/ng2/services/user.service.ts b/catalog-ui/src/app/ng2/services/user.service.ts index 87e90432dc..f4186a1087 100644 --- a/catalog-ui/src/app/ng2/services/user.service.ts +++ b/catalog-ui/src/app/ng2/services/user.service.ts @@ -19,80 +19,34 @@ */ import { Injectable, Inject } from "@angular/core"; -import { Headers } from "@angular/http"; import { Observable } from "rxjs/Observable"; -import { HttpService } from "./http.service"; -import { Cookie2Service } from "./cookie.service"; import { IUserProperties } from "../../models/user"; -import {ICookie} from "../../models/app-config"; import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; - +import { HttpClient } from "@angular/common/http"; +import { HttpHelperService } from "./http-hepler.service"; +/** + * User Service provides CRUD for Users. See authentication service for authentication/login. + */ @Injectable() -export class UserService { +export class UserService { private url:string; - private authorizeUrl:string; - private _loggedinUser:IUserProperties; - - constructor(private httpService:HttpService, - private cookieService:Cookie2Service, + constructor(private http: HttpClient, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig) { this.url = this.sdcConfig.api.root + this.sdcConfig.api.GET_user; - this.authorizeUrl = this.sdcConfig.api.root + this.sdcConfig.api.GET_user_authorize; } - public authorize() :Observable { - let cookie:ICookie = this.sdcConfig.cookie; - let authorizeHeaders:Headers = new Headers(); - authorizeHeaders.set(cookie.userFirstName, this.cookieService.getFirstName()); - authorizeHeaders.set(cookie.userLastName, this.cookieService.getLastName()); - authorizeHeaders.set(cookie.userEmail, this.cookieService.getEmail()); - authorizeHeaders.set(cookie.userIdSuffix, this.cookieService.getUserId()); - - return this.httpService.get( - this.authorizeUrl, - { headers: authorizeHeaders } - ).map(resp => resp.json()); - } public getAllUsers() :Observable { - return this.httpService.get( + return this.http.get( this.sdcConfig.api.root + this.sdcConfig.api.GET_all_users - ).map(resp => resp.json()); + ).map(resp => resp) ; } public getUser(userId:string) :Observable { - return this.httpService.get( - HttpService.replaceUrlParams(this.url, { id: userId }) - ).map(resp => resp.json()); - } - - public createUser(userData:{[index:string]: any}) :Observable { - return this.httpService.post( - this.sdcConfig.api.root + this.sdcConfig.api.POST_create_user, - userData - ).map(resp => resp.json()); + return this.http.get( + HttpHelperService.replaceUrlParams(this.url, { id: userId }) + ).map(resp => resp); } - - public deleteUser(userId:string) :Observable { - return this.httpService.delete( - HttpService.replaceUrlParams(this.sdcConfig.api.root + this.sdcConfig.api.DELETE_delete_user, { id: userId }) - ).map(resp => resp.json()); - } - - public editUserRole(userId:string, role:string) :Observable { - return this.httpService.post( - HttpService.replaceUrlParams(this.sdcConfig.api.root + this.sdcConfig.api.POST_edit_user_role, { id: userId }), - { role: role } - ).map(resp => resp.json()); - } - - public getLoggedinUser():IUserProperties { - return this._loggedinUser; - } - - public setLoggedinUser(loggedinUser:IUserProperties) { - this._loggedinUser = loggedinUser; - }; } diff --git a/catalog-ui/src/app/ng2/services/workflow.service.ts b/catalog-ui/src/app/ng2/services/workflow.service.ts index 81a2ea3b7f..044ca37266 100644 --- a/catalog-ui/src/app/ng2/services/workflow.service.ts +++ b/catalog-ui/src/app/ng2/services/workflow.service.ts @@ -1,14 +1,13 @@ import { Injectable, Inject } from "@angular/core"; -import { Response } from "@angular/http"; import { Observable } from "rxjs/Observable"; -import { HttpService } from "./http.service"; import { SdcConfigToken, ISdcConfig } from "../config/sdc-config.config"; +import { HttpClient } from "@angular/common/http"; import { Component, OperationModel } from "app/models"; interface WorkflowOutputParameter { - name: string, - type: string, - mandatory: boolean + name: string; + type: string; + mandatory: boolean; } interface WorkflowInputParameter extends WorkflowOutputParameter { @@ -25,40 +24,31 @@ export class WorkflowServiceNg2 { WF_STATE_ARCHIVED = 'ARCHIVED'; VERSION_STATE_CERTIFIED = 'CERTIFIED'; - constructor(private http: HttpService, @Inject(SdcConfigToken) sdcConfig: ISdcConfig) { + constructor(private http: HttpClient, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { this.baseUrl = sdcConfig.api.workflow_root; this.catalogBaseUrl = sdcConfig.api.POST_workflow_artifact; } public associateWorkflowArtifact(component: Component, operation: OperationModel): Observable { - return this.http.post(this.baseUrl + '/workflows/' + operation.workflowId + '/versions/' + operation.workflowVersionId + '/artifact-deliveries', { - endpoint: this.catalogBaseUrl + '/' + component.getTypeUrl() + component.uuid + '/interfaces/' + operation.interfaceId + '/operations/' + operation.uniqueId + '/artifacts/' + operation.implementation.artifactUUID, - method: 'POST' - }) - .map((res:Response) => { - return res.json(); + return this.http.post(this.baseUrl + '/workflows/' + operation.workflowId + '/versions/' + operation.workflowVersionId + '/artifact-deliveries', { + endpoint: this.catalogBaseUrl + '/' + component.getTypeUrl() + component.uuid + '/interfaces/' + operation.interfaceId + '/operations/' + + operation.uniqueId + '/artifacts/' + operation.implementation.artifactUUID, method: 'POST' }); } public getWorkflows(filterCertified: boolean = true): Observable { - return this.http.get(this.baseUrl + '/workflows' + (filterCertified ? '?versionState=' + this.VERSION_STATE_CERTIFIED : '')) - .map((res:Response) => { - return res.json().items; - }); + return this.http.get(this.baseUrl + '/workflows' + (filterCertified ? '?versionState=' + this.VERSION_STATE_CERTIFIED : '')); } public getWorkflowVersions(workflowId: string, filterCertified: boolean = true): Observable { - return this.http.get(this.baseUrl + '/workflows/' + workflowId + '/versions' + (filterCertified ? '?state=' + this.VERSION_STATE_CERTIFIED : '')) - .map((res:Response) => { - return _.map(res.json().items, version => version); + return this.http.get(this.baseUrl + '/workflows/' + workflowId + '/versions' + (filterCertified ? '?state=' + this.VERSION_STATE_CERTIFIED : '')) + .map((res) => { + return res.items; }); } public updateWorkflowVersion(workflowId: string, versionId: string, payload: any): Observable { - return this.http.put(this.baseUrl + '/workflows/' + workflowId + '/versions/' + versionId, payload) - .map((res:Response) => { - return res.json(); - }); + return this.http.put(this.baseUrl + '/workflows/' + workflowId + '/versions/' + versionId, payload); } } diff --git a/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.html b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.html index bee493f2ae..7b9a5ff700 100644 --- a/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.html +++ b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.html @@ -13,7 +13,6 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -
diff --git a/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.ts b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.ts index 68cfedb32b..4cff9de72a 100644 --- a/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.ts +++ b/catalog-ui/src/app/ng2/shared/multiline-ellipsis/multiline-ellipsis.component.ts @@ -1,4 +1,4 @@ -import {Component, OnChanges, AfterViewChecked, ViewChild, ElementRef, Input, Output, SimpleChanges, EventEmitter} from "@angular/core"; +import {Component, OnChanges, AfterContentInit, ViewChild, ElementRef, Input, Output, SimpleChanges, EventEmitter} from "@angular/core"; import {WindowRef} from "../../services/window.service"; @Component({ @@ -6,7 +6,7 @@ import {WindowRef} from "../../services/window.service"; templateUrl: 'multiline-ellipsis.component.html', styleUrls: ['multiline-ellipsis.component.less'] }) -export class MultilineEllipsisComponent implements OnChanges, AfterViewChecked { +export class MultilineEllipsisComponent implements OnChanges, AfterContentInit { @Input() public lines: number; @Input() public lineHeight: string; @@ -30,7 +30,7 @@ export class MultilineEllipsisComponent implements OnChanges, AfterViewChecked { this.prepareStyles() } - public ngAfterViewChecked() { + public ngAfterContentInit() { const hasEllipsis = (this.elmContainer.nativeElement.offsetHeight < this.elmContent.nativeElement.offsetHeight); if (hasEllipsis !== this.hasEllipsis) { this.hasEllipsis = hasEllipsis; diff --git a/catalog-ui/src/app/ng2/shared/translator/translate.pipe.ts b/catalog-ui/src/app/ng2/shared/translator/translate.pipe.ts index 4ec756bcc2..04f6650f38 100644 --- a/catalog-ui/src/app/ng2/shared/translator/translate.pipe.ts +++ b/catalog-ui/src/app/ng2/shared/translator/translate.pipe.ts @@ -19,38 +19,32 @@ */ import { Pipe, PipeTransform } from '@angular/core'; -import { TranslateService, ITranslateArgs } from "./translate.service"; +import { ITranslateArgs, TranslateService } from './translate.service'; +// tslint:disable-next-line:interface-name +interface ITranslateParams { + phrase: string; + args: ITranslateArgs; + language: string; +} @Pipe({ name: 'translate', pure: false }) export class TranslatePipe implements PipeTransform { - private translated:string; - private lastParams: { - phrase: string; - args: {[index: string]: any}; - language: string; - } = { + private translated: string; + private lastParams: ITranslateParams = { phrase: undefined, args: undefined, language: undefined }; - constructor(private translateService:TranslateService) { - } - - private shouldUpdate(curParams:{[index:string]: any}) : boolean { - return ( - curParams.language !== this.lastParams.language || - curParams.args !== this.lastParams.args || - curParams.phrase !== this.lastParams.phrase - ); + constructor(private translateService: TranslateService) { } - public transform(phrase:string, args:ITranslateArgs, language:string=this.translateService.activeLanguage) : string { - const curParams = { phrase, args, language }; + public transform(phrase: string, args: ITranslateArgs, language: string = this.translateService.activeLanguage): string { + const curParams: ITranslateParams = { phrase, args, language }; if (this.shouldUpdate(curParams)) { this.lastParams = curParams; this.translated = this.translateService.translate(phrase, args, language); @@ -58,4 +52,12 @@ export class TranslatePipe implements PipeTransform { return this.translated; } + + private shouldUpdate(curParams: ITranslateParams): boolean { + return ( + curParams.language !== this.lastParams.language || + curParams.args !== this.lastParams.args || + curParams.phrase !== this.lastParams.phrase + ); + } } diff --git a/catalog-ui/src/app/ng2/shared/translator/translate.service.config.ts b/catalog-ui/src/app/ng2/shared/translator/translate.service.config.ts index a1d7833062..6413f6a64e 100644 --- a/catalog-ui/src/app/ng2/shared/translator/translate.service.config.ts +++ b/catalog-ui/src/app/ng2/shared/translator/translate.service.config.ts @@ -18,9 +18,9 @@ * ============LICENSE_END========================================================= */ -import { OpaqueToken } from "@angular/core"; +import { InjectionToken } from "@angular/core"; -export const TranslateServiceConfigToken = new OpaqueToken('TranslateServiceConfigToken'); +export const TranslateServiceConfigToken = new InjectionToken('TranslateServiceConfigToken'); export interface ITranslateServiceConfig { filePrefix:string; diff --git a/catalog-ui/src/app/ng2/shared/translator/translate.service.ts b/catalog-ui/src/app/ng2/shared/translator/translate.service.ts index ff7c643b46..0b5ddae557 100644 --- a/catalog-ui/src/app/ng2/shared/translator/translate.service.ts +++ b/catalog-ui/src/app/ng2/shared/translator/translate.service.ts @@ -19,9 +19,9 @@ */ import { Injectable, Inject } from "@angular/core"; -import { Response, Http } from "@angular/http"; -import { Observable, Observer, ConnectableObservable, Subscription } from "rxjs"; import { ITranslateServiceConfig, TranslateServiceConfigToken } from "./translate.service.config"; +import { Observer, Subscription, Observable, ConnectableObservable } from 'rxjs/Rx'; +import { HttpClient } from "@angular/common/http"; export { ITranslateServiceConfig, TranslateServiceConfigToken }; @@ -145,7 +145,7 @@ export class TranslateService { private _cacheLanguagesJsons:{[index:string]:ITranslateLanguageJson} = {}; private _cacheLanguagesLoaders:{[index:string]:Observable} = {}; - constructor(@Inject(TranslateServiceConfigToken) private config:ITranslateServiceConfig, private http:Http) { + constructor(@Inject(TranslateServiceConfigToken) private config:ITranslateServiceConfig, private http: HttpClient) { this.initLanguageObservable(); this.loadAndActivateLanguage(this.config.defaultLanguage); } @@ -176,8 +176,7 @@ export class TranslateService { if (!(language in this._cacheLanguagesLoaders)) { const filePath = `${this.config.filePrefix}${language}${this.config.fileSuffix}`; - this._cacheLanguagesLoaders[language] = this.http.get(filePath) - .map(resp => resp.json()) + this._cacheLanguagesLoaders[language] = this.http.get(filePath) .catch(() => Observable.throw(`Failed to load language file for "${language}"`)) .publish(); (>this._cacheLanguagesLoaders[language]).connect(); @@ -204,12 +203,12 @@ export class TranslateService { return false; } - public loadAndActivateLanguage(language:string) : Observable { + public loadAndActivateLanguage(language:string) : void { + const loadLanguageObservable = this.loadLanguageJsonFile(language, false); loadLanguageObservable.subscribe(() => { this.activateLanguage(language); }, () => {}); - return loadLanguageObservable; } public translate(phraseKey:string, args:ITranslateArgs={}, language:string=this._activeLanguage) : string { diff --git a/catalog-ui/src/app/ng2/store/actions/artifacts.action.ts b/catalog-ui/src/app/ng2/store/actions/artifacts.action.ts new file mode 100644 index 0000000000..a00cc3a9ec --- /dev/null +++ b/catalog-ui/src/app/ng2/store/actions/artifacts.action.ts @@ -0,0 +1,25 @@ +/** + * Created by ob0695 + */ +import {ArtifactModel} from "../../../models/artifacts"; + +export class GetArtifactsByTypeAction { + static readonly type = '[ARTIFACTS] GetArtifactsByType'; + + constructor(public payload: {componentType:string, componentId:string, artifactType: string}) { + } +} + +export class CreateOrUpdateArtifactAction { + static readonly type = '[ARTIFACTS] CreateOrUpdateArtifactAction'; + + constructor(public payload: {componentType:string, componentId:string, artifact:ArtifactModel}) { + } +} + +export class DeleteArtifactAction { + static readonly type = '[ARTIFACTS] DeleteArtifactAction'; + + constructor(public payload: {componentType:string, componentId:string, artifact: ArtifactModel}) { + } +} diff --git a/catalog-ui/src/app/ng2/store/actions/instance-artifacts.actions.ts b/catalog-ui/src/app/ng2/store/actions/instance-artifacts.actions.ts new file mode 100644 index 0000000000..0f1df78352 --- /dev/null +++ b/catalog-ui/src/app/ng2/store/actions/instance-artifacts.actions.ts @@ -0,0 +1,32 @@ +/** + * Created by ob0695 + */ +import {ArtifactModel} from "../../../models/artifacts"; + +export class GetInstanceArtifactsByTypeAction { + static readonly type = '[INSTANCE_ARTIFACTS] GetInstanceArtifactsByTypeAction'; + + constructor(public payload: { componentType: string, componentId: string, artifactType: string, instanceId: string }) { + } +} + +export class CreateInstanceArtifactAction { + static readonly type = '[INSTANCE_ARTIFACTS] CreateInstanceArtifactAction'; + + constructor(public payload: { componentType: string, componentId: string, instanceId: string, artifact: ArtifactModel }) { + } +} + +export class UpdateInstanceArtifactAction { + static readonly type = '[INSTANCE_ARTIFACTS] UpdateInstanceArtifactAction'; + + constructor(public payload: { componentType: string, componentId: string, instanceId: string, artifact: ArtifactModel }) { + } +} + +export class DeleteInstanceArtifactAction { + static readonly type = '[INSTANCE_ARTIFACTS] DeleteInstanceArtifactAction'; + + constructor(public payload: { componentType: string, componentId: string, instanceId: string, artifact: ArtifactModel }) { + } +} diff --git a/catalog-ui/src/app/ng2/store/actions/workspace.action.ts b/catalog-ui/src/app/ng2/store/actions/workspace.action.ts new file mode 100644 index 0000000000..c7f18e0ac6 --- /dev/null +++ b/catalog-ui/src/app/ng2/store/actions/workspace.action.ts @@ -0,0 +1,17 @@ +/** + * Created by ob0695 on 7/17/2018. + */ + +export class UpdateIsViewOnly { + static readonly type = '[WORKSPACE] UpdateIsViewOnly'; + + constructor(public isViewOnly:boolean) { + } +} + +export class UpdateIsDesigner { + static readonly type = '[WORKSPACE] UpdateIsDesigner'; + + constructor(public isDesigner:boolean) { + } +} diff --git a/catalog-ui/src/app/ng2/store/states/artifacts.state.spec.ts b/catalog-ui/src/app/ng2/store/states/artifacts.state.spec.ts new file mode 100644 index 0000000000..c59a4455b6 --- /dev/null +++ b/catalog-ui/src/app/ng2/store/states/artifacts.state.spec.ts @@ -0,0 +1,62 @@ +import { Store } from '@ngxs/store'; +import { Observable } from 'rxjs/Rx'; +import { Mock } from 'ts-mockery'; +import { ArtifactModel } from '../../../models/artifacts'; +import { ArtifactGroupType } from '../../../utils/constants'; +import { ComponentInstanceServiceNg2 } from '../../services/component-instance-services/component-instance.service'; +import { GetInstanceArtifactsByTypeAction, UpdateInstanceArtifactAction } from '../actions/instance-artifacts.actions'; +import { InstanceArtifactsState } from './instance-artifacts.state'; + +describe('Test Artifact State', () => { + + const heat1 = Mock.of({ + uniqueId: '1', artifactName: 'heat1', timeout: 0, artifactDisplayName: 'heat1', artifactGroupType: ArtifactGroupType.DEPLOYMENT + }); + + const heat1env = Mock.of({ + uniqueId: '2', artifactName: 'heat1env', timeout: 0, generatedFromId: '1', artifactDisplayName: 'heat1env', artifactGroupType: ArtifactGroupType.DEPLOYMENT + }); + + const storeMock = Mock.of( { dispatch : jest.fn() }); + + const artifacts = [ + heat1, + heat1env + ]; + + /** + * NGXS Store state before we run the update + */ + const ngxsState = { + deploymentArtifacts : artifacts + }; + + /** + * The ENV artifact that we wish to update + */ + const updatedArtifact = Mock.of({ + uniqueId: '2', artifactName: 'heat1env', timeout: 33, generatedFromId: '1', artifactDisplayName: 'heat1env-UPDATE', artifactGroupType: ArtifactGroupType.DEPLOYMENT + }); + + const componentInstanceServiceMock: ComponentInstanceServiceNg2 = Mock.of({ + updateInstanceArtifact: jest.fn().mockImplementation(() => Observable.of(updatedArtifact)), + getComponentInstanceArtifactsByGroupType: jest.fn().mockImplementation(() => Observable.of([heat1, updatedArtifact])) + }); + + const actionMock: UpdateInstanceArtifactAction = Mock.of({ + payload: { + componentType: '', + componentId: '', + instanceId: '', + artifact: updatedArtifact + } + }); + + it('Test that HEAT timeout is updated', () => { + const state: InstanceArtifactsState = new InstanceArtifactsState(storeMock, componentInstanceServiceMock); + const context = { getState: jest.fn().mockImplementation(() => ngxsState), patchState: jest.fn(), setState: jest.fn(), dispatch: jest.fn() }; + state.updateArtifact(context, actionMock ).subscribe( (v) => console.log('OK')); + expect(storeMock.dispatch).toBeCalled(); + }); + +}); diff --git a/catalog-ui/src/app/ng2/store/states/artifacts.state.ts b/catalog-ui/src/app/ng2/store/states/artifacts.state.ts new file mode 100644 index 0000000000..64efbe96a9 --- /dev/null +++ b/catalog-ui/src/app/ng2/store/states/artifacts.state.ts @@ -0,0 +1,140 @@ +/** + * Created by ob0695 on 7/17/2018. + */ +import { Action, Selector, State, StateContext } from '@ngxs/store'; +import * as _ from 'lodash'; +import { tap } from 'rxjs/operators'; +import { ArtifactModel } from '../../../models/artifacts'; +import { ArtifactGroupType } from '../../../utils/constants'; +import { TopologyTemplateService } from '../../services/component-services/topology-template.service'; +import { ComponentGenericResponse } from '../../services/responses/component-generic-response'; +import { ServiceGenericResponse } from '../../services/responses/service-generic-response'; +import { CreateOrUpdateArtifactAction, DeleteArtifactAction, GetArtifactsByTypeAction } from '../actions/artifacts.action'; + +export interface ArtifactsStateModel { + artifacts: ArtifactModel[]; + deploymentArtifacts: ArtifactModel[]; + toscaArtifacts: ArtifactModel[]; + serviceApiArtifacts: ArtifactModel[]; +} + +@State({ + name: 'artifacts', + defaults: { + artifacts: [], + deploymentArtifacts: [], + toscaArtifacts: [], + serviceApiArtifacts: [] + } +}) + +export class ArtifactsState { + + constructor(protected topologyTemplateService: TopologyTemplateService) { + } + + @Selector() + static getEnvArtifact(state: ArtifactsStateModel, heatEnvArtifact: ArtifactModel) { + return (heatEnvArtifact: ArtifactModel) => { + _.find(state.deploymentArtifacts, (artifact)=> { + return artifact.generatedFromId === heatEnvArtifact.uniqueId + }) + }; + } + + @Selector() + static getArtifactsByType(state: ArtifactsStateModel, type: string) { + return (type: string) => { + switch (type) { + case ArtifactGroupType.TOSCA: + return state.toscaArtifacts; + case ArtifactGroupType.INFORMATION: + return state.artifacts; + case ArtifactGroupType.DEPLOYMENT: + return state.deploymentArtifacts; + case ArtifactGroupType.SERVICE_API: + return state.serviceApiArtifacts; + } + }; + } + + private updateArtifactState = (artifactsState: ArtifactModel[], artifactToUpdate: ArtifactModel, updatedArtifact: ArtifactModel) => { + if (!artifactToUpdate.uniqueId) { // Create Artifact + return [...artifactsState, updatedArtifact] + } else { // Update Artifact + let artifactToUpdateIndex = _.findIndex(artifactsState, (artifact) => { + return artifact.uniqueId === artifactToUpdate.uniqueId + }) + let artifacts = Array.from(artifactsState); + artifacts[artifactToUpdateIndex] = updatedArtifact; + return [...artifacts]; + } + } + + @Action(GetArtifactsByTypeAction) + getArtifactsByType({getState, patchState}: StateContext, action: GetArtifactsByTypeAction) { + const state = getState(); + return this.topologyTemplateService.getArtifactsByType(action.payload.componentType, action.payload.componentId, action.payload.artifactType) + .pipe(tap((resp: ComponentGenericResponse) => { + switch (action.payload.artifactType) { + case ArtifactGroupType.INFORMATION: + patchState({ + artifacts: _.values(resp.artifacts) + }); + + case ArtifactGroupType.DEPLOYMENT: + patchState({ + deploymentArtifacts: _.values(resp.deploymentArtifacts) + }); + + case ArtifactGroupType.TOSCA: + patchState({ + toscaArtifacts: _.values(resp.toscaArtifacts) + }); + + case ArtifactGroupType.SERVICE_API: + patchState({ + serviceApiArtifacts: _.values((resp).serviceApiArtifacts) + }); + } + })); + } + + @Action(CreateOrUpdateArtifactAction) + createOrUpdateArtifact({getState, patchState}: StateContext, action: CreateOrUpdateArtifactAction) { + const state = getState(); + return this.topologyTemplateService.addOrUpdateArtifact(action.payload.componentType, action.payload.componentId, action.payload.artifact) + .pipe(tap((resp: ArtifactModel) => { + + switch (resp.artifactGroupType) { + case ArtifactGroupType.DEPLOYMENT: + patchState({ + deploymentArtifacts: this.updateArtifactState(state.deploymentArtifacts, action.payload.artifact, resp) + }); + + case ArtifactGroupType.INFORMATION: + patchState({ + artifacts: this.updateArtifactState(state.artifacts, action.payload.artifact, resp) + }); + } + })); + } + + @Action(DeleteArtifactAction) + deleteArtifact({getState, patchState}: StateContext, action: DeleteArtifactAction) { + const state = getState(); + return this.topologyTemplateService.deleteArtifact(action.payload.componentId, action.payload.componentType, action.payload.artifact.uniqueId, action.payload.artifact.artifactLabel) + .pipe(tap((resp: ArtifactModel) => { + switch (resp.artifactGroupType) { + case ArtifactGroupType.DEPLOYMENT: + patchState({ + deploymentArtifacts: state.deploymentArtifacts.filter(({uniqueId}) => uniqueId !== action.payload.artifact.uniqueId) + }); + case ArtifactGroupType.INFORMATION: + patchState({ + artifacts: state.artifacts.filter(({uniqueId}) => uniqueId !== action.payload.artifact.uniqueId) + }); + } + })); + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/store/states/instance-artifacts.state.ts b/catalog-ui/src/app/ng2/store/states/instance-artifacts.state.ts new file mode 100644 index 0000000000..12ba1ae7ba --- /dev/null +++ b/catalog-ui/src/app/ng2/store/states/instance-artifacts.state.ts @@ -0,0 +1,145 @@ +/** + * Created by ob0695 on 7/17/2018. + */ +import { Action, Selector, State, StateContext, Store } from '@ngxs/store'; +import * as _ from 'lodash'; +import { tap } from 'rxjs/operators'; +import { ArtifactModel } from '../../../models/artifacts'; +import { ArtifactGroupType } from '../../../utils/constants'; +import { ComponentInstanceServiceNg2 } from '../../services/component-instance-services/component-instance.service'; +import { ComponentGenericResponse } from '../../services/responses/component-generic-response'; +import { + CreateInstanceArtifactAction, + DeleteInstanceArtifactAction, + GetInstanceArtifactsByTypeAction, + UpdateInstanceArtifactAction +} from '../actions/instance-artifacts.actions'; +import { ArtifactsStateModel } from './artifacts.state'; + +export interface InstanceArtifactsStateModel { + artifacts: ArtifactModel[]; + deploymentArtifacts: ArtifactModel[]; +} + +@State({ + name: 'instance_artifacts', + defaults: { + artifacts: [], + deploymentArtifacts: [] + } +}) +export class InstanceArtifactsState { + + constructor(private store: Store, protected componentInstanceService: ComponentInstanceServiceNg2) { + } + + @Selector() + static getArtifactsByType(state: InstanceArtifactsStateModel) { + return (type: string) => { + switch (type) { + case ArtifactGroupType.INFORMATION: + return state.artifacts; + case ArtifactGroupType.DEPLOYMENT: + return state.deploymentArtifacts; + } + }; + } + + @Action(GetInstanceArtifactsByTypeAction) + getInstanceArtifactsByType({getState, patchState}: StateContext, action: GetInstanceArtifactsByTypeAction) { + const state = getState(); + return this.componentInstanceService.getComponentInstanceArtifactsByGroupType(action.payload.componentType, action.payload.componentId, action.payload.instanceId, action.payload.artifactType) + .pipe(tap((resp: ComponentGenericResponse) => { + switch (action.payload.artifactType) { + case ArtifactGroupType.INFORMATION: + patchState({ + artifacts: _.values(resp) as ArtifactModel[] + }); + break; + case ArtifactGroupType.DEPLOYMENT: + patchState({ + deploymentArtifacts: _.values(resp) as ArtifactModel[] + }); + break; + } + })); + } + + @Action(CreateInstanceArtifactAction) + createArtifact({getState, patchState}: StateContext, action: CreateInstanceArtifactAction) { + const state = getState(); + return this.componentInstanceService.addInstanceArtifact(action.payload.componentType, action.payload.componentId, action.payload.instanceId, action.payload.artifact) + .pipe(tap((resp: ArtifactModel) => { + switch (resp.artifactGroupType) { + case ArtifactGroupType.DEPLOYMENT: + patchState({ + deploymentArtifacts: [...state.deploymentArtifacts, resp] + }); + break; + case ArtifactGroupType.INFORMATION: + patchState({ + artifacts: [...state.artifacts, resp] + }); + break; + } + })); + } + + @Action(UpdateInstanceArtifactAction) + updateArtifact({getState, patchState}: StateContext, action: UpdateInstanceArtifactAction) { + const state = getState(); + return this.componentInstanceService.updateInstanceArtifact(action.payload.componentType, action.payload.componentId, action.payload.instanceId, action.payload.artifact) + .pipe(tap((resp: ArtifactModel) => { + switch (resp.artifactGroupType) { + case ArtifactGroupType.DEPLOYMENT: + // We cannot simply update the updated artifact state because updating a deployment ENV file may cause an update to his parent HEAT + // file. + // Just dispatch an action to refresh the deployment artifacts list + this.store.dispatch(new GetInstanceArtifactsByTypeAction(({ + componentType: action.payload.componentType, + componentId: action.payload.componentId, + instanceId: action.payload.instanceId, + artifactType: ArtifactGroupType.DEPLOYMENT + }))); + break; + case ArtifactGroupType.INFORMATION: + patchState({ + artifacts: this.updateInstanceArtifactState(state.artifacts, action.payload.artifact, resp) + }); + break; + } + })); + } + + @Action(DeleteInstanceArtifactAction) + deleteInstanceArtifact({getState, patchState}: StateContext, action: DeleteInstanceArtifactAction) { + const state = getState(); + return this.componentInstanceService. + deleteInstanceArtifact(action.payload.componentId, action.payload.componentType, action.payload.instanceId, action.payload.artifact.uniqueId, action.payload.artifact.artifactLabel) + .pipe(tap((resp: ArtifactModel) => { + switch (resp.artifactGroupType) { + case ArtifactGroupType.DEPLOYMENT: + patchState({ + deploymentArtifacts: state.deploymentArtifacts.filter(({uniqueId}) => uniqueId !== action.payload.artifact.uniqueId) + }); + break; + case ArtifactGroupType.INFORMATION: + patchState({ + artifacts: state.artifacts.filter(({uniqueId}) => uniqueId !== action.payload.artifact.uniqueId) + }); + break; + } + })); + } + + private updateInstanceArtifactState = (artifactsState: ArtifactModel[], artifactToUpdate: ArtifactModel, updatedArtifact: ArtifactModel) => { + const artifactToUpdateIndex = _.findIndex(artifactsState, (artifact) => { + return artifact.uniqueId === artifactToUpdate.uniqueId; + }); + const artifacts = Array.from(artifactsState); + artifacts[artifactToUpdateIndex] = updatedArtifact; + const ret = [...artifacts]; + return ret; + } + +} diff --git a/catalog-ui/src/app/ng2/store/states/workspace.state.ts b/catalog-ui/src/app/ng2/store/states/workspace.state.ts new file mode 100644 index 0000000000..eb8200f6e0 --- /dev/null +++ b/catalog-ui/src/app/ng2/store/states/workspace.state.ts @@ -0,0 +1,48 @@ +/** + * Created by ob0695 on 7/17/2018. + */ +import {State, Action, StateContext} from '@ngxs/store'; +import {UpdateIsDesigner, UpdateIsViewOnly} from "../actions/workspace.action"; +import {Selector} from "@ngxs/store"; + +export interface WorkspaceStateModel { + isViewOnly: boolean; + isDesigner: boolean; +} + +@State({ + name: 'workspace', + defaults: { + isViewOnly: false, + isDesigner: true + } +}) + +export class WorkspaceState { + + constructor(){} + + @Selector() static isViewOnly(state: WorkspaceStateModel):boolean { + return state.isViewOnly; + } + @Selector() static isDesigner(state: WorkspaceStateModel): boolean { + return state.isDesigner; + } + + @Action(UpdateIsViewOnly) + updateIsViewOnly({getState, setState}: StateContext, action:UpdateIsViewOnly) { + const state = getState(); + setState({ + ...state, + isViewOnly: action.isViewOnly + }); + } + + @Action(UpdateIsDesigner) + updateIsDesigner({getState, patchState}: StateContext, action:UpdateIsDesigner) { + const state = getState(); + patchState({ + isDesigner: action.isDesigner + }); + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts b/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts index fcb21c0c83..aab531d205 100644 --- a/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts +++ b/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts @@ -23,13 +23,12 @@ */ import { DataTypesService } from "../../services/data-types-service"; import ICacheObject = angular.ICacheObject; -import { SharingService } from "../../services/sharing-service"; import { CookieService } from "../../services/cookie-service"; -import { CacheService } from "../../services/cache-service"; import {ComponentFactory} from "../../utils/component-factory" import { EventListenerService } from "app/services/event-listener-service"; import { ModalsHandler } from "app/utils"; import IScope = angular.IScope; +import { SharingService } from "../services/sharing.service"; /** Services we need to upgrade from angular1 to angular2 - in the future we need to rewrite them all to angular2 **/ @@ -61,10 +60,6 @@ export function scopeServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('$scope'); } -export function cacheServiceFactory(cacheObj: ICacheObject) { - return cacheObj.get('Sdc.Services.CacheService'); -} - export function eventListenerServiceServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('EventListenerService'); } @@ -73,6 +68,10 @@ export function notificationServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('Notification'); } +export function ModalsHandlerFactory(cacheObj: ICacheObject) { + return cacheObj.get('ModalsHandler'); +} + export const ComponentFactoryProvider = { provide: ComponentFactory, @@ -80,18 +79,12 @@ export const ComponentFactoryProvider = { deps: ['$injector'] }; - -export function ModalsHandlerFactory(cacheObj: ICacheObject) { - return cacheObj.get('ModalsHandler'); -} - export const DataTypesServiceProvider = { provide: DataTypesService, useFactory: dataTypesServiceFactory, deps: ['$injector'] }; - export const SharingServiceProvider = { provide: SharingService, useFactory: sharingServiceFactory, @@ -122,17 +115,12 @@ export const StateParamsServiceFactory = { useFactory: stateParamsServiceFactory, deps: ['$injector'] }; -export const CacheServiceProvider = { - provide: CacheService, - useFactory: cacheServiceFactory, - deps: ['$injector'] -}; - -export const EventListenerServiceProvider = { - provide: EventListenerService, - useFactory: eventListenerServiceServiceFactory, - deps: ['$injector'] -}; +// +// export const EventListenerServiceProvider = { +// provide: EventListenerService, +// useFactory: eventListenerServiceServiceFactory, +// deps: ['$injector'] +// }; export const NotificationServiceProvider = { provide: 'Notification', @@ -144,4 +132,4 @@ export const ModalsHandlerProvider = { provide: ModalsHandler, useFactory: ModalsHandlerFactory, deps: ['$injector'] -} +}; diff --git a/catalog-ui/src/app/ng2/utils/queue-service-utils.ts b/catalog-ui/src/app/ng2/utils/queue-service-utils.ts new file mode 100644 index 0000000000..8cf7f98383 --- /dev/null +++ b/catalog-ui/src/app/ng2/utils/queue-service-utils.ts @@ -0,0 +1,58 @@ +/** + * Created by ob0695 on 6/3/2018. + */ +/*- + * ============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========================================================= + */ +import {Injectable} from "@angular/core"; + +@Injectable() +export class QueueServiceUtils { + + private executionQueue:any; + + constructor() { + if(!this.executionQueue) { + this.executionQueue = this.getQueue(); + } + } + + private getQueue = () => new Promise((resolve, reject) => { + resolve(true); + }); + + 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); + }; +} -- cgit 1.2.3-korg