Sync Integ to Master
Change-Id: I71e3acc26fa612127756ac04073a522b9cc6cd74
Issue-ID: SDC-977
Signed-off-by: Gitelman, Tal (tg851x) <tg851x@intl.att.com>
diff --git a/catalog-ui/src/app/ng2/app.module.ts b/catalog-ui/src/app/ng2/app.module.ts
index ffeb1fd..727f0fe 100644
--- a/catalog-ui/src/app/ng2/app.module.ts
+++ b/catalog-ui/src/app/ng2/app.module.ts
@@ -28,7 +28,8 @@
import {PropertiesAssignmentModule} from './pages/properties-assignment/properties-assignment.module';
import {
DataTypesServiceProvider, SharingServiceProvider, CookieServiceProvider, StateServiceFactory,
- StateParamsServiceFactory, CacheServiceProvider, EventListenerServiceProvider, ScopeServiceFactory
+ StateParamsServiceFactory, CacheServiceProvider, EventListenerServiceProvider, ScopeServiceFactory,
+ NotificationServiceProvider
} from "./utils/ng1-upgraded-provider";
import {ConfigService} from "./services/config.service";
import {HttpModule} from '@angular/http';
@@ -36,6 +37,7 @@
import {AuthenticationService} from './services/authentication.service';
import {Cookie2Service} from "./services/cookie.service";
import {ComponentServiceNg2} from "./services/component-services/component.service";
+import {ComponentServiceFactoryNg2} from "./services/component-services/component.service.factory";
import {ServiceServiceNg2} from "./services/component-services/service.service";
import {ComponentInstanceServiceNg2} from "./services/component-instance-services/component-instance.service";
import {ModalService} from "./services/modal.service";
@@ -43,12 +45,18 @@
import {ConnectionWizardModule} from "./pages/connection-wizard/connection-wizard.module";
import {LayoutModule} from "./components/layout/layout.module";
import {UserService} from "./services/user.service";
+import {PoliciesService} from "./services/policies.service";
+import {DynamicComponentService} from "./services/dynamic-component.service";
import {SdcConfig} from "./config/sdc-config.config";
import { TranslateModule } from "./shared/translator/translate.module";
import { TranslationServiceConfig } from "./config/translation.service.config";
+import {ServicePathCreatorModule} from './pages/service-path-creator/service-path-creator.module';
+import {ServicePathsListModule} from './pages/service-paths-list/service-paths-list.module';
import {PluginFrameModule} from "./components/ui/plugin/plugin-frame.module";
import {PluginsService} from "./services/plugins.service";
import {EventBusService} from "./services/event-bus.service";
+import {ServicePathModule} from 'app/ng2/components/logic/service-path/service-path.module';
+import {ServicePathSelectorModule} from 'app/ng2/components/logic/service-path-selector/service-path-selector.module';
export const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule));
@@ -76,7 +84,11 @@
//We need to import them here since we use them in angular1
ConnectionWizardModule,
PropertiesAssignmentModule,
- PluginFrameModule
+ PluginFrameModule,
+ ServicePathCreatorModule,
+ ServicePathsListModule,
+ ServicePathModule,
+ ServicePathSelectorModule
],
exports: [],
entryComponents: [],
@@ -85,18 +97,22 @@
SharingServiceProvider,
CookieServiceProvider,
StateServiceFactory,
- ScopeServiceFactory,
StateParamsServiceFactory,
+ ScopeServiceFactory,
CacheServiceProvider,
EventListenerServiceProvider,
+ NotificationServiceProvider,
AuthenticationService,
Cookie2Service,
ConfigService,
ComponentServiceNg2,
+ ComponentServiceFactoryNg2,
ModalService,
ServiceServiceNg2,
HttpService,
UserService,
+ PoliciesService,
+ DynamicComponentService,
SdcConfig,
ComponentInstanceServiceNg2,
TranslationServiceConfig,
diff --git a/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts b/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts
index b8b0e80..12f8df8 100644
--- a/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts
+++ b/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts
@@ -18,6 +18,7 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import {Component, Inject, Input, Output, EventEmitter} from "@angular/core";
import {IHostedApplication, IUserProperties} from "app/models";
import {MenuItemGroup, MenuItem} from "app/utils";
@@ -142,9 +143,9 @@
}
}
- goToState(state:string, params:any):Promise<boolean> {
+ goToState(state:string, params:Array<any>):Promise<boolean> {
return new Promise((resolve, reject) => {
- this.$state.go(state, params || undefined);
+ this.$state.go(state, params && params.length > 0 ? [0] : undefined);
resolve(true);
});
}
diff --git a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html
index ddda82a..cc8aed7 100644
--- a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html
+++ b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html
@@ -7,7 +7,7 @@
<checkbox [label]="'All'" [(checked)]="allSelected" (checkedChange)="selectAll()" data-tests-id="filter-checkbox-all"></checkbox>
</div>
<div *ngFor="let type of typesOptions">
- <checkbox [label]="type" [(checked)]="selectedTypes[type]" (checkedChange)="onTypeSelected(type)"></checkbox>
+ <checkbox [label]="type" [(checked)]="selectedTypes[type]" (checkedChange)="onTypeSelected(type)" [attr.data-tests-id]="'filter-checkbox-' + type.toLowerCase()"></checkbox>
</div>
</div>
<div class="field">
diff --git a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts
index 5a9bfbc..fe2c7bb 100644
--- a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts
+++ b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts
@@ -21,6 +21,7 @@
/**
* Created by rc2122 on 5/16/2017.
*/
+import * as _ from "lodash";
import {Component, Input, Output, EventEmitter, ViewChild} from '@angular/core';
import {ButtonModel, ButtonsModelMap, FilterPropertiesAssignmentData} from "app/models";
import * as sdcConfig from "../../../../../../configurations/dev"
diff --git a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts
index dc0a02c..1698157 100644
--- a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts
+++ b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts
@@ -37,12 +37,10 @@
onClick = ($event, item) => {
$event.stopPropagation();
- this.selectedItem = item;
this.updateSelected.emit(item);
};
onSelectedUpdate = ($event) => {
- this.selectedItem = $event;
this.updateSelected.emit($event);
}
}
diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html
index 57e0474..b7cde7e 100644
--- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html
+++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html
@@ -30,11 +30,11 @@
<dynamic-element class="value-input"
*ngIf="input.isSimpleType"
pattern="validationUtils.getValidationPattern(input.type)"
- [(value)]="input.defaultValue"
+ [value]="input.defaultValueObj"
[type]="input.type"
[name]="input.name"
- (change)="onInputValueChanged(input);"
- [readonly]="readonly">
+ (elementChanged)="onInputChanged(input, $event)"
+ [readonly]="readonly">
</dynamic-element>
<div class="delete-button-container">
<span *ngIf="input.instanceUniqueId && !readonly" class="sprite-new delete-btn" (click)="openDeleteModal(input)" data-tests-id="delete-input-button"></span>
diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less
index 0545874..d709f3f 100644
--- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less
+++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less
@@ -11,7 +11,7 @@
text-align:left;
.inner-cell-div{
- width: 100%;
+ max-width: 100%;
text-overflow: ellipsis;
overflow: hidden;
height: 20px;
@@ -85,8 +85,8 @@
border-right:#d2d2d2 solid 1px;
}
&.col1 {
- flex: 1 0 200px;
- max-width:300px;
+ flex: 1 0 210px;
+ max-width:210px;
display: flex;
justify-content: space-between;
@@ -121,7 +121,6 @@
.value-input {
flex: 1;
- max-height: 24px;
border: none;
background-color: inherit;
diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts
index 0add1cd..ebecbc9 100644
--- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts
+++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts
@@ -36,7 +36,7 @@
@Input() instanceNamesMap: Map<string, string>;
@Input() readonly:boolean;
@Input() isLoading:boolean;
- @Output() inputValueChanged: EventEmitter<any> = new EventEmitter<any>();
+ @Output() inputChanged: EventEmitter<any> = new EventEmitter<any>();
@Output() deleteInput: EventEmitter<any> = new EventEmitter<any>();
selectedInputToDelete:InputFEModel;
@@ -44,8 +44,9 @@
constructor(private modalService: ModalService){
}
- onInputValueChanged = (input) => {
- this.inputValueChanged.emit(input);
+ onInputChanged = (input, event) => {
+ input.updateDefaultValueObj(event.value, event.isValid);
+ this.inputChanged.emit(input);
};
onDeleteInput = () => {
diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html
index 14b6c7d..4805875 100644
--- a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html
+++ b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html
@@ -10,7 +10,16 @@
</div>
<div class="table-cell" *ngIf="!canBeDeclared && !property.isChildOfListOrMap">{{property.name}}</div> <!-- simple children of complex type within map or list -->
<div class="table-cell map-entry" *ngIf="property.isChildOfListOrMap && propType == derivedPropertyTypes.MAP"><!-- map left cell -->
- <input [value]="property.mapKey" #mapKey (change)="mapKeyChanged.emit(mapKey)" [readonly]="readonly" type="text" [ngClass]="{'disabled':readonly, 'error':!mapKey.validity.valid}" required/>
+ <!--<input [value]="property.mapKey" [placeholder]="property.name" (input)="mapKeyChanged.emit($event.target.value)" [readonly]="readonly" type="text" [ngClass]="{'disabled':readonly, 'error':property.mapKeyError}" required/>-->
+ <dynamic-element #mapKeyInput
+ class="value-input"
+ pattern="validationUtils.getValidationPattern(string)"
+ [value]="property.mapKey"
+ type="string"
+ [name]="property.name"
+ (elementChanged)="mapKeyChanged.emit($event.value)"
+ [readonly]="readonly"
+ ></dynamic-element>
</div>
</ng-container>
<!-- RIGHT CELL OR FULL WIDTH CELL-->
@@ -18,11 +27,11 @@
<div class="table-cell">
<dynamic-element class="value-input"
pattern="validationUtils.getValidationPattern(property.type)"
- [(value)]="property.valueObj"
+ [value]="property.valueObj"
[type]="property.isDeclared ? 'string' : property.type"
[name]="property.name"
[path]="property.propertiesName"
- (valueChange)="valueChanged.emit();"
+ (elementChanged)="onElementChanged($event)"
[readonly]="readonly || property.isDeclared || property.isDisabled"
></dynamic-element>
</div>
@@ -54,8 +63,8 @@
[propertyNameSearchText]="propertyNameSearchText"
[readonly]="readonly"
[hasChildren]="getHasChildren(prop)"
- (valueChanged)="childValueChanged(prop)"
- (mapKeyChanged)="removeValueFromParent(prop, $event)"
+ (propertyChanged)="childValueChanged(prop)"
+ (mapKeyChanged)="updateChildKeyInParent(prop, $event)"
(expandChild)="expandChildById($event)"
(deleteItem)="deleteListOrMapItem($event)"
(clickOnPropertyRow)="onClickPropertyRow($event)"
diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts
index 04cb26d..6f7e57b 100644
--- a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts
+++ b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts
@@ -18,13 +18,15 @@
* ============LICENSE_END=========================================================
*/
-import {Component, Input, Output, EventEmitter} from "@angular/core";
+import * as _ from "lodash";
+import {Component, Input, Output, EventEmitter, ViewChild, ComponentRef} from "@angular/core";
import { PropertyFEModel, DerivedFEProperty, DerivedPropertyType } from "app/models";
import { PROPERTY_TYPES } from 'app/utils';
import { DataTypeService } from "../../../../services/data-type.service";
import { trigger, state, style, transition, animate } from '@angular/core';
import {PropertiesUtils} from "../../../../pages/properties-assignment/services/properties.utils";
-
+import {IUiElementChangeEvent} from "../../../ui/form-components/ui-element-base.component";
+import {DynamicElementComponent} from "../../../ui/dynamic-element/dynamic-element.component";
@Component({
selector: 'dynamic-property',
@@ -49,7 +51,7 @@
@Input() hasChildren: boolean;
@Input() hasDeclareOption:boolean;
- @Output() valueChanged: EventEmitter<any> = new EventEmitter<any>();
+ @Output('propertyChanged') emitter: EventEmitter<void> = new EventEmitter<void>();
@Output() expandChild: EventEmitter<string> = new EventEmitter<string>();
@Output() checkProperty: EventEmitter<string> = new EventEmitter<string>();
@Output() deleteItem: EventEmitter<string> = new EventEmitter<string>();
@@ -57,6 +59,7 @@
@Output() mapKeyChanged: EventEmitter<string> = new EventEmitter<string>();
@Output() addChildPropsToParent: EventEmitter<Array<DerivedFEProperty>> = new EventEmitter<Array<DerivedFEProperty>>();
+ @ViewChild('mapKeyInput') public mapKeyInput: DynamicElementComponent;
constructor(private propertiesUtils: PropertiesUtils, private dataTypeService: DataTypeService) {
}
@@ -68,6 +71,17 @@
this.nestedLevel = (this.property.propertiesName.match(/#/g) || []).length;
}
+ ngDoCheck() {
+ // set custom error for mapKeyInput
+ if (this.mapKeyInput) {
+ const mapKeyInputControl = this.mapKeyInput.cmpRef.instance.control;
+ const mapKeyError = (<DerivedFEProperty>this.property).mapKeyError;
+ if (mapKeyInputControl.getError('mapKeyError') !== mapKeyError) {
+ mapKeyInputControl.setErrors({mapKeyError});
+ }
+ }
+ }
+
onClickPropertyRow = (property, event) => {
// Because DynamicPropertyComponent is recrusive second time the event is fire event.stopPropagation = undefined
@@ -91,9 +105,15 @@
}).length > 1;
}
+ onElementChanged = (event: IUiElementChangeEvent) => {
+ this.property.updateValueObj(event.value, event.isValid);
+ this.emitter.emit();
+ };
+
createNewChildProperty = (): void => {
- let newProps: Array<DerivedFEProperty> = this.propertiesUtils.createListOrMapChildren(this.property, "", undefined);
+ let newProps: Array<DerivedFEProperty> = this.propertiesUtils.createListOrMapChildren(this.property, "", null);
+ this.propertiesUtils.assignFlattenedChildrenValues(this.property.valueObj, [newProps[0]], this.property.propertiesName);
if (this.property instanceof PropertyFEModel) {
this.addChildProps(newProps, this.property.name);
} else {
@@ -108,28 +128,25 @@
this.property.flattenedChildren.splice(insertIndex, 0, ...newProps); //using ES6 spread operator
this.expandChildById(newProps[0].propertiesName);
-
- if(!newProps[0].schema.property.isSimpleType){
- if ( newProps[0].mapKey ) {//prevent update the new item value on parent property valueObj and saving on BE if it is map item, it will be updated and saved only after user enter key (when it is list item- the map key is the es type)
- this.updateMapKeyValueOnMainParent(newProps);
- if (this.property.getParentNamesArray(newProps[0].propertiesName, []).indexOf('') === -1) {
- this.valueChanged.emit(this.property.name);
- }
- }
- }
+ this.updateMapKeyValueOnMainParent(newProps);
+ this.emitter.emit();
}
}
updateMapKeyValueOnMainParent(childrenProps: Array<DerivedFEProperty>){
if (this.property instanceof PropertyFEModel) {
+ const property: PropertyFEModel = <PropertyFEModel>this.property;
//Update only if all this property parents has key name
- if (this.property.getParentNamesArray(childrenProps[0].propertiesName, []).indexOf('') === -1){
+ if (property.getParentNamesArray(childrenProps[0].propertiesName, []).indexOf('') === -1){
angular.forEach(childrenProps, (prop:DerivedFEProperty):void => { //Update parent PropertyFEModel with value for each child, including nested props
- (<PropertyFEModel>this.property).childPropUpdated(prop);
+ property.childPropUpdated(prop);
+ if (prop.isChildOfListOrMap && prop.mapKey !== undefined) {
+ property.childPropMapKeyUpdated(prop, prop.mapKey, true);
+ }
},this);
//grab the cumulative value for the new item from parent PropertyFEModel and assign that value to DerivedFEProp[0] (which is the list or map parent with UUID of the set we just added)
- let parentNames = (<PropertyFEModel>this.property).getParentNamesArray(childrenProps[0].propertiesName, []);
- childrenProps[0].valueObj = _.get(this.property.valueObj, parentNames.join('.'));
+ let parentNames = (<PropertyFEModel>property).getParentNamesArray(childrenProps[0].propertiesName, []);
+ childrenProps[0].valueObj = _.get(property.valueObj, parentNames.join('.'), null);
}
}
}
@@ -140,7 +157,7 @@
if (this.property.getParentNamesArray(property.propertiesName, []).indexOf('') === -1) {//If one of the parents is empty key -don't save
this.property.childPropUpdated(property);
this.dataTypeService.checkForCustomBehavior(this.property);
- this.valueChanged.emit(this.property.name);
+ this.emitter.emit();
}
}
}
@@ -153,46 +170,42 @@
}
}
- removeValueFromParent = (item: DerivedFEProperty, target?: any) => {
+ removeValueFromParent = (item: DerivedFEProperty) => {
if (this.property instanceof PropertyFEModel) {
- let itemParent = (item.parentName == this.property.name) ? this.property : this.property.flattenedChildren.find(prop => prop.propertiesName == item.parentName);
+ let itemParent = (item.parentName == this.property.name)
+ ? this.property : this.property.flattenedChildren.find(prop => prop.propertiesName == item.parentName);
+ if (!itemParent) {
+ return;
+ }
if (item.derivedDataType == DerivedPropertyType.MAP) {
- let oldKey = item.mapKey;
- if (target && typeof target.value == 'string') { //allow saving empty string
- let replaceKey:string = target.value;
- if (!replaceKey) {//prevent delete map key
- return;
- }
- if(Object.keys(itemParent.valueObj).indexOf(replaceKey) > -1){//the key is exists
- target.setCustomValidity('This key is already exists.');
- return;
- }else {
- target.setCustomValidity('');
- _.set(itemParent.valueObj, replaceKey, itemParent.valueObj[oldKey]);
- item.mapKey = replaceKey;
- //If the map key was empty its valueObj was not updated on its prent property valueObj, and now we should update it.
- if(!oldKey && !item.schema.property.isSimpleType){
- //Search this map item children and update these value on parent property valueOBj
- let mapKeyFlattenChildren:Array<DerivedFEProperty> = _.filter(this.property.flattenedChildren, (prop:DerivedFEProperty) => {
- return _.startsWith(prop.propertiesName, item.propertiesName);
- });
- this.updateMapKeyValueOnMainParent(mapKeyFlattenChildren);
- }
- }
- }
+ const oldKey = item.getActualMapKey();
delete itemParent.valueObj[oldKey];
+ if (itemParent instanceof PropertyFEModel) {
+ delete itemParent.valueObjValidation[oldKey];
+ itemParent.valueObjIsValid = itemParent.calculateValueObjIsValid();
+ }
+ this.property.childPropMapKeyUpdated(item, null); // remove map key
} else {
- let itemIndex: number = this.property.flattenedChildren.filter(prop => prop.parentName == item.parentName).map(prop => prop.propertiesName).indexOf(item.propertiesName);
+ const itemIndex: number = this.property.flattenedChildren.filter(prop => prop.parentName == item.parentName).map(prop => prop.propertiesName).indexOf(item.propertiesName);
itemParent.valueObj.splice(itemIndex, 1);
- }
- if (item.mapKey) {//prevent going to BE if user tries to delete map item without key (it was not saved in BE)
- if (itemParent instanceof PropertyFEModel) { //direct child
- this.valueChanged.emit(this.property.name);
- } else { //nested child - need to update parent prop by getting flattened name (recurse through parents and replace map/list keys, etc)
- this.childValueChanged(itemParent);
+ if (itemParent instanceof PropertyFEModel) {
+ itemParent.valueObjValidation.splice(itemIndex, 1);
+ itemParent.valueObjIsValid = itemParent.calculateValueObjIsValid();
}
}
+ if (itemParent instanceof PropertyFEModel) { //direct child
+ this.emitter.emit();
+ } else { //nested child - need to update parent prop by getting flattened name (recurse through parents and replace map/list keys, etc)
+ this.childValueChanged(itemParent);
+ }
+ }
+ }
+
+ updateChildKeyInParent(childProp: DerivedFEProperty, newMapKey: string) {
+ if (this.property instanceof PropertyFEModel) {
+ this.property.childPropMapKeyUpdated(childProp, newMapKey);
+ this.emitter.emit();
}
}
diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html
index 933b80f..ecfa7e7 100644
--- a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html
+++ b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html
@@ -48,7 +48,7 @@
[expandedChildId]="property.expandedChildPropertyId"
[propertyNameSearchText]="propertyNameSearchText"
[readonly]="readonly"
- (valueChanged)="propValueChanged(property);"
+ (propertyChanged)="onPropertyChanged(property)"
(expandChild)="property.updateExpandedChildPropertyId($event)"
(clickOnPropertyRow)="onClickPropertyInnerRow($event, instanceId)"
(checkProperty)="propertyChecked(property, $event)"
diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts
index 32bbb1b..093fae1 100644
--- a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts
+++ b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts
@@ -39,7 +39,7 @@
@Input() hasDeclareOption:boolean;
@Input() hidePropertyType:boolean;
- @Output() valueChanged: EventEmitter<any> = new EventEmitter<any>();
+ @Output('propertyChanged') emitter: EventEmitter<PropertyFEModel> = new EventEmitter<PropertyFEModel>();
@Output() selectPropertyRow: EventEmitter<PropertyRowSelectedEvent> = new EventEmitter<PropertyRowSelectedEvent>();
@Output() updateCheckedPropertyCount: EventEmitter<boolean> = new EventEmitter<boolean>();//only for hasDeclareOption
@@ -49,8 +49,8 @@
ngOnInit() {
}
- propValueChanged = (property) => {
- this.valueChanged.emit(property);
+ onPropertyChanged = (property) => {
+ this.emitter.emit(property);
};
// Click on main row (row of propertyFEModel)
diff --git a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html
index bcd33ef..6e0f93f 100644
--- a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html
+++ b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html
@@ -22,7 +22,8 @@
</div>
</div>
<div class="inner-container properties-table-container" *ngIf="selectedReqOrCapOption == 'Capability'" [ngClass]="{'cap-selected':selectedReqOrCapModel}">
- <properties-table *ngIf="selectedReqOrCapModel"
+ <loader [display]="loadingCapabilityProperties" size="medium" [relative]="true"></loader>
+ <properties-table *ngIf="selectedReqOrCapModel && !loadingCapabilityProperties"
class="properties-table"
[readonly]="true"
[fePropertiesMap]="capabilityProperties"
diff --git a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts
index ab67dc1..2e3c21c 100644
--- a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts
+++ b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts
@@ -1,10 +1,10 @@
/**
* Created by rc2122 on 9/4/2017.
*/
+import * as _ from "lodash";
import {Component, EventEmitter, Input, OnInit, Output, SimpleChanges} from '@angular/core';
-import {RadioButtonModel, Match, PropertyModel, InstanceFePropertiesMap, Component as ComponentModel} from "app/models";
+import {RadioButtonModel, PropertyModel, InstanceFePropertiesMap, Component as ComponentModel} from "app/models";
import {Dictionary} from "lodash";
-import {DropdownValue} from "../../ui/form-components/dropdown/ui-element-dropdown.component";
import {ComponentInstanceServiceNg2} from "../../../services/component-instance-services/component-instance.service";
import {PropertiesUtils} from "app/ng2/pages/properties-assignment/services/properties.utils";
import {Requirement} from "../../../../models/requirement";
@@ -33,7 +33,6 @@
@Input() selectedReqOrCapModel:RequirementCapabilityModel;
@Output() updateSelectedReqOrCap:EventEmitter<RequirementCapabilityModel> = new EventEmitter<RequirementCapabilityModel>();
- @Output() updateCapabilityProperties:EventEmitter<Array<PropertyModel>> = new EventEmitter<Array<PropertyModel>>();
types:Array<string> = [];
selectedType:string;
@@ -47,10 +46,14 @@
displayCapReqListFilterByType:RequirementCapabilityModel[];
capabilityProperties:InstanceFePropertiesMap;
+ loadingCapabilityProperties:boolean;
+
+ private _loadingCapabilityProperties: Array<Capability>;
constructor(private componentInstanceServiceNg2:ComponentInstanceServiceNg2,
private propertiesUtils:PropertiesUtils) {
this.selectOptions = [new RadioButtonModel(REQUIREMENT, REQUIREMENT), new RadioButtonModel(CAPABILITY, CAPABILITY)];
+ this._loadingCapabilityProperties = [];
}
private initDefaultReqOrCapSelection = (): void => {
@@ -87,14 +90,17 @@
initCapabilityPropertiesTable = ():void => {
if(this.selectedReqOrCapModel instanceof Capability ) {
let selectedCapability = <Capability>this.selectedReqOrCapModel;
- if(selectedCapability.properties){
+ if (selectedCapability.properties && selectedCapability.properties.length) {
this.capabilityProperties = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({ CAPABILITY : selectedCapability.properties}, false);
+ } else {
+ this.capabilityProperties = null;
}
}
}
ngOnChanges(changes:SimpleChanges) {
if (changes.selectedReqOrCapModel) {
+ this.capabilityProperties = null;
if (this.selectedReqOrCapModel && this.selectedReqOrCapOption === CAPABILITY) {
this.setCapabilityProperties();
}
@@ -168,15 +174,27 @@
private setCapabilityProperties = ():void => {
let selectedCapability = <Capability>this.selectedReqOrCapModel;
- if (selectedCapability.properties === undefined) {
- this.componentInstanceServiceNg2.getInstanceCapabilityProperties(this.currentComponent, this.componentInstanceId, selectedCapability.type, selectedCapability.name)
- .subscribe((response:Array<PropertyModel>) => {
- this.capabilityProperties = (response && response.length) ? this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({CAPABILITY : response}, false) : null;
- this.updateCapabilityProperties.emit(response);
- }, error => {});
- }else{
- this.capabilityProperties = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({CAPABILITY : selectedCapability.properties}, false);
- this.updateCapabilityProperties.emit(selectedCapability.properties);
+ if (!selectedCapability.properties) {
+ this.loadingCapabilityProperties = true;
+ if (this._loadingCapabilityProperties.indexOf(selectedCapability) == -1) {
+ this._loadingCapabilityProperties.push(selectedCapability);
+ this.componentInstanceServiceNg2.getInstanceCapabilityProperties(this.currentComponent, this.componentInstanceId, selectedCapability)
+ .subscribe((response: Array<PropertyModel>) => {
+ if (this.selectedReqOrCapModel === selectedCapability) {
+ delete this.loadingCapabilityProperties;
+ }
+ this.initCapabilityPropertiesTable();
+ }, (error) => {
+ if (this.selectedReqOrCapModel === selectedCapability) {
+ delete this.loadingCapabilityProperties;
+ }
+ }, () => {
+ this._loadingCapabilityProperties.splice(this._loadingCapabilityProperties.indexOf(selectedCapability), 1);
+ });
+ }
+ } else {
+ delete this.loadingCapabilityProperties;
+ this.initCapabilityPropertiesTable();
}
}
}
diff --git a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts
index cb12dea..259ae12 100644
--- a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts
+++ b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts
@@ -5,6 +5,7 @@
import {CommonModule} from "@angular/common";
import {GlobalPipesModule} from "app/ng2/pipes/global-pipes.module";
import {PropertyTableModule} from "../properties-table/property-table.module";
+import {UiElementsModule} from "../../ui/ui-elements.module";
@NgModule({
declarations: [
@@ -15,7 +16,9 @@
FormsModule,
FormElementsModule,
GlobalPipesModule,
- PropertyTableModule],
+ UiElementsModule,
+ PropertyTableModule
+ ],
exports: [SelectRequirementOrCapabilityComponent],
providers: []
diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html
new file mode 100644
index 0000000..72e0835
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html
@@ -0,0 +1,11 @@
+<div class="service-path-selector">
+ <label>Service Paths:</label>
+ <ui-element-dropdown
+ class="path-dropdown"
+ data-tests-id="service-path-selector"
+ [readonly]="dropdownOptions.length < 3"
+ [(value)]="selectedPathId"
+ [values]="dropdownOptions"
+ (valueChange)="onSelectPath()">
+ </ui-element-dropdown>
+</div>
diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.less b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.less
new file mode 100644
index 0000000..f3cb4a3
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.less
@@ -0,0 +1,24 @@
+@import './../../../../../assets/styles/variables.less';
+.service-path-selector {
+ margin: 10px 35px 10px 0;
+ display: flex;
+ font-size: 12px;
+
+ /deep/ .path-dropdown {
+ width: 150px;
+ select {
+ font-size: 14px;
+ font-family: @font-opensans-regular;
+ padding: 4px 10px;
+ }
+ }
+
+ label {
+ margin-right: 10px;
+ align-self: center;
+ font-size: 14px;
+ font-family: @font-opensans-regular;
+ font-weight: normal;
+ margin-bottom: initial;
+ }
+}
diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts
new file mode 100644
index 0000000..be9966a
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts
@@ -0,0 +1,134 @@
+import * as _ from "lodash";
+import {Component, Input, KeyValueDiffer, IterableDiffers, KeyValueDiffers, DoCheck} from '@angular/core';
+import {Service} from "app/models/components/service";
+import {TranslateService} from "app/ng2/shared/translator/translate.service";
+import {ForwardingPath} from "app/models/forwarding-path";
+import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component";
+
+@Component({
+ selector: 'service-path-selector',
+ templateUrl: './service-path-selector.component.html',
+ styleUrls:['service-path-selector.component.less'],
+ providers: [TranslateService]
+})
+
+export class ServicePathSelectorComponent implements DoCheck {
+
+ defaultSelectedId: string;
+ hideAllValue: string;
+ hideAllId: string = '0';
+ showAllValue: string;
+ showAllId: string = '1';
+
+ paths: Array<ForwardingPath> = [];
+ dropdownOptions: Array<DropdownValue>;
+ differ: KeyValueDiffer;
+
+ @Input() service: Service;
+ @Input() drawPath: Function;
+ @Input() deletePaths: Function;
+ @Input() selectedPathId: string;
+
+ constructor(private differs: KeyValueDiffers, private translateService: TranslateService) {
+
+ this.defaultSelectedId = this.hideAllId;
+ this.convertPathsToDropdownOptions();
+
+ this.translateService.languageChangedObservable.subscribe(lang => {
+ this.hideAllValue = this.translateService.translate("SERVICE_PATH_SELECTOR_HIDE_ALL_VALUE");
+ this.showAllValue = this.translateService.translate("SERVICE_PATH_SELECTOR_SHOW_ALL_VALUE");
+ this.convertPathsToDropdownOptions();
+ });
+
+ }
+
+ ngOnInit(): void {
+
+ this.selectedPathId = this.defaultSelectedId;
+ this.differ = this.differs.find(this.service.forwardingPaths).create(null);
+
+ }
+
+ ngDoCheck(): void {
+
+ const pathsChanged = this.differ.diff(this.service.forwardingPaths);
+
+ if (pathsChanged) {
+ let oldPaths = _.cloneDeep(this.paths);
+ this.populatePathsFromService();
+
+ if (!(_.isEqual(oldPaths, this.paths))) {
+ this.convertPathsToDropdownOptions();
+
+ let temp = this.selectedPathId;
+ this.selectedPathId = '-1';
+
+ setTimeout(() => {
+ this.selectedPathId = temp;
+ this.onSelectPath();
+ }, 0);
+ }
+ }
+
+ }
+
+ populatePathsFromService(): void {
+
+ this.paths = [];
+ let {forwardingPaths} = this.service;
+
+ _.forEach(forwardingPaths, path => {
+ this.paths.push(path);
+ });
+ this.paths.sort((a:ForwardingPath, b:ForwardingPath)=> {
+ return a.name.localeCompare(b.name);
+ });
+
+ }
+
+ convertPathsToDropdownOptions(): void {
+
+ let result = [
+ new DropdownValue(this.hideAllId, this.hideAllValue),
+ new DropdownValue(this.showAllId, this.showAllValue)
+ ];
+
+ _.forEach(this.paths, (value: ForwardingPath) => {
+ result[result.length] = new DropdownValue(value.uniqueId, value.name);
+ });
+
+ this.dropdownOptions = result;
+
+ }
+
+ onSelectPath = (): void => {
+
+ if (this.selectedPathId !== '-1') {
+ this.deletePaths();
+
+ switch (this.selectedPathId) {
+ case this.hideAllId:
+ break;
+
+ case this.showAllId:
+ _.forEach(this.paths, path =>
+ this.drawPath(path)
+ );
+ break;
+
+ default:
+ let path = this.paths.find(path =>
+ path.uniqueId === this.selectedPathId
+ );
+ if (!path) {
+ this.selectedPathId = this.defaultSelectedId;
+ this.onSelectPath(); // currently does nothing in default case, but if one day it does, we want the selection to behave accordingly.
+ break;
+ }
+ this.drawPath(path);
+ break;
+ }
+ }
+
+ }
+}
diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.module.ts b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.module.ts
new file mode 100644
index 0000000..c07061c
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.module.ts
@@ -0,0 +1,21 @@
+import { NgModule } from "@angular/core";
+import {CommonModule} from "@angular/common";
+import {ServicePathSelectorComponent} from "./service-path-selector.component";
+import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module";
+
+@NgModule({
+ declarations: [
+ ServicePathSelectorComponent
+ ],
+ imports: [
+ CommonModule,
+ UiElementsModule
+ ],
+ exports: [],
+ entryComponents: [
+ ServicePathSelectorComponent
+ ],
+ providers: []
+})
+export class ServicePathSelectorModule {
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html
new file mode 100644
index 0000000..ff7902e
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html
@@ -0,0 +1,22 @@
+<div class='service-path'>
+ <button class='zoom-icons create-path-button' data-tests-id="pathsMenuBtn" (click)="showServicePathMenu = !showServicePathMenu">...</button>
+ <div class="service-path-menu"
+ *ngIf="showServicePathMenu" >
+ <div >
+ <ul>
+ <li><div class="hand" (click)="onCreateServicePath()" data-tests-id="createPathMenuItem">
+ Create Service Path
+ </div></li>
+ <li><div class="hand" (click)="onListServicePath()" data-tests-id="pathsListMenuItem">
+ Service Paths List
+ </div></li>
+ </ul>
+ </div>
+ </div>
+ <!-- TODO - ask Orit about positioning issues and styling issues -->
+ <!--
+ <menu-list [open]="showServicePathMenu" [position]="menuPos" >
+ <menu-item [action]="onCreateServicePath">Create Path</menu-item>
+ <menu-item [action]="onListServicePath">Paths' List</menu-item>
+ </menu-list> -->
+</div>
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.less b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.less
new file mode 100644
index 0000000..777b206
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.less
@@ -0,0 +1,51 @@
+//@import 'src/assets/styles/variables.less';
+@import './../../../../../assets/styles/variables.less';
+.service-path {
+ position: relative;
+ .create-path-button{
+ &:extend(.search-bar-button);
+ width: 30px;
+ height: 30px;
+ &:hover {
+ color: @main_color_a;
+ }
+ &:active {
+ background: @main_color_a;
+ color: @main_color_p;
+ }
+ &:focus {
+ outline: none;
+ }
+ }
+ .service-path-menu {
+ border: 1px solid @main_color_o;
+ border-radius: 0 0 2px 2px;
+ border-top-color: @main_color_a;
+ border-top-width: 3px;
+
+ box-sizing: border-box;
+ box-shadow: 0 2px 4px 0 rgba(0,0,0,0.30);
+
+ background-color: @main_color_p;
+
+ padding: 5px 0;
+ right: 34px;
+ position: absolute;
+ top: 10px;
+ width: 150px;
+ font-size: 13px;
+ font-family: @font-opensans-regular;
+
+ li {
+ color: @main_color_m;
+ padding: 0 10px;
+ line-height: 20px;
+ &:hover {
+ cursor: pointer;
+ color: @main_color_a;
+ background-color: fade(@main_color_a, 5%);
+ }
+ }
+
+ }
+}
diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts
new file mode 100644
index 0000000..4a6209f
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts
@@ -0,0 +1,83 @@
+/*-
+ * ============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, Input, ComponentRef} from '@angular/core';
+import {ModalService} from 'app/ng2/services/modal.service';
+import {ModalModel, ButtonModel} from 'app/models';
+import {ServicePathCreatorComponent} from 'app/ng2/pages/service-path-creator/service-path-creator.component';
+import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
+import ServicePathsListComponent from "app/ng2/pages/service-paths-list/service-paths-list.component";
+import {Service} from "app/models/components/service";
+
+@Component({
+ selector: 'service-path',
+ templateUrl: './service-path.component.html',
+ styleUrls: ['service-path.component.less'],
+ providers: [ModalService]
+})
+
+export class ServicePathComponent {
+ showServicePathMenu: boolean = false;
+ modalInstance: ComponentRef<ModalComponent>;
+ @Input() service: Service;
+ @Input() onCreate: Function;
+ @Input() onSave: Function;
+
+ constructor(private ModalServiceNg2: ModalService) {}
+
+ onCreateServicePath = ():void => {
+ this.showServicePathMenu = false;
+ let cancelButton: ButtonModel = new ButtonModel('Cancel', 'outline white', this.ModalServiceNg2.closeCurrentModal);
+ let saveButton: ButtonModel = new ButtonModel('Create', 'blue', this.createPath, this.getDisabled );
+ let modalModel: ModalModel = new ModalModel('l', 'Create Service Path', '', [saveButton, cancelButton], 'standard', true);
+ this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel);
+ this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathCreatorComponent, {service: this.service});
+ this.modalInstance.instance.open();
+ };
+
+ onListServicePath = ():void => {
+ this.showServicePathMenu = false;
+ let cancelButton: ButtonModel = new ButtonModel('Close', 'outline white', this.ModalServiceNg2.closeCurrentModal);
+ let modalModel: ModalModel = new ModalModel('md', 'Service Paths List','', [cancelButton], 'standard', true);
+ this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel);
+ this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathsListComponent, {service: this.service,
+ onCreateServicePath: this.onCreateServicePath, onEditServicePath: this.onEditServicePath});
+ this.modalInstance.instance.open();
+ };
+
+ createPath = ():void => {
+ this.onCreate(this.modalInstance.instance.dynamicContent.instance.createServicePathData());
+ this.ModalServiceNg2.closeCurrentModal();
+ };
+
+ onEditServicePath = (id:string):void => {
+ let cancelButton: ButtonModel = new ButtonModel('Cancel', 'outline white', this.ModalServiceNg2.closeCurrentModal);
+ let saveButton: ButtonModel = new ButtonModel('Save', 'blue', this.createPath, this.getDisabled );
+ let modalModel: ModalModel = new ModalModel('l', 'Edit Path', '', [saveButton, cancelButton]);
+ this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel);
+ this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathCreatorComponent, {service: this.service, pathId: id});
+ this.modalInstance.instance.open();
+ };
+
+ getDisabled = ():boolean => {
+ return !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit();
+ };
+}
+
diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.module.ts b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.module.ts
new file mode 100644
index 0000000..96af247
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.module.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============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 { NgModule } from "@angular/core";
+import {CommonModule} from "@angular/common";
+import {ServicePathComponent} from "./service-path.component";
+import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module";
+
+@NgModule({
+ declarations: [
+ ServicePathComponent
+ ],
+ imports: [CommonModule,
+ UiElementsModule],
+ exports: [],
+ entryComponents: [
+ ServicePathComponent
+ ],
+ providers: []
+})
+export class ServicePathModule {
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html
new file mode 100644
index 0000000..e279e3f
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html
@@ -0,0 +1,11 @@
+<div class="sdc-canvas-zone {{class}}-zone" [class.minimized]="minifyZone" [class.hidden]="!showZone">
+ <div class="sdc-canvas-zone__header" (click)="unminifyZone()" >
+ <div class="sdc-canvas-zone__title">{{title}}
+ <span class="sdc-canvas-zone__counter">{{count}}</span>
+ </div>
+ <span class="sdc-canvas-zone__state-button">–</span>
+ </div>
+ <div class="sdc-canvas-zone__container">
+ <ng-content></ng-content>
+ </div>
+</div>
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less
new file mode 100644
index 0000000..3e77c5c
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less
@@ -0,0 +1,62 @@
+.sdc-canvas-zone {
+ width: 280px;
+ max-height:186px;
+ display:flex;
+ flex-direction:column;
+ align-self: flex-end;
+ color:white;
+ font-family:OpenSans-Regular, sans-serif;
+ transition: width .2s ease-in-out, max-height .2s ease-in-out .1s;
+ position:relative;
+ bottom:0px;
+
+ .sdc-canvas-zone__header {
+ background: #5A5A5A;
+ border-radius: 2px 2px 0 0;
+ padding: 5px 10px;
+ display:flex;
+ justify-content: space-between;
+ font-size: 14px;
+ text-transform:uppercase;
+ .sdc-canvas-zone__state-button {
+ font-weight:bold;
+ cursor:pointer;
+ }
+ }
+
+ .sdc-canvas-zone__container {
+ padding:5px;
+ background-color: #5A5A5A;
+ opacity:0.9;
+ flex: 1;
+ display:flex;
+ flex-direction: row;
+ align-items: flex-start;
+ flex-wrap:wrap;
+ overflow-y:auto;
+ min-height: 80px;
+ max-height: 170px;
+ }
+
+
+ &.minimized {
+ max-height:30px;
+ width:120px;
+ cursor:pointer;
+
+ .sdc-canvas-zone__state-button {
+ display:none;
+ }
+ .sdc-canvas-zone__container {
+ flex: 0 0 0;
+ min-height: 0;
+ padding: 0;
+ overflow-y:hidden;
+ transition: min-height .2s ease-in-out .2s;
+ transition: padding .1s ease-in-out 0s;
+ }
+ }
+ &.hidden {
+ display:none;
+ }
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts
new file mode 100644
index 0000000..7e60cb3
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts
@@ -0,0 +1,25 @@
+import { Component, Input, Output, ViewEncapsulation, EventEmitter } from '@angular/core';
+import { EventListenerService } from 'app/services';
+import { GRAPH_EVENTS } from 'app/utils';
+
+@Component({
+ selector: 'zone-container',
+ templateUrl: './zone-container.component.html',
+ styleUrls: ['./zone-container.component.less'],
+ encapsulation: ViewEncapsulation.None
+})
+
+export class ZoneContainerComponent {
+ @Input() title:string;
+ @Input() class:string;
+ @Input() count:number;
+ @Input() showZone:boolean;
+ @Input() minifyZone:boolean;
+ constructor(private eventListenerService:EventListenerService) {}
+
+ private unminifyZone = () => {
+ this.minifyZone = !this.minifyZone;
+ this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ZONE_SIZE_CHANGE);
+ }
+
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html
new file mode 100644
index 0000000..d36b7ae
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html
@@ -0,0 +1,8 @@
+<div class="zone-child mode-{{config.mode}}" [class.locked]="activeInstanceMode > MODE.HOVER"
+ (mouseenter)="setMode(MODE.HOVER)" (mouseleave)="setMode(MODE.NONE)" (click)="setMode(MODE.SELECTED)">
+ <div class="zone-child__handle" (click)="setMode(MODE.TAG, $event)">+</div>
+ <div class="zone-child__body">
+ <div class="zone-child__body-content">{{config.count || defaultIconText}}</div>
+ </div>
+ <div class="zone-child__name">{{config.name}}</div>
+</div>
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less
new file mode 100644
index 0000000..a1d56df
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less
@@ -0,0 +1,110 @@
+.zone-child {
+ position:relative;
+ width:76px;
+ margin:5px;
+
+ .zone-child__handle {
+ display:none;
+ position:absolute;
+ right:4px;
+ top:10px;
+ width:22px;
+ height:22px;
+ cursor:pointer;
+ border: solid white 1px;
+ border-radius: 2px;
+ text-align: center;
+ font-weight:bold;
+ }
+
+ .zone-child__body {
+ margin:0 auto;
+ width:43px;
+ height:43px;
+ display:flex;
+ padding:3px;
+
+ }
+
+ .zone-child__body-content {
+ border-radius: 2px;
+ flex:1;
+ color:white;
+ font-size:18px;
+ text-align:center;
+ display:flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .zone-child__name {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ text-align:center;
+ }
+ /* Dynamic classes below */
+
+ &.mode-1, &.mode-2, &.mode-3 { //hover, selected, tag
+ .zone-child__body {
+ border:solid 2px;
+ border-radius: 2px;
+ padding:2px;
+ cursor:pointer;
+ }
+ .zone-child__handle{
+ display:block;
+ cursor:pointer;
+ }
+ }
+
+ &.locked {
+ cursor: default;
+ }
+
+ // &:not(.locked):hover .zone-child__handle{
+ // display:block;
+ // }
+ .zone-child__body {
+ cursor: default;
+ }
+ &.mode-3 .zone-child__handle {
+ width:24px;
+ height:24px;
+ right:3px;
+ top:9px;
+ display:block;
+ background-image: linear-gradient(-140deg, #009E98 0%, #97D648 100%);
+ border: 2px solid #FFFFFF;
+ border-radius: 2px;
+ box-shadow: inset 2px -2px 3px 0 #007A3E;
+ cursor: pointer;
+ }
+
+}
+.sdc-canvas-zone.group-zone {
+ .zone-child__handle {
+ background-color:#009FDB;
+ }
+ .zone-child__body {
+ border-color:#009FDB;
+ }
+ .zone-child__body-content {
+ background: #009FDB;
+ }
+}
+
+.sdc-canvas-zone.policy-zone {
+ .zone-child__handle {
+ background-color:#0568AE;
+ }
+ .zone-child__body {
+ border-color:#1287D9;
+ .zone-child__body-content {
+ background: #1287D9;
+ }
+ }
+ .zone-child__body-content {
+ background: #0568AE;
+ }
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts
new file mode 100644
index 0000000..8057ae9
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts
@@ -0,0 +1,28 @@
+import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core';
+import { ZoneInstanceConfig, ZoneInstanceMode } from 'app/models/graph/zones/zone-child';
+
+@Component({
+ selector: 'zone-instance',
+ templateUrl: './zone-instance.component.html',
+ styleUrls: ['./zone-instance.component.less'],
+ encapsulation: ViewEncapsulation.None
+})
+export class ZoneInstanceComponent {
+
+ @Input() config:ZoneInstanceConfig;
+ @Input() defaultIconText:string;
+ @Input() isActive:boolean;
+ @Input() activeInstanceMode: ZoneInstanceMode;
+ @Output() modeChange: EventEmitter<any> = new EventEmitter<any>();
+ private MODE = ZoneInstanceMode;
+
+ private setMode = (mode:ZoneInstanceMode, event?:any):void => {
+ if(!this.isActive || this.isActive && mode == ZoneInstanceMode.TAG){ //when active, do not allow hover/select mode toggling
+ this.modeChange.emit({newMode: mode, instance: this.config});
+ }
+ if(event){
+ event.stopPropagation();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts b/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts
index 53d1590..d1e68f3 100644
--- a/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts
+++ b/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts
@@ -18,14 +18,25 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import { Component, Compiler, EventEmitter, ViewContainerRef, ViewChild, Input, Output, ElementRef, ComponentRef, ComponentFactoryResolver } from '@angular/core'
import {ValidationConfiguration} from "app/models";
+import {IUiElementChangeEvent} from "../form-components/ui-element-base.component";
import {UiElementInputComponent} from "../form-components/input/ui-element-input.component";
import {UiElementPopoverInputComponent} from "../form-components/popover-input/ui-element-popover-input.component";
import {UiElementIntegerInputComponent} from "../form-components/integer-input/ui-element-integer-input.component";
import {UiElementDropDownComponent, DropdownValue} from "../form-components/dropdown/ui-element-dropdown.component";
import {PROPERTY_DATA} from "../../../../utils/constants";
+enum DynamicElementComponentCreatorIdentifier {
+ STRING,
+ INTEGER,
+ FLOAT,
+ BOOLEAN,
+ SUBNETPOOLID,
+ DEFAULT
+}
+
@Component({
selector: 'dynamic-element',
template: `<div #target></div>`,
@@ -44,16 +55,14 @@
@Input() name: string;
@Input() readonly:boolean;
@Input() path:string;//optional param. used only for for subnetpoolid type
- value:any;
- // Two way binding for value (need to write the "Change" word like this)
- @Output('valueChange') emitter: EventEmitter<string> = new EventEmitter<any>();
- @Input('value') set setValueValue(value) {
- this.value = value;
- }
+ @Input() value: any;
+ @Output() valueChange: EventEmitter<any> = new EventEmitter<any>();
+ @Output('elementChanged') emitter: EventEmitter<IUiElementChangeEvent> = new EventEmitter<IUiElementChangeEvent>();
cmpRef: ComponentRef<any>;
private isViewInitialized: boolean = false;
+ private elementCreatorIdentifier: DynamicElementComponentCreatorIdentifier;
validation = ValidationConfiguration.validation;
constructor(
@@ -66,29 +75,72 @@
if (!this.isViewInitialized) {
return;
}
- if (this.cmpRef) {
- this.cmpRef.destroy();
- }
- // Factory to create component based on type or peroperty name.
+ // Factory to create component based on type or other property attributes.
+ const prevElementCreatorIdentifier: DynamicElementComponentCreatorIdentifier = this.elementCreatorIdentifier;
switch(true) {
case this.path && this.path.toUpperCase().indexOf("SUBNETPOOLID") !== -1:
+ this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.SUBNETPOOLID;
+ break;
+ case this.type === 'integer':
+ this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.INTEGER;
+ break;
+ case this.type === 'float':
+ this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.FLOAT;
+ break;
+ case PROPERTY_DATA.SCALAR_TYPES.indexOf(this.type) > -1:
+ case this.type === 'string':
+ this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.STRING;
+ break;
+ case this.type === 'boolean':
+ this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.BOOLEAN;
+ break;
+ default:
+ this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.DEFAULT;
+ }
+
+ // In case the dynamic element creator is changed, then destroy old and build new.
+ if (this.elementCreatorIdentifier !== prevElementCreatorIdentifier) {
+ if (this.cmpRef) {
+ this.cmpRef.destroy();
+ }
+ this.createComponentByIdentifier();
+ }
+
+ // Update attributes in base element class
+ if (this.cmpRef) {
+ this.cmpRef.instance.name = this.name;
+ this.cmpRef.instance.type = this.type;
+ this.cmpRef.instance.value = this.value;
+ this.cmpRef.instance.readonly = this.readonly;
+ }
+ }
+
+ createComponentByIdentifier() {
+ switch(this.elementCreatorIdentifier) {
+ case DynamicElementComponentCreatorIdentifier.SUBNETPOOLID:
if(this.name.toUpperCase().indexOf("SUBNETPOOLID") == -1){//if it's an item of subnetpoolid list get the parent name
let pathArray = this.path.split("#");
this.name = pathArray[pathArray.length - 2];
}
this.createComponent(UiElementPopoverInputComponent);
break;
- case this.type == 'integer':
+
+ case DynamicElementComponentCreatorIdentifier.INTEGER:
this.createComponent(UiElementIntegerInputComponent);
this.cmpRef.instance.pattern = this.validation.validationPatterns.integer;
break;
- case PROPERTY_DATA.SCALAR_TYPES.indexOf(this.type) > -1:
- case this.type == 'string':
+
+ case DynamicElementComponentCreatorIdentifier.FLOAT:
+ this.createComponent(UiElementIntegerInputComponent);
+ this.cmpRef.instance.pattern = /^[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?$/;
+ break;
+
+ case DynamicElementComponentCreatorIdentifier.STRING:
this.createComponent(UiElementInputComponent);
break;
- case this.type == 'boolean':
+ case DynamicElementComponentCreatorIdentifier.BOOLEAN:
this.createComponent(UiElementDropDownComponent);
// Build drop down values
@@ -96,25 +148,20 @@
tmp.push(new DropdownValue(true,'TRUE'));
tmp.push(new DropdownValue(false,'FALSE'));
this.cmpRef.instance.values = tmp;
+ if(!_.isUndefined(this.value)){//contains the real value (and not a string)
+ this.value = JSON.parse(this.value);
+ }
break;
+
+ case DynamicElementComponentCreatorIdentifier.DEFAULT:
default:
this.createComponent(UiElementInputComponent);
console.log("ERROR: No ui component to handle type: " + this.type);
}
- // Additional attributes in base element class
- if (this.cmpRef) {
- this.cmpRef.instance.name = this.name;
- this.cmpRef.instance.type = this.type;
- this.cmpRef.instance.value = this.value;
- this.cmpRef.instance.readonly = this.readonly;
- }
-
// Subscribe to change event of of ui-element-basic and fire event to change the value
- this.cmpRef.instance.baseEmitter.subscribe((value):void => {
- this.emitter.emit(value)
- });
-
+ this.cmpRef.instance.baseEmitter.subscribe((event) => { this.emitter.emit(event); });
+ this.cmpRef.instance.valueChange.subscribe((event) => { this.valueChange.emit(event); });
}
createComponent(ComponentToCreate:any):void {
diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html
index c6b8384..805e5ac 100644
--- a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html
+++ b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html
@@ -1,3 +1,3 @@
-<select name='{{name}}' [(ngModel)]="value" (change)="onSave()" [ngClass]="{'disabled':readonly}" data-tests-id="SelectType">
+<select name='{{name}}' [(ngModel)]="value" (change)="onChange()" [ngClass]="{'disabled':readonly}" data-tests-id="SelectType">
<option *ngFor="let ddvalue of values" [ngValue]="ddvalue.label != undefined ? ddvalue.value : ddvalue">{{ddvalue.label||ddvalue}}</option>
</select>
diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts
index 5abf32c..03a1fc6 100644
--- a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts
+++ b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts
@@ -43,9 +43,4 @@
constructor() {
super();
}
-
- onSave() {
- this.baseEmitter.emit(this.value);
- }
-
}
diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html
index b7d7c85..057e731 100644
--- a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html
+++ b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html
@@ -4,7 +4,7 @@
type="text"
[name]="name"
[(ngModel)]="value"
- (change)="onSave()"
+ (input)="onChange()"
[attr.maxlength]="validation.propertyValue.max"
[attr.minlength]="validation.propertyValue.min"
[pattern]="pattern"
diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts
index fb3b3db..ce231e7 100644
--- a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts
+++ b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts
@@ -32,10 +32,4 @@
super();
this.pattern = this.validation.validationPatterns.comment;
}
-
- onSave() {
- if (!this.control.invalid){
- this.baseEmitter.emit(this.value);
- }
- }
}
diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html
index 9fbc9e1..e1555e8 100644
--- a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html
+++ b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html
@@ -4,7 +4,7 @@
type="text"
[name]="name"
[(ngModel)]="value"
- (change)="onSave()"
+ (input)="onChange()"
[attr.maxlength]="validation.propertyValue.max"
[attr.minlength]="validation.propertyValue.min"
[pattern]="pattern"
diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts
index 1667f43..9aa4b58 100644
--- a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts
+++ b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts
@@ -33,9 +33,8 @@
//this.pattern = this.validation.validationPatterns.comment;
}
- onSave() {
- if (!this.control.invalid){
- this.baseEmitter.emit(this.value ? JSON.parse(this.value) : this.value);
- }
+ onChange() {
+ this.value = this.control.valid && this.value ? JSON.parse(this.value) : this.value;
+ super.onChange();
}
}
diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts
index 61688df..525cd17 100644
--- a/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts
+++ b/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts
@@ -35,17 +35,10 @@
saveButton: ButtonModel;
buttonsArray: ButtonsModelMap;
- onSave = ():void => {
- if (!this.control.invalid){
- this.baseEmitter.emit(this.value);
- this.popoverContentComponent.hide();
- }
- }
-
constructor() {
super();
// Create Save button and insert to buttons map
- this.saveButton = new ButtonModel('save', 'blue', this.onSave);
+ this.saveButton = new ButtonModel('save', 'blue', this.onChange);
this.buttonsArray = { 'test': this.saveButton };
// Define the regex pattern for this controller
diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts
index 0f80e2a..3e8c3b5 100644
--- a/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts
+++ b/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts
@@ -12,10 +12,6 @@
})
export class RadioButtonComponent extends UiElementBase implements UiElementBaseInterface {
- onSave() {
- this.baseEmitter.emit(this.value);
- }
-
@Input() options:Array<RadioButtonModel>;
@Input() readonly:boolean;
@Input() direction:string = 'vertical'; //get 'horizontal' | 'vertical'
@@ -23,7 +19,7 @@
select(value:any) {
this.value = value;
- this.baseEmitter.emit(this.value);
+ this.onChange();
}
}
diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts
index ae2013f..b4e9e7d 100644
--- a/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts
+++ b/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts
@@ -23,7 +23,12 @@
import { FormControl, Validators } from '@angular/forms';
export interface UiElementBaseInterface {
- onSave();
+ onChange();
+}
+
+export interface IUiElementChangeEvent {
+ value: any;
+ isValid: boolean;
}
@Component({
@@ -35,21 +40,29 @@
protected validation = ValidationConfiguration.validation;
protected control: FormControl;
- // Two way binding for value (need to write the "Change" word like this)
- @Output('valueChange') baseEmitter: EventEmitter<string> = new EventEmitter<any>();
- @Input('value') set setValueValue(value) {
- this.value = value;
- }
+ @Input() value: any;
+ @Output() valueChange: EventEmitter<any> = new EventEmitter<any>();
+ @Output('elementChanged') baseEmitter: EventEmitter<IUiElementChangeEvent> = new EventEmitter<IUiElementChangeEvent>();
@Input() name: string;
@Input() type: string;
- @Input() value: any;
@Input() pattern: any;
@Input() readonly:boolean;
constructor() {
//this.control = new FormControl('', [Validators.required]);
this.control = new FormControl('', []);
+
+ this.baseEmitter.subscribe((changeEvent: IUiElementChangeEvent) => {
+ this.valueChange.emit(changeEvent.value);
+ })
+ }
+
+ onChange() {
+ this.baseEmitter.emit({
+ value: this.value,
+ isValid: this.control.valid
+ });
}
}
diff --git a/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts b/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts
index f66aa55..585c366 100644
--- a/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts
+++ b/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts
@@ -74,10 +74,10 @@
if (this.relative === true) {
let parentElement = this.viewContainerRef.element.nativeElement.parentElement;
this.offset = {
- left: (parentElement.offsetLeft) ? parentElement.offsetLeft + "px" : undefined,
- top: (parentElement.offsetTop) ? parentElement.offsetTop + "px" : undefined,
- width: (parentElement.offsetWidth) ? parentElement.offsetWidth + "px" : undefined,
- height: (parentElement.offsetHeight) ? parentElement.offsetHeight + "px" : undefined
+ left: (parentElement.offsetLeft !== undefined) ? parentElement.offsetLeft + "px" : undefined,
+ top: (parentElement.offsetTop !== undefined) ? parentElement.offsetTop + "px" : undefined,
+ width: (parentElement.offsetWidth !== undefined) ? parentElement.offsetWidth + "px" : undefined,
+ height: (parentElement.offsetHeight !== undefined) ? parentElement.offsetHeight + "px" : undefined
};
}
this.isVisible = true;
diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html
index 9a0fdf6..6fc55d1 100644
--- a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html
+++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html
@@ -1,6 +1,14 @@
<div class="custom-modal {{input.size}}">
- <div class="ng2-modal-content">
- <div class="ng2-modal-header modal-type-{{input.type}}">
+ <div class="ng2-modal-content"
+ ngDraggable
+ [ngDraggable]="input.isMovable"
+ [handle]="ModalHandle"
+ [bounds]="ModalBounds"
+ [inBounds]="true"
+ [preventDefaultEvent]="false">
+ <div #ModalHandle
+ class="ng2-modal-header modal-type-{{input.type}}"
+ [ngClass]="{'movable': input.isMovable}">
<span class="title">{{ input.title }}</span>
<span class="close-button" (click)="close()"></span>
</div>
@@ -18,4 +26,4 @@
</div>
</div>
</div>
-<div class="modal-background"></div>
+<div #ModalBounds class="modal-background" [ngClass]="{'transparent': input.isMovable}"></div>
diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less
index fac1ae7..03b2a70 100644
--- a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less
+++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less
@@ -12,7 +12,7 @@
bottom: 0;
left: 0;
z-index: 15007;
- overflow: auto;
+ overflow: visible;
margin: auto;
display: flex;
align-items: center;
@@ -22,6 +22,8 @@
width: 100%;
box-shadow: 0 5px 15px rgba(0,0,0,.5);
border-radius: 4px;
+
+
.ng2-modal-body{
padding: 20px;
}
@@ -29,19 +31,28 @@
.ng2-modal-header{
.m_18_r;
font-weight: bold;
+
-webkit-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
+
height: 50px;
line-height: 50px;
+ margin: 0px 20px;
+
display: -webkit-box;
display: -ms-flexbox;
display: flex;
+
text-align: left;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
- margin: 0px 20px;
+
+ &.ng-draggable {
+ cursor: move;
+ user-select: none;
+ }
&.modal-type-standard {
border-bottom: solid 3px @main_color_a;
@@ -101,9 +112,14 @@
background-color: #000;
opacity: 0.5;
z-index: 900;
+
+ &.transparent {
+ background-color: transparent;
+ }
}
+
.xl {
width: 1200px;
}
diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts
index 89db8d1..777e9bd 100644
--- a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts
+++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts
@@ -33,6 +33,7 @@
})
export class ModalComponent implements OnInit, OnDestroy {
+ @Input() isMovable: boolean;
@Input() input: ModalModel;
@Input() dynamicContent: any;
@ViewChild('dynamicContentContainer', { read: ViewContainerRef }) dynamicContentContainer: ViewContainerRef; //Allows for custom component as body instead of simple message. See ModalService.createActionModal for implementation details, and HttpService's catchError() for example.
diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts b/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts
index c38e601..2999528 100644
--- a/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts
+++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts
@@ -1,5 +1,6 @@
import { NgModule } from "@angular/core";
import { CommonModule } from '@angular/common';
+import { AngularDraggableModule } from 'angular2-draggable';
import { ModalService } from 'app/ng2/services/modal.service';
import { ErrorMessageComponent } from "./error-message/error-message.component";
import {ModalComponent} from "./modal.component";
@@ -9,7 +10,7 @@
ModalComponent,
ErrorMessageComponent
],
- imports: [CommonModule],
+ imports: [CommonModule, AngularDraggableModule],
exports: [ModalComponent, ErrorMessageComponent],
entryComponents: [ //need to add anything that will be dynamically created
ModalComponent,
diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.html b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.html
new file mode 100644
index 0000000..2a776d4
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.html
@@ -0,0 +1,4 @@
+<div class="palette-animation-wrapper" [style.top]="from.y + 50 + 'px'" [style.left]="from.x + 'px'" [style.transform]="transformStyle" [class.hidden]="!visible"
+ (transitionend)="animationComplete()">
+<div class="medium small sprite-resource-icons sprite-{{iconName}}-icons {{iconName}}" ></div>
+</div>
diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.less b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.less
new file mode 100644
index 0000000..54f0418
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.less
@@ -0,0 +1,5 @@
+.palette-animation-wrapper{
+ position: absolute;
+ z-index: 100;
+ transition: all 2s ease-in-out;
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.ts b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.ts
new file mode 100644
index 0000000..609a1fc
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.ts
@@ -0,0 +1,64 @@
+/*-
+ * ============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, Input } from '@angular/core';
+import {BrowserModule} from '@angular/platform-browser';
+import { setTimeout } from 'core-js/library/web/timers';
+import { EventListenerService } from 'app/services';
+import { GRAPH_EVENTS } from 'app/utils';
+import { Point } from 'app/models';
+
+
+
+@Component({
+ selector: 'palette-animation',
+ templateUrl: './palette-animation.component.html',
+ styleUrls:['./palette-animation.component.less'],
+})
+
+export class PaletteAnimationComponent {
+
+ @Input() from : Point;
+ @Input() to : Point;
+ @Input() iconName : string;
+ @Input() data : any;
+
+ public animation;
+ private visible:boolean = false;
+ private transformStyle:string = "";
+
+
+ constructor(private eventListenerService:EventListenerService) {}
+
+ public runAnimation() {
+ this.visible = true;
+ let positionDiff:Point = new Point(this.to.x - this.from.x, this.to.y - this.from.y);
+ setTimeout(()=>{
+ this.transformStyle = 'translate('+ positionDiff.x + 'px,' + positionDiff.y +'px)';
+ }, 0);
+ };
+
+ public animationComplete = (e) => {
+ this.visible = false;
+ this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_FINISH_ANIMATION_ZONE);
+ };
+
+
+}
diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.module.ts b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.module.ts
new file mode 100644
index 0000000..8674571
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.module.ts
@@ -0,0 +1,16 @@
+import { NgModule } from "@angular/core";
+import { CommonModule } from "@angular/common";
+import { PaletteAnimationComponent } from "./palette-animation.component";
+
+
+@NgModule({
+ declarations: [
+ PaletteAnimationComponent
+ ],
+ imports: [ CommonModule ],
+ exports: [ PaletteAnimationComponent ]
+})
+
+export class PaletteAnimationModule {
+
+}
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.html b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.html
new file mode 100644
index 0000000..ed172bf
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.html
@@ -0,0 +1,9 @@
+<div class="popup-panel" [ngClass]="{'hide':!isShowPanel}" [style.left]="popupPanelPosition.x + 'px'" [style.top]="popupPanelPosition.y + 'px'"
+ (mousedown)="addZoneInstance()"
+ (mouseenter)="onMouseEnter()"
+ (mouseleave)="onMouseLeave()">
+ <div class="popup-panel-group">
+ <div class="popup-panel-plus">+</div>
+ <div class="popup-panel-title">{{panelTitle}}</div>
+ </div>
+</div>
diff --git a/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.less b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.less
new file mode 100644
index 0000000..24f0485
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.less
@@ -0,0 +1,37 @@
+.popup-panel {
+ position: absolute;
+ display: inline-block;
+ background-color: white;
+ border: solid 1px #d2d2d2;
+ border-top: solid 3px #13a7df;
+ left: 208px; top: 0px;
+ width: 140px;
+ height: 40px;
+ z-index: 10000;
+
+ &:hover {
+ background-color: whitesmoke;
+ }
+
+ .popup-panel-group {
+ padding-left: 8px;
+ padding-top: 8px;
+ cursor: pointer;
+
+ .popup-panel-plus {
+ border-radius: 50%;
+ color: white;
+ background-color: #13a7df;
+ width: 20px;
+ text-align: center;
+ display: inline-block;
+ }
+
+ .popup-panel-title {
+ padding-left: 10px;
+ display: inline-block;
+ }
+
+ }
+
+}
diff --git a/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts
new file mode 100644
index 0000000..d30d5f6
--- /dev/null
+++ b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts
@@ -0,0 +1,87 @@
+import {Component, OnInit} from '@angular/core';
+import {GRAPH_EVENTS} from "app/utils";
+import {LeftPaletteComponent, Point} from "app/models";
+import {EventListenerService} from "app/services";
+import {LeftPaletteMetadataTypes} from "../../../../models/components/displayComponent";
+
+@Component({
+ selector: 'app-palette-popup-panel',
+ templateUrl: './palette-popup-panel.component.html',
+ styleUrls: [ './palette-popup-panel.component.less' ],
+})
+export class PalettePopupPanelComponent implements OnInit {
+
+ public panelTitle: string;
+ public isShowPanel: boolean;
+ private component: Component;
+ private displayComponent: LeftPaletteComponent;
+ private popupPanelPosition:Point = new Point(0,0);
+
+ constructor(private eventListenerService: EventListenerService) {
+ this.isShowPanel = false;
+ }
+
+ ngOnInit() {
+ this.registerObserverCallbacks();
+ }
+
+ public onMouseEnter() {
+ this.isShowPanel = true;
+ }
+
+ public onMouseLeave() {
+ this.isShowPanel = false;
+ }
+
+ public addZoneInstance(): void {
+ if(this.displayComponent) {
+ this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ADD_COMPONENT_INSTANCE_ZONE_START, this.component, this.displayComponent, this.popupPanelPosition);
+ }
+ }
+
+ private registerObserverCallbacks() {
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_SHOW_POPUP_PANEL,
+ (component: Component, displayComponent: LeftPaletteComponent, sectionElem: HTMLElement) => {
+
+ this.component = component;
+ this.showPopupPanel(displayComponent, sectionElem);
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HIDE_POPUP_PANEL, () => this.hidePopupPanel());
+ }
+
+ private getPopupPanelPosition (sectionElem: HTMLElement):Point {
+ let pos: ClientRect = sectionElem.getBoundingClientRect();
+ let offsetX: number = -30;
+ const offsetY: number = pos.height / 2;
+ return new Point((pos.right + offsetX), (pos.top - offsetY + window.pageYOffset));
+ };
+
+ private setPopupPanelTitle(component: LeftPaletteComponent): void {
+ if (component.categoryType === LeftPaletteMetadataTypes.Group) {
+ this.panelTitle = "Add Group";
+ return;
+ }
+
+ if (component.categoryType === LeftPaletteMetadataTypes.Policy) {
+ this.panelTitle = "Add Policy";
+ return;
+ }
+ }
+
+ private showPopupPanel(displayComponent:LeftPaletteComponent, sectionElem: HTMLElement) {
+ if(!this.isShowPanel){
+ this.displayComponent = displayComponent;
+ this.setPopupPanelTitle(displayComponent);
+ this.popupPanelPosition = this.getPopupPanelPosition(sectionElem);
+ this.isShowPanel = true;
+ }
+ };
+
+ private hidePopupPanel() {
+ if(this.isShowPanel){
+ this.isShowPanel = false;
+ }
+ };
+}
diff --git a/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html b/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html
index 6d76f0a..fc6e821 100644
--- a/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html
+++ b/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html
@@ -15,7 +15,8 @@
<ng-content></ng-content>
<div class="popover-footer">
<button *ngFor="let buttonName of buttonsNames"
- class="tlv-btn {{buttons[buttonName].cssClass}}"
+ class="tlv-btn {{buttons[buttonName].cssClass}}"
+ [attr.data-tests-id]="'filter-' + buttons[buttonName].text.toLowerCase() + '-button'"
[disabled] = "buttons[buttonName].getDisabled && buttons[buttonName].getDisabled()"
(click) = "buttons[buttonName].callback()">{{buttons[buttonName].text}}</button>
</div>
diff --git a/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts b/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts
index 21d2bba..f4b4103 100644
--- a/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts
+++ b/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts
@@ -23,6 +23,8 @@
import { ViewEncapsulation } from '@angular/core';
import { trigger, state, style, transition, animate, keyframes } from '@angular/core';
+export {Tab};
+
@Component({
selector: 'tabs',
templateUrl: './tabs.component.html',
diff --git a/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts b/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts
index 5260965..4431473 100644
--- a/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts
+++ b/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts
@@ -27,6 +27,10 @@
import {PopoverModule} from "./popover/popover.module";
import {SearchBarComponent} from "./search-bar/search-bar.component";
import {SearchWithAutoCompleteComponent} from "./search-with-autocomplete/search-with-autocomplete.component";
+import {PalettePopupPanelComponent} from "./palette-popup-panel/palette-popup-panel.component";
+import {ZoneContainerComponent} from "./canvas-zone/zone-container.component";
+import {ZoneInstanceComponent } from "./canvas-zone/zone-instance/zone-instance.component";
+import {PaletteAnimationComponent} from "./palette-animation/palette-animation.component"
import {TabModule} from "./tabs/tabs.module";
import {TooltipModule} from "./tooltip/tooltip.module";
import {CommonModule} from "@angular/common";
@@ -35,15 +39,21 @@
import {MultiStepsWizardModule} from "./multi-steps-wizard/multi-steps-wizard.module";
import {MenuListModule} from "./menu/menu-list.module";
import {MenuListNg2Module} from "../downgrade-wrappers/menu-list-ng2/menu-list-ng2.module";
+//import {SdcUiComponentsModule} from "sdc-ui/lib/angular";
@NgModule({
declarations: [
LoaderComponent,
SearchBarComponent,
- SearchWithAutoCompleteComponent
- ],
+ SearchWithAutoCompleteComponent,
+ PalettePopupPanelComponent,
+ ZoneContainerComponent,
+ ZoneInstanceComponent,
+ PaletteAnimationComponent
+],
imports: [
+ //SdcUiComponentsModule,
BrowserModule,
FormsModule,
CommonModule,
@@ -63,6 +73,9 @@
MultiStepsWizardModule,
SearchBarComponent,
SearchWithAutoCompleteComponent,
+ PalettePopupPanelComponent,
+ ZoneContainerComponent,
+ ZoneInstanceComponent,
DynamicElementModule,
NavbarModule,
FormElementsModule,
@@ -71,10 +84,10 @@
TabModule,
TooltipModule,
MenuListModule,
- MenuListNg2Module
+ MenuListNg2Module,
+ PaletteAnimationComponent
],
-
- entryComponents: [SearchWithAutoCompleteComponent]
+ entryComponents: [SearchWithAutoCompleteComponent, PalettePopupPanelComponent, ZoneContainerComponent, ZoneInstanceComponent, PaletteAnimationComponent]
})
export class UiElementsModule {}
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 a097fb0..bf5ec4c 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 0c9d9e6..a45f07b 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 edbbf8a..054d38b 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 @@
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 9e34893..9b1df69 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 3e48785..946d185 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 @@
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 @@
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 67bb12e..a8343ce 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 9c7bf4d..ea3b129 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 @@
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 203c75d..907f763 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 {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 @@
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 beea3fe..03e9f1f 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 03974bf..6de6dda 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 9603648..40818bc 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 {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 @@
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;
+ changedData:Array<PropertyFEModel|InputFEModel>;
+ hasChangedData:boolean;
+ isValidChangedData:boolean;
+ savingChangedData:boolean;
+ stateChangeStartUnregister:Function;
- @ViewChild('hierarchyNavTabs') hierarchyNavTabs: ElementRef;
- @ViewChild('propertyInputTabs') propertyInputTabs: ElementRef;
+ @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 @@
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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
/*** 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();
}
- };
+ 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);
+ }
+ }
- inputValueChanged = (event) => {
- console.log("==>" + this.constructor.name + ": inputValueChanged");
- let inputToUpdate = new PropertyBEModel(event);
-
- 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 @@
// 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 @@
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 @@
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 @@
};
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 @@
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 @@
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 016b047..1a800ba 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 0000000..408a00e
--- /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 8f46c6f..e7b59b9 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 @@
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 @@
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 @@
* 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 @@
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 @@
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 @@
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 0000000..bbbf6ae
--- /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 0000000..beec9bd
--- /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 0000000..1643324
--- /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 0000000..80128eb
--- /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 0000000..96cd83ee
--- /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 0000000..5c9e53e
--- /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 0000000..dac41a3
--- /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 0000000..7800531
--- /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 0000000..8a31c76
--- /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 0000000..aff597f
--- /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 0000000..04083e8
--- /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 0000000..c236934
--- /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
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 e89a816..3761aa8 100644
--- a/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts
+++ b/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts
@@ -13,7 +13,6 @@
SearchFilterPipe,
SafeUrlSanitizerPipe
],
-
exports: [
ContentAfterLastDotPipe,
GroupByPipe,
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 b852539..0947b2a 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
@@ -23,10 +23,9 @@
import { Observable } from 'rxjs/Observable';
import {PropertyFEModel, PropertyBEModel} from "app/models";
import {CommonUtils} from "app/utils";
-import {Component, ComponentInstance, InputModel} from "app/models";
+import {Component, ComponentInstance, Capability, PropertyModel} from "app/models";
import { HttpService } from '../http.service';
import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config";
-import {isEqual} from "lodash";
@Injectable()
export class ComponentInstanceServiceNg2 {
@@ -52,43 +51,45 @@
})
}
- updateInstanceProperty(component: Component, componentInstanceId: string, property: PropertyBEModel): Observable<PropertyBEModel> {
+ updateInstanceProperties(component: Component, componentInstanceId: string, properties: PropertyBEModel[]) {
- return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/property', property)
- .map((res: Response) => {
- return new PropertyBEModel(res.json());
- })
- }
-
- getInstanceCapabilityProperties(component: Component, componentInstanceId: string, capabilityType: string, capabilityName: string): Observable<Array<PropertyBEModel>> {
-
- return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capabilityType +
- '/capabilityName/' + capabilityName + '/properties')
- .map((res: Response) => {
- return CommonUtils.initBeProperties(res.json());
- })
- }
-
- updateInstanceCapabilityProperties(component: Component, componentInstanceId: string, capabilityType: string, capabilityName: string, properties: PropertyBEModel[]): Observable<PropertyBEModel[]> {
-
- return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capabilityType +
- '/capabilityName/' + capabilityName +'/properties', properties)
+ 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));
- })
+ });
}
- updateInstanceInput(component: Component, componentInstanceId: string, input: PropertyBEModel): Observable<PropertyBEModel> {
+ getInstanceCapabilityProperties(component: Component, componentInstanceId: string, capability: Capability): Observable<Array<PropertyModel>> {
- return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/input', input)
+ return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capability.type +
+ '/capabilityName/' + capability.name + '/ownerId/' + capability.ownerId + '/properties')
.map((res: Response) => {
- return new PropertyBEModel(res.json());
+ capability.properties = res.json().map((capProp) => new PropertyModel(capProp)); // update capability properties
+ return capability.properties;
})
}
- hasPropertyChanged(property: PropertyFEModel) {
- let oldValue: any = property.value;
- const newValue = property.getJSONValue();
- return ((oldValue || newValue) && !isEqual(oldValue, newValue));
+ updateInstanceCapabilityProperties(component: Component, componentInstanceId: string, capability: Capability, properties: PropertyBEModel[]): Observable<Array<PropertyModel>> {
+
+ 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));
+ savedProperties.forEach((savedProperty) => {
+ const propIdx = capability.properties.findIndex((p) => p.uniqueId === savedProperty.uniqueId);
+ if (propIdx !== -1) {
+ capability.properties.splice(propIdx, 1, savedProperty);
+ }
+ });
+ return savedProperties;
+ })
+ }
+
+ updateInstanceInputs(component: Component, componentInstanceId: string, inputs: PropertyBEModel[]): Observable<PropertyBEModel[]> {
+
+ 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));
+ });
}
}
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
new file mode 100644
index 0000000..6e9d0e8
--- /dev/null
+++ b/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts
@@ -0,0 +1,41 @@
+/*-
+ * ============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 {Component} from "../../../models/components/component";
+import {ComponentServiceNg2} from "./component.service";
+import {ServiceServiceNg2} from "./service.service";
+
+@Injectable()
+export class ComponentServiceFactoryNg2 {
+ componentService: ComponentServiceNg2;
+ serviceService: ServiceServiceNg2;
+ constructor(componentService: ComponentServiceNg2, serviceService: ServiceServiceNg2) {
+ this.serviceService = serviceService;
+ this.componentService = componentService;
+ }
+ getComponentService(component: Component):ComponentServiceNg2 {
+ if (component.isService()) {
+ return this.serviceService;
+ }
+ return this.componentService;
+ }
+}
\ No newline at end of file
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 ba1cb15..9c3f78a 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
@@ -18,13 +18,13 @@
* ============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 {Response, URLSearchParams} from '@angular/http';
-import { Component, PropertyBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData} from "app/models";
-import {downgradeInjectable} from '@angular/upgrade/static';
+import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData} 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";
@@ -40,11 +40,11 @@
protected baseUrl;
- constructor(private http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) {
+ constructor(protected http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) {
this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root;
}
- private getComponentDataByFieldsName(componentType:string, componentId: string, fields:Array<string>):Observable<ComponentGenericResponse> {
+ protected getComponentDataByFieldsName(componentType:string, componentId: string, fields:Array<string>):Observable<ComponentGenericResponse> {
let params:URLSearchParams = new URLSearchParams();
_.forEach(fields, (field:string):void => {
@@ -53,10 +53,14 @@
return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/filteredDataByParams', {search: params})
.map((res:Response) => {
- return new ComponentGenericResponse().deserialize(res.json());
+ return this.analyzeComponentDataResponse(res);
});
}
+ protected analyzeComponentDataResponse(res: Response):ComponentGenericResponse {
+ return new ComponentGenericResponse().deserialize(res.json());
+ }
+
private getServerTypeUrl = (componentType:string):string => {
switch (componentType) {
case ComponentType.SERVICE:
@@ -78,8 +82,8 @@
return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_ATTRIBUTES]);
}
- getComponentInstancesAndRelation(component:Component):Observable<ComponentGenericResponse> {
- return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES]);
+ getComponentCompositionData(component:Component):Observable<ComponentGenericResponse> {
+ return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_POLICIES, COMPONENT_FIELDS.COMPONENT_GROUPS]);
}
getComponentResourceInstances(component:Component):Observable<ComponentGenericResponse> {
@@ -125,19 +129,19 @@
})
}
- deleteInput(component:Component, input:PropertyBEModel):Observable<PropertyBEModel> {
+ deleteInput(component:Component, input:InputBEModel):Observable<InputBEModel> {
return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/delete/' + input.uniqueId + '/input')
.map((res:Response) => {
- return new PropertyBEModel(res.json());
+ return new InputBEModel(res.json());
})
}
- updateComponentInput(component:Component, input:PropertyBEModel):Observable<PropertyBEModel> {
+ updateComponentInputs(component:Component, inputs:InputBEModel[]):Observable<InputBEModel[]> {
- return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/update/inputs', input)
+ return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/update/inputs', inputs)
.map((res:Response) => {
- return new PropertyBEModel(res.json())
+ return res.json().map((input) => new InputBEModel(input));
})
}
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 f38dbef..0439f20 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,19 +22,30 @@
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
-import { Response } from '@angular/http';
+import { Response, URLSearchParams } from '@angular/http';
import {Service} from "app/models";
import { downgradeInjectable } from '@angular/upgrade/static';
import { HttpService } from '../http.service';
+
import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config";
+import {ForwardingPath} from "app/models/forwarding-path";
+import {ComponentMetadata} from "app/models/component-metadata";
+import {ComponentType} from "app/utils";
+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";
@Injectable()
-export class ServiceServiceNg2 {
+export class ServiceServiceNg2 extends ComponentServiceNg2 {
protected baseUrl = "";
- constructor(private http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) {
+ constructor(protected http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) {
+ super(http, sdcConfig);
this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root;
}
@@ -46,4 +57,106 @@
});
}
+ getNodesAndLinksMap(service: Service):Observable<Array<ServicePathMapItem>> {
+ return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/linksMap').map(res => {
+ return <Array<ServicePathMapItem>>res.json();
+ });
+ }
+
+ getServicePath(service: Service, id: string):Observable<any> {
+ return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths/' + id)
+ .map(res => {
+ return res.json();
+ })
+ }
+
+ getServicePaths(service: Service):Observable<any> {
+ return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths')
+ .map(res => {
+ return res.json();
+ })
+ }
+
+ createOrUpdateServicePath(service: Service, inputsToCreate: ForwardingPath):Observable<ForwardingPath> {
+ if (inputsToCreate.uniqueId) {
+ return this.updateServicePath(service, inputsToCreate);
+ } else {
+ return this.createServicePath(service, inputsToCreate);
+ }
+ }
+
+ createServicePath(service: Service, inputsToCreate: ForwardingPath):Observable<ForwardingPath> {
+ let input = new ServicePathRequestData(inputsToCreate);
+
+ return this.http.post(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths', input)
+ .map(res => {
+ return this.parseServicePathResponse(res);
+ });
+ }
+
+ deleteServicePath(service: Service, id: string):Observable<any> {
+ return this.http.delete(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths/' + id )
+ .map((res) => {
+ return res.json();
+ });
+ }
+
+ updateServicePath(service: Service, inputsToUpdate:ForwardingPath):Observable<ForwardingPath> {
+ let input = new ServicePathRequestData(inputsToUpdate);
+
+ return this.http.put(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths', input)
+ .map((res) => {
+ return this.parseServicePathResponse(res);
+ });
+ }
+
+ checkComponentInstanceVersionChange(service: Service, newVersionId: string):Observable<Array<string>> {
+ let instanceId = service.selectedInstance.uniqueId;
+ let queries = {componentInstanceId: instanceId, newComponentInstanceId: newVersionId};
+
+ let params:URLSearchParams = new URLSearchParams();
+ _.map(_.keys(queries), (key:string):void => {
+ 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;
+ });
+ }
+
+ getComponentCompositionData(component:Component):Observable<ComponentGenericResponse> {
+ return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, SERVICE_FIELDS.FORWARDING_PATHS]);
+ }
+
+ protected analyzeComponentDataResponse(res: Response):ComponentGenericResponse {
+ return new ServiceGenericResponse().deserialize(res.json());
+ }
+
+ private parseServicePathResponse(res: Response):ForwardingPath {
+ let resJSON = res.json();
+ let pathId = Object.keys(resJSON.forwardingPaths)[0];
+ let forwardingPath = resJSON.forwardingPaths[pathId];
+ let path:ForwardingPath = new ForwardingPath();
+ path.deserialize(forwardingPath);
+ path.uniqueId = pathId;
+ return path;
+ }
}
+
+class ServicePathRequestData {
+ forwardingPaths: { [key:string]:ForwardingPath } = {};
+ componentMetadataDefinition: ComponentMetadata;
+ toscaType: string = "topology_template";
+
+ constructor(fp? : ForwardingPath) {
+ this.componentMetadataDefinition = new ComponentMetadata();
+ this.componentMetadataDefinition.ecompGeneratedNaming = true;
+ this.componentMetadataDefinition.componentType = ComponentType.SERVICE;
+ if (fp) {
+ let id = fp.uniqueId ? fp.uniqueId : "NEW";
+ this.forwardingPaths[fp.uniqueId] = fp;
+ }
+ }
+}
+
diff --git a/catalog-ui/src/app/ng2/services/config.service.ts b/catalog-ui/src/app/ng2/services/config.service.ts
index 053f2c7..3e6e667 100644
--- a/catalog-ui/src/app/ng2/services/config.service.ts
+++ b/catalog-ui/src/app/ng2/services/config.service.ts
@@ -37,8 +37,8 @@
public api:IApi;
constructor(private http: Http, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig) {
- this.api = this.sdcConfig.api;
- this.baseUrl = this.api.root + this.api.component_api_root;
+ this.api = this.sdcConfig.api;
+ this.baseUrl = this.sdcConfig.api.root + this.sdcConfig.api.component_api_root;
}
loadValidationConfiguration(): Promise<ValidationConfiguration> {
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 30c02a4..6b59089 100644
--- a/catalog-ui/src/app/ng2/services/data-type.service.ts
+++ b/catalog-ui/src/app/ng2/services/data-type.service.ts
@@ -18,6 +18,7 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import { Injectable } from '@angular/core';
import { DataTypeModel, DataTypesMap, PropertyBEModel, PropertyFEModel, DerivedFEProperty, DerivedFEPropertyMap } from "app/models";
import { DataTypesService } from "app/services/data-types-service";
diff --git a/catalog-ui/src/app/ng2/services/dynamic-component.service.ts b/catalog-ui/src/app/ng2/services/dynamic-component.service.ts
new file mode 100644
index 0000000..29dd1e9
--- /dev/null
+++ b/catalog-ui/src/app/ng2/services/dynamic-component.service.ts
@@ -0,0 +1,28 @@
+import {
+ Injectable, Type, ViewContainerRef, ApplicationRef, ComponentFactory, ComponentFactoryResolver, ComponentRef
+} from '@angular/core';
+
+
+
+@Injectable()
+export class DynamicComponentService {
+
+ constructor(private componentFactoryResolver: ComponentFactoryResolver, private applicationRef: ApplicationRef) { }
+
+ //Creates a component dynamically (aka during runtime). If a view container is not specified, it will append the new component to the app root.
+ //To subscribe to an event from invoking component: componentRef.instance.clicked.subscribe((m) => console.log(m.name));
+ public createDynamicComponent<T>(componentType: Type<T>, viewContainerRef?:ViewContainerRef): ComponentRef<T> {
+
+ viewContainerRef = viewContainerRef || this.getRootViewContainerRef();
+ viewContainerRef.clear();
+
+ let factory: ComponentFactory<T> = this.componentFactoryResolver.resolveComponentFactory(componentType); //Ref: https://angular.io/guide/dynamic-component-loader
+ let componentRef: ComponentRef<T> = viewContainerRef.createComponent(factory);
+ return componentRef;
+ }
+
+
+ private getRootViewContainerRef(): ViewContainerRef {
+ return this.applicationRef.components[0].instance.viewContainerRef;
+ }
+};
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/services/modal.service.ts b/catalog-ui/src/app/ng2/services/modal.service.ts
index 22b56c7..4e86d6a 100644
--- a/catalog-ui/src/app/ng2/services/modal.service.ts
+++ b/catalog-ui/src/app/ng2/services/modal.service.ts
@@ -1,11 +1,12 @@
import {
Injectable, Type, ViewContainerRef, ApplicationRef, ComponentFactory, ComponentFactoryResolver, ComponentRef,
-
+ TemplateRef
} from '@angular/core';
import { ModalModel, ButtonModel, StepModel } from 'app/models';
import {MultiStepsWizardComponent} from "../components/ui/multi-steps-wizard/multi-steps-wizard.component";
import {ModalComponent} from "../components/ui/modal/modal.component";
import {WizardHeaderBaseComponent} from "app/ng2/components/ui/multi-steps-wizard/multi-steps-wizard-header-base.component";
+import { DynamicComponentService } from 'app/ng2/services/dynamic-component.service';
@Injectable()
@@ -13,7 +14,7 @@
currentModal: ComponentRef<any>;
- constructor(private componentFactoryResolver: ComponentFactoryResolver, private applicationRef: ApplicationRef) { }
+ constructor(private dynamicComponentService: DynamicComponentService) { }
/* Shortcut method to open an alert modal with title, message, and close button that simply closes the modal. */
@@ -52,7 +53,7 @@
/* Use this method to create a modal with title, message, and completely custom buttons. Use response.instance.open() to open */
public createCustomModal = (customModalData: ModalModel): ComponentRef<ModalComponent> => {
- let customModal: ComponentRef<ModalComponent> = this.createDynamicComponent(ModalComponent);
+ let customModal: ComponentRef<ModalComponent> = this.dynamicComponentService.createDynamicComponent(ModalComponent);
customModal.instance.input = customModalData;
this.currentModal = customModal;
@@ -62,12 +63,12 @@
public createMultiStepsWizard = (title: string, steps:Array<StepModel>, callback: Function, dynamicHeaderType?: Type<WizardHeaderBaseComponent>): ComponentRef<MultiStepsWizardComponent> => {
let cancelButton: ButtonModel = new ButtonModel('Cancel', 'outline blue', this.closeCurrentModal);
let modalModel: ModalModel = new ModalModel('xl', title, '', [cancelButton]);
- let wizardInstance: ComponentRef<MultiStepsWizardComponent> = this.createDynamicComponent(MultiStepsWizardComponent);
+ let wizardInstance: ComponentRef<MultiStepsWizardComponent> = this.dynamicComponentService.createDynamicComponent(MultiStepsWizardComponent);
wizardInstance.instance.input = modalModel;
wizardInstance.instance.steps = steps;
wizardInstance.instance.callback = callback;
if(dynamicHeaderType){
- let dynamicHeader = this.createDynamicComponent(dynamicHeaderType, wizardInstance.instance.dynamicHeaderContainer);
+ let dynamicHeader = this.dynamicComponentService.createDynamicComponent(dynamicHeaderType, wizardInstance.instance.dynamicHeaderContainer);
wizardInstance.instance.dynamicHeader = dynamicHeader;
wizardInstance.instance.dynamicHeader.instance.currentStepIndex = 0;
}
@@ -76,38 +77,28 @@
return wizardInstance;
}
-
+
public closeCurrentModal = () => {
if (!this.currentModal) return;
this.currentModal.instance.close();
this.currentModal.destroy();
+ delete this.currentModal;
}
public addDynamicContentToModal = (modalInstance: ComponentRef<ModalComponent>, dynamicComponentType: Type<any>, dynamicComponentInput?: any) => {
- let dynamicContent = this.createDynamicComponent(dynamicComponentType, modalInstance.instance.dynamicContentContainer);
+ let dynamicContent = this.dynamicComponentService.createDynamicComponent(dynamicComponentType, modalInstance.instance.dynamicContentContainer);
dynamicContent.instance.input = dynamicComponentInput;
modalInstance.instance.dynamicContent = dynamicContent;
return modalInstance;
}
- //Creates a component dynamically (aka during runtime). If a view container is not specified, it will append the new component to the app root.
- //To subscribe to an event from invoking component: componentRef.instance.clicked.subscribe((m) => console.log(m.name));
- private createDynamicComponent<T>(componentType: Type<T>, viewContainerRef?:ViewContainerRef): ComponentRef<T> {
+ public addDynamicTemplateToModal = (modalInstance: ComponentRef<ModalComponent>, templateRef: TemplateRef<void>) => {
+ modalInstance.instance.dynamicContentContainer.clear();
+ modalInstance.instance.dynamicContentContainer.createEmbeddedView(templateRef);
+ return modalInstance;
+ };
- viewContainerRef = viewContainerRef || this.getRootViewContainerRef();
- viewContainerRef.clear();
- let factory: ComponentFactory<T> = this.componentFactoryResolver.resolveComponentFactory(componentType); //Ref: https://angular.io/guide/dynamic-component-loader
- let componentRef: ComponentRef<T> = viewContainerRef.createComponent(factory);
- return componentRef;
- }
-
-
- private getRootViewContainerRef(): ViewContainerRef {
- return this.applicationRef.components[0].instance.viewContainerRef;
- }
}
-
-
diff --git a/catalog-ui/src/app/ng2/services/policies.service.ts b/catalog-ui/src/app/ng2/services/policies.service.ts
new file mode 100644
index 0000000..2b564b8
--- /dev/null
+++ b/catalog-ui/src/app/ng2/services/policies.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, 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 {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config";
+
+
+@Injectable()
+export class PoliciesService {
+ protected baseUrl;
+
+ private mapApiDirections = {
+ 'RESOURCE':'resources',
+ 'SERVICE':'services'
+ }
+
+ constructor(private http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) {
+ this.baseUrl = sdcConfig.api.root ;
+ }
+
+ public createPolicyInstance(entityType:string, id:string, policyType:string) {
+ return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[entityType] + '/' + id +'/policies/' + policyType, {}).map(resp => {
+ return resp.json();
+ });
+ }
+
+}
+
diff --git a/catalog-ui/src/app/ng2/services/properties.service.ts b/catalog-ui/src/app/ng2/services/properties.service.ts
index 86cd2f5..c86d207 100644
--- a/catalog-ui/src/app/ng2/services/properties.service.ts
+++ b/catalog-ui/src/app/ng2/services/properties.service.ts
@@ -18,6 +18,7 @@
* ============LICENSE_END=========================================================
*/
+import * as _ from "lodash";
import { Injectable } from '@angular/core';
import { PropertyFEModel, PropertyBEModel, PropertyDeclareAPIModel, DerivedFEProperty} from "app/models";
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 9450e4b..e7c88a0 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
@@ -27,6 +27,7 @@
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";
export class ComponentGenericResponse implements Serializable<ComponentGenericResponse> {
@@ -43,6 +44,7 @@
public requirements:RequirementsGroup;
public properties:Array<PropertyModel>;
public attributes:Array<AttributeModel>;
+ public policies:Array<PolicyInstance>;
public groups:Array<Module>;
public interfaces:any;
public additionalInformation:any;
@@ -92,6 +94,9 @@
if(response.groups) {
this.groups = CommonUtils.initModules(response.groups);
}
+ if(response.policies) {
+ this.policies = CommonUtils.initPolicies(response.policies);
+ }
return this;
}
}
diff --git a/catalog-ui/src/app/ng2/services/responses/service-generic-response.ts b/catalog-ui/src/app/ng2/services/responses/service-generic-response.ts
new file mode 100644
index 0000000..d32ed26
--- /dev/null
+++ b/catalog-ui/src/app/ng2/services/responses/service-generic-response.ts
@@ -0,0 +1,22 @@
+import * as _ from "lodash";
+import {Serializable} from "../utils/serializable";
+import {ComponentGenericResponse} from "./component-generic-response";
+import {ForwardingPath} from "../../../models/forwarding-path";
+
+export class ServiceGenericResponse extends ComponentGenericResponse implements Serializable<ServiceGenericResponse> {
+ public forwardingPaths: { [key:string]:ForwardingPath } = {};
+
+ deserialize (response): ServiceGenericResponse {
+ super.deserialize(response);
+ if(response.forwardingPaths) {
+ _.forEach(response.forwardingPaths, (pathResponse, id) => {
+ let pathId = id;
+ let path:ForwardingPath = new ForwardingPath();
+ path.deserialize(pathResponse);
+ path.uniqueId = pathId;
+ this.forwardingPaths[pathId] = path;
+ });
+ }
+ return this;
+ }
+}
\ 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 51314c0..c005efc 100644
--- a/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts
+++ b/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts
@@ -27,6 +27,7 @@
import {CookieService} from "../../services/cookie-service";
import {CacheService} from "../../services/cache-service";
import {EventListenerService} from "app/services/event-listener-service";
+import IScope = angular.IScope;
/** Services we need to upgrade from angular1 to angular2 - in the future we need to rewrite them all to angular2 **/
@@ -62,6 +63,10 @@
return cacheObj.get('EventListenerService');
}
+export function notificationServiceFactory(cacheObj: ICacheObject) {
+ return cacheObj.get('Notification');
+}
+
export const DataTypesServiceProvider = {
provide: DataTypesService,
useFactory: dataTypesServiceFactory,
@@ -111,3 +116,9 @@
useFactory: eventListenerServiceServiceFactory,
deps: ['$injector']
};
+
+export const NotificationServiceProvider = {
+ provide: 'Notification',
+ useFactory: notificationServiceFactory,
+ deps: ['$injector']
+};