diff options
Diffstat (limited to 'catalog-ui/src/app/ng2/pages/properties-assignment/tosca-function/tosca-get-function')
4 files changed, 576 insertions, 0 deletions
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 |