diff options
author | ojasdubey <ojas.dubey@amdocs.com> | 2019-03-18 14:15:03 +0530 |
---|---|---|
committer | Avi Gaffa <avi.gaffa@amdocs.com> | 2019-03-18 11:38:42 +0000 |
commit | 4192e3caac2662624a7368252a3bc5619539caa7 (patch) | |
tree | c070e560ac56c9c246e059d4b4a510b9eec9320f /catalog-ui/src/app/ng2/pages | |
parent | 2ca2fc5c0da1eb862fcd79d1f9345aa89e62b396 (diff) |
Service Consumption FE
Service consumption feature
frontend implementation
Change-Id: I68e1b507b1d4379b271fe97428ff8ae86dc11b4c
Issue-ID: SDC-1990
Signed-off-by: ojasdubey <ojas.dubey@amdocs.com>
Diffstat (limited to 'catalog-ui/src/app/ng2/pages')
4 files changed, 563 insertions, 0 deletions
diff --git a/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.html b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.html new file mode 100644 index 0000000000..5d42fa7694 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.html @@ -0,0 +1,98 @@ + +<div class="service-consumption-editor"> + <form class="w-sdc-form"> + + <div class="sdc-modal-top-bar"> + <div class="operation-name">{{serviceOperation.operation.name}}</div> + + <div class="sdc-modal-top-bar-buttons"> + <span (click)="onChangePage(currentIndex - 1)" [ngClass]="{'disabled' : currentIndex === 0 || !checkFormValidForNavigation()}" class="sprite-new left-arrow" data-tests-id="get-prev" tooltip="Previous"></span> + <span (click)="onChangePage(currentIndex + 1)" [ngClass]="{'disabled' : currentIndex === serviceOperationsList.length - 1 || !checkFormValidForNavigation()}" class="sprite-new right-arrow" data-tests-id="get-next" tooltip="Next"></span> + </div> + </div> + <div class="expand-collapse-all" *ngIf="serviceOperation.consumptionInputs.length"> + <div class="expand-all" (click)="onExpandAll()" [ngClass]="{'disabled': isAllInputExpanded()}"> {{'CONSUMPTION_EXPAND_ALL' | translate}}</div> + <div class="separator-line"></div> + <div class="collapse-all" (click)="onCollapseAll()" [ngClass]="{'disabled': isAllInputCollapsed()}"> {{'CONSUMPTION_COLLAPSE_ALL' | translate}}</div> + </div> + + + <loader [display]="isLoading" [size]="'large'" [relative]="true"></loader> + + <div class="i-sdc-form-content-operation-inputs-content" [ngClass]="{'no-inputs': !serviceOperation.consumptionInputs.length}"> + <div class="no-inputs-text" *ngIf="!serviceOperation.consumptionInputs.length">{{'CONSUMPTION_NO_INPUTS_TO_SHOW' | translate}}</div> + <div class="i-sdc-form-content-operation-input-box" *ngFor="let consumptionInput of serviceOperation.consumptionInputs"> + <div class="i-sdc-form-content-operation-input-box-title" (click)="onExpandCollapse(consumptionInput)"> + <div class="expand-collapse-icon" [ngClass]="{'expanded': consumptionInput.expanded}"></div> + <div class="operation-input-name">{{consumptionInput.name}}</div> + <div class="operation-input-type"> + <span class="type-text"> | {{ 'CONSUMPTION_TYPE' | translate}}:</span> + <span class="type-val"> {{consumptionInput.type}} </span> + </div> + </div> + + <div class="operation-input-section-row with-top-border" *ngIf="consumptionInput.expanded"> + <div class="i-sdc-form-item operation-input-section-col"> + <label class="i-sdc-form-label" [ngClass]="{'required': consumptionInput.required}" >{{ 'CONSUMPTION_SOURCE' | translate}}</label> + <ui-element-dropdown + class="i-sdc-form-select" + data-tests-id="sourceType" + [values]="sourceTypes" + [(value)]="consumptionInput.source" + (change)="onSourceChanged(consumptionInput)"> + </ui-element-dropdown> + </div> + + <div class="operation-input-section-col assigned-value"> + <label class="i-sdc-form-label" *ngIf="consumptionInput.source !== SOURCE_TYPES.STATIC || consumptionInput.isSimpleType"> + {{consumptionInput.assignValueLabel}} + </label> + <dynamic-element + class="dynamic-input-field" + *ngIf="consumptionInput.isSimpleType && (consumptionInput.source === SOURCE_TYPES.STATIC || consumptionInput.source === '')" + data-tests-id="inputValue" + [(value)]="consumptionInput.value" + (elementChanged)="onChange($event.value, $event.isValid, consumptionInput)" + [type]="consumptionInput.type"> + </dynamic-element> + <select + class="i-sdc-form-select" + *ngIf="consumptionInput.source !== SOURCE_TYPES.STATIC" + [attr.data-tests-id]="inputValue" + (change)="onChange(value, true, consumptionInput)" + [(ngModel)]="consumptionInput.value" + [ngModelOptions]="{standalone: true}"> + <option + *ngFor="let propName of consumptionInput.associatedProps" + [ngValue]="propName"> + {{propName}} + </option> + <optgroup + *ngFor="let interfaceOperation of consumptionInput.associatedInterfaces" + label="{{interfaceOperation.label}}"> + <option + *ngFor="let output of interfaceOperation.outputs" + [ngValue]="interfaceOperation.name + '.' + output.name"> + {{output.name}} + </option> + </optgroup> + </select> + </div> + </div> + <div class="operation-input-complex-type-section" *ngIf="consumptionInput.expanded && consumptionInput.source === SOURCE_TYPES.STATIC && !consumptionInput.isSimpleType"> + <div class="separator"></div> + <label class="static-values-title-for-complex-type">{{ 'CONSUMPTION_STATIC_VALUES' | translate}}</label> + <div class="dynamic-property" *ngFor="let property of inputFePropertiesMap[consumptionInput.name]"> + <dynamic-property + [property]="property" + [readonly]="false" + [expandedChildId]="property.expandedChildPropertyId" + (propertyChanged)="onComplexPropertyChanged(property, consumptionInput)" + (expandChild)="property.updateExpandedChildPropertyId($event)"> + </dynamic-property> + </div> + </div> + </div> + </div> + </form> +</div> diff --git a/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.less b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.less new file mode 100644 index 0000000000..83481c1d74 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.less @@ -0,0 +1,143 @@ +@import './../../../../assets/styles/variables.less'; +@import './../../../../assets/styles/variables-old.less'; +@import './../../../../assets/styles/sprite.less'; +@import './../../../../assets/styles/mixins_old.less'; + +.service-consumption-editor { + .sdc-modal-top-bar { + display: flex; + justify-content: space-between; + .operation-name { + text-transform: capitalize; + font-family: @font-opensans-bold; + font-size: 18px; + } + } + .expand-collapse-all { + display: flex; + .expand-all, .collapse-all { + color: @main_color_a; + border-bottom: solid 1px @main_color_a; + font-size: 12px; + font-family: @font-opensans-regular; + cursor: pointer; + width: max-content; + line-height: 24px; + } + .separator-line { + border-left: 1px solid @main_color_o; + margin: 0 7px; + } + } + + .i-sdc-form-content-operation-inputs-content { + margin-top: 6px; + padding-top: 10px; + height: 390px; + overflow: auto; + &.no-inputs { + border: 1px solid @main_color_o; + } + .no-inputs-text { + text-align: center; + padding: 30px; + opacity: 0.7; + } + .i-sdc-form-content-operation-input-box { + border: solid 1px @main_color_o; + &:not(:last-of-type) { + margin-bottom: 17px; + } + + .i-sdc-form-content-operation-input-box-title { + .hand; + display: flex; + align-items: center; + padding-left: 10px; + height: 38px; + font-size: 14px; + .expand-collapse-icon { + .sprite-new; + .expand-collapse-plus-icon; + vertical-align: middle; + &.expanded { + .expand-collapse-minus-icon; + } + } + .operation-input-name { + margin-left: 10px; + margin-right: 7px; + font-family: @font-opensans-bold + } + .operation-input-type { + .type-text { + font-family: @font-opensans-bold; + } + .type-val { + font-family: @font-opensans-regular; + } + } + } + + .operation-input-section-row { + &.with-top-border, .separator { + border-top: 1px solid @main_color_o; + } + background-color: @tlv_color_t; + display: flex; + padding: 19px 20px 12px 19px; + .operation-input-section-col { + flex: 45%; + &:not(:last-of-type) { + margin-right: 38px; + } + .i-sdc-form-label { + font-size: 12px; + font-family: @font-opensans-bold; + } + + &.assigned-value { + display: flex; + flex-direction: column; + .i-sdc-form-label { + margin-bottom: 0; + } + } + + /deep/ ui-element-dropdown select, select, + /deep/ dynamic-element input { + height: 30px; + } + + select { + margin-top: 2px; + border: solid 1px @main_color_o; + } + } + } + + //for complex types + /deep/ .operation-input-complex-type-section { + background-color: @tlv_color_t; + padding: 0 20px 12px 19px; + .separator { + border-top: 1px solid @main_color_o; + padding-bottom: 19px; + } + .static-values-title-for-complex-type { + font-size: 14px; + font-family: @font-opensans-bold; + margin-bottom: 10px; + } + .flat-children-container { + border: solid 1px @main_color_o; + } + .dynamic-property { + .dynamic-property-row { + background-color: @tlv_color_t; + } + } + } + } + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.ts b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.ts new file mode 100644 index 0000000000..25f412671f --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.component.ts @@ -0,0 +1,294 @@ +/*! + * Copyright © 2016-2018 European Support Limited + * + * 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. + */ + +import * as _ from "lodash"; +import { Component } from '@angular/core'; +import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; +import {Service, ServiceInstanceObject, InstanceFePropertiesMap, InstanceBePropertiesMap, PropertyBEModel, InputBEModel, OperationModel, InterfaceModel} from 'app/models'; +import {ConsumptionInput, ConsumptionInputDetails, ServiceOperation} from 'app/ng2/components/logic/service-consumption/service-consumption.component'; +import {PropertiesUtils} from "app/ng2/pages/properties-assignment/services/properties.utils"; +import { PROPERTY_DATA } from 'app/utils'; + + +@Component({ + selector: 'service-consumption-editor', + templateUrl: './service-consumption-editor.component.html', + styleUrls:['./service-consumption-editor.component.less'], + providers: [] +}) + +export class ServiceConsumptionCreatorComponent { + + input: { + interfaceId: string; + serviceOperationIndex: number, + serviceOperations: Array<ServiceOperation>, + parentService: Service, + selectedService: Service, + parentServiceInputs: Array<InputBEModel>, + selectedServiceProperties: Array<PropertyBEModel>, + selectedServiceInstanceId: String, + selectedInstanceSiblings: Array<ServiceInstanceObject> + }; + sourceTypes: Array<any> = []; + serviceOperationsList: Array<ServiceOperation>; + serviceOperation: ServiceOperation; + currentIndex: number; + isLoading: boolean = false; + parentService: Service; + selectedService: Service; + selectedServiceInstanceId: String; + parentServiceInputs: Array<InputBEModel>; + selectedServiceProperties: Array<PropertyBEModel>; + changedData: Array<ConsumptionInputDetails> = []; + inputFePropertiesMap: any = []; + + SOURCE_TYPES = { + STATIC: 'Static', + SELF: 'Self', + SERVICE_PROPERTY_LABEL: 'Service Property', + SERVICE_INPUT_LABEL: 'Service Input' + }; + + constructor(private serviceServiceNg2: ServiceServiceNg2, private propertiesUtils:PropertiesUtils) {} + + ngOnInit() { + this.serviceOperationsList = this.input.serviceOperations; + this.currentIndex = this.input.serviceOperationIndex; + this.serviceOperation = this.serviceOperationsList[this.currentIndex]; + this.parentService = this.input.parentService; + this.selectedService = this.input.selectedService; + this.selectedServiceInstanceId = this.input.selectedServiceInstanceId; + this.parentServiceInputs = this.input.parentServiceInputs || []; + this.selectedServiceProperties = this.input.selectedServiceProperties || []; + this.initSourceTypes(); + this.initConsumptionInputs(); + } + + initSourceTypes() { + this.sourceTypes = [ + { label: this.SOURCE_TYPES.STATIC, value: this.SOURCE_TYPES.STATIC, options: [], interfaces: []}, + { label: this.SOURCE_TYPES.SELF + ' (' + this.selectedService.name + ')', + value: this.selectedServiceInstanceId, + options: this.selectedServiceProperties, + interfaces: this.selectedService.interfaces + }, + { label: this.parentService.name, + value: this.parentService.uniqueId, + options: this.parentServiceInputs, + interfaces: [] + } + ]; + _.forEach(this.input.selectedInstanceSiblings, sib => + this.sourceTypes.push({ + label: sib.name, + value: sib.id, + options: _.unionBy(sib.inputs, sib.properties, 'uniqueId'), + interfaces: sib.interfaces + }) + ); + } + + onExpandCollapse(consumptionInput: ConsumptionInputDetails) { + consumptionInput.expanded = !consumptionInput.expanded; + } + + onExpandAll() { + _.forEach(this.serviceOperation.consumptionInputs, coInput => { + coInput.expanded = true; + }) + } + onCollapseAll() { + _.forEach(this.serviceOperation.consumptionInputs, coInput => { + coInput.expanded = false; + }) + } + + isAllInputExpanded() { + return _.every(this.serviceOperation.consumptionInputs, coInput => coInput.expanded === true); + } + isAllInputCollapsed() { + return _.every(this.serviceOperation.consumptionInputs, coInput => coInput.expanded === false); + } + + onChangePage(newIndex) { + if (newIndex >= 0 && newIndex < this.serviceOperationsList.length) { + this.currentIndex = newIndex; + this.serviceOperation = this.serviceOperationsList[newIndex]; + if(!this.serviceOperation.consumptionInputs || this.serviceOperation.consumptionInputs.length === 0) { + this.initConsumptionInputs(); + } + this.getComplexPropertiesForCurrentInputsOfOperation(this.serviceOperation.consumptionInputs); + } + } + + private initConsumptionInputs() { + this.isLoading = true; + this.serviceServiceNg2.getServiceConsumptionInputs(this.parentService, this.selectedServiceInstanceId, this.input.interfaceId, this.serviceOperation.operation).subscribe((result: Array<ConsumptionInput>) => { + this.isLoading = false; + this.serviceOperation.consumptionInputs = this.analyzeCurrentConsumptionInputs(result); + this.getComplexPropertiesForCurrentInputsOfOperation(this.serviceOperation.consumptionInputs); + }, err=> { + this.isLoading = false; + }); + } + + private analyzeCurrentConsumptionInputs(result: Array<any>): Array<ConsumptionInputDetails> { + let inputsResult: Array<ConsumptionInputDetails> = []; + let currentOp = this.serviceOperation.operation; + if(currentOp) { + inputsResult = _.map(result, input => { + let sourceVal = input.source || this.SOURCE_TYPES.STATIC; + let consumptionInputDetails: ConsumptionInputDetails = _.cloneDeep(input); + consumptionInputDetails.source = sourceVal; + consumptionInputDetails.isValid = true; + consumptionInputDetails.expanded = false; + let filteredListsObj = this.getFilteredProps(sourceVal, input.type); + consumptionInputDetails.assignValueLabel = this.getAssignValueLabel(sourceVal); + consumptionInputDetails.associatedProps = filteredListsObj.associatedPropsList; + consumptionInputDetails.associatedInterfaces = filteredListsObj.associatedInterfacesList; + return new ConsumptionInputDetails(consumptionInputDetails); + }); + } + return inputsResult; + } + + private onSourceChanged(consumptionInput: ConsumptionInputDetails): void { + consumptionInput.assignValueLabel = this.getAssignValueLabel(consumptionInput.source); + let filteredListsObj = this.getFilteredProps(consumptionInput.source, consumptionInput.type); + consumptionInput.associatedProps = filteredListsObj.associatedPropsList; + consumptionInput.associatedInterfaces = filteredListsObj.associatedInterfacesList; + if(consumptionInput.source === this.SOURCE_TYPES.STATIC) { + if(PROPERTY_DATA.SIMPLE_TYPES.indexOf(consumptionInput.type) !== -1) { + consumptionInput.value = consumptionInput.defaultValue || ""; + } + else { + consumptionInput.value = null; + Object.assign(this.inputFePropertiesMap, this.processPropertiesOfComplexTypeInput(consumptionInput)); + } + } + } + + private getFilteredProps(sourceVal, inputType) { + let currentSourceObj = this.sourceTypes.find(s => s.value === sourceVal); + let associatedInterfacesList = [], associatedPropsList = []; + if(currentSourceObj) { + if (currentSourceObj.interfaces) { + associatedInterfacesList = this.getFilteredInterfaceOutputs(currentSourceObj, inputType); + } + associatedPropsList = currentSourceObj.options.reduce((result, prop) => { + if (prop.type === inputType) { + result.push(prop.name); + } + return result; + }, []); + } + return { + associatedPropsList: associatedPropsList, + associatedInterfacesList: associatedInterfacesList + } + } + + private getFilteredInterfaceOutputs(currentSourceObj, inputType) { + let currentServiceOperationId = this.serviceOperation.operation.uniqueId; + let filteredInterfacesList = []; + Object.keys(currentSourceObj.interfaces).map(interfId => { + let interfaceObj: InterfaceModel = new InterfaceModel(currentSourceObj.interfaces[interfId]); + Object.keys(interfaceObj.operations).map(opId => { + if(currentServiceOperationId !== opId) { + let operationObj: OperationModel = interfaceObj.operations[opId]; + let filteredOutputsList = _.filter(operationObj.outputs.listToscaDataDefinition, output => output.type === inputType); + if (filteredOutputsList.length) { + filteredInterfacesList.push({ + name: `${interfaceObj.type}.${operationObj.name}`, + label: `${interfaceObj.displayType()}.${operationObj.name}`, + outputs: filteredOutputsList + }); + } + } + }); + }); + return filteredInterfacesList; + } + + getAssignValueLabel(selectedSource: string): string { + if(selectedSource === this.SOURCE_TYPES.STATIC || selectedSource === "") { + return this.SOURCE_TYPES.STATIC; + } + else { + if(selectedSource === this.parentService.uniqueId) { //parent is the source + return this.SOURCE_TYPES.SERVICE_INPUT_LABEL; + } + return this.SOURCE_TYPES.SERVICE_PROPERTY_LABEL; + } + } + + + private isValidInputsValues(): boolean { + return this.changedData.length > 0 && this.changedData.every((changedItem) => changedItem.isValid); + } + + private isMandatoryFieldsValid(): boolean { + const invalid: Array<ConsumptionInputDetails> = this.serviceOperation.consumptionInputs.filter(item => + item.required && (item.value === null || typeof item.value === 'undefined' || item.value === '')); + if (invalid.length > 0) { + return false; + } + return true; + } + + checkFormValidForSubmit(): boolean { + return this.isValidInputsValues() && this.isMandatoryFieldsValid(); + } + + checkFormValidForNavigation(): boolean { + return this.isMandatoryFieldsValid() && (this.changedData.length === 0 || this.isValidInputsValues()); + } + + onChange(value: any, isValid: boolean, consumptionInput: ConsumptionInputDetails) { + consumptionInput.updateValidity(isValid); + const dataChangedIndex = this.changedData.findIndex((changedItem) => changedItem.inputId === consumptionInput.inputId); + if (value !== consumptionInput.origVal) { + if (dataChangedIndex === -1) { + this.changedData.push(consumptionInput); + } + } else { + if (dataChangedIndex !== -1) { + this.changedData.splice(dataChangedIndex, 1); + } + } + } + + private getComplexPropertiesForCurrentInputsOfOperation(opInputs: Array<ConsumptionInput>) { + _.forEach(opInputs, input => { + if(PROPERTY_DATA.SIMPLE_TYPES.indexOf(input.type) === -1 && input.source === this.SOURCE_TYPES.STATIC) { + Object.assign(this.inputFePropertiesMap, this.processPropertiesOfComplexTypeInput(input)); + } + }); + } + + private processPropertiesOfComplexTypeInput(input: ConsumptionInput): InstanceFePropertiesMap { + let inputBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); + inputBePropertiesMap[input.name] = [input]; + let originTypeIsVF = false; + return this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren(inputBePropertiesMap, originTypeIsVF); //create flattened children and init values + } + + onComplexPropertyChanged(property, consumptionInput) { + consumptionInput.value = JSON.stringify(property.valueObj); + this.onChange(property.valueObj, property.valueObjIsValid , consumptionInput); + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.module.ts b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.module.ts new file mode 100644 index 0000000000..e37cd76716 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-consumption-editor/service-consumption-editor.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {ServiceConsumptionCreatorComponent} from "./service-consumption-editor.component"; +import {FormsModule} from "@angular/forms"; +import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module"; +import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; +import {PropertyTableModule} from 'app/ng2/components/logic/properties-table/property-table.module'; +import {TranslateModule} from 'app/ng2/shared/translator/translate.module'; + +@NgModule({ + declarations: [ + ServiceConsumptionCreatorComponent + ], + imports: [CommonModule, + FormsModule, + FormElementsModule, + UiElementsModule, + PropertyTableModule, + TranslateModule + ], + exports: [], + entryComponents: [ + ServiceConsumptionCreatorComponent + ], + providers: [] +}) +export class ServiceConsumptionCreatorModule { +}
\ No newline at end of file |