aboutsummaryrefslogtreecommitdiffstats
path: root/catalog-ui/src/app/ng2/pages
diff options
context:
space:
mode:
Diffstat (limited to 'catalog-ui/src/app/ng2/pages')
-rw-r--r--catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts1
-rw-r--r--catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html3
-rw-r--r--catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts4
-rw-r--r--catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html2
-rw-r--r--catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts5
-rw-r--r--catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html3
-rw-r--r--catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts4
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts3
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html86
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less12
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts329
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts1
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts40
-rw-r--r--catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts83
-rw-r--r--catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html5
-rw-r--r--catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less21
-rw-r--r--catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts110
-rw-r--r--catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts36
-rw-r--r--catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html43
-rw-r--r--catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less45
-rw-r--r--catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts137
-rw-r--r--catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts25
-rw-r--r--catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html21
-rw-r--r--catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less21
-rw-r--r--catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts66
-rw-r--r--catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts17
26 files changed, 972 insertions, 151 deletions
diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts
index a097fb04ea..bf5ec4c4f6 100644
--- a/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts
+++ b/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts
@@ -1,3 +1,4 @@
+import * as _ from "lodash";
import {ConnectRelationModel} from "../../../models/graph/connectRelationModel";
import {Injectable} from "@angular/core";
import { Requirement, Capability} from "app/models";
diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html
index 0c9d9e6e26..a45f07b3e2 100644
--- a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html
+++ b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html
@@ -3,6 +3,5 @@
[selectedReqOrCapModel]="connectWizardService.selectedMatch && (connectWizardService.selectedMatch.isFromTo ? connectWizardService.selectedMatch.requirement : connectWizardService.selectedMatch.capability)"
[currentComponent]="connectWizardService.currentComponent"
[componentInstanceId]="connectWizardService.connectRelationModel.fromNode.componentInstance.uniqueId"
- (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)"
- (updateCapabilityProperties)="onCapabilityPropertiesUpdate($event)">
+ (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)">
</select-requirement-or-capability> \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts
index edbbf8a0a3..054d38b063 100644
--- a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts
+++ b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts
@@ -32,10 +32,6 @@ export class FromNodeStepComponent implements IStepComponent, OnInit{
return true;
}
- onCapabilityPropertiesUpdate(capabilityProperties:Array<PropertyModel>) {
- this.connectWizardService.selectedMatch.capabilityProperties = capabilityProperties;
- }
-
private updateSelectedReqOrCap = (selected:Requirement|Capability):void => {
if(!selected){
this.connectWizardService.selectedMatch = null;
diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html
index 9e34893272..9b1df69d77 100644
--- a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html
+++ b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html
@@ -5,7 +5,7 @@
</div>
<div class="properties-table-container">
<properties-table class="properties-table"
- (valueChanged)="propertyValueChanged($event)"
+ (propertyChanged)="propertyValueChanged($event)"
[fePropertiesMap]="capabilityPropertiesMap"
[selectedPropertyId]="''"
[hidePropertyType]="true">
diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts
index 3e48785a3c..946d1858dc 100644
--- a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts
+++ b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts
@@ -25,7 +25,7 @@ export class PropertiesStepComponent implements IStepComponent{
constructor(@Inject(forwardRef(() => ConnectionWizardService)) public connectWizardService: ConnectionWizardService, private componentInstanceServiceNg2:ComponentInstanceServiceNg2, private propertiesUtils:PropertiesUtils) {
- this.capabilityPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({'capability' : connectWizardService.selectedMatch.capabilityProperties}, false);
+ this.capabilityPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({'capability' : connectWizardService.selectedMatch.capability.properties}, false);
}
ngOnInit() {
@@ -42,7 +42,8 @@ export class PropertiesStepComponent implements IStepComponent{
propertyValueChanged = (property: PropertyFEModel) => {
if (!property.isDeclared) {
const propChangedIdx = this.connectWizardService.changedCapabilityProperties.indexOf(property);
- if (this.componentInstanceServiceNg2.hasPropertyChanged(property)) {
+ if (property.hasValueObjChanged()) {
+ // if (this.componentInstanceServiceNg2.hasPropertyChanged(property)) {
console.log("==>" + this.constructor.name + ": propertyValueChanged " + property);
if (propChangedIdx === -1) {
this.connectWizardService.changedCapabilityProperties.push(property);
diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html
index 67bb12e6fc..a8343ce7e2 100644
--- a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html
+++ b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html
@@ -4,6 +4,5 @@
[selectedReqOrCapOption]="displayRequirementsOrCapabilities"
[currentComponent]="connectWizardService.currentComponent"
[componentInstanceId]="connectWizardService.connectRelationModel.toNode.componentInstance.uniqueId"
- (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)"
- (updateCapabilityProperties)="onCapabilityPropertiesUpdate($event)">
+ (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)">
</select-requirement-or-capability> \ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts
index 9c7bf4dfe6..ea3b129c7b 100644
--- a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts
+++ b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts
@@ -43,10 +43,6 @@ export class ToNodeStepComponent implements IStepComponent{
return false;
}
- onCapabilityPropertiesUpdate(capabilityProperties:Array<PropertyModel>) {
- this.connectWizardService.selectedMatch.capabilityProperties = capabilityProperties;
- }
-
private updateSelectedReqOrCap = (selected:Requirement|Capability):void => {
if (!selected) {
if (this.connectWizardService.selectedMatch.isFromTo) {
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts
index 203c75dd11..907f7638bb 100644
--- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts
@@ -32,6 +32,7 @@ import {DataTypeService} from "../../services/data-type.service";
import {PropertiesAssignmentComponent} from "./properties-assignment.page.component";
import {HierarchyNavService} from "./services/hierarchy-nav.service";
import {PropertiesUtils} from "./services/properties.utils";
+import {InputsUtils} from "./services/inputs.utils";
import {ComponentModeService} from "../../services/component-services/component-mode.service";
@NgModule({
@@ -53,7 +54,7 @@ import {ComponentModeService} from "../../services/component-services/component-
exports: [
PropertiesAssignmentComponent
],
- providers: [PropertiesService, HierarchyNavService, PropertiesUtils, DataTypeService, ComponentModeService]
+ providers: [PropertiesService, HierarchyNavService, PropertiesUtils, InputsUtils, DataTypeService, ComponentModeService]
})
export class PropertiesAssignmentModule {
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html
index beea3fe73f..03e9f1fa7e 100644
--- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html
@@ -1,43 +1,50 @@
<div class="properties-assignment-page">
<div class="main-content">
<div class="left-column">
- <tabs #propertyInputTabs tabStyle="round-tabs" (tabChanged)="tabChanged($event)" [hideIndicationOnTabChange]="true">
- <tab tabTitle="Properties">
- <properties-table class="properties-table"
- [fePropertiesMap]="instanceFePropertiesMap"
- [feInstanceNamesMap]="componentInstanceNamesMap"
- [searchTerm]="searchQuery"
- [selectedPropertyId]="selectedFlatProperty.path"
- [propertyNameSearchText]="searchPropertyName"
- [readonly]="isReadonly"
- [isLoading]="loadingProperties"
- [hasDeclareOption]="true"
- (valueChanged)="propertyValueChanged($event)"
- (propertySelected)="propertySelected($event)"
- (selectPropertyRow)="selectPropertyRow($event)"
- (selectChildProperty)="selectChildProperty($event)"
- (updateCheckedPropertyCount)="updateCheckedPropertyCount($event)"
- (selectInstanceRow)="selectInstanceRow($event)">
+ <div class="main-tabs-section">
+ <tabs #propertyInputTabs tabStyle="round-tabs" (tabChanged)="tabChanged($event)" [hideIndicationOnTabChange]="true">
+ <tab tabTitle="Properties">
+ <properties-table class="properties-table"
+ [fePropertiesMap]="instanceFePropertiesMap"
+ [feInstanceNamesMap]="componentInstanceNamesMap"
+ [searchTerm]="searchQuery"
+ [selectedPropertyId]="selectedFlatProperty.path"
+ [propertyNameSearchText]="searchPropertyName"
+ [readonly]="isReadonly"
+ [isLoading]="loadingProperties || savingChangedData"
+ [hasDeclareOption]="true"
+ (propertyChanged)="dataChanged($event)"
+ (propertySelected)="propertySelected($event)"
+ (selectPropertyRow)="selectPropertyRow($event)"
+ (selectChildProperty)="selectChildProperty($event)"
+ (updateCheckedPropertyCount)="updateCheckedPropertyCount($event)"
+ (selectInstanceRow)="selectInstanceRow($event)">
</properties-table>
- </tab>
- <tab tabTitle="Inputs">
- <inputs-table class="properties-table"
- [readonly]="isReadonly"
- [inputs]="inputs | searchFilter:'name':searchQuery"
- [instanceNamesMap]="componentInstanceNamesMap"
- [isLoading]="loadingInputs"
- (deleteInput)="deleteInput($event)"
- (inputValueChanged)="inputValueChanged($event)"></inputs-table>
- </tab>
- </tabs>
+ </tab>
+ <tab tabTitle="Inputs">
+ <inputs-table class="properties-table"
+ [readonly]="isReadonly"
+ [inputs]="inputs | searchFilter:'name':searchQuery"
+ [instanceNamesMap]="componentInstanceNamesMap"
+ [isLoading]="loadingInputs"
+ (deleteInput)="deleteInput($event)"
+ (inputChanged)="dataChanged($event)">
+ </inputs-table>
+ </tab>
+ </tabs>
+ <div class="main-tabs-section-buttons">
+ <button class="tlv-btn outline blue" [disabled]="!hasChangedData || savingChangedData" (click)="reverseChangedData()" data-tests-id="properties-reverse-button">Discard</button>
+ <button class="tlv-btn blue" [disabled]="!hasChangedData || !isValidChangedData || savingChangedData" (click)="doSaveChangedData()" data-tests-id="properties-save-button">{{savingChangedData ? 'Saving' : 'Save'}}</button>
+ </div>
+ </div>
<div class="header">
- <div class="search-filter-container" [class.without-filter]="isInpusTabSelected">
- <span *ngIf="displayClearSearch && !isInpusTabSelected" (click)="clickOnClearSearch()" class="clear-filter" data-tests-id="clear-filter-button">Clear All</span>
+ <div class="search-filter-container" [class.without-filter]="isInputsTabSelected">
+ <span *ngIf="displayClearSearch && isPropertiesTabSelected" (click)="clickOnClearSearch()" class="clear-filter" data-tests-id="clear-filter-button">Clear All</span>
<input type="text" class="search-box" placeholder="Search" [(ngModel)]="searchQuery" data-tests-id="search-box"/>
<span class="sprite search-icon" data-tests-id="search-button"></span>
- <filter-properties-assignment *ngIf="!isInpusTabSelected" #advanceSearch class="advance-search" [componentType]="component.componentType" (searchProperties)="searchPropertiesInstances($event)"></filter-properties-assignment>
+ <filter-properties-assignment *ngIf="isPropertiesTabSelected" #advanceSearch class="advance-search" [componentType]="component.componentType" (searchProperties)="searchPropertiesInstances($event)"></filter-properties-assignment>
</div>
- <button class="tlv-btn blue declare-button" [disabled]="!checkedPropertiesCount || isReadonly" (click)="declareProperties()" data-tests-id="declare-button">Declare</button>
+ <button class="tlv-btn blue declare-button" [disabled]="!checkedPropertiesCount || isReadonly || hasChangedData" (click)="declareProperties()" data-tests-id="declare-button">Declare</button>
</div>
</div>
<div class="right-column gray-border">
@@ -48,10 +55,10 @@
<div class="hierarchy-header white-sub-header">
<span tooltip="{{component.name}}">{{component.name}}</span>
</div>
- <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0 || isInpusTabSelected">No data to display</div>
+ <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0 || isInputsTabSelected">No data to display</div>
<hierarchy-navigation class="hierarchy-nav"
(updateSelected)="onInstanceSelectedUpdate($event)"
- [displayData]="isInpusTabSelected ? []: instancesNavigationData"
+ [displayData]="isInputsTabSelected ? []: instancesNavigationData"
[selectedItem]="selectedInstanceData.uniqueId"
[displayOptions]="hierarchyInstancesDisplayOptions"></hierarchy-navigation>
</div>
@@ -59,12 +66,12 @@
<tab tabTitle="Property Structure">
<div class="hierarchy-nav-container">
<div class="hierarchy-header white-sub-header" [class.selected]="selectedFlatProperty.path == propertyStructureHeader">
- <span tooltip="{{!isInpusTabSelected ? propertyStructureHeader : ''}}">{{!isInpusTabSelected ? (propertyStructureHeader || "No Property Selected") : "No Property Selected"}}</span>
+ <span tooltip="{{isPropertiesTabSelected ? propertyStructureHeader : ''}}">{{isPropertiesTabSelected ? (propertyStructureHeader || "No Property Selected") : "No Property Selected"}}</span>
</div>
- <div *ngIf="!propertiesNavigationData || propertiesNavigationData.length === 0 || isInpusTabSelected">No data to display</div>
+ <div *ngIf="!propertiesNavigationData || propertiesNavigationData.length === 0 || isInputsTabSelected">No data to display</div>
<hierarchy-navigation class="hierarchy-nav"
(updateSelected)="onPropertySelectedUpdate($event)"
- [displayData]="isInpusTabSelected ? [] : propertiesNavigationData"
+ [displayData]="isInputsTabSelected ? [] : propertiesNavigationData"
[selectedItem]="selectedFlatProperty.path"
[displayOptions]="hierarchyPropertiesDisplayOptions"></hierarchy-navigation>
</div>
@@ -72,4 +79,9 @@
</tabs>
</div>
</div>
+ <template #saveChangedDataModalContentTemplate>
+ <loader [display]="savingChangedData" [size]="'medium'" [relative]="true"></loader>
+ Your changes{{isValidChangedData ? '' : ' (invalid)'}} have not been saved.<br>
+ Do you want to {{isValidChangedData ? 'save' : 'discard'}} them?
+ </template>
</div>
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 03974bf723..6de6dda7bb 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
@@ -76,6 +76,7 @@
outline: none;
font-style: italic;
color:@ng2-med-dark-gray;
+ width: auto;
&::-moz-placeholder { color:@ng2-med-dark-gray;}
&::-webkit-input-placeholder{ color:@ng2-med-dark-gray;}
@@ -113,6 +114,17 @@
top: 0;
right: 0;
}
+
+ .main-tabs-section {
+ position: relative;
+
+ .main-tabs-section-buttons {
+ position: absolute;
+ top: 45px;
+ right: 0;
+ padding: 4px;
+ }
+ }
}
.right-column {
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 9603648bd8..40818bce78 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
@@ -18,11 +18,11 @@
* ============LICENSE_END=========================================================
*/
-import {Component, ViewChild, ElementRef, Renderer, Inject} from "@angular/core";
+import * as _ from "lodash";
+import {Component, ViewChild, Inject, TemplateRef} from "@angular/core";
import { PropertiesService } from "../../services/properties.service";
-import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData } from "app/models";
+import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData, ModalModel, ButtonModel } from "app/models";
import { ResourceType } from "app/utils";
-import property = require("lodash/property");
import {ComponentServiceNg2} from "../../services/component-services/component.service";
import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service"
import { InputBEModel, InputFEModel, ComponentInstance, PropertyBEModel, DerivedFEProperty, ResourceInstance, SimpleFlatProperty } from "app/models";
@@ -35,6 +35,9 @@ import {PropertyRowSelectedEvent} from "../../components/logic/properties-table/
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";
@Component({
templateUrl: './properties-assignment.page.component.html',
@@ -64,24 +67,36 @@ export class PropertiesAssignmentComponent {
hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name');
displayClearSearch = false;
searchPropertyName:string;
- isInpusTabSelected:boolean;
+ currentMainTab:Tab;
+ isInputsTabSelected:boolean;
+ isPropertiesTabSelected:boolean;
isReadonly:boolean;
loadingInstances:boolean = false;
loadingInputs:boolean = false;
loadingProperties:boolean = false;
-
- @ViewChild('hierarchyNavTabs') hierarchyNavTabs: ElementRef;
- @ViewChild('propertyInputTabs') propertyInputTabs: ElementRef;
+ changedData:Array<PropertyFEModel|InputFEModel>;
+ hasChangedData:boolean;
+ isValidChangedData:boolean;
+ savingChangedData:boolean;
+ stateChangeStartUnregister:Function;
+
+ @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs;
+ @ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
@ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent;
+ @ViewChild('saveChangedDataModalContentTemplate') saveChangedDataModalContentTemplateRef: TemplateRef<void>;
constructor(private propertiesService: PropertiesService,
private hierarchyNavService: HierarchyNavService,
private propertiesUtils:PropertiesUtils,
+ private inputsUtils:InputsUtils,
private componentServiceNg2:ComponentServiceNg2,
private componentInstanceServiceNg2:ComponentInstanceServiceNg2,
@Inject("$stateParams") _stateParams,
- private renderer: Renderer,
+ @Inject("$scope") private $scope:ng.IScope,
+ @Inject("$state") private $state:ng.ui.IStateService,
+ @Inject("Notification") private Notification:any,
private componentModeService:ComponentModeService,
+ private ModalService:ModalService,
private EventListenerService:EventListenerService) {
this.instanceFePropertiesMap = new InstanceFePropertiesMap();
@@ -91,6 +106,10 @@ export class PropertiesAssignmentComponent {
this.component = _stateParams.component;
this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.onCheckout);
this.updateViewMode();
+
+ this.changedData = [];
+ this.updateHasChangedData();
+ this.isValidChangedData = true;
}
ngOnInit() {
@@ -102,7 +121,9 @@ export class PropertiesAssignmentComponent {
.getComponentInputs(this.component)
.subscribe(response => {
_.forEach(response.inputs, (input: InputBEModel) => {
- this.inputs.push(new InputFEModel(input)); //only push items that were declared via SDC
+ const newInput: InputFEModel = new InputFEModel(input);
+ this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
+ this.inputs.push(newInput); //only push items that were declared via SDC
});
this.loadingInputs = false;
@@ -123,10 +144,22 @@ export class PropertiesAssignmentComponent {
this.selectFirstInstanceByDefault();
}, error => {}); //ignore error
+ this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => {
+ // stop if has changed properties
+ if (this.hasChangedData) {
+ event.preventDefault();
+ this.openChangedDataModal().then((proceed) => {
+ if (proceed) {
+ this.$state.go(toState, toParams);
+ }
+ });
+ }
+ });
};
ngOnDestroy() {
this.EventListenerService.unRegisterObserver(EVENTS.ON_CHECKOUT);
+ this.stateChangeStartUnregister();
}
selectFirstInstanceByDefault = () => {
@@ -147,12 +180,23 @@ export class PropertiesAssignmentComponent {
onInstanceSelectedUpdate = (resourceInstance: ResourceInstance) => {
console.log("==>" + this.constructor.name + ": onInstanceSelectedUpdate");
+
+ // stop if has changed properties
+ if (this.hasChangedData) {
+ this.openChangedDataModal().then((proceed) => {
+ if (proceed) {
+ this.onInstanceSelectedUpdate(resourceInstance);
+ }
+ });
+ return;
+ }
+
let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap();
this.selectedInstanceData = resourceInstance;
this.selectedInstanceType = resourceInstance.originType;
this.loadingProperties = true;
- if(this.isInput(resourceInstance.originType)) {
+ if (this.isInput(resourceInstance.originType)) {
this.componentInstanceServiceNg2
.getComponentInstanceInputs(this.component, resourceInstance)
.subscribe(response => {
@@ -160,7 +204,8 @@ export class PropertiesAssignmentComponent {
this.processInstancePropertiesResponse(instanceBePropertiesMap, true);
this.loadingProperties = false;
- }, error => {}); //ignore error
+ }, error => {
+ }); //ignore error
} else {
this.componentInstanceServiceNg2
.getComponentInstanceProperties(this.component, resourceInstance.uniqueId)
@@ -168,14 +213,15 @@ export class PropertiesAssignmentComponent {
instanceBePropertiesMap[resourceInstance.uniqueId] = response;
this.processInstancePropertiesResponse(instanceBePropertiesMap, false);
this.loadingProperties = false;
- }, error => {}); //ignore error
+ }, error => {
+ }); //ignore error
}
- if(resourceInstance.componentName === "vnfConfiguration") {
+ if (resourceInstance.componentName === "vnfConfiguration") {
this.isReadonly = true;
}
- if( this.searchPropertyName ){
+ if (this.searchPropertyName) {
this.clearSearch();
}
//clear selected property from the navigation
@@ -193,41 +239,31 @@ export class PropertiesAssignmentComponent {
/*** VALUE CHANGE EVENTS ***/
- propertyValueChanged = (event: PropertyFEModel) => {
- console.log("==>" + this.constructor.name + ": propertyValueChanged " + event);
- // Copying the actual value from the object ref into the value if it's from a complex type
- event.value = event.getJSONValue();
-
- if (this.isInput(this.selectedInstanceData.originType)) {
- console.log("I want to update input value on the resource instance");
- let inputToUpdate = new PropertyBEModel(event);
- this.componentInstanceServiceNg2
- .updateInstanceInput(this.component, this.selectedInstanceData.uniqueId, inputToUpdate)
- .subscribe(response => {
- console.log("Update resource instance input response: ", response);
- }, error => {}); //ignore error
- }
- else {
- let propertyBe = new PropertyBEModel(event);
- this.componentInstanceServiceNg2
- .updateInstanceProperty(this.component, this.selectedInstanceData.uniqueId, propertyBe)
- .subscribe(response => {
- console.log("Update resource instance property response: ", response);
- }, error => {}); //ignore error
- console.log(event);
+ dataChanged = (item:PropertyFEModel|InputFEModel) => {
+ let itemHasChanged;
+ if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) {
+ itemHasChanged = item.hasValueObjChanged();
+ } else if (this.isInputsTabSelected && item instanceof InputFEModel) {
+ itemHasChanged = item.hasDefaultValueChanged();
}
- };
-
- inputValueChanged = (event) => {
- console.log("==>" + this.constructor.name + ": inputValueChanged");
- let inputToUpdate = new PropertyBEModel(event);
+ const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item);
+ if (itemHasChanged) {
+ if (dataChangedIdx === -1) {
+ this.changedData.push(item);
+ }
+ } else {
+ if (dataChangedIdx !== -1) {
+ this.changedData.splice(dataChangedIdx, 1);
+ }
+ }
- this.componentServiceNg2
- .updateComponentInput(this.component, inputToUpdate)
- .subscribe(response => {
- console.log("updated the component input and got this response: ", response);
- }, error => {}); //ignore error
+ if (this.isPropertiesTabSelected) {
+ this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid);
+ } else if (this.isInputsTabSelected) {
+ this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid);
+ }
+ this.updateHasChangedData();
};
@@ -272,7 +308,7 @@ export class PropertiesAssignmentComponent {
// Set selected property in table
this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName);
- this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Property Structure']);
+ this.hierarchyNavTabs.triggerTabChange('Property Structure');
};
@@ -280,13 +316,27 @@ export class PropertiesAssignmentComponent {
this.selectedInstanceData = _.find(this.instancesNavigationData, (instance:ComponentInstance) => {
return instance.name == $event;
});
- this.renderer.invokeElementMethod(
- this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
+ this.hierarchyNavTabs.triggerTabChange('Composition');
};
tabChanged = (event) => {
+ // stop if has changed properties
+ if (this.hasChangedData) {
+ this.openChangedDataModal().then((proceed) => {
+ if (proceed) {
+ this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title));
+ }
+ });
+
+ // return to show the current tab
+ this.propertyInputTabs.triggerTabChange(this.currentMainTab.title);
+ return;
+ }
+
console.log("==>" + this.constructor.name + ": tabChanged " + event);
- this.isInpusTabSelected = event.title === "Inputs";
+ this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title);
+ this.isPropertiesTabSelected = this.currentMainTab.title === "Properties";
+ this.isInputsTabSelected = this.currentMainTab.title === "Inputs";
this.propertyStructureHeader = null;
this.searchQuery = '';
};
@@ -320,12 +370,180 @@ export class PropertiesAssignmentComponent {
this.checkedPropertiesCount = 0;
_.forEach(response, (input: InputBEModel) => {
let newInput: InputFEModel = new InputFEModel(input);
+ this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue);
this.inputs.push(newInput);
this.updatePropertyValueAfterDeclare(newInput);
});
}, error => {}); //ignore error
};
+ saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => {
+ return new Promise((resolve, reject) => {
+ if (!this.isValidChangedData) {
+ reject('Changed data is invalid - cannot save!');
+ return;
+ }
+ if (!this.changedData.length) {
+ resolve([]);
+ return;
+ }
+
+ // make request and its handlers
+ let request;
+ let handleSuccess, handleError;
+ if (this.isPropertiesTabSelected) {
+ const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => {
+ changedProp = <PropertyFEModel>changedProp;
+ const propBE = new PropertyBEModel(changedProp);
+ propBE.value = changedProp.getJSONValue();
+ return propBE;
+ });
+
+ if (this.isInput(this.selectedInstanceData.originType)) {
+ request = this.componentInstanceServiceNg2
+ .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedProperties);
+ handleSuccess = (response) => {
+ // reset each changed property with new value and remove it from changed properties list
+ response.forEach((resInput) => {
+ const changedProp = <PropertyFEModel>this.changedData.shift();
+ this.propertiesUtils.resetPropertyValue(changedProp, resInput.value);
+ });
+ console.log('updated instance inputs:', response);
+ };
+ } else {
+ request = this.componentInstanceServiceNg2
+ .updateInstanceProperties(this.component, 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 = <PropertyFEModel>this.changedData.shift();
+ this.propertiesUtils.resetPropertyValue(changedProp, resProp.value);
+ });
+ resolve(response);
+ console.log("updated instance properties: ", response);
+ };
+ }
+ } else if (this.isInputsTabSelected) {
+ const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => {
+ changedInput = <InputFEModel>changedInput;
+ const inputBE = new InputBEModel(changedInput);
+ inputBE.defaultValue = changedInput.getJSONDefaultValue();
+ return inputBE;
+ });
+ request = this.componentServiceNg2
+ .updateComponentInputs(this.component, changedInputs);
+ handleSuccess = (response) => {
+ // reset each changed property with new value and remove it from changed properties list
+ response.forEach((resInput) => {
+ const changedInput = <InputFEModel>this.changedData.shift();
+ this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue);
+ });
+ console.log("updated the component inputs and got this response: ", response);
+ }
+ }
+
+ this.savingChangedData = true;
+ request.subscribe(
+ (response) => {
+ this.savingChangedData = false;
+ handleSuccess && handleSuccess(response);
+ this.updateHasChangedData();
+ resolve(response);
+ },
+ (error) => {
+ this.savingChangedData = false;
+ handleError && handleError(error);
+ this.updateHasChangedData();
+ reject(error);
+ }
+ );
+ });
+ };
+
+ reverseChangedData = ():void => {
+ // make reverse item handler
+ let handleReverseItem;
+ if (this.isPropertiesTabSelected) {
+ handleReverseItem = (changedItem) => {
+ changedItem = <PropertyFEModel>changedItem;
+ this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value);
+ };
+ } else if (this.isInputsTabSelected) {
+ handleReverseItem = (changedItem) => {
+ changedItem = <InputFEModel>changedItem;
+ this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue);
+ };
+ }
+
+ this.changedData.forEach(handleReverseItem);
+ this.changedData = [];
+ this.updateHasChangedData();
+ };
+
+ updateHasChangedData = ():boolean => {
+ const curHasChangedData:boolean = (this.changedData.length > 0);
+ if (curHasChangedData !== this.hasChangedData) {
+ this.hasChangedData = curHasChangedData;
+ this.$scope.$emit('setWorkspaceTopBarActive', !this.hasChangedData);
+ }
+ return this.hasChangedData;
+ };
+
+ doSaveChangedData = ():void => {
+ this.saveChangedData().then(
+ () => {
+ this.Notification.success({
+ message: 'Successfully saved changes',
+ title: 'Saved'
+ });
+ },
+ () => {
+ this.Notification.error({
+ message: 'Failed to save changes!',
+ title: 'Failure'
+ });
+ }
+ );
+ };
+
+ openChangedDataModal = ():Promise<boolean> => {
+ let modalTitle;
+ if (this.isPropertiesTabSelected) {
+ modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`;
+ } else if (this.isInputsTabSelected) {
+ modalTitle = `Unsaved inputs for ${this.component.name}`;
+ }
+
+ return new Promise<boolean>((resolve) => {
+ const modal = this.ModalService.createCustomModal(new ModalModel(
+ 'sm',
+ modalTitle,
+ null,
+ [
+ new ButtonModel('Cancel', 'outline grey', () => {
+ modal.instance.close();
+ resolve(false);
+ }),
+ new ButtonModel('Discard', 'outline blue', () => {
+ this.reverseChangedData();
+ modal.instance.close();
+ resolve(true);
+ }),
+ new ButtonModel('Save', 'blue', () => {
+ this.saveChangedData().then(() => {
+ modal.instance.close();
+ resolve(true);
+ }, () => {
+ modal.instance.close();
+ resolve(false);
+ });
+ }, () => !this.isValidChangedData)
+ ]
+ ));
+ this.ModalService.addDynamicTemplateToModal(modal, this.saveChangedDataModalContentTemplateRef);
+ modal.instance.open();
+ });
+ };
updatePropertyValueAfterDeclare = (input: InputFEModel) => {
if (this.instanceFePropertiesMap[input.instanceUniqueId]) {
@@ -346,12 +564,12 @@ export class PropertiesAssignmentComponent {
};
setInputTabIndication = (numInputs: number): void => {
- this.renderer.invokeElementMethod(this.propertyInputTabs, 'setTabIndication', ['Inputs', numInputs]);
+ this.propertyInputTabs.setTabIndication('Inputs', numInputs);
};
deleteInput = (input: InputFEModel) => {
console.log("==>" + this.constructor.name + ": deleteInput");
- let inputToDelete = new PropertyBEModel(input);
+ let inputToDelete = new InputBEModel(input);
this.componentServiceNg2
.deleteInput(this.component, inputToDelete)
@@ -389,7 +607,7 @@ export class PropertiesAssignmentComponent {
this.processInstancePropertiesResponse(response, false);
this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree
this.searchPropertyName = filterData.propertyName;//mark in table
- this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
+ this.hierarchyNavTabs.triggerTabChange('Composition');
this.propertiesNavigationData = [];
this.displayClearSearch = true;
}, error => {}); //ignore error
@@ -408,12 +626,11 @@ export class PropertiesAssignmentComponent {
clickOnClearSearch = () => {
this.clearSearch();
this.selectFirstInstanceByDefault();
- this.renderer.invokeElementMethod(
- this.hierarchyNavTabs, 'triggerTabChange', ['Composition']);
+ this.hierarchyNavTabs.triggerTabChange('Composition');
};
private isInput = (instanceType:string):boolean =>{
- return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC;
+ return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR;
}
}
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts
index 016b04788e..1a800baac7 100644
--- a/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts
@@ -18,6 +18,7 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import { Injectable } from '@angular/core';
import { SimpleFlatProperty, PropertyFEModel, DerivedFEProperty } from 'app/models';
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts
new file mode 100644
index 0000000000..408a00e7b4
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts
@@ -0,0 +1,40 @@
+/*-
+ * ============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 { InputFEModel} from "app/models";
+
+@Injectable()
+export class InputsUtils {
+
+ constructor() {}
+
+ public initDefaultValueObject = (input: InputFEModel): void => {
+ input.resetDefaultValueObjValidation();
+ input.defaultValueObj = input.getDefaultValueObj();
+ input.updateDefaultValueObjOrig();
+ };
+
+ public resetInputDefaultValue = (input: InputFEModel, newDefaultValue: string): void => {
+ input.defaultValue = newDefaultValue;
+ this.initDefaultValueObject(input);
+ }
+
+}
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 8f46c6f603..e7b59b96ba 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
@@ -18,6 +18,7 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import { Injectable } from '@angular/core';
import { DataTypeModel, PropertyFEModel, PropertyBEModel, InstanceBePropertiesMap, InstanceFePropertiesMap, DerivedFEProperty, DerivedPropertyType, InputFEModel} from "app/models";
import { DataTypeService } from "app/ng2/services/data-type.service";
@@ -47,10 +48,13 @@ export class PropertiesUtils {
let newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE
- if (newFEProp.derivedDataType == DerivedPropertyType.COMPLEX) { //Create children if prop is not simple, list, or map.
- newFEProp.flattenedChildren = this.createFlattenedChildren(newFEProp.type, newFEProp.name);
- }
- if (newFEProp.getInputValues && newFEProp.getInputValues.length) { //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children
+ this.initValueObjectRef(newFEProp); //initialize valueObj.
+ propertyFeArray.push(newFEProp);
+ newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children
+ this.dataTypeService.checkForCustomBehavior(newFEProp);
+
+ //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children
+ if (newFEProp.getInputValues && newFEProp.getInputValues.length) {
newFEProp.getInputValues.forEach(propInputDetail => {
let inputPath = propInputDetail.inputPath;
if (!inputPath) { //TODO: this is a workaround until Marina adds inputPath
@@ -63,10 +67,6 @@ export class PropertiesUtils {
this.propertiesService.disableRelatedProperties(newFEProp, inputPath);
});
}
- this.initValueObjectRef(newFEProp); //initialize valueObj.
- propertyFeArray.push(newFEProp);
- newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children
- this.dataTypeService.checkForCustomBehavior(newFEProp);
}
});
instanceFePropertiesMap[instanceId] = propertyFeArray;
@@ -103,33 +103,29 @@ export class PropertiesUtils {
* Note: This logic is different than assignflattenedchildrenvalues - here we merge values, there we pick either the parents value, props value, or default value - without merging.
*/
public initValueObjectRef = (property: PropertyFEModel): void => {
- if (property.derivedDataType == DerivedPropertyType.SIMPLE || property.isDeclared) { //if property is declared, it gets a simple input instead. List and map values and pseudo-children will be handled in property component
- property.valueObj = property.value || property.defaultValue;
- if (property.isDeclared) {
- if(typeof property.valueObj == 'object'){
- property.valueObj = JSON.stringify(property.valueObj);
- }
- }else if(property.valueObj &&
- property.type !== PROPERTY_TYPES.STRING &&
- property.type !== PROPERTY_TYPES.JSON &&
- PROPERTY_DATA.SCALAR_TYPES.indexOf(property.type) == -1){
- property.valueObj = JSON.parse(property.valueObj);//The valueObj contains the real value ans not the value as string
+ property.resetValueObjValidation();
+ if (property.isDeclared) { //if property is declared, it gets a simple input instead. List and map values and pseudo-children will be handled in property component
+ property.valueObj = property.value || property.defaultValue || null; // use null for empty value object
+ if (property.valueObj && typeof property.valueObj == 'object') {
+ property.valueObj = JSON.stringify(property.valueObj);
}
} else {
- if (property.derivedDataType == DerivedPropertyType.LIST) {
- property.valueObj = _.merge([], JSON.parse(property.defaultValue || '[]'), JSON.parse(property.value || '[]')); //value object should be merged value and default value. Value takes higher precendence. Set valueObj to empty obj if undefined.
- } else {
- property.valueObj = _.merge({}, JSON.parse(property.defaultValue || '{}'), JSON.parse(property.value || '{}')); //value object should be merged value and default value. Value takes higher precendence. Set valueObj to empty obj if undefined.
- }
- if ((property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) && Object.keys(property.valueObj).length) {
+ property.valueObj = property.getValueObj();
+ if (property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) {
+ property.flattenedChildren = [];
Object.keys(property.valueObj).forEach((key) => {
property.flattenedChildren.push(...this.createListOrMapChildren(property, key, property.valueObj[key]))
});
- } else {
+ } else if (property.derivedDataType === DerivedPropertyType.COMPLEX) {
+ property.flattenedChildren = this.createFlattenedChildren(property.type, property.name);
this.assignFlattenedChildrenValues(property.valueObj, property.flattenedChildren, property.name);
+ property.flattenedChildren.forEach((childProp) => {
+ property.childPropUpdated(childProp);
+ });
}
}
- }
+ property.updateValueObjOrig();
+ };
/*
* Loops through flattened properties array and to assign values
@@ -142,22 +138,24 @@ export class PropertiesUtils {
derivedPropArray.forEach((prop, index) => {
let propNameInObj = prop.propertiesName.substring(prop.propertiesName.indexOf(parentName) + parentName.length + 1).split('#').join('.'); //extract everything after parent name
- prop.valueObj = _.get(parentValueJSON, propNameInObj, prop.value || prop.defaultValue); //assign value -first value of parent if exists. If not, prop.value if not, prop.defaultvalue
+ prop.valueObj = _.get(parentValueJSON, propNameInObj, prop.value || prop.defaultValue || null); //assign value -first value of parent if exists. If not, prop.value if not, prop.defaultvalue
+ prop.value = (prop.valueObj !== null && (typeof prop.valueObj) != 'string') ? JSON.stringify(prop.valueObj) : prop.valueObj;
- if ( prop.isDeclared && typeof prop.valueObj == 'object') { //Stringify objects of items that are declared
- prop.valueObj = JSON.stringify(prop.valueObj);
- } else if(typeof prop.valueObj == PROPERTY_TYPES.STRING
- && (prop.type == PROPERTY_TYPES.INTEGER || prop.type == PROPERTY_TYPES.FLOAT || prop.type == PROPERTY_TYPES.BOOLEAN)){ //parse ints and non-string simple types
- prop.valueObj = JSON.parse(prop.valueObj);
+ if ((prop.isDeclared || prop.type == PROPERTY_TYPES.STRING || prop.type == PROPERTY_TYPES.JSON)) { //Stringify objects of items that are declared or from type string/json
+ prop.valueObj = (prop.valueObj !== null && typeof prop.valueObj == 'object') ? JSON.stringify(prop.valueObj) : prop.valueObj;
+ } else if(prop.type == PROPERTY_TYPES.INTEGER || prop.type == PROPERTY_TYPES.FLOAT || prop.type == PROPERTY_TYPES.BOOLEAN){ //parse ints and non-string simple types
+ prop.valueObj = (prop.valueObj !== null && typeof prop.valueObj == PROPERTY_TYPES.STRING) ? JSON.parse(prop.valueObj) : prop.valueObj;
} else { //parse strings that should be objects
- if (prop.derivedDataType == DerivedPropertyType.COMPLEX && typeof prop.valueObj != 'object') {
- prop.valueObj = JSON.parse(prop.valueObj || '{}');
- } else if (prop.derivedDataType == DerivedPropertyType.LIST && typeof prop.valueObj != 'object') {
- prop.valueObj = JSON.parse(prop.valueObj || '[]');
- } else if (prop.derivedDataType == DerivedPropertyType.MAP && typeof prop.valueObj != 'object' && (!prop.isChildOfListOrMap || !prop.schema.property.isSimpleType)) { //dont parse values for children of map of simple
- prop.valueObj = JSON.parse(prop.valueObj || '{}');
+ if (prop.derivedDataType == DerivedPropertyType.COMPLEX) {
+ prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '{}') : prop.valueObj;
+ } else if (prop.derivedDataType == DerivedPropertyType.LIST) {
+ prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '[]') : prop.valueObj;
+ } else if (prop.derivedDataType == DerivedPropertyType.MAP) {
+ if (!prop.isChildOfListOrMap || !prop.schema.property.isSimpleType) {
+ prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '{}') : prop.valueObj;
+ }
}
- if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj == 'object' && Object.keys(prop.valueObj).length) {
+ if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj == 'object' && prop.valueObj !== null && Object.keys(prop.valueObj).length) {
let newProps: Array<DerivedFEProperty> = [];
Object.keys(prop.valueObj).forEach((key) => {
newProps.push(...this.createListOrMapChildren(prop, key, prop.valueObj[key]));//create new children, assign their values, and then add to array
@@ -165,6 +163,8 @@ export class PropertiesUtils {
propsToPushMap[index + 1] = newProps;
}
}
+
+ prop.valueObj = PropertyFEModel.cleanValueObj(prop.valueObj);
});
//add props after we're done looping (otherwise our loop gets messed up). Push in reverse order, so we dont mess up indexes.
@@ -178,11 +178,10 @@ export class PropertiesUtils {
if (nestedPath) {
let newProp = property.flattenedChildren.find(prop => prop.propertiesName == nestedPath);
newProp && this.assignFlattenedChildrenValues(JSON.parse(newValue), [newProp], property.name);
+ property.updateValueObjOrig();
} else {
this.initValueObjectRef(property);
}
}
-
-
}
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
new file mode 100644
index 0000000000..bbbf6ae694
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html
@@ -0,0 +1,5 @@
+<ui-element-dropdown data-tests-id="linkSrc" [readonly]="!link.isFirst || (link.isFirst && !link.canEdit)" class="cell link-selector" [values]="source" [(value)]="link.fromNode" (valueChange)="onSourceSelected($event)"></ui-element-dropdown>
+<ui-element-dropdown data-tests-id="linkSrcCP" [readonly]="!link.isFirst || (link.isFirst && !link.canEdit)" class="cell link-selector" [values]="srcCP" [(value)]="link.fromCP" (valueChange)="onSrcCPSelected($event)"></ui-element-dropdown>
+<ui-element-dropdown data-tests-id="linkTarget" [readonly]="!link.canEdit" class="cell link-selector" [values]="target" [(value)]="link.toNode" (valueChange)="onTargetSelected($event)"></ui-element-dropdown>
+<ui-element-dropdown data-tests-id="linkTargetCP" [readonly]="!link.canEdit" class="cell link-selector" [values]="targetCP" [(value)]="link.toCP" (valueChange)="onTargetCPSelected($event)"></ui-element-dropdown>
+<div class="cell remove" data-tests-id="removeLnk"><span *ngIf="link.canRemove" class="sprite-new delete-item-icon" (click)="removeRow()"></span></div> \ No newline at end of file
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
new file mode 100644
index 0000000000..beec9bd567
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less
@@ -0,0 +1,21 @@
+@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
new file mode 100644
index 0000000000..16433242d6
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts
@@ -0,0 +1,110 @@
+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<ServicePathMapItem>;
+ @Input() link:Link;
+ @Input() removeRow:Function;
+ source:Array<DropdownValue> = [];
+ target: Array<DropdownValue> = [];
+ srcCP: Array<DropdownValue> = [];
+ targetCP: Array<DropdownValue> = [];
+
+ ngOnChanges() {
+ if (this.data) {
+ this.parseInitialData(this.data);
+ }
+ }
+
+ parseInitialData(data: Array<ServicePathMapItem>) {
+ 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) {
+ let targetOptions = this.findOptions(srcCPOptions, this.link.fromCP);
+ if (!targetOptions) { return; }
+ this.target = this.convertValuesToDropDownOptions(targetOptions);
+ if (this.link.toNode) {
+ let targetCPOptions = this.findOptions(targetOptions, this.link.toNode);
+ if (!targetCPOptions) { return; }
+ this.targetCP = this.convertValuesToDropDownOptions(targetCPOptions);
+ }
+ }
+ }
+ }
+
+ private findOptions(items: Array<ServicePathMapItem>, nodeOrCPId: string) {
+ let item = items.find((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<ServicePathMapItem>) : Array<DropdownValue> {
+ let result = [];
+ for (let i = 0; i < values.length ; i++) {
+ result[result.length] = new DropdownValue(values[i].id, values[i].data.name);
+ }
+ return result;
+ }
+
+ 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 srcCPData = this.data.find((dataItem)=> this.link.fromNode === dataItem.id).data;
+ let srcCPOptions = srcCPData.options;
+ let targetOptions = this.findOptions(srcCPOptions, id);
+ this.target = this.convertValuesToDropDownOptions(targetOptions);
+ this.link.fromCPOriginId = srcCPData.ownerId;
+ this.link.toNode = '';
+ this.link.toCP = '';
+ this.targetCP = [];
+ }
+
+ }
+
+ onTargetSelected(id) {
+ if (id) {
+ let srcCPOptions = this.findOptions(this.data, this.link.fromNode);
+ let targetOptions = this.findOptions(srcCPOptions, this.link.fromCP);
+ let targetCPOptions = this.findOptions(targetOptions, id);
+ this.targetCP = this.convertValuesToDropDownOptions(targetCPOptions);
+ this.link.toCP = '';
+ }
+
+ }
+
+ onTargetCPSelected(id) {
+ if (id) {
+ let srcCPOptions = this.findOptions(this.data, this.link.fromNode);
+ let targetOptions = this.findOptions(srcCPOptions, this.link.fromCP);
+ let targetCPOptions = this.findOptions(targetOptions, this.link.toNode);
+ let targetCPDataObj = targetCPOptions.find((dataItem)=> id === dataItem.id).data;
+ this.link.toCPOriginId = targetCPDataObj.ownerId;
+ }
+ }
+} \ No newline at end of file
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
new file mode 100644
index 0000000000..80128eb42e
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts
@@ -0,0 +1,36 @@
+/*-
+ * ============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
new file mode 100644
index 0000000000..96cd83eef6
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html
@@ -0,0 +1,43 @@
+<div class="service-path-creator">
+ <form class="w-sdc-form">
+ <div class="i-sdc-form-item" >
+ <label class="i-sdc-form-label required">Path Name</label>
+ <!-- <ui-element-input type="text" name="pathName" [value]="pathName" ></ui-element-input> -->
+ <input type="text" data-tests-id="pathName" name="pathName" [(ngModel)]="forwardingPath.name" [attr.maxLength]="100" /> <!-- TODO - make unique -->
+ </div>
+
+ <div class="side-by-side">
+ <div class="i-sdc-form-item" >
+ <label class="i-sdc-form-label">Protocol</label>
+ <!-- <ui-element-input type="text" name="protocol" [value]="protocol" ></ui-element-input> -->
+ <input type="text" data-tests-id="pathProtocol" name="protocol" [(ngModel)]="forwardingPath.protocol" [attr.maxLength]="100" />
+ </div>
+ <div class="i-sdc-form-item" >
+ <label class="i-sdc-form-label">Destination Port Numbers</label>
+ <!-- <ui-element-input type="text" name="portNumbers" [value]="portNumbers" ></ui-element-input> -->
+ <input type="text" data-tests-id="pathPortNumbers" name="portNumbers" [(ngModel)]="forwardingPath.destinationPortNumber" pattern="[0-9,]*" /> <!-- TODO - validate delimiter -->
+ </div>
+ </div>
+
+ <div class="separator-buttons">
+ <span class="based-on-title">Based On</span>
+ <a (click)="addRow()" [ngClass]="{'disabled':!isExtendAllowed()}" data-tests-id="extendPathlnk">Extend Path</a>
+ </div>
+
+ <div class="generic-table">
+ <div class="header-row">
+ <div class="cell header-cell" *ngFor="let header of headers">
+ {{header}}
+ </div>
+ </div>
+ <div *ngIf="links && links.length === 0" class="no-row-text" >
+ There is no data to display
+ </div>
+ <div>
+ <link-row *ngFor="let link of links" [data]="linksMap" [link]="link" [removeRow]="removeRow" class="data-row" ></link-row>
+ </div>
+ </div>
+
+
+ </form>
+</div> \ 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
new file mode 100644
index 0000000000..5c9e53e229
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less
@@ -0,0 +1,45 @@
+@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
new file mode 100644
index 0000000000..dac41a37bc
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts
@@ -0,0 +1,137 @@
+/*-
+ * ============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<ServicePathMapItem>;
+ links:Array<Link> = [];
+ input:any;
+ headers: Array<string> = [];
+ 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 = <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
new file mode 100644
index 0000000000..78005317a2
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts
@@ -0,0 +1,25 @@
+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
new file mode 100644
index 0000000000..8a31c76998
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html
@@ -0,0 +1,21 @@
+<div class="service-path-list">
+ <div class="add-path-link"><a (click)="onAddServicePath()" data-tests-id="add-service-path-lnk" >+ Add Path</a></div>
+ <div class="generic-table table-container" >
+ <div class="header-row">
+ <div class="cell header-cell" *ngFor="let header of headers">
+ {{header}}
+ </div>
+ </div>
+ <div *ngFor="let path of paths" class="data-row" >
+ <div class="cell" data-tests-id="path-name" >{{path.name}}</div>
+ <div class="cell path-action-buttons">
+ <span class="sprite-new update-component-icon" (click)="onEditServicePath(path.uniqueId)" data-tests-id="update-service-path-btn" ></span>
+ <span class="sprite-new delete-item-icon" (click)="deletePath(path.uniqueId)" data-tests-id="delete-service-path-btn"></span>
+ </div>
+ </div>
+ <div *ngIf="paths && paths.length === 0" class="no-row-text" >
+ No paths have been added yet.
+ </div>
+ </div>
+
+</div> \ 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
new file mode 100644
index 0000000000..aff597fd85
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less
@@ -0,0 +1,21 @@
+@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;
+ }
+} \ 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
new file mode 100644
index 0000000000..04083e8685
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts
@@ -0,0 +1,66 @@
+/*-
+ * ============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<ModalComponent>;
+ headers: Array<string> = [];
+ paths: Array<ForwardingPath> = [];
+ input:any;
+ onAddServicePath: Function;
+ onEditServicePath: Function;
+
+ constructor(private serviceService:ServiceServiceNg2) {
+ this.headers = ['Path 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;
+ }
+
+ 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
new file mode 100644
index 0000000000..c236934002
--- /dev/null
+++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts
@@ -0,0 +1,17 @@
+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