diff options
Diffstat (limited to 'catalog-ui/src/app/ng2/pages/properties-assignment')
5 files changed, 379 insertions, 213 deletions
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 6ef3e57678..b59ef9dbda 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 @@ -13,21 +13,20 @@ import { SearchFilterPipe } from "../../pipes/searchFilter.pipe"; import { FilterChildPropertiesPipe } from "../../pipes/filterChildProperties.pipe"; import { DataTypeService } from './../../services/data-type.service'; import { PropertiesService } from './../../services/properties.service'; +import { HierarchyNavService } from './../../services/hierarchy-nav.service'; import { PropertiesUtils } from './properties.utils'; import { PostsService } from "../../services/posts.service"; -import { PropertiesValueInnerTableComponent } from "./../../components/properties-table/properties-value-inner-table/properties-value-inner-table.component"; -import { ListPropertyComponent } from "./../../components/properties-table/list-property/list-property.component"; -import { MapPropertyComponent } from "./../../components/properties-table/map-property/map-property.component"; import { DynamicElementModule } from 'app/ng2/components/dynamic-element/dynamic-element.module'; import { DynamicPropertyComponent } from './../../components/properties-table/dynamic-property/dynamic-property.component'; -import { DerivedPropertyComponent } from './../../components/properties-table/derived-property/derived-property.component'; -// import {PopoverContentComponent} from "../../components/popover/popover-content.component" -// import {PopoverComponent} from "../../components/popover/popover.component" +import {ConfirmationDeleteInputComponent} from "app/ng2/components/inputs-table/confirmation-delete-input/confirmation-delete-input.component" import { PopoverModule } from "../../components/popover/popover.module" import { FilterPropertiesAssignmentComponent } from "./../../components/filter-properties-assignment/filter-properties-assignment.component"; import { GroupByPipe } from 'app/ng2/pipes/groupBy.pipe'; import { KeysPipe } from 'app/ng2/pipes/keys.pipe'; import {TooltipModule} from "../../components/tooltip/tooltip.module"; +import { ComponentModeService } from "app/ng2/services/component-mode.service" +import { ModalComponent } from "app/ng2/components/modal/modal.component" +import {LoaderComponent} from "app/ng2/components/loader/loader.component" @NgModule({ declarations: [ @@ -40,14 +39,13 @@ import {TooltipModule} from "../../components/tooltip/tooltip.module"; SearchFilterPipe, FilterChildPropertiesPipe, HierarchyNavigationComponent, - PropertiesValueInnerTableComponent, - ListPropertyComponent, - MapPropertyComponent, - DerivedPropertyComponent, DynamicPropertyComponent, // PopoverContentComponent, // PopoverComponent, - FilterPropertiesAssignmentComponent + FilterPropertiesAssignmentComponent, + ModalComponent, + ConfirmationDeleteInputComponent, + LoaderComponent ], imports: [ BrowserModule, @@ -65,7 +63,7 @@ import {TooltipModule} from "../../components/tooltip/tooltip.module"; // PopoverContentComponent, // PopoverComponent ], - providers: [PropertiesService, PropertiesUtils, DataTypeService, PostsService, ContentAfterLastDotPipe, GroupByPipe, KeysPipe] + providers: [PropertiesService, HierarchyNavService, PropertiesUtils, DataTypeService, PostsService, ContentAfterLastDotPipe, GroupByPipe, KeysPipe, 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 d1b671cff2..317a1fc827 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 @@ -4,9 +4,12 @@ <tabs #propertyInputTabs tabStyle="round-tabs" (tabChanged)="tabChanged($event)" [hideIndicationOnTabChange]="true"> <tab tabTitle="Properties"> <properties-table class="properties-table" - [fePropertiesMap]="instanceFePropertiesMap | searchFilter:'name':searchQuery" - [selectedPropertyId]="selectedFlatProperty.uniqueId" + [fePropertiesMap]="instanceFePropertiesMap" + [searchTerm]="searchQuery" + [selectedPropertyId]="selectedFlatProperty.path" [propertyNameSearchText]="searchPropertyName" + [readonly]="isReadonly" + [isLoading]="loadingProperties" (valueChanged)="propertyValueChanged($event)" (propertySelected)="propertySelected($event)" (selectPropertyRow)="selectPropertyRow($event)" @@ -16,39 +19,51 @@ </properties-table> </tab> <tab tabTitle="Inputs"> - <inputs-table class="properties-table" [inputs]="inputs | searchFilter:'name':searchQuery" (deleteInput)="deleteInput($event)" (inputValueChanged)="inputValueChanged($event)"></inputs-table> + <inputs-table class="properties-table" + [readonly]="isReadonly" + [inputs]="inputs | searchFilter:'name':searchQuery" + [isLoading]="loadingInputs" + (deleteInput)="deleteInput($event)" + (inputValueChanged)="inputValueChanged($event)"></inputs-table> </tab> </tabs> <div class="header"> <div class="search-filter-container"> <input type="text" class="search-box" placeholder="Search" [(ngModel)]="searchQuery" /> <span class="sprite search-icon"></span> - <filter-properties-assignment *ngIf="!hideAdvanceSearch" #advanceSearch class="advance-search" [componentType]="component.componentType" (searchProperties)="searchPropertiesInstances($event)"></filter-properties-assignment> - <span *ngIf="displayClearSearch && !hideAdvanceSearch" (click)="clickOnClearSearch()" class="clear-filter">Clear All</span> + <filter-properties-assignment *ngIf="!isInpusTabSelected" #advanceSearch class="advance-search" [componentType]="component.componentType" (searchProperties)="searchPropertiesInstances($event)"></filter-properties-assignment> + <span *ngIf="displayClearSearch && !isInpusTabSelected" (click)="clickOnClearSearch()" class="clear-filter">Clear All</span> </div> - <button class="tlv-btn blue declare-button" [disabled]="!checkedPropertiesCount" (click)="declareProperties()">Declare</button> + <button class="tlv-btn blue declare-button" [disabled]="!checkedPropertiesCount || isReadonly" (click)="declareProperties()">Declare</button> </div> </div> <div class="right-column gray-border"> <tabs #hierarchyNavTabs tabStyle="simple-tabs"> <tab tabTitle="Composition"> <div class="hierarchy-nav-container"> - <div class="hierarchy-header">{{component.name}}</div> - <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0">No data to display</div> - <hierarchy-navigation class="hierarchy-nav" (updateSelected)="onInstanceSelectedUpdate($event)" - [displayData]="instancesNavigationData" - [selectedItem]="selectedInstanceData.uniqueId" - [displayOptions]="hierarchyInstancesDisplayOptions"></hierarchy-navigation> + <loader [display]="loadingInstances" size="medium" [relative]="true"></loader> + <div class="hierarchy-header"> + <span tooltip="{{component.name}}">{{component.name}}</span> + </div> + <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0 || isInpusTabSelected">No data to display</div> + <hierarchy-navigation class="hierarchy-nav" + (updateSelected)="onInstanceSelectedUpdate($event)" + [displayData]="isInpusTabSelected ? []: instancesNavigationData" + [selectedItem]="selectedInstanceData.uniqueId" + [displayOptions]="hierarchyInstancesDisplayOptions"></hierarchy-navigation> </div> </tab> <tab tabTitle="Property Structure"> <div class="hierarchy-nav-container"> - <div class="hierarchy-header">{{propertyStructureHeader || selectedFlatProperty.name || "No Property Selected"}}</div> - <div *ngIf="!propertiesNavigationData || propertiesNavigationData.length === 0">No data to display</div> - <hierarchy-navigation class="hierarchy-nav" (updateSelected)="onPropertySelectedUpdate($event)" - [displayData]="propertiesNavigationData" - [selectedItem]="selectedFlatProperty.uniqueId" - [displayOptions]="hierarchyPropertiesDisplayOptions"></hierarchy-navigation> + <div class="hierarchy-header"> + <span tooltip="{{!isInpusTabSelected ? (propertyStructureHeader || selectedFlatProperty.name) : ''}}">{{!isInpusTabSelected ? (propertyStructureHeader || selectedFlatProperty.name || "No Property Selected") : "No Property Selected"}}</span> + </div> + <div *ngIf="!propertiesNavigationData || propertiesNavigationData.length === 0 || isInpusTabSelected">No data to display</div> + <hierarchy-navigation class="hierarchy-nav" + (updateSelected)="onPropertySelectedUpdate($event)" + [displayData]="isInpusTabSelected ? [] : propertiesNavigationData" + [selectedItem]="selectedFlatProperty.path" + [displayOptions]="hierarchyPropertiesDisplayOptions"></hierarchy-navigation> </div> </tab> </tabs> 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 f7a62bbcb5..e56374a2c2 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 @@ -145,6 +145,12 @@ text-align: left; background-color: @ng2-light-gray; font-size: 13px; + span{ + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + max-width: 290px; + } } .hierarchy-nav { 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 299615b122..98fdc7391a 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 @@ -1,18 +1,23 @@ import {Component, ViewChild, ElementRef, Renderer, Inject} from "@angular/core"; import {PostsService} from "../../services/posts.service"; -import {PropertiesService, SimpleFlatProperty} from "../../services/properties.service"; +import { PropertiesService } from "../../services/properties.service"; +import { HierarchyNavService } from "../../services/hierarchy-nav.service"; import { PropertiesUtils } from './properties.utils'; import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData } from "app/models"; import { PROPERTY_TYPES, 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 {InputFEModel, ComponentInstance, PropertyBEModel, DerivedFEProperty, ResourceInstance} from "app/models"; +import { InputFEModel, ComponentInstance, PropertyBEModel, DerivedPropertyType, DerivedFEProperty, ResourceInstance, SimpleFlatProperty } from "app/models"; import {HierarchyDisplayOptions} from "../../components/hierarchy-navigtion/hierarchy-display-options" import {PropertyRowSelectedEvent} from "./../../components/properties-table/properties-table.component"; import { KeysPipe } from 'app/ng2/pipes/keys.pipe'; import {FilterPropertiesAssignmentComponent} from "../../components/filter-properties-assignment/filter-properties-assignment.component"; - +import { ComponentModeService } from "app/ng2/services/component-mode.service" +import {WorkspaceMode, EVENTS} from "../../../utils/constants"; +import {ComponentInstanceProperty, InputBEModel} from "app/models" +import {ComponentInstanceInput} from "../../../models/properties-inputs/input-be-model"; +import {EventListenerService} from "app/services/event-listener-service" @Component({ templateUrl: './properties-assignment.page.component.html', styleUrls: ['./properties-assignment.page.component.less'] @@ -29,39 +34,60 @@ export class PropertiesAssignmentComponent { inputs: Array<InputFEModel> = []; instances: Array<ComponentInstance> = []; searchQuery: string; - propertyStructureHeader: string + propertyStructureHeader: string; selectedFlatProperty: SimpleFlatProperty = new SimpleFlatProperty(); selectedInstanceType: string; selectedInstanceData: ComponentInstance = new ComponentInstance(); checkedPropertiesCount: number = 0; - hierarchyPropertiesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'childrens'); + hierarchyPropertiesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('path', 'name', 'childrens'); hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name'); displayClearSearch = false; searchPropertyName:string; - hideAdvanceSearch:boolean; + isInpusTabSelected:boolean; + isReadonly:boolean; + loadingInstances:boolean = false; + loadingInputs:boolean = false; + loadingProperties:boolean = false; @ViewChild('hierarchyNavTabs') hierarchyNavTabs: ElementRef; @ViewChild('propertyInputTabs') propertyInputTabs: ElementRef; @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent; - constructor(private propertiesService:PropertiesService, + constructor(private propertiesService: PropertiesService, + private hierarchyNavService: HierarchyNavService, private propertiesUtils:PropertiesUtils, private componentServiceNg2:ComponentServiceNg2, private componentInstanceServiceNg2:ComponentInstanceServiceNg2, @Inject("$stateParams") _stateParams, - private renderer: Renderer) { + private renderer: Renderer, + private componentModeService:ComponentModeService, + private EventListenerService:EventListenerService) { this.instanceFePropertiesMap = new InstanceFePropertiesMap(); /* This is the way you can access the component data, please do not use any data except metadata, all other data should be received from the new api calls on the first time than if the data is already exist, no need to call the api again - Ask orit if you have any questions*/ this.component = _stateParams.component; + this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.onCheckout); + this.updateViewMode(); } ngOnInit() { console.log("==>" + this.constructor.name + ": ngOnInit"); + this.loadingInputs = true; + this.loadingInstances = true; + this.loadingProperties = true; + this.componentServiceNg2 + .getComponentInputs(this.component) + .subscribe(response => { + _.forEach(response.inputs, (input: InputBEModel) => { + this.inputs.push(new InputFEModel(input)); + }); + this.loadingInputs = false; + + }); this.componentServiceNg2 .getComponentResourceInstances(this.component) .subscribe(response => { @@ -70,95 +96,33 @@ export class PropertiesAssignmentComponent { _.forEach(this.instances, (instance) => { this.instancesNavigationData.push(instance); }); - + this.loadingInstances = false; + if (this.instancesNavigationData[0] == undefined) { + this.loadingProperties = false; + } this.selectFirstInstanceByDefault(); }); + }; - this.componentServiceNg2 - .getComponentInputs(this.component) - .subscribe(response => { - _.forEach(response.inputs, (input: PropertyBEModel) => { - this.inputs.push(new InputFEModel(input)); - }); - }) + ngOnDestroy() { + this.EventListenerService.unRegisterObserver(EVENTS.ON_CHECKOUT); } selectFirstInstanceByDefault = () => { if (this.instancesNavigationData[0] !== undefined) { this.onInstanceSelectedUpdate(this.instancesNavigationData[0]); } - } - - propertyValueChanged = (event) => { - console.log("==>" + this.constructor.name + ": propertyValueChanged " + event); - - if(this.selectedInstanceData.originType === ResourceType.VF) { - 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 and got this response: ",response); - }) - } - else { - // Copying the actual value from the object ref into the value if it's from a complex type - if(event.isDataType) { - event.value = JSON.stringify(event.valueObjectRef); - } - let propertyBe = new PropertyBEModel(event); - this.componentInstanceServiceNg2 - .updateInstanceProperty(this.component, this.selectedInstanceData.uniqueId, propertyBe) - .subscribe(response => { - console.log("updated resource instance property and got this response: ",response); - }); - console.log(event); - } - - }; - - 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); - }) }; - declareProperties = ():void => { - console.log("==>" + this.constructor.name + ": declareProperties"); - - let selectedProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap(); - - let instancesNames = new KeysPipe().transform(this.instanceFePropertiesMap,[]); - angular.forEach(instancesNames, (instanceName:string):void=>{ - selectedProperties[instanceName] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceName]); - //selectedProperties[this.selectedInstanceData.uniqueId] = this.propertiesService.getCheckedProperties(this.properties); - }); + updateViewMode = () => { + this.isReadonly = this.componentModeService.getComponentMode(this.component) === WorkspaceMode.VIEW; + } - let inputsToCreate: InstancePropertiesAPIMap; - if (this.selectedInstanceType !== ResourceType.VF) { - inputsToCreate = new InstancePropertiesAPIMap(null, selectedProperties); - } else { - inputsToCreate = new InstancePropertiesAPIMap(selectedProperties, null); - } - this.componentServiceNg2 - .createInput(this.component, inputsToCreate) - .subscribe(response => { - this.setInputTabIndication(response.length); - this.checkedPropertiesCount = 0; - _.forEach(response, (input: PropertyBEModel) => { this.inputs.push(new InputFEModel(input)); }); - this.findAndDisableDeclaredProperties(); - }); + onCheckout = (component:ComponentData) => { + this.component = component; + this.updateViewMode(); } - //TODO: Can remove? no one use it - // getSelectedFEProps = (): Array<PropertyFEModel> => { - // return this.properties.filter(prop => prop.isSelected && !prop.isDeclared); - // } onInstanceSelectedUpdate = (resourceInstance: ResourceInstance) => { console.log("==>" + this.constructor.name + ": onInstanceSelectedUpdate"); @@ -166,12 +130,15 @@ export class PropertiesAssignmentComponent { this.selectedInstanceData = resourceInstance; this.selectedInstanceType = resourceInstance.originType; + this.loadingProperties = true; if(resourceInstance.originType === ResourceType.VF) { this.componentInstanceServiceNg2 .getComponentInstanceInputs(this.component, resourceInstance) .subscribe(response => { instanceBePropertiesMap[resourceInstance.uniqueId] = response; this.processInstancePropertiesResponse(instanceBePropertiesMap); + this.loadingProperties = false; + }); } else { this.componentInstanceServiceNg2 @@ -179,23 +146,68 @@ export class PropertiesAssignmentComponent { .subscribe(response => { instanceBePropertiesMap[resourceInstance.uniqueId] = response; this.processInstancePropertiesResponse(instanceBePropertiesMap); + this.loadingProperties = false; }); } if( this.searchPropertyName ){ this.clearSearch(); } + //clear selected property from the navigation + this.selectedFlatProperty = new SimpleFlatProperty(); + this.propertiesNavigationData = []; }; /** * Entry point handling response from server */ processInstancePropertiesResponse = (instanceBePropertiesMap:InstanceBePropertiesMap) => { - this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap); //create flattened children - this.findAndDisableDeclaredProperties(); //disable properties or flattened children that are declared + this.instanceFePropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(instanceBePropertiesMap, this.inputs); //create flattened children, disable declared props, and init values this.checkedPropertiesCount = 0; }; + + /*** 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.selectedInstanceData.originType === ResourceType.VF) { + 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 and got this response: ", response); + }) + } + else { + let propertyBe = new PropertyBEModel(event); + this.componentInstanceServiceNg2 + .updateInstanceProperty(this.component, this.selectedInstanceData.uniqueId, propertyBe) + .subscribe(response => { + console.log("updated resource instance property and got this response: ", response); + }); + console.log(event); + } + + }; + + 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); + }) + }; + + + /*** HEIRARCHY/NAV RELATED FUNCTIONS ***/ + /** * Handle select node in navigation area, and select the row in table */ @@ -219,13 +231,13 @@ export class PropertiesAssignmentComponent { if(this.selectedInstanceData.originType !== ResourceType.VF) { let simpleFlatProperty:Array<SimpleFlatProperty>; if (property instanceof PropertyFEModel) { - simpleFlatProperty = this.propertiesService.getSimplePropertiesTree(property, instanceName); + simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(property, instanceName); } else if (property instanceof DerivedFEProperty) { // Need to find parent PropertyFEModel let parentPropertyFEModel:PropertyFEModel = _.find(this.instanceFePropertiesMap[instanceName], (tmpFeProperty):boolean => { return property.propertiesName.indexOf(tmpFeProperty.name)===0; }); - simpleFlatProperty = this.propertiesService.getSimplePropertiesTree(parentPropertyFEModel, instanceName); + simpleFlatProperty = this.hierarchyNavService.getSimplePropertiesTree(parentPropertyFEModel, instanceName); } this.propertiesNavigationData = simpleFlatProperty; } @@ -236,20 +248,10 @@ export class PropertiesAssignmentComponent { } // Set selected property in table - this.selectedFlatProperty = new SimpleFlatProperty(property.uniqueId, null, property.name, null); + this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName); this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Property Structure']); }; - //TODO: Can remove? no one use it - // findParentProperty = (childProp: DerivedFEProperty): PropertyFEModel => { - // return this.properties.find(prop => prop.name == childProp.propertiesName.substring(0, childProp.propertiesName.indexOf("#"))); - // } - - //used for declare button, to keep count of newly checked properties (and ignore declared properties) - updateCheckedPropertyCount = (increment: boolean):void => { - this.checkedPropertiesCount += (increment) ? 1 : -1; - console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount); - } selectInstanceRow = ($event) => {//get instance name this.selectedInstanceData = _.find(this.instancesNavigationData, (instance:ComponentInstance) => { @@ -257,15 +259,90 @@ export class PropertiesAssignmentComponent { }); this.renderer.invokeElementMethod( this.hierarchyNavTabs, 'triggerTabChange', ['Composition']); - } + }; tabChanged = (event) => { console.log("==>" + this.constructor.name + ": tabChanged " + event); - this.hideAdvanceSearch = event.title !== "Properties"; + this.isInpusTabSelected = event.title === "Inputs"; + this.propertyStructureHeader = null; this.searchQuery = ''; }; - deleteInput = (input:InputFEModel) => { + + + /*** DECLARE PROPERTIES/INPUTS ***/ + declareProperties = (): void => { + console.log("==>" + this.constructor.name + ": declareProperties"); + + let selectedProperties: InstanceBePropertiesMap = new InstanceBePropertiesMap(); + + let instancesNames = new KeysPipe().transform(this.instanceFePropertiesMap, []); + angular.forEach(instancesNames, (instanceName: string): void => { + selectedProperties[instanceName] = this.propertiesService.getCheckedProperties(this.instanceFePropertiesMap[instanceName]); + //selectedProperties[this.selectedInstanceData.uniqueId] = this.propertiesService.getCheckedProperties(this.properties); + }); + + let inputsToCreate: InstancePropertiesAPIMap; + if (this.selectedInstanceType !== ResourceType.VF) { + inputsToCreate = new InstancePropertiesAPIMap(null, selectedProperties); + } else { + inputsToCreate = new InstancePropertiesAPIMap(selectedProperties, null); + } + this.componentServiceNg2 + .createInput(this.component, inputsToCreate) + .subscribe(response => { + this.setInputTabIndication(response.length); + this.checkedPropertiesCount = 0; + _.forEach(response, (input: InputBEModel) => { + this.inputs.push(new InputFEModel(input)); + this.updatePropertyValueAfterDeclare(input); + }); + }); + }; + + updatePropertyValueAfterDeclare = (input: InputBEModel) => { + _.forEach(input.properties, (property: ComponentInstanceProperty) => { + this.updatePropertyOrInputValueAfterDeclare(property, input); + }); + + _.forEach(input.inputs, (inputInstance: ComponentInstanceInput) => { + this.updatePropertyOrInputValueAfterDeclare(inputInstance, input); + }); + } + + updatePropertyOrInputValueAfterDeclare = (inputSource: ComponentInstanceProperty | ComponentInstanceInput, input: InputBEModel) => { + if (this.instanceFePropertiesMap[inputSource.componentInstanceId]) { + let propertyForUpdatindVal = _.find(this.instanceFePropertiesMap[inputSource.componentInstanceId], (feProperty: PropertyFEModel) => { + return feProperty.name == inputSource.name; + }); + + if (input.inputPath == propertyForUpdatindVal.name) input.inputPath = null; //Fix - if inputPath is sent for parent props, remove it + + propertyForUpdatindVal.setAsDeclared(input.inputPath); //set prop as declared before assigning value + this.propertiesService.disableRelatedProperties(propertyForUpdatindVal, input.inputPath); + this.propertiesUtils.resetPropertyValue(propertyForUpdatindVal, inputSource.value, input.inputPath); + // if (input.inputPath) { + // let childProp = _.find(propertyForUpdatindVal.flattenedChildren, (child: DerivedFEProperty) => { + // return child.propertiesName == input.inputPath; + // }); + // this.propertiesUtils.assignFlattenedChildrenValues(JSON.parse(inputSource.value), [childProp], inputSource.name); + // } else { + // propertyForUpdatindVal.valueObj = inputSource.value; + // } + } + } + + //used for declare button, to keep count of newly checked properties (and ignore declared properties) + updateCheckedPropertyCount = (increment: boolean): void => { + this.checkedPropertiesCount += (increment) ? 1 : -1; + console.log("CheckedProperties count is now.... " + this.checkedPropertiesCount); + }; + + setInputTabIndication = (numInputs: number): void => { + this.renderer.invokeElementMethod(this.propertyInputTabs, 'setTabIndication', ['Inputs', numInputs]); + }; + + deleteInput = (input: InputFEModel) => { console.log("==>" + this.constructor.name + ": deleteInput"); let inputToDelete = new PropertyBEModel(input); @@ -273,51 +350,45 @@ export class PropertiesAssignmentComponent { .deleteInput(this.component, inputToDelete) .subscribe(response => { this.inputs = this.inputs.filter(input => input.uniqueId !== response.uniqueId); - let propToEnable: PropertyFEModel = this.instanceFePropertiesMap[input.instanceName].find(prop => prop.name == input.propertyName); - propToEnable.setNonDeclared(response.inputPath); - this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath); - //this.propertiesService.initValueObjectRef(propToEnable); //TODO:speak to BE about value returned by server - }); - } - setInputTabIndication = (numInputs: number): void => { - this.renderer.invokeElementMethod( this.propertyInputTabs, 'setTabIndication', ['Inputs', numInputs]); - } + //Reload the whole instance for now - TODO: CHANGE THIS after the BE starts returning properties within the response, use commented code below instead! + this.onInstanceSelectedUpdate(this.selectedInstanceData); + // let instanceFeProperties = this.instanceFePropertiesMap[this.getInstanceUniqueId(input.instanceName)]; + + // if (instanceFeProperties) { + // let propToEnable: PropertyFEModel = instanceFeProperties.find((prop) => { + // return prop.name == input.propertyName; + // }); + + // if (propToEnable) { + // if (propToEnable.name == response.inputPath) response.inputPath = null; + // propToEnable.setNonDeclared(response.inputPath); + // //this.propertiesUtils.resetPropertyValue(propToEnable, newValue, response.inputPath); + // this.propertiesService.undoDisableRelatedProperties(propToEnable, response.inputPath); + // } + // } + }); + }; - findAndDisableDeclaredProperties = () => { - this.inputs.filter(input => input.instanceName === this.selectedInstanceData.normalizedName).forEach(input => { - let prop: PropertyFEModel = this.instanceFePropertiesMap[this.selectedInstanceData.uniqueId].find(prop => prop.name === input.propertyName); - if (prop) { - prop.setAsDeclared(input.inputPath); //if a path was sent, its a child prop. this param is optional - this.propertiesService.disableRelatedProperties(prop, input.inputPath) - //this.propertiesService.initValueObjectRef(prop); - } + getInstanceUniqueId = (instanceName: string): string => { + let wantedInstance: ComponentInstance = this.instances.find((instance) => { + return instance.normalizedName === instanceName; }); + + return wantedInstance.uniqueId; }; + + + /*** SEARCH RELATED FUNCTIONS ***/ searchPropertiesInstances = (filterData:FilterPropertiesAssignmentData) => { - //let filteredProperties = this.componentServiceNg2.filterComponentInstanceProperties(this.component, filterData); let instanceBePropertiesMap:InstanceBePropertiesMap; this.componentServiceNg2 .filterComponentInstanceProperties(this.component, filterData) .subscribe(response => { - //instanceBePropertiesMap=response; - //console.log("================filter results============="); - //console.table(instanceBePropertiesMap); - this.processInstancePropertiesResponse(response); - - - //this.properties = []; - // _.forEach(instanceBePropertiesMap, (InstanceProperties:Array<PropertyBEModel>, instanceName:string) => { - // this.properties = this.properties.concat(this.propertiesService.convertPropertiesToFEAndCreateChildren(InstanceProperties, instanceName)); - // }); - - - // this.instancesNavigationData = _.filter(this.instancesNavigationData, (instance:ComponentInstance) => { - // return instanceBePropertiesMap[instance.name]; - // }); - // this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree + this.processInstancePropertiesResponse(response); + this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree this.searchPropertyName = filterData.propertyName;//mark in table this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Composition']); this.propertiesNavigationData = []; @@ -332,13 +403,13 @@ export class PropertiesAssignmentComponent { this.hierarchyPropertiesDisplayOptions.searchText = ""; this.displayClearSearch = false; this.advanceSearch.clearAll(); - } + }; clickOnClearSearch = () => { this.clearSearch(); this.selectFirstInstanceByDefault(); this.renderer.invokeElementMethod( this.hierarchyNavTabs, 'triggerTabChange', ['Composition']); - } + }; } diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties.utils.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/properties.utils.ts index 79769e21b5..dfde2a40b2 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties.utils.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties.utils.ts @@ -1,78 +1,154 @@ import { Injectable } from '@angular/core'; import { DataTypeModel, PropertyFEModel, PropertyBEModel, InstanceBePropertiesMap, InstanceFePropertiesMap, SchemaProperty, DerivedFEProperty, DerivedFEPropertyMap, DerivedPropertyType, InputFEModel} from "app/models"; import { DataTypeService } from "app/ng2/services/data-type.service"; +import { PropertiesService } from "app/ng2/services/properties.service"; import { PROPERTY_TYPES } from "app/utils"; import { UUID } from "angular2-uuid"; @Injectable() export class PropertiesUtils { - constructor(private dataTypeService:DataTypeService) {} + constructor(private dataTypeService:DataTypeService, private propertiesService: PropertiesService) {} /** * Entry point when getting properties from server - * Returning InstanceFePropertiesMap + * For each instance, loop through each property, and: + * 1. Create flattened children + * 2. Check against inputs to see if any props are declared and disable them + * 3. Initialize valueObj (which also creates any new list/map flattened children as needed) + * Returns InstanceFePropertiesMap */ - public convertPropertiesMapToFEAndCreateChildren = (instancePropertiesMap:InstanceBePropertiesMap): InstanceFePropertiesMap => { + public convertPropertiesMapToFEAndCreateChildren = (instancePropertiesMap:InstanceBePropertiesMap, inputs:Array<InputFEModel>): InstanceFePropertiesMap => { let instanceFePropertiesMap:InstanceFePropertiesMap = new InstanceFePropertiesMap(); angular.forEach(instancePropertiesMap, (properties:Array<PropertyBEModel>, instanceName:string) => { - instanceFePropertiesMap[instanceName] = this.convertPropertiesToFEAndCreateChildren(properties); + let instanceInputs: Array<InputFEModel> = inputs.filter(input => input.instanceName == instanceName.split('.').pop()); + let propertyFeArray: Array<PropertyFEModel> = []; + _.forEach(properties, (property: PropertyBEModel) => { + + if (!this.dataTypeService.getDataTypeByTypeName(property.type)) { // if type not exist in data types remove property from list + console.log("ERROR: missing type " + property.type + " in dataTypes , of property ", property); + } else { + + 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 (instanceInputs.length) { //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children + instanceInputs.filter(input => input.propertyName == newFEProp.name).forEach((input) => { + newFEProp.setAsDeclared(input.inputPath); //if a path was sent, its a child prop. this param is optional + this.propertiesService.disableRelatedProperties(newFEProp, input.inputPath); + }); + } + this.initValueObjectRef(newFEProp); //initialize valueObj. + propertyFeArray.push(newFEProp); + newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children + } + }); + instanceFePropertiesMap[instanceName] = propertyFeArray; + }); return instanceFePropertiesMap; } + private createListOrMapChildrenFromValueObj = (property: PropertyFEModel) => { + if ((property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) + && Object.keys(property.valueObj).length) { - /** - * Convert the properties Array<PropertyBEModel> to Array<PropertyFEModel> - */ - private convertPropertiesToFEAndCreateChildren = (properties: Array<PropertyBEModel>): Array<PropertyFEModel> => { - let propertyFeArray: Array<PropertyFEModel> = []; - _.forEach(properties, (property: PropertyBEModel, index: number) => { - //console.log("=======" + property.name + "========"); - if(!this.dataTypeService.getDataTypeByTypeName(property.type)){ // if type not exist in data types remove property from list - console.log("ERROR: missing type " + property.type + " in dataTypes , of property ",property); - return; - } - let propertyFe:PropertyFEModel = new PropertyFEModel(property); - if (propertyFe.isDataType) { //prop is not simple, list, or map. Need to create children. - let tempProps: Array<DerivedFEProperty> = []; - let dataTypeObj: DataTypeModel = this.dataTypeService.getDataTypeByTypeName(propertyFe.type); - this.dataTypeService.getDerivedDataTypeProperties(dataTypeObj, tempProps, propertyFe.name); - propertyFe.flattenedChildren = tempProps; - propertyFe.expandedChildPropertyId = propertyFe.name; - this.initValueObjectRef(propertyFe); - } - propertyFeArray.push(propertyFe); - + Object.keys(property.valueObj).forEach((key) => { + let newProps: Array<DerivedFEProperty> = this.createListOrMapChildren(property, key, property.valueObj[key]); + property.flattenedChildren.push(...newProps); + }); + + } + } - }); - return propertyFeArray; + public createListOrMapChildren = (property:PropertyBEModel, key: string, valueObj: any): Array<DerivedFEProperty> => { + let newProps: Array<DerivedFEProperty> = []; + let parentProp = new DerivedFEProperty(property, property.propertiesName, true, key, valueObj); + newProps.push(parentProp); - //TODO: need to look at schema to create the nested properties for the following cases: - // 1 - when value is populated for a complex type (list or map) - // 2 - when adding new entries to a complex type (eg. adding a new entry to a list of AddressRequirements) + if (!property.schema.property.isSimpleType) { + let additionalChildren:Array<DerivedFEProperty> = this.createFlattenedChildren(property.schema.property.type, parentProp.propertiesName); + this.assignFlattenedChildrenValues(parentProp.valueObj, additionalChildren, parentProp.propertiesName); + additionalChildren.forEach(prop => prop.canBeDeclared = false); + newProps.push(...additionalChildren); + } + return newProps; } + /** + * Creates derivedFEProperties of a specified type and returns them. + */ + private createFlattenedChildren = (type: string, parentName: string):Array<DerivedFEProperty> => { + let tempProps: Array<DerivedFEProperty> = []; + let dataTypeObj: DataTypeModel = this.dataTypeService.getDataTypeByTypeName(type); + this.dataTypeService.getDerivedDataTypeProperties(dataTypeObj, tempProps, parentName); + return tempProps; + } + + /* Sets the valueObj of parent property and its children. + * 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 => { - //console.log("Property " + property.name + " has value: " + property.value); - if (!property.isDataType || 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.value = property.value || property.defaultValue; - } else if (property.value){ //we have a complex property with a value. Lets parse property.value and populate our flattened children with those values - this.assignValuesRecursively(JSON.parse(property.value), property.flattenedChildren, property.name); + 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 && 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.COMPLEX) { + this.assignFlattenedChildrenValues(property.valueObj, property.flattenedChildren, property.name); + } else { + this.createListOrMapChildrenFromValueObj(property); + } } } - public assignValuesRecursively = (valueJSON: any, derivedPropArray: Array<DerivedFEProperty>, propName: string) => { - if (valueJSON && Object.keys(valueJSON)) { - Object.keys(valueJSON).forEach(valueKey => { - let childProp: DerivedFEProperty = derivedPropArray.find(prop => prop.propertiesName == propName + "#" + valueKey); - if (!childProp) return; - if (childProp.isDeclared || (childProp.derivedDataType != DerivedPropertyType.COMPLEX && !_.isEmpty(valueJSON[valueKey]))) { - childProp.value = (typeof valueJSON[valueKey] === 'object')? JSON.stringify(valueJSON[valueKey]) : valueJSON[valueKey]; - } else { - this.assignValuesRecursively(valueJSON[valueKey], derivedPropArray, childProp.propertiesName) + /* + * Loops through flattened properties array and to assign values + * Then, convert any neccessary strings to objects, and vis-versa + * For list or map property, creates new children props if valueObj has values + */ + public assignFlattenedChildrenValues = (parentValueJSON: any, derivedPropArray: Array<DerivedFEProperty>, parentName: string) => { + if (!derivedPropArray || !parentName) return; + 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 + + if ((prop.derivedDataType == DerivedPropertyType.SIMPLE || prop.isDeclared) && typeof prop.valueObj == 'object') { //Stringify objects that should be strings + prop.valueObj = JSON.stringify(prop.valueObj); + } else { //parse strings that should be objects + if ((prop.derivedDataType == DerivedPropertyType.COMPLEX || prop.derivedDataType == DerivedPropertyType.MAP) && 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 || '[]'); } - }); + if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && 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 + }); + derivedPropArray.splice(index + 1, 0, ...newProps); + } + } + }); + } + + public resetPropertyValue = (property: PropertyFEModel, newValue: string, inputPath?: string): void => { + property.value = newValue; + if (inputPath) { + let newProp = property.flattenedChildren.find(prop => prop.propertiesName == inputPath); + newProp && this.assignFlattenedChildrenValues(JSON.parse(newValue), [newProp], property.name); + } else { + this.initValueObjectRef(property); } } + + } |