diff options
Diffstat (limited to 'catalog-ui/src/app/ng2/pages')
12 files changed, 1026 insertions, 413 deletions
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 9f721d5cdd..2ae5ce8c09 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 @@ -61,13 +61,12 @@ import {UnsavedChangesComponent} from "app/ng2/components/ui/forms/unsaved-chang import {PropertyCreatorComponent} from "./property-creator/property-creator.component"; import {ModalService} from "../../services/modal.service"; import {DeclareListComponent} from "./declare-list/declare-list.component"; -import {ToscaFunctionComponent} from "./tosca-function/tosca-function.component"; +import {ToscaFunctionComponent, ToscaFunctionValidationEvent} from "./tosca-function/tosca-function.component"; import {CapabilitiesGroup, Capability} from "../../../models/capability"; import {ToscaPresentationData} from "../../../models/tosca-presentation"; import {Observable} from "rxjs"; import {TranslateService} from "../../shared/translator/translate.service"; -import {ToscaGetFunctionDtoBuilder} from '../../../models/tosca-get-function-dto'; -import {ToscaGetFunction} from "../../../models/tosca-get-function"; +import {ToscaFunction} from "../../../models/tosca-function"; const SERVICE_SELF_TITLE = "SELF"; @Component({ @@ -539,36 +538,37 @@ export class PropertiesAssignmentComponent { const modalTitle = this.translateService.translate('TOSCA_FUNCTION_MODAL_TITLE'); const modalButtons = []; let disableSaveButtonFlag = true; + const modal = this.modalService.createCustomModal(new ModalModel( + 'sm', + modalTitle, + null, + modalButtons, + null /* type */ + )); modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_SAVE'), 'blue', () => { - const toscaGetFunction: ToscaGetFunction = modal.instance.dynamicContent.instance.toscaGetFunction; - if (toscaGetFunction.functionType) { - this.updateCheckedInstancePropertyGetFunctionValue(toscaGetFunction); + const toscaGetFunction: ToscaFunction = modal.instance.dynamicContent.instance.toscaFunctionForm.value; + if (toscaGetFunction) { + this.updateCheckedInstancePropertyFunctionValue(toscaGetFunction); } else { this.clearCheckedInstancePropertyValue(); } - modal.instance.close(); + this.modalService.closeCurrentModal(); }, (): boolean => { return disableSaveButtonFlag } )); const checkedInstanceProperty = this.buildCheckedInstanceProperty(); modalButtons.push(new ButtonModel(this.translateService.translate('MODAL_CANCEL'), 'outline grey', () => { - modal.instance.close(); + this.modalService.closeCurrentModal(); })); - const modal = this.modalService.createCustomModal(new ModalModel( - 'sm', - modalTitle, - null, - modalButtons, - null /* type */ - )); + this.modalService.addDynamicContentToModalAndBindInputs(modal, ToscaFunctionComponent, { 'property': checkedInstanceProperty, 'componentInstanceMap': this.componentInstanceMap }); - modal.instance.dynamicContent.instance.onValidityChange.subscribe(isValid => { - disableSaveButtonFlag = !isValid; + modal.instance.dynamicContent.instance.onValidityChange.subscribe((validationEvent: ToscaFunctionValidationEvent) => { + disableSaveButtonFlag = !validationEvent.isValid; }); modal.instance.open(); } @@ -577,23 +577,13 @@ export class PropertiesAssignmentComponent { const checkedInstanceProperty: PropertyBEModel = this.buildCheckedInstanceProperty(); checkedInstanceProperty.getInputValues = null; checkedInstanceProperty.value = null; - checkedInstanceProperty.toscaGetFunction = null; + checkedInstanceProperty.toscaFunction = null; this.updateInstanceProperty(checkedInstanceProperty); } - private updateCheckedInstancePropertyGetFunctionValue(toscaGetFunction: ToscaGetFunction) { - const toscaGetFunctionBuilder: ToscaGetFunctionDtoBuilder = - new ToscaGetFunctionDtoBuilder() - .withPropertyUniqueId(toscaGetFunction.propertyUniqueId) - .withFunctionType(toscaGetFunction.functionType) - .withPropertySource(toscaGetFunction.propertySource) - .withPropertyName(toscaGetFunction.propertyName) - .withSourceName(toscaGetFunction.sourceName) - .withSourceUniqueId(toscaGetFunction.sourceUniqueId) - .withPropertyPathFromSource(toscaGetFunction.propertyPathFromSource); - + private updateCheckedInstancePropertyFunctionValue(toscaFunction: ToscaFunction) { const checkedProperty: PropertyBEModel = this.buildCheckedInstanceProperty(); - checkedProperty.toscaGetFunction = toscaGetFunctionBuilder.build(); + checkedProperty.toscaFunction = toscaFunction; this.updateInstanceProperty(checkedProperty); } diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.html b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.html new file mode 100644 index 0000000000..6320058d9d --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.html @@ -0,0 +1,48 @@ +<!-- + ~ - + ~ ============LICENSE_START======================================================= + ~ Copyright (C) 2022 Nordix Foundation. + ~ ================================================================================ + ~ 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. + ~ + ~ SPDX-License-Identifier: Apache-2.0 + ~ ============LICENSE_END========================================================= + --> + +<div class="component-container"> + <ng-container [formGroup]="formGroup"> + <div formArrayName="concatParameterList"> + <div *ngFor="let parameter of parameters; let idx = index"> + <div *ngIf="idx > 0" class="text-center"><span class="concat-plus-icon"></span></div> + <div class="parameter-card"> + <div class="card-content"> + <ng-container *ngIf="parameter.type === STRING_FUNCTION_TYPE"> + <input type="text" [formControlName]="idx" [value]="parameter.value"/><br/> + </ng-container> + <ng-container *ngIf="parameter.type !== STRING_FUNCTION_TYPE"> + <tosca-function [property]="propertyInputList[idx]" [componentInstanceMap]="componentInstanceMap" [allowClear]="false" + (onValidityChange)="onFunctionValidityChange($event, idx)"> + </tosca-function> + </ng-container> + <div class="buttons-container"> + <span class="delete-icon" (click)="removeParameter(idx)"></span> + </div> + </div> + </div> + </div> + </div> + </ng-container> + <div class="buttons-container"> + <a class="add-link" (click)="addStringParameter()">String Value</a> <a class="add-link" (click)="addFunction()">String Value Expression</a> + </div> +</div> diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.less b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.less new file mode 100644 index 0000000000..b9c59831ad --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.less @@ -0,0 +1,98 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +@import "../../../../../../assets/styles/mixins.less"; +@import "../../../../../../assets/styles/sprite.less"; + +.component-container { + max-height: 500px; + overflow: scroll; + padding: 0 5px; + &::-webkit-scrollbar-track { + border: 0; + } +} + +.buttons-container { + display: flex; + flex-direction: row; + justify-content: flex-end; + gap: 10px; + margin-bottom: 10px; + + .add-link { + .f-color.a(); + .f-type._14_m(); + cursor: pointer; + + &:before { + .sprite-new(); + .plus-icon(); + margin-right: 5px; + content: ""; + } + + &:hover { + .f-color.b(); + &:before { + .sprite-new(); + .plus-icon-hover(); + } + } + } + + .delete-icon { + .sprite-new(); + .delete-btn(); + cursor: pointer; + } +} + +.parameter-card { + border: 2px solid @main_color_o; + box-shadow: 0 0 0 0 rgba(0,0,0,0.2); + //padding: 10px; + border-radius: 2px; + transition: 0.3s; + margin-bottom: 5px; + &:hover { + box-shadow: 0 1px 8px 2px rgba(0,0,0,0.2); + } + + .card-content { + padding: 5px 10px; + } + + .text-center { + text-align: center; + } + + input { + border: solid 1px @main_color_o; + } +} + +.concat-plus-icon { + .sprite-new(); + background-position: -216px -1388px; + width: 14px; + height: 14px; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.spec.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.spec.ts new file mode 100644 index 0000000000..5c9af47361 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.spec.ts @@ -0,0 +1,57 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {ToscaConcatFunctionComponent} from './tosca-concat-function.component'; +import {FormsModule, ReactiveFormsModule} from "@angular/forms"; +import {ToscaFunctionComponent} from "../tosca-function.component"; +import {TranslateModule} from "../../../../shared/translator/translate.module"; +import {ToscaGetFunctionComponent} from "../tosca-get-function/tosca-get-function.component"; +import {UiElementsModule} from "../../../../components/ui/ui-elements.module"; + +describe('ToscaConcatFunctionComponent', () => { + let component: ToscaConcatFunctionComponent; + let fixture: ComponentFixture<ToscaConcatFunctionComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ToscaConcatFunctionComponent, ToscaFunctionComponent, ToscaGetFunctionComponent], + imports: [ + FormsModule, + ReactiveFormsModule, + TranslateModule, + UiElementsModule + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ToscaConcatFunctionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.ts new file mode 100644 index 0000000000..d808c284a8 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-concat-function/tosca-concat-function.component.ts @@ -0,0 +1,140 @@ +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {FormArray, FormControl, FormGroup, Validators} from "@angular/forms"; +import {ToscaConcatFunction} from "../../../../../models/tosca-concat-function"; +import {ToscaFunctionParameter} from "../../../../../models/tosca-function-parameter"; +import {ToscaStringParameter} from "../../../../../models/tosca-string-parameter"; +import {ToscaFunctionType} from "../../../../../models/tosca-function-type.enum"; +import {PropertyBEModel} from "../../../../../models/properties-inputs/property-be-model"; +import {PROPERTY_TYPES} from "../../../../../utils/constants"; +import {InstanceFeDetails} from "../../../../../models/instance-fe-details"; +import {ToscaFunctionValidationEvent} from "../tosca-function.component"; + +@Component({ + selector: 'app-tosca-concat-function', + templateUrl: './tosca-concat-function.component.html', + styleUrls: ['./tosca-concat-function.component.less'] +}) +export class ToscaConcatFunctionComponent implements OnInit { + + @Input() toscaConcatFunction: ToscaConcatFunction; + @Input() componentInstanceMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>(); + @Output() onValidFunction: EventEmitter<ToscaConcatFunction> = new EventEmitter<ToscaConcatFunction>(); + @Output() onValidityChange: EventEmitter<ToscaConcatFunctionValidationEvent> = new EventEmitter<ToscaConcatFunctionValidationEvent>(); + + concatParameterFormArray: FormArray = new FormArray([], Validators.minLength(2)); + formGroup: FormGroup = new FormGroup( + { + 'concatParameterList': this.concatParameterFormArray + } + ); + + parameters: ToscaFunctionParameter[] = []; + propertyInputList: Array<PropertyBEModel> = []; + + stringProperty: PropertyBEModel + + STRING_FUNCTION_TYPE = ToscaFunctionType.STRING + + constructor() { + this.stringProperty = new PropertyBEModel(); + this.stringProperty.type = PROPERTY_TYPES.STRING + } + + ngOnInit() { + this.initForm(); + } + + private initForm() { + this.formGroup.valueChanges.subscribe(() => { + this.onValidityChange.emit({ + isValid: this.formGroup.valid, + toscaConcatFunction: this.formGroup.valid ? this.buildConcatFunctionFromForm() : undefined + }) + if (this.formGroup.valid) { + this.onValidFunction.emit(this.buildConcatFunctionFromForm()); + } + }); + if (!this.toscaConcatFunction) { + return; + } + + if (this.toscaConcatFunction.parameters) { + this.parameters = Array.from(this.toscaConcatFunction.parameters); + for (const parameter of this.parameters) { + if (parameter.type !== PROPERTY_TYPES.STRING) { + this.propertyInputList.push(this.createStringProperty(parameter)); + this.concatParameterFormArray.push( + new FormControl(parameter, [Validators.required, Validators.minLength(1)]) + ); + } else { + this.propertyInputList.push(undefined); + this.concatParameterFormArray.push( + new FormControl(parameter.value, [Validators.required, Validators.minLength(1)]) + ); + } + } + } + } + + private buildConcatFunctionFromForm(): ToscaConcatFunction { + const toscaConcatFunction1 = new ToscaConcatFunction(); + this.concatParameterFormArray.controls.forEach(control => { + const value = control.value; + if (typeof value === 'string') { + const stringParameter = new ToscaStringParameter(); + stringParameter.value = value; + toscaConcatFunction1.parameters.push(stringParameter); + } else { + toscaConcatFunction1.parameters.push(control.value); + } + }); + + return toscaConcatFunction1; + } + + addFunction() { + this.propertyInputList.push(this.createStringProperty()); + this.parameters.push({} as ToscaFunctionParameter); + this.concatParameterFormArray.push( + new FormControl(undefined, [Validators.required, Validators.minLength(1)]) + ); + } + + addStringParameter() { + this.parameters.push({ + type: ToscaFunctionType.STRING, + value: '' + }); + this.propertyInputList.push(undefined); + this.concatParameterFormArray.push( + new FormControl('', [Validators.required, Validators.minLength(1)]) + ); + } + + removeParameter(position) { + this.propertyInputList.splice(position, 1); + this.parameters.splice(position, 1); + this.concatParameterFormArray.removeAt(position); + } + + createStringProperty(toscaFunctionParameter?: ToscaFunctionParameter) { + const property = new PropertyBEModel(); + property.type = PROPERTY_TYPES.STRING; + property.toscaFunction = toscaFunctionParameter ? toscaFunctionParameter : undefined; + property.value = toscaFunctionParameter ? toscaFunctionParameter.value : undefined; + return property; + } + + onFunctionValidityChange(event: ToscaFunctionValidationEvent, index: number) { + if (event.isValid && event.toscaFunction) { + this.concatParameterFormArray.controls[index].setValue(event.toscaFunction) + } else { + this.concatParameterFormArray.controls[index].setValue(undefined); + } + } +} + +export interface ToscaConcatFunctionValidationEvent { + isValid: boolean, + toscaConcatFunction: ToscaConcatFunction, +} diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.html b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.html index b6b313d93c..e98f688eef 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.html +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.html @@ -18,32 +18,27 @@ --> <div class="tosca-function"> - <loader [display]="isLoading" [loaderDelay]="500" [relative]="true" [size]="'large'"></loader> - <form class="w-sdc-form"> + <div class="w-sdc-form" [formGroup]="formGroup"> <div class="i-sdc-form-item"> <label class="i-sdc-form-label">{{'TOSCA_FUNCTION_LABEL' | translate}}</label> - <select [(ngModel)]="toscaGetFunction.functionType" (change)="onToscaFunctionChange()" name="toscaFunctionType"> + <select formControlName="toscaFunctionType"> <option *ngFor="let toscaFunction of toscaFunctions" [ngValue]="toscaFunction">{{toscaFunction | lowercase}}</option> </select> </div> - <div class="i-sdc-form-item" *ngIf="showPropertySourceDropdown()"> - <label class="i-sdc-form-label required">{{'TOSCA_FUNCTION_PROPERTY_SOURCE_LABEL' | translate}}</label> - <select name="propertySource" [(ngModel)]="propertySource" (change)="onPropertySourceChange()"> - <option *ngFor="let propertySource of propertySourceList" - [ngValue]="propertySource">{{propertySource}}</option> - </select> + <div *ngIf="isConcatSelected()"> + <app-tosca-concat-function [toscaConcatFunction]="toscaFunction" [componentInstanceMap]="componentInstanceMap" + (onValidityChange)="onConcatFunctionValidityChange($event)"></app-tosca-concat-function> </div> - <div *ngIf="showPropertyDropdown()" class="i-sdc-form-item"> - <label class="i-sdc-form-label required">{{dropdownValuesLabel}}</label> - <select [(ngModel)]="selectedProperty" name="selectedProperty" (change)="onPropertyChange()"> - <option *ngFor="let value of propertyDropdownList" [ngValue]="value">{{value.propertyLabel}}</option> - </select> + <div *ngIf="isGetFunctionSelected()"> + <app-tosca-get-function [property]="property" [toscaGetFunction]="toscaFunction" + [componentInstanceMap]="componentInstanceMap" + [functionType]="toscaFunctionTypeForm.value" + (onValidityChange)="onGetFunctionValidityChange($event)"></app-tosca-get-function> </div> - <div *ngIf="dropDownErrorMsg">{{dropDownErrorMsg}}</div> <div *ngIf="showClearButton()" class="button-container"> <button (click)="onClearValues()" class="tlv-btn red ng-star-inserted">{{'TOSCA_FUNCTION_CLEAR_VALUE_BUTTON' | translate}}</button> </div> - </form> + </div> <loader [display]="isLoading" [size]="'medium'" [relative]="true"></loader> </div> diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.ts index b71a61dc01..076e1182ad 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.component.ts @@ -18,20 +18,18 @@ */ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; -import {AttributeModel, ComponentMetadata, DataTypeModel, PropertyBEModel, PropertyModel} from 'app/models'; +import {ComponentMetadata, PropertyBEModel} from 'app/models'; import {TopologyTemplateService} from "../../../services/component-services/topology-template.service"; import {WorkspaceService} from "../../workspace/workspace.service"; -import {PropertiesService} from "../../../services/properties.service"; -import {PROPERTY_DATA, PROPERTY_TYPES} from "../../../../utils/constants"; -import {DataTypeService} from "../../../services/data-type.service"; import {ToscaGetFunctionType} from "../../../../models/tosca-get-function-type"; -import {TranslateService} from "../../../shared/translator/translate.service"; -import {ComponentGenericResponse} from '../../../services/responses/component-generic-response'; -import {Observable} from 'rxjs/Observable'; -import {PropertySource} from "../../../../models/property-source"; import {InstanceFeDetails} from "../../../../models/instance-fe-details"; import {ToscaGetFunction} from "../../../../models/tosca-get-function"; -import {AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn} from "@angular/forms"; +import {FormControl, FormGroup, Validators} from "@angular/forms"; +import {ToscaFunctionType} from "../../../../models/tosca-function-type.enum"; +import {ToscaGetFunctionValidationEvent} from "./tosca-get-function/tosca-get-function.component"; +import {ToscaFunction} from "../../../../models/tosca-function"; +import {ToscaConcatFunctionValidationEvent} from "./tosca-concat-function/tosca-concat-function.component"; +import {PROPERTY_TYPES} from "../../../../utils/constants"; @Component({ selector: 'tosca-function', @@ -44,418 +42,124 @@ export class ToscaFunctionComponent implements OnInit { @Input() componentInstanceMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>(); @Input() allowClear: boolean = true; @Output() onValidFunction: EventEmitter<ToscaGetFunction> = new EventEmitter<ToscaGetFunction>(); - @Output() onValidityChange: EventEmitter<boolean> = new EventEmitter<boolean>(); + @Output() onValidityChange: EventEmitter<ToscaFunctionValidationEvent> = new EventEmitter<ToscaFunctionValidationEvent>(); - toscaGetFunctionValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { - const toscaGetFunction: ToscaGetFunction = control.value; - const hasAnyValue = Object.keys(toscaGetFunction).find(key => toscaGetFunction[key]); - if (!hasAnyValue) { - return null; - } - const errors: ValidationErrors = {}; - if (!toscaGetFunction.sourceName) { - errors.sourceName = { required: true }; - } - if (!toscaGetFunction.functionType) { - errors.functionType = { required: true }; - } - if (!toscaGetFunction.sourceUniqueId) { - errors.sourceUniqueId = { required: true }; - } - if (!toscaGetFunction.sourceName) { - errors.sourceName = { required: true }; - } - if (!toscaGetFunction.propertyPathFromSource) { - errors.propertyPathFromSource = { required: true }; - } - if (!toscaGetFunction.propertyName) { - errors.propertyName = { required: true }; - } - if (!toscaGetFunction.propertySource) { - errors.propertySource = { required: true }; - } - return errors ? errors : null; - }; - - toscaGetFunctionForm: FormControl = new FormControl(new ToscaGetFunction(undefined), [this.toscaGetFunctionValidator]); + toscaFunctionForm: FormControl = new FormControl(undefined, [Validators.required]); + toscaFunctionTypeForm: FormControl = new FormControl(undefined, Validators.required); formGroup: FormGroup = new FormGroup({ - 'toscaGetFunction': this.toscaGetFunctionForm + 'toscaFunction': this.toscaFunctionForm, + 'toscaFunctionType': this.toscaFunctionTypeForm, }); - selectedProperty: PropertyDropdownValue; isLoading: boolean = false; - propertyDropdownList: Array<PropertyDropdownValue> = []; + toscaFunction: ToscaFunction; toscaFunctions: Array<string> = []; - propertySourceList: Array<string> = []; - instanceNameAndIdMap: Map<string, string> = new Map<string, string>(); - dropdownValuesLabel: string; - dropDownErrorMsg: string; - propertySource: string - toscaGetFunction: ToscaGetFunction = new ToscaGetFunction(undefined); + private isInitialized: boolean = false; private componentMetadata: ComponentMetadata; constructor(private topologyTemplateService: TopologyTemplateService, - private workspaceService: WorkspaceService, - private propertiesService: PropertiesService, - private dataTypeService: DataTypeService, - private translateService: TranslateService) { + private workspaceService: WorkspaceService) { } ngOnInit(): void { this.componentMetadata = this.workspaceService.metadata; + this.toscaFunction = this.property.toscaFunction ? this.property.toscaFunction : undefined; this.loadToscaFunctions(); - this.loadPropertySourceDropdown(); - this.initToscaGetFunction(); - } - - private initToscaGetFunction(): void { - this.toscaGetFunctionForm.valueChanges.subscribe(toscaGetFunction => { - this.onValidityChange.emit(this.toscaGetFunctionForm.valid); - if (this.toscaGetFunctionForm.valid) { - this.onValidFunction.emit(toscaGetFunction); - } - }); - if (!this.property.isToscaGetFunction()) { - return; - } - this.toscaGetFunction = new ToscaGetFunction(this.property.toscaGetFunction); - this.toscaGetFunctionForm.setValue(this.toscaGetFunction); - if (this.isGetPropertySelected() || this.isGetAttributeSelected()) { - if (this.toscaGetFunction.propertySource === PropertySource.SELF) { - this.propertySource = PropertySource.SELF; - } else { - this.propertySource = this.toscaGetFunction.sourceName; - } - } - if (this.toscaGetFunction.propertyName) { - this.loadPropertyDropdown(() => { - this.selectedProperty = this.propertyDropdownList.find(property => property.propertyName === this.toscaGetFunction.propertyName) - }); - } - } - - private loadToscaFunctions(): void { - this.toscaFunctions.push(ToscaGetFunctionType.GET_ATTRIBUTE); - this.toscaFunctions.push(ToscaGetFunctionType.GET_INPUT); - this.toscaFunctions.push(ToscaGetFunctionType.GET_PROPERTY); - } - - private loadPropertySourceDropdown(): void { - this.propertySourceList.push(PropertySource.SELF); - this.componentInstanceMap.forEach((value, key) => { - const instanceName = value.name; - this.instanceNameAndIdMap.set(instanceName, key); - if (instanceName !== PropertySource.SELF) { - this.addToPropertySource(instanceName); - } - }); - } - - private addToPropertySource(source: string): void { - this.propertySourceList.push(source); - this.propertySourceList.sort((a, b) => { - if (a === PropertySource.SELF) { - return -1; - } else if (b === PropertySource.SELF) { - return 1; - } - - return a.localeCompare(b); - }); - } - - onToscaFunctionChange(): void { - this.resetPropertySource(); - this.resetPropertyDropdown(); - if (this.isGetInputSelected()) { - this.setSelfPropertySource(); - this.loadPropertyDropdown(); - } - } - - private loadPropertyDropdown(onComplete?: () => any): void { - this.loadPropertyDropdownLabel(); - this.loadPropertyDropdownValues(onComplete); - } - - private resetForm(): void { - this.toscaGetFunction = new ToscaGetFunction(); - this.toscaGetFunctionForm.setValue(new ToscaGetFunction()); - this.propertySource = undefined; - this.selectedProperty = undefined; - } - - private resetPropertySource(): void { - this.toscaGetFunction.propertyUniqueId = undefined; - this.toscaGetFunction.propertyName = undefined; - this.toscaGetFunction.propertySource = undefined; - this.toscaGetFunction.sourceUniqueId = undefined; - this.toscaGetFunction.sourceName = undefined; - this.toscaGetFunction.propertyPathFromSource = undefined; - this.propertySource = undefined; - this.selectedProperty = undefined; - - const toscaGetFunction1 = new ToscaGetFunction(undefined); - toscaGetFunction1.functionType = this.toscaGetFunction.functionType; - this.toscaGetFunctionForm.setValue(toscaGetFunction1); - } - - private loadPropertyDropdownLabel(): void { - if (!this.toscaGetFunction.functionType) { - return; - } - if (this.isGetInputSelected()) { - this.dropdownValuesLabel = this.translateService.translate('INPUT_DROPDOWN_LABEL'); - } else if (this.isGetPropertySelected()) { - this.dropdownValuesLabel = this.translateService.translate('TOSCA_FUNCTION_PROPERTY_DROPDOWN_LABEL'); - } else if (this.isGetAttributeSelected()) { - this.dropdownValuesLabel = this.translateService.translate('TOSCA_FUNCTION_ATTRIBUTE_DROPDOWN_LABEL'); - } - } - - private loadPropertyDropdownValues(onComplete?: () => any): void { - if (!this.toscaGetFunction.functionType) { - return; - } - this.resetPropertyDropdown(); - this.fillPropertyDropdownValues(onComplete); - } - - private resetPropertyDropdown(): void { - this.dropDownErrorMsg = undefined; - this.selectedProperty = undefined; - this.propertyDropdownList = []; - } - - private fillPropertyDropdownValues(onComplete?: () => any): void { - this.startLoading(); - const propertiesObservable: Observable<ComponentGenericResponse> = this.getPropertyObservable(); - propertiesObservable.subscribe( (response: ComponentGenericResponse) => { - const properties: Array<PropertyBEModel | AttributeModel> = this.extractProperties(response); - if (!properties || properties.length === 0) { - const msgCode = this.getNotFoundMsgCode(); - this.dropDownErrorMsg = this.translateService.translate(msgCode, {type: this.propertyTypeToString()}); + this.formGroup.valueChanges.subscribe(() => { + if (!this.isInitialized) { return; } - this.addPropertiesToDropdown(properties); - if (this.propertyDropdownList.length == 0) { - const msgCode = this.getNotFoundMsgCode(); - this.dropDownErrorMsg = this.translateService.translate(msgCode, {type: this.propertyTypeToString()}); - } - }, (error) => { - console.error('An error occurred while loading properties.', error); - this.stopLoading(); - }, () => { - if (onComplete) { - onComplete(); + this.emitValidityChange(); + if (this.formGroup.valid) { + this.onValidFunction.emit(this.toscaFunctionForm.value); } - this.stopLoading(); }); + this.initToscaGetFunction(); + this.emitValidityChange(); + this.isInitialized = true; } - private getNotFoundMsgCode(): string { - if (this.isGetInputSelected()) { - return 'TOSCA_FUNCTION_NO_INPUT_FOUND'; - } - if (this.isGetAttributeSelected()) { - return 'TOSCA_FUNCTION_NO_ATTRIBUTE_FOUND'; - } - if (this.isGetPropertySelected()) { - return 'TOSCA_FUNCTION_NO_PROPERTY_FOUND'; - } - - return undefined; - } - - private propertyTypeToString() { - if (this.property.schemaType) { - return `${this.property.type} of ${this.property.schemaType}`; - } - return this.property.type; - } - - private extractProperties(componentGenericResponse: ComponentGenericResponse): Array<PropertyBEModel | AttributeModel> { - if (this.isGetInputSelected()) { - return componentGenericResponse.inputs; - } - if (this.isGetPropertySelected()) { - if (this.propertySource === PropertySource.SELF) { - return componentGenericResponse.properties; - } - const componentInstanceProperties: PropertyModel[] = componentGenericResponse.componentInstancesProperties[this.instanceNameAndIdMap.get(this.propertySource)]; - return this.removeSelectedProperty(componentInstanceProperties); - } - if (this.propertySource === PropertySource.SELF) { - return componentGenericResponse.attributes; - } - return componentGenericResponse.componentInstancesAttributes[this.instanceNameAndIdMap.get(this.propertySource)]; - } - - private getPropertyObservable(): Observable<ComponentGenericResponse> { - if (this.isGetInputSelected()) { - return this.topologyTemplateService.getComponentInputsValues(this.componentMetadata.componentType, this.componentMetadata.uniqueId); - } - if (this.isGetPropertySelected()) { - if (this.propertySource === PropertySource.SELF) { - return this.topologyTemplateService.findAllComponentProperties(this.componentMetadata.componentType, this.componentMetadata.uniqueId); - } - return this.topologyTemplateService.getComponentInstanceProperties(this.componentMetadata.componentType, this.componentMetadata.uniqueId); - } - if (this.isGetAttributeSelected()) { - if (this.propertySource === PropertySource.SELF) { - return this.topologyTemplateService.findAllComponentAttributes(this.componentMetadata.componentType, this.componentMetadata.uniqueId); - } - return this.topologyTemplateService.findAllComponentInstanceAttributes(this.componentMetadata.componentType, this.componentMetadata.uniqueId); - } - } - - private removeSelectedProperty(componentInstanceProperties: PropertyModel[]): PropertyModel[] { - if (!componentInstanceProperties) { - return []; - } - return componentInstanceProperties.filter(property => - (property.uniqueId !== this.property.uniqueId) || - (property.uniqueId === this.property.uniqueId && property.resourceInstanceUniqueId !== this.property.parentUniqueId) - ); - } - - private addPropertyToDropdown(propertyDropdownValue: PropertyDropdownValue): void { - this.propertyDropdownList.push(propertyDropdownValue); - this.propertyDropdownList.sort((a, b) => a.propertyLabel.localeCompare(b.propertyLabel)); + private validate() { + return (!this.toscaFunctionForm.value && !this.toscaFunctionTypeForm.value) || this.formGroup.valid; } - private addPropertiesToDropdown(properties: Array<PropertyBEModel | AttributeModel>): void { - for (const property of properties) { - if (this.hasSameType(property)) { - this.addPropertyToDropdown({ - propertyName: property.name, - propertyId: property.uniqueId, - propertyLabel: property.name, - propertyPath: [property.name] - }); - } else if (this.isComplexType(property.type)) { - this.fillPropertyDropdownWithMatchingChildProperties(property); - } - } - } - - private fillPropertyDropdownWithMatchingChildProperties(inputProperty: PropertyBEModel | AttributeModel, - parentPropertyList: Array<PropertyBEModel | AttributeModel> = []): void { - const dataTypeFound: DataTypeModel = this.dataTypeService.getDataTypeByModelAndTypeName(this.componentMetadata.model, inputProperty.type); - if (!dataTypeFound || !dataTypeFound.properties) { + private initToscaGetFunction() { + if (!this.property.isToscaGetFunction()) { return; } - parentPropertyList.push(inputProperty); - dataTypeFound.properties.forEach(dataTypeProperty => { - if (this.hasSameType(dataTypeProperty)) { - this.addPropertyToDropdown({ - propertyName: dataTypeProperty.name, - propertyId: parentPropertyList[0].uniqueId, - propertyLabel: parentPropertyList.map(property => property.name).join('->') + '->' + dataTypeProperty.name, - propertyPath: [...parentPropertyList.map(property => property.name), dataTypeProperty.name] - }); - } else if (this.isComplexType(dataTypeProperty.type)) { - this.fillPropertyDropdownWithMatchingChildProperties(dataTypeProperty, [...parentPropertyList]) - } - }); + this.toscaFunctionForm.setValue(this.property.toscaFunction); + this.toscaFunctionTypeForm.setValue(this.property.toscaFunction.type); } - private hasSameType(property: PropertyBEModel | AttributeModel) { - if (this.typeHasSchema(this.property.type)) { - if (!property.schema || !property.schema.property) { - return false; - } - return property.type === this.property.type && this.property.schema.property.type === property.schema.property.type; + private loadToscaFunctions(): void { + this.toscaFunctions.push(ToscaFunctionType.GET_ATTRIBUTE); + this.toscaFunctions.push(ToscaFunctionType.GET_INPUT); + this.toscaFunctions.push(ToscaFunctionType.GET_PROPERTY); + if (this.property.type === PROPERTY_TYPES.STRING) { + this.toscaFunctions.push(ToscaFunctionType.CONCAT); } + } - return property.type === this.property.type; + private resetForm(): void { + this.formGroup.reset(); + this.toscaFunction = undefined; } private isGetPropertySelected(): boolean { - return this.toscaGetFunction.functionType === ToscaGetFunctionType.GET_PROPERTY; + return this.formGroup.get('toscaFunctionType').value === ToscaGetFunctionType.GET_PROPERTY; } private isGetAttributeSelected(): boolean { - return this.toscaGetFunction.functionType === ToscaGetFunctionType.GET_ATTRIBUTE; + return this.formGroup.get('toscaFunctionType').value === ToscaGetFunctionType.GET_ATTRIBUTE; } private isGetInputSelected(): boolean { - return this.toscaGetFunction.functionType === ToscaGetFunctionType.GET_INPUT; + return this.formGroup.get('toscaFunctionType').value === ToscaGetFunctionType.GET_INPUT; } - private isComplexType(propertyType: string): boolean { - return PROPERTY_DATA.SIMPLE_TYPES.indexOf(propertyType) === -1; + isConcatSelected(): boolean { + return this.formGroup.get('toscaFunctionType').value === ToscaFunctionType.CONCAT; } - private typeHasSchema(propertyType: string): boolean { - return PROPERTY_TYPES.MAP === propertyType || PROPERTY_TYPES.LIST === propertyType; + isGetFunctionSelected(): boolean { + return this.isGetInputSelected() || this.isGetPropertySelected() || this.isGetAttributeSelected(); } - private stopLoading(): void { - this.isLoading = false; + onClearValues() { + this.resetForm(); } - private startLoading(): void { - this.isLoading = true; + showClearButton(): boolean { + return this.allowClear && this.toscaFunctionTypeForm.value; } - showPropertyDropdown(): boolean { - if (this.isGetPropertySelected() || this.isGetAttributeSelected()) { - return this.toscaGetFunction.propertySource && !this.isLoading && !this.dropDownErrorMsg; + onConcatFunctionValidityChange(validationEvent: ToscaConcatFunctionValidationEvent) { + if (validationEvent.isValid) { + this.toscaFunctionForm.setValue(validationEvent.toscaConcatFunction); + } else { + this.toscaFunctionForm.setValue(undefined); } - - return this.toscaGetFunction.functionType && !this.isLoading && !this.dropDownErrorMsg; } - onPropertySourceChange(): void { - if (!this.toscaGetFunction.functionType || !this.propertySource) { - return; - } - this.toscaGetFunction.propertyUniqueId = undefined; - this.toscaGetFunction.propertyName = undefined; - this.toscaGetFunction.propertyPathFromSource = undefined; - if (this.propertySource === PropertySource.SELF) { - this.setSelfPropertySource(); + onGetFunctionValidityChange(validationEvent: ToscaGetFunctionValidationEvent) { + if (validationEvent.isValid) { + this.toscaFunctionForm.setValue(validationEvent.toscaGetFunction); } else { - this.toscaGetFunction.propertySource = PropertySource.INSTANCE; - this.toscaGetFunction.sourceName = this.propertySource; - this.toscaGetFunction.sourceUniqueId = this.instanceNameAndIdMap.get(this.propertySource); + this.toscaFunctionForm.setValue(undefined); } - this.toscaGetFunctionForm.setValue(this.toscaGetFunction); - this.loadPropertyDropdown(); - } - - private setSelfPropertySource(): void { - this.toscaGetFunction.propertySource = PropertySource.SELF; - this.toscaGetFunction.sourceName = this.componentMetadata.name; - this.toscaGetFunction.sourceUniqueId = this.componentMetadata.uniqueId; - this.toscaGetFunctionForm.setValue(this.toscaGetFunction); - } - - onPropertyChange(): void { - this.toscaGetFunction.propertyUniqueId = this.selectedProperty.propertyId; - this.toscaGetFunction.propertyName = this.selectedProperty.propertyName; - this.toscaGetFunction.propertyPathFromSource = this.selectedProperty.propertyPath; - this.toscaGetFunctionForm.setValue(this.toscaGetFunction); - } - - onClearValues() { - this.resetForm(); } - showClearButton(): boolean { - return this.allowClear && this.toscaGetFunction.functionType !== undefined; - } - - showPropertySourceDropdown(): boolean { - return this.isGetPropertySelected() || this.isGetAttributeSelected(); + private emitValidityChange() { + const isValid = this.validate(); + this.onValidityChange.emit({ + isValid: isValid, + toscaFunction: isValid ? this.toscaFunctionForm.value : undefined + }); } } -export interface PropertyDropdownValue { - propertyName: string; - propertyId: string; - propertyLabel: string; - propertyPath: Array<string>; -} +export class ToscaFunctionValidationEvent { + isValid: boolean; + toscaFunction: ToscaFunction; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.module.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.module.ts index efe45c3d27..2db76cf544 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.module.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-function.module.ts @@ -19,20 +19,25 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import { FormElementsModule } from 'app/ng2/components/ui/form-components/form-elements.module'; import { UiElementsModule } from 'app/ng2/components/ui/ui-elements.module'; import { TranslateModule } from '../../../shared/translator/translate.module'; import { ToscaFunctionComponent } from './tosca-function.component'; import { SdcUiComponentsModule } from 'onap-ui-angular'; +import { ToscaGetFunctionComponent } from './tosca-get-function/tosca-get-function.component'; +import { ToscaConcatFunctionComponent } from './tosca-concat-function/tosca-concat-function.component'; @NgModule({ declarations: [ ToscaFunctionComponent, + ToscaGetFunctionComponent, + ToscaConcatFunctionComponent, ], imports: [ CommonModule, FormsModule, + ReactiveFormsModule, FormElementsModule, UiElementsModule, TranslateModule, diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.html b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.html new file mode 100644 index 0000000000..6f19d5eff4 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.html @@ -0,0 +1,38 @@ +<!-- + ~ ============LICENSE_START======================================================= + ~ Copyright (C) 2021 Nordix Foundation + ~ ================================================================================ + ~ 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. + ~ + ~ SPDX-License-Identifier: Apache-2.0 + ~ ============LICENSE_END========================================================= + --> + +<div class="tosca-function"> + <form class="w-sdc-form" [formGroup]="formGroup"> + <div class="i-sdc-form-item" *ngIf="showPropertySourceDropdown()"> + <label class="i-sdc-form-label required">{{'TOSCA_FUNCTION_PROPERTY_SOURCE_LABEL' | translate}}</label> + <select formControlName="propertySource" (change)="onPropertySourceChange()"> + <option *ngFor="let propertySource of propertySourceList" + [ngValue]="propertySource">{{propertySource}}</option> + </select> + </div> + <div *ngIf="showPropertyDropdown()" class="i-sdc-form-item"> + <label class="i-sdc-form-label required">{{dropdownValuesLabel}}</label> + <select formControlName="selectedProperty"> + <option *ngFor="let value of propertyDropdownList" [ngValue]="value">{{value.propertyLabel}}</option> + </select> + </div> + <div *ngIf="dropDownErrorMsg">{{dropDownErrorMsg}}</div> + </form> + <loader [display]="isLoading" [size]="'medium'" [relative]="true"></loader> +</div> diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.less b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.less new file mode 100644 index 0000000000..b14edc25e9 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.less @@ -0,0 +1,21 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.spec.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.spec.ts new file mode 100644 index 0000000000..6c7d986150 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.spec.ts @@ -0,0 +1,74 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2022 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {ToscaGetFunctionComponent} from './tosca-get-function.component'; +import {FormsModule, ReactiveFormsModule} from "@angular/forms"; +import {TranslateModule} from "../../../../shared/translator/translate.module"; +import {UiElementsModule} from "../../../../components/ui/ui-elements.module"; +import {TopologyTemplateService} from "../../../../services/component-services/topology-template.service"; +import {WorkspaceService} from "../../../workspace/workspace.service"; +import {PropertiesService} from "../../../../services/properties.service"; +import {DataTypeService} from "../../../../services/data-type.service"; +import {TranslateService} from "../../../../shared/translator/translate.service"; +import {ComponentMetadata} from "../../../../../models/component-metadata"; + +describe('ToscaGetFunctionComponent', () => { + let component: ToscaGetFunctionComponent; + let fixture: ComponentFixture<ToscaGetFunctionComponent>; + let topologyTemplateServiceMock: Partial<TopologyTemplateService>; + let workspaceServiceMock: Partial<WorkspaceService> = { + metadata: new ComponentMetadata() + }; + let propertiesServiceMock: Partial<PropertiesService>; + let dataTypeServiceMock: Partial<DataTypeService>; + let translateServiceMock: Partial<TranslateService>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ToscaGetFunctionComponent], + imports: [ + FormsModule, + ReactiveFormsModule, + TranslateModule, + UiElementsModule + ], + providers: [ + {provide: TopologyTemplateService, useValue: topologyTemplateServiceMock}, + {provide: WorkspaceService, useValue: workspaceServiceMock}, + {provide: PropertiesService, useValue: propertiesServiceMock}, + {provide: DataTypeService, useValue: dataTypeServiceMock}, + {provide: TranslateService, useValue: translateServiceMock} + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ToscaGetFunctionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.ts new file mode 100644 index 0000000000..8f50cc14cd --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function/tosca-get-function.component.ts @@ -0,0 +1,443 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +import {Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges} from '@angular/core'; +import {AttributeModel, ComponentMetadata, DataTypeModel, PropertyBEModel, PropertyModel} from 'app/models'; +import {TopologyTemplateService} from "../../../../services/component-services/topology-template.service"; +import {WorkspaceService} from "../../../workspace/workspace.service"; +import {PropertiesService} from "../../../../services/properties.service"; +import {PROPERTY_DATA, PROPERTY_TYPES} from "../../../../../utils/constants"; +import {DataTypeService} from "../../../../services/data-type.service"; +import {ToscaGetFunctionType} from "../../../../../models/tosca-get-function-type"; +import {TranslateService} from "../../../../shared/translator/translate.service"; +import {ComponentGenericResponse} from '../../../../services/responses/component-generic-response'; +import {Observable} from 'rxjs/Observable'; +import {PropertySource} from "../../../../../models/property-source"; +import {InstanceFeDetails} from "../../../../../models/instance-fe-details"; +import {ToscaGetFunction} from "../../../../../models/tosca-get-function"; +import {FormControl, FormGroup, Validators} from "@angular/forms"; +import {ToscaGetFunctionTypeConverter} from "../../../../../models/tosca-get-function-type-converter"; + +@Component({ + selector: 'app-tosca-get-function', + templateUrl: './tosca-get-function.component.html', + styleUrls: ['./tosca-get-function.component.less'] +}) +export class ToscaGetFunctionComponent implements OnInit, OnChanges { + + @Input() property: PropertyBEModel; + @Input() toscaGetFunction: ToscaGetFunction; + @Input() componentInstanceMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>(); + @Input() functionType: ToscaGetFunctionType; + @Output() onValidFunction: EventEmitter<ToscaGetFunction> = new EventEmitter<ToscaGetFunction>(); + @Output() onValidityChange: EventEmitter<ToscaGetFunctionValidationEvent> = new EventEmitter<ToscaGetFunctionValidationEvent>(); + + formGroup: FormGroup = new FormGroup({ + 'selectedProperty': new FormControl(undefined, Validators.required), + 'propertySource': new FormControl(undefined, Validators.required) + }); + + isLoading: boolean = false; + propertyDropdownList: Array<PropertyDropdownValue> = []; + propertySourceList: Array<string> = []; + instanceNameAndIdMap: Map<string, string> = new Map<string, string>(); + dropdownValuesLabel: string; + dropDownErrorMsg: string; + + private isInitialized: boolean = false; + private componentMetadata: ComponentMetadata; + + constructor(private topologyTemplateService: TopologyTemplateService, + private workspaceService: WorkspaceService, + private propertiesService: PropertiesService, + private dataTypeService: DataTypeService, + private translateService: TranslateService) { + } + + ngOnInit(): void { + this.componentMetadata = this.workspaceService.metadata; + this.formGroup.valueChanges.subscribe(() => { + if (!this.isInitialized) { + return; + } + this.onValidityChange.emit({ + isValid: this.formGroup.valid, + toscaGetFunction: this.formGroup.valid ? this.buildGetFunctionFromForm() : undefined + }); + if (this.formGroup.valid) { + this.onValidFunction.emit(this.buildGetFunctionFromForm()); + } + }); + this.loadPropertySourceDropdown(); + this.loadPropertyDropdownLabel(); + this.initToscaGetFunction().subscribe(() => { + this.isInitialized = true; + }); + + } + + ngOnChanges(_changes: SimpleChanges): void { + if (!this.isInitialized) { + return; + } + this.isInitialized = false; + this.resetForm(); + this.loadPropertySourceDropdown(); + this.loadPropertyDropdownLabel(); + this.initToscaGetFunction().subscribe(() => { + this.isInitialized = true; + }); + } + + private initToscaGetFunction(): Observable<void> { + return new Observable(subscriber => { + if (!this.toscaGetFunction) { + if (this.isGetInput()) { + this.setSelfPropertySource(); + this.loadPropertyDropdown(); + } + subscriber.next(); + return; + } + if (this.toscaGetFunction.propertySource == PropertySource.SELF) { + this.propertySource.setValue(PropertySource.SELF); + } else if (this.toscaGetFunction.propertySource == PropertySource.INSTANCE) { + this.propertySource + .setValue(this.propertySourceList.find(source => this.toscaGetFunction.sourceName === source)); + } + if (this.propertySource.valid) { + this.loadPropertyDropdown(() => { + this.selectedProperty + .setValue(this.propertyDropdownList.find(property => property.propertyName === this.toscaGetFunction.propertyName)); + subscriber.next(); + }); + } else { + subscriber.next(); + } + }); + } + + private buildGetFunctionFromForm() { + const toscaGetFunction = new ToscaGetFunction(); + toscaGetFunction.type = ToscaGetFunctionTypeConverter.convertToToscaFunctionType(this.functionType); + toscaGetFunction.functionType = this.functionType; + const propertySource = this.propertySource.value; + if (this.isPropertySourceSelf()) { + toscaGetFunction.propertySource = propertySource + toscaGetFunction.sourceName = this.componentMetadata.name; + toscaGetFunction.sourceUniqueId = this.componentMetadata.uniqueId; + } else { + toscaGetFunction.propertySource = PropertySource.INSTANCE; + toscaGetFunction.sourceName = propertySource; + toscaGetFunction.sourceUniqueId = this.instanceNameAndIdMap.get(propertySource); + } + + const selectedProperty: PropertyDropdownValue = this.selectedProperty.value; + toscaGetFunction.propertyUniqueId = selectedProperty.propertyId; + toscaGetFunction.propertyName = selectedProperty.propertyName; + toscaGetFunction.propertyPathFromSource = selectedProperty.propertyPath; + + return toscaGetFunction; + } + + private loadPropertySourceDropdown(): void { + if (this.isGetInput()) { + return; + } + this.propertySourceList = []; + this.propertySourceList.push(PropertySource.SELF); + this.componentInstanceMap.forEach((value, key) => { + const instanceName = value.name; + this.instanceNameAndIdMap.set(instanceName, key); + if (instanceName !== PropertySource.SELF) { + this.addToPropertySource(instanceName); + } + }); + } + + private addToPropertySource(source: string): void { + this.propertySourceList.push(source); + this.propertySourceList.sort((a, b) => { + if (a === PropertySource.SELF) { + return -1; + } else if (b === PropertySource.SELF) { + return 1; + } + + return a.localeCompare(b); + }); + } + + private loadPropertyDropdown(onComplete?: () => any): void { + this.loadPropertyDropdownLabel(); + this.loadPropertyDropdownValues(onComplete); + } + + private resetForm(): void { + this.formGroup.reset(); + } + + private loadPropertyDropdownLabel(): void { + if (!this.functionType) { + return; + } + if (this.isGetInput()) { + this.dropdownValuesLabel = this.translateService.translate('INPUT_DROPDOWN_LABEL'); + } else if (this.isGetProperty()) { + this.dropdownValuesLabel = this.translateService.translate('TOSCA_FUNCTION_PROPERTY_DROPDOWN_LABEL'); + } else if (this.isGetAttribute()) { + this.dropdownValuesLabel = this.translateService.translate('TOSCA_FUNCTION_ATTRIBUTE_DROPDOWN_LABEL'); + } + } + + private loadPropertyDropdownValues(onComplete?: () => any): void { + if (!this.functionType) { + return; + } + this.resetPropertyDropdown(); + this.fillPropertyDropdownValues(onComplete); + } + + private resetPropertyDropdown(): void { + this.dropDownErrorMsg = undefined; + this.selectedProperty.reset(); + this.propertyDropdownList = []; + } + + private fillPropertyDropdownValues(onComplete?: () => any): void { + this.startLoading(); + const propertiesObservable: Observable<ComponentGenericResponse> = this.getPropertyObservable(); + propertiesObservable.subscribe( (response: ComponentGenericResponse) => { + const properties: Array<PropertyBEModel | AttributeModel> = this.extractProperties(response); + if (!properties || properties.length === 0) { + const msgCode = this.getNotFoundMsgCode(); + this.dropDownErrorMsg = this.translateService.translate(msgCode, {type: this.propertyTypeToString()}); + return; + } + this.addPropertiesToDropdown(properties); + if (this.propertyDropdownList.length == 0) { + const msgCode = this.getNotFoundMsgCode(); + this.dropDownErrorMsg = this.translateService.translate(msgCode, {type: this.propertyTypeToString()}); + } + }, (error) => { + console.error('An error occurred while loading properties.', error); + this.stopLoading(); + }, () => { + if (onComplete) { + onComplete(); + } + this.stopLoading(); + }); + } + + private getNotFoundMsgCode(): string { + if (this.isGetInput()) { + return 'TOSCA_FUNCTION_NO_INPUT_FOUND'; + } + if (this.isGetAttribute()) { + return 'TOSCA_FUNCTION_NO_ATTRIBUTE_FOUND'; + } + if (this.isGetProperty()) { + return 'TOSCA_FUNCTION_NO_PROPERTY_FOUND'; + } + + return undefined; + } + + private propertyTypeToString() { + if (this.property.schemaType) { + return `${this.property.type} of ${this.property.schemaType}`; + } + return this.property.type; + } + + private extractProperties(componentGenericResponse: ComponentGenericResponse): Array<PropertyBEModel | AttributeModel> { + if (this.isGetInput()) { + return componentGenericResponse.inputs; + } + const propertySource = this.propertySource.value; + if (this.isGetProperty()) { + if (this.isPropertySourceSelf()) { + return componentGenericResponse.properties; + } + const componentInstanceProperties: PropertyModel[] = componentGenericResponse.componentInstancesProperties[this.instanceNameAndIdMap.get(propertySource)]; + return this.removeSelectedProperty(componentInstanceProperties); + } + if (this.isPropertySourceSelf()) { + return componentGenericResponse.attributes; + } + return componentGenericResponse.componentInstancesAttributes[this.instanceNameAndIdMap.get(propertySource)]; + } + + private isPropertySourceSelf() { + return this.propertySource.value === PropertySource.SELF; + } + + private getPropertyObservable(): Observable<ComponentGenericResponse> { + if (this.isGetInput()) { + return this.topologyTemplateService.getComponentInputsValues(this.componentMetadata.componentType, this.componentMetadata.uniqueId); + } + if (this.isGetProperty()) { + if (this.isPropertySourceSelf()) { + return this.topologyTemplateService.findAllComponentProperties(this.componentMetadata.componentType, this.componentMetadata.uniqueId); + } + return this.topologyTemplateService.getComponentInstanceProperties(this.componentMetadata.componentType, this.componentMetadata.uniqueId); + } + if (this.isGetAttribute()) { + if (this.isPropertySourceSelf()) { + return this.topologyTemplateService.findAllComponentAttributes(this.componentMetadata.componentType, this.componentMetadata.uniqueId); + } + return this.topologyTemplateService.findAllComponentInstanceAttributes(this.componentMetadata.componentType, this.componentMetadata.uniqueId); + } + } + + private removeSelectedProperty(componentInstanceProperties: PropertyModel[]): PropertyModel[] { + if (!componentInstanceProperties) { + return []; + } + return componentInstanceProperties.filter(property => + (property.uniqueId !== this.property.uniqueId) || + (property.uniqueId === this.property.uniqueId && property.resourceInstanceUniqueId !== this.property.parentUniqueId) + ); + } + + private addPropertyToDropdown(propertyDropdownValue: PropertyDropdownValue): void { + this.propertyDropdownList.push(propertyDropdownValue); + this.propertyDropdownList.sort((a, b) => a.propertyLabel.localeCompare(b.propertyLabel)); + } + + private addPropertiesToDropdown(properties: Array<PropertyBEModel | AttributeModel>): void { + for (const property of properties) { + if (this.hasSameType(property)) { + this.addPropertyToDropdown({ + propertyName: property.name, + propertyId: property.uniqueId, + propertyLabel: property.name, + propertyPath: [property.name] + }); + } else if (this.isComplexType(property.type)) { + this.fillPropertyDropdownWithMatchingChildProperties(property); + } + } + } + + private fillPropertyDropdownWithMatchingChildProperties(inputProperty: PropertyBEModel | AttributeModel, + parentPropertyList: Array<PropertyBEModel | AttributeModel> = []): void { + const dataTypeFound: DataTypeModel = this.dataTypeService.getDataTypeByModelAndTypeName(this.componentMetadata.model, inputProperty.type); + if (!dataTypeFound || !dataTypeFound.properties) { + return; + } + parentPropertyList.push(inputProperty); + dataTypeFound.properties.forEach(dataTypeProperty => { + if (this.hasSameType(dataTypeProperty)) { + this.addPropertyToDropdown({ + propertyName: dataTypeProperty.name, + propertyId: parentPropertyList[0].uniqueId, + propertyLabel: parentPropertyList.map(property => property.name).join('->') + '->' + dataTypeProperty.name, + propertyPath: [...parentPropertyList.map(property => property.name), dataTypeProperty.name] + }); + } else if (this.isComplexType(dataTypeProperty.type)) { + this.fillPropertyDropdownWithMatchingChildProperties(dataTypeProperty, [...parentPropertyList]) + } + }); + } + + private hasSameType(property: PropertyBEModel | AttributeModel) { + if (this.typeHasSchema(this.property.type)) { + if (!property.schema || !property.schema.property) { + return false; + } + return property.type === this.property.type && this.property.schema.property.type === property.schema.property.type; + } + + return property.type === this.property.type; + } + + private isGetProperty(): boolean { + return this.functionType === ToscaGetFunctionType.GET_PROPERTY; + } + + private isGetAttribute(): boolean { + return this.functionType === ToscaGetFunctionType.GET_ATTRIBUTE; + } + + private isGetInput(): boolean { + return this.functionType === ToscaGetFunctionType.GET_INPUT; + } + + private isComplexType(propertyType: string): boolean { + return PROPERTY_DATA.SIMPLE_TYPES.indexOf(propertyType) === -1; + } + + private typeHasSchema(propertyType: string): boolean { + return PROPERTY_TYPES.MAP === propertyType || PROPERTY_TYPES.LIST === propertyType; + } + + private stopLoading(): void { + this.isLoading = false; + } + + private startLoading(): void { + this.isLoading = true; + } + + showPropertyDropdown(): boolean { + if (this.isGetProperty() || this.isGetAttribute()) { + return this.propertySource.valid && !this.isLoading && !this.dropDownErrorMsg; + } + + return this.functionType && !this.isLoading && !this.dropDownErrorMsg; + } + + onPropertySourceChange(): void { + if (!this.functionType || !this.propertySource.valid) { + return; + } + this.loadPropertyDropdown(); + } + + showPropertySourceDropdown(): boolean { + return this.isGetProperty() || this.isGetAttribute(); + } + + private setSelfPropertySource(): void { + this.propertySource.setValue(PropertySource.SELF); + } + + private get propertySource(): FormControl { + return this.formGroup.get('propertySource') as FormControl; + } + + private get selectedProperty(): FormControl { + return this.formGroup.get('selectedProperty') as FormControl; + } + +} + +export interface PropertyDropdownValue { + propertyName: string; + propertyId: string; + propertyLabel: string; + propertyPath: Array<string>; +} + +export interface ToscaGetFunctionValidationEvent { + isValid: boolean, + toscaGetFunction: ToscaGetFunction, +}
\ No newline at end of file |