diff options
Diffstat (limited to 'catalog-ui')
8 files changed, 308 insertions, 15 deletions
diff --git a/catalog-ui/src/app/models/metadata.ts b/catalog-ui/src/app/models/metadata.ts new file mode 100644 index 0000000000..4db08006b4 --- /dev/null +++ b/catalog-ui/src/app/models/metadata.ts @@ -0,0 +1,31 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 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========================================================= + */ + +'use strict'; +import * as _ from "lodash"; + +export class Metadata { + + constructor(metaDataValues?:Metadata) { + _.forEach(metaDataValues, (metaDataValue:string, key) => { + this[key] = metaDataValue; + }); + } +} + diff --git a/catalog-ui/src/app/models/metadataEntry.ts b/catalog-ui/src/app/models/metadataEntry.ts new file mode 100644 index 0000000000..c8dddc8d30 --- /dev/null +++ b/catalog-ui/src/app/models/metadataEntry.ts @@ -0,0 +1,25 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 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========================================================= + */ + +export class MetadataEntry { + public metaDataMapKey: string; + constructor(public key: string, public value: string) { + this.metaDataMapKey = key; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts b/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts index 85c514bcbc..909f712a4e 100644 --- a/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts +++ b/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts @@ -24,6 +24,8 @@ import {PropertyFEModel} from "../../models"; import {PROPERTY_DATA} from "../../utils/constants"; import {InputBEModel} from "./input-be-model"; import {DerivedPropertyType} from "./property-be-model"; +import { Metadata } from "app/models/metadata"; +import { MetadataEntry } from "app/models/metadataEntry"; export class InputFEModel extends InputBEModel { isSimpleType: boolean; @@ -35,6 +37,10 @@ export class InputFEModel extends InputBEModel { defaultValueObjIsChanged:boolean; derivedDataType: DerivedPropertyType; requiredOrig: boolean; + metadataOrig: Metadata; + metadataIsValid:boolean; + metadataEntries: MetadataEntry[] = []; + metadataMapKeyError: string; constructor(input?: InputBEModel) { super(input); @@ -50,6 +56,14 @@ export class InputFEModel extends InputBEModel { this.updateDefaultValueObjOrig(); this.requiredOrig = this.required; + this.metadataOrig = _.cloneDeep(input.metadata); + this.metadataIsValid = true; + + if (input.metadata){ + for (let key of Object.keys(input.metadata)){ + this.metadataEntries.push(new MetadataEntry(key, input.metadata[key])); + } + } } } @@ -84,7 +98,66 @@ export class InputFEModel extends InputBEModel { return this.required !== this.requiredOrig; } + public updateMetadataKey(metadataEntry: MetadataEntry, newKey){ + if (!newKey){ + this.metadataIsValid = false; + this.metadataMapKeyError = 'Key cannot be empty.'; + metadataEntry.key = newKey; + return; + } else if (metadataEntry.metaDataMapKey != newKey && this.metadata[newKey]){ + this.metadataIsValid = false; + this.metadataMapKeyError = 'This key already exists!!.'; + metadataEntry.key = newKey; + return; + } + this.metadataIsValid = true; + this.metadataMapKeyError = null; + + if(metadataEntry.metaDataMapKey != newKey){ + this.metadata[newKey] = _.cloneDeep(this.metadata[metadataEntry.metaDataMapKey]); + delete this.metadata[metadataEntry.metaDataMapKey]; + metadataEntry.metaDataMapKey = newKey; + } + metadataEntry.key = newKey; + } + + public updateMetadataValue(metadataEntry: MetadataEntry, value: string){ + metadataEntry.value = value; + this.metadata[metadataEntry.key] = value; + } + + public addMetadataEntry(metadataEntry: MetadataEntry){ + this.metadataEntries.push(metadataEntry); + if (!this.metadata){ + this.metadata = new Metadata; + } + this.metadata[metadataEntry.key] = metadataEntry.value; + } + + public deleteMetadataEntry(metadataEntry: MetadataEntry){ + let metadataEntryIndex = this.metadataEntries.findIndex(item => item.key === metadataEntry.key); + if (metadataEntryIndex != -1){ + this.metadataEntries.splice(metadataEntryIndex, 1); + } + delete this.metadata[metadataEntry.key]; + } + + public resetMetadata = (): void => { + this.metadata = _.cloneDeep(this.metadataOrig); + this.metadataIsValid = true; + + this.metadataEntries = []; + for (let key of Object.keys(this.metadata)){ + this.metadataEntries.push(new MetadataEntry(key, this.metadata[key])); + } + } + + hasMetadataChanged(): boolean { + return !_.isEqual(this.metadata, this.metadataOrig); + } + hasChanged(): boolean { - return this.hasDefaultValueChanged() || this.hasRequiredChanged(); + return this.hasDefaultValueChanged() || this.hasRequiredChanged() || this.hasMetadataChanged(); } + }
\ No newline at end of file diff --git a/catalog-ui/src/app/models/properties-inputs/property-be-model.ts b/catalog-ui/src/app/models/properties-inputs/property-be-model.ts index 1d263bd8b0..b997ea4563 100644 --- a/catalog-ui/src/app/models/properties-inputs/property-be-model.ts +++ b/catalog-ui/src/app/models/properties-inputs/property-be-model.ts @@ -22,6 +22,7 @@ import { PROPERTY_DATA, PROPERTY_TYPES } from 'app/utils/constants'; import { SchemaProperty, SchemaPropertyGroupModel } from '../aschema-property'; import { ToscaPresentationData } from '../tosca-presentation'; import { PropertyInputDetail } from './property-input-detail'; +import { Metadata } from '../metadata'; export enum DerivedPropertyType { SIMPLE, @@ -63,6 +64,7 @@ export class PropertyBEModel { subPropertyInputPath: string; inputPath: string; toscaPresentation: ToscaPresentationData; + metadata: Metadata; constructor(property?: PropertyBEModel) { if (property) { @@ -87,6 +89,7 @@ export class PropertyBEModel { this.toscaPresentation = property.toscaPresentation; this.getPolicyValues = property.getPolicyValues; this.inputPath = property.inputPath; + this.metadata = property.metadata; } if (!this.schema || !this.schema.property) { diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html index d3db53aa43..ee090245f0 100644 --- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html +++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html @@ -32,6 +32,9 @@ <div class="table-cell col4" (click)="sort('required')" *ngIf="componentType == 'SERVICE'"> <span tooltip="Required in Runtime" tooltipDelay="400">Req. in RT</span> </div> + <div class="table-cell" [class.metadata-col-small]="!hasInputMetadata()" [class.metadata-col-wide]="hasInputMetadata()"> + <div [class.metadata-col-wide-text]="hasInputMetadata()">Metadata</div> + </div> <div class="table-cell valueCol">Value</div> </div> <div class="table-body"> @@ -65,8 +68,51 @@ (checkedChange)="onRequiredChanged(input, $event)" [disabled]="readonly"></sdc-checkbox> </div> + <!-- Metadata --> + <div class="table-cell" [class.metadata-col-small]="!hasInputMetadata()" [class.metadataCol]="hasInputMetadata()"> + <div class="inner-cell-div metadata-add" tooltip="Add key value pair"> + <a class="property-icon add-item" (click)="createNewMetadataEntry(input);" [ngClass]="{'disabled':readonly}">Add metadata</a> + </div> + <ng-container > + <div class="metadata-container"> + <ng-container *ngFor="let metadataEntry of input.metadataEntries;"> + <div class="metadata-entry"> + <ng-container> + <div class="metadata-key-value metadata-key" > + <dynamic-element #metadataViewChildren + class="value-input" + pattern="validationUtils.getValidationPattern(string)" + [value]="metadataEntry.key" + type="string" + name="{{input.name}}_{{metadataEntry.key}}" + (elementChanged)="onMetadataKeyChanged(input, $event, metadataEntry)" + [readonly]="readonly" + [testId]="'prop-key-' + propertyTestsId" + ></dynamic-element> + </div> + </ng-container> + <ng-container> + <div class="metadata-key-value"> + <dynamic-element + class="value-input" + pattern="validationUtils.getValidationPattern(string)" + [value]="metadataEntry.value" + type="string" + [name]="metadataEntry.key" + (elementChanged)="onMetadataValueChanged(input, $event, metadataEntry)" + [readonly]="readonly" + [testId]="'prop-key-' + propertyTestsId" + ></dynamic-element> + </div> + <span (click)="deleteMetadataEntry(input, metadataEntry);" class="delete-icon sprite-new delete-item-icon" [ngClass]="{'disabled':readonly}"></span> + </ng-container> + </div> + </ng-container> + </div> + </ng-container> + </div> <!-- Value --> - <div class="table-cell valueCol input-value-col" [class.inner-table-container]="input.childrenProperties || !input.isSimpleType"> + <div class="table-cell valueCol" [class.inner-table-container]="input.childrenProperties || !input.isSimpleType"> <dynamic-element class="value-input" *ngIf="checkInstanceFePropertiesMapIsFilled() && input.isSimpleType" pattern="validationUtils.getValidationPattern(input.type)" @@ -82,7 +128,6 @@ <span *ngIf="input.instanceUniqueId && !readonly" class="sprite-new delete-btn" (click)="openDeleteModal(input)" data-tests-id="delete-input-button"></span> </div> </div> - </div> </div> </div> diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less index 77c002c899..74520b6314 100644 --- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less +++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less @@ -3,6 +3,37 @@ :host /deep/ input { width:100%;} +.metadata-container { + .metadata-entry { + border-top:solid 1px #d2d2d2; + flex: 1; + display:flex; + flex-direction:row; + align-items: stretch; + + .metadata-key-value { + flex: 1; + padding:8px; + justify-content: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .metadata-key { + flex: 0 0 50%; + border-right:#d2d2d2 solid 1px; + } + + .delete-icon { + flex: 0 0 auto; + margin-right:10px; + align-self:center; + cursor:pointer; + } + } +} + .properties-table { display:flex; flex-direction:column; @@ -46,7 +77,22 @@ } .valueCol { justify-content: flex-start; + padding: 10px 8px 10px 8px; + } + + .metadata-col-small { padding: 10px; + flex: 0 0 115px; + } + .metadata-col-wide { + justify-content: flex-start; + padding: 0px; + flex: 1; + + .metadata-col-wide-text { + justify-content: flex-start; + padding: 10px; + } } } .table-header, .table-row { @@ -85,6 +131,37 @@ .selected-row { background-color:#e6f6fb; } + .table-cell.valueCol { + padding:8px; + } + .table-cell.metadataCol { + padding:0px; + .value-input { + flex: 1; + border: none; + background-color: inherit; + + &:focus, &:active { + border:none; + outline:none; + } + } + .metadata-add { + padding: 10px; + height: 40px; + } + } + + .table-cell.metadata-col-small { + flex: 0 0 115px; + padding: 0px 10px 0px 10px; + .metadata-add { + padding: 10px 0px 10px 0px; + height: 40px; + margin-right: 0px; + } + } + } .table-cell { @@ -123,13 +200,13 @@ // Column: Type &.col2 { - flex: 0 0 140px; + flex: 0 1 140px; max-width: 140px; } // Column: From Instance &.col3 { - flex: 0 0 120px; + flex: 0 1 120px; max-width: 120px; } @@ -169,12 +246,6 @@ } } } - - &.input-value-col { - padding: 8px; - } - - } .filtered { diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts index 682a3e2463..3fa7ab4a80 100644 --- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts @@ -22,12 +22,14 @@ /** * Created by rc2122 on 5/4/2017. */ -import { Component, Input, Output, EventEmitter } from "@angular/core"; +import { Component, Input, Output, EventEmitter, ViewChildren, QueryList } from "@angular/core"; import { InputFEModel } from "app/models"; import { ModalService } from "../../../services/modal.service"; import { InstanceFeDetails } from "app/models/instance-fe-details"; import { InstanceFePropertiesMap } from "../../../../models/properties-inputs/property-fe-map"; import { DataTypeService } from "../../../services/data-type.service"; +import { MetadataEntry } from "app/models/metadataEntry"; +import { DynamicElementComponent } from "../../ui/dynamic-element/dynamic-element.component"; @Component({ selector: 'inputs-table', @@ -41,10 +43,13 @@ export class InputsTableComponent { @Input() readonly: boolean; @Input() isLoading: boolean; @Input() componentType: string; + @Output() inputChanged: EventEmitter<any> = new EventEmitter<any>(); @Output() deleteInput: EventEmitter<any> = new EventEmitter<any>(); @Input() fePropertiesMap: InstanceFePropertiesMap; + + @ViewChildren('metadataViewChildren') public metadataViewChildren: QueryList<DynamicElementComponent>; sortBy: String; reverse: boolean; @@ -93,6 +98,35 @@ export class InputsTableComponent { this.inputChanged.emit(input); } + onMetadataKeyChanged = (input: InputFEModel, event, metadataEntry: MetadataEntry) => { + let dynamicElementComponent = this.metadataViewChildren.filter(element => element.name == input.name + "_" + metadataEntry.key).pop(); + + input.updateMetadataKey(metadataEntry, event.value); + this.inputChanged.emit(input); + + var mapKeyError = input.metadataMapKeyError; + if(input.metadataMapKeyError){ + dynamicElementComponent.cmpRef.instance.control.setErrors({mapKeyError}); + } + }; + + onMetadataValueChanged = (input: InputFEModel, event, metadataEntry: MetadataEntry) => { + input.updateMetadataValue(metadataEntry, event.value); + this.inputChanged.emit(input); + }; + + + createNewMetadataEntry = (input: InputFEModel): void => { + let metadataEntry = new MetadataEntry("", ""); + input.addMetadataEntry(metadataEntry); + this.inputChanged.emit(input); + } + + deleteMetadataEntry = (input: InputFEModel, metadataEntry: MetadataEntry) => { + input.deleteMetadataEntry(metadataEntry); + this.inputChanged.emit(input); + } + onDeleteInput = () => { this.deleteInput.emit(this.selectedInputToDelete); this.modalService.closeCurrentModal(); @@ -100,8 +134,6 @@ export class InputsTableComponent { openDeleteModal = (input: InputFEModel) => { console.log('exist inputs: ' + this.inputs) - - this.selectedInputToDelete = input; this.modalService.createActionModal("Delete Input", "Are you sure you want to delete this input?", "Delete", this.onDeleteInput, "Close").instance.open(); } @@ -133,6 +165,16 @@ export class InputsTableComponent { checkInstanceFePropertiesMapIsFilled(){ return _.keys(this.fePropertiesMap).length > 0 } + + hasInputMetadata(){ + for(let input of this.inputs){ + if (input.metadataEntries.length > 0){ + return true; + } + } + return false; + } + } 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 83174fa87a..e4a8749386 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 @@ -367,7 +367,9 @@ export class PropertiesAssignmentComponent { if (this.isPropertiesTabSelected) { this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid); - } else if (this.isInputsTabSelected || this.isPoliciesTabSelected) { + } else if (this.isInputsTabSelected) { + this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid && (<InputFEModel>changedItem).metadataIsValid); + } else if (this.isPoliciesTabSelected) { this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid); } this.updateHasChangedData(); @@ -846,6 +848,7 @@ export class PropertiesAssignmentComponent { handleReverseItem = (changedItem) => { changedItem = <InputFEModel>changedItem; this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue); + changedItem.resetMetadata(); changedItem.required = changedItem.requiredOrig; }; } |