UI support for metadata when editing properties of VFCs 38/135338/5
authorJvD_Ericsson <jeff.van.dam@est.tech>
Thu, 6 Jul 2023 12:17:48 +0000 (13:17 +0100)
committerMichael Morris <michael.morris@est.tech>
Thu, 13 Jul 2023 15:05:06 +0000 (15:05 +0000)
Issue-ID: SDC-4561
Signed-off-by: JvD_Ericsson <jeff.van.dam@est.tech>
Change-Id: I642fa4ce09fe669e5722dbbf18dd23daa23761d3

catalog-ui/src/app/models/properties.ts
catalog-ui/src/app/modules/directive-module.ts
catalog-ui/src/app/ng2/app.module.ts
catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.less [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.spec.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.module.ts [new file with mode: 0644]
catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts
catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view.html

index cff1324..83cc062 100644 (file)
@@ -136,6 +136,7 @@ export class PropertyModel extends PropertyBEModel implements IPropertyModel {
             "defaultValue": this.defaultValue != "" && this.defaultValue != "[]" && this.defaultValue != "{}" ? this.defaultValue : null,
             "description": this.description,
             "constraints": this.constraints,
+            "metadata": this.metadata,
             "propertyConstraints": this.propertyConstraints,
             "isPassword": this.password || false,
             "schema": this.schema,
index 8907f5a..2cf3832 100644 (file)
@@ -97,6 +97,7 @@ import {AttributesOutputsComponent} from "../ng2/pages/attributes-outputs/attrib
 import {InterfaceDefinitionComponent} from "../ng2/pages/interface-definition/interface-definition.page.component";
 import {ToscaFunctionComponent} from '../ng2/pages/properties-assignment/tosca-function/tosca-function.component';
 import {ConstraintsComponent} from '../ng2/pages/properties-assignment/constraints/constraints.component';
+import {PropertyMetadataComponent} from '../ng2/pages/properties-assignment/property-metadata/property-metadata.component';
 import {TypeWorkspaceComponent} from "../ng2/pages/type-workspace/type-workspace.component";
 import {TypeWorkspaceGeneralComponent} from "../ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component";
 import {DeclareInputComponent} from "../ng2/pages/properties-assignment/declare-input/declare-input.component";
@@ -332,6 +333,12 @@ directiveModule.directive('appConstraints', downgradeComponent({
   outputs: ['onConstraintChange']
 }) as angular.IDirectiveFactory);
 
+directiveModule.directive('appPropertyMetadata', downgradeComponent({
+  component: PropertyMetadataComponent,
+  inputs: ['propertyMetadata', 'isViewOnly'],
+  outputs: ['onPropertyMetadataChange']
+}) as angular.IDirectiveFactory);
+
 directiveModule.directive('appTypeWorkspace', downgradeComponent({
   component: TypeWorkspaceComponent,
   inputs: [],
index 0c5b10f..ffb894f 100644 (file)
@@ -101,6 +101,7 @@ import {PropertyCreatorModule} from './pages/properties-assignment/property-crea
 import {DeclareListModule} from './pages/properties-assignment/declare-list/declare-list.module';
 import {ToscaFunctionModule} from "./pages/properties-assignment/tosca-function/tosca-function.module";
 import {ConstraintsModule} from "./pages/properties-assignment/constraints/constraints.module";
+import {PropertyMetadataModule} from "./pages/properties-assignment/property-metadata/property-metadata.module";
 import {WorkflowServiceNg2} from './services/workflow.service';
 import {ToscaTypesServiceNg2} from "./services/tosca-types.service";
 import {InterfaceOperationHandlerModule} from "./pages/composition/interface-operatons/operation-creator/interface-operation-handler.module";
@@ -163,6 +164,7 @@ export function configServiceFactory(config: ConfigService, authService: Authent
     DeclareListModule,
     ToscaFunctionModule,
     ConstraintsModule,
+    PropertyMetadataModule,
     PluginFrameModule,
     PluginsModule,
     InterfaceOperationModule,
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.html b/catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.html
new file mode 100644 (file)
index 0000000..420d7eb
--- /dev/null
@@ -0,0 +1,64 @@
+<!--
+  ~ -
+  ~  ============LICENSE_START=======================================================
+  ~  Copyright (C) 2023 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="app-property-metadata">
+  <form novalidate class="w-sdc-form two-columns" [formGroup]="metadataForm">
+    <div formArrayName="metadataFormList">
+      <div *ngFor="let metadata of propertyMetadataArray; let idx = index">
+        <div class="w-sdc-form-columns-wrapper">
+            <div class="w-sdc-form-column">
+              <input type="text" 
+                     class="i-sdc-form-input myClass"
+                     placeholder="new metadata key"
+                     (input)="onChangePropertyMetadataKey(idx, $event.target.value)"
+                     [value]="metadata.key"
+                     [ngClass]="{'disabled': isViewOnly}"
+                     required/>
+            </div>
+            <div class="w-sdc-form-column">
+              <input type="text" 
+                     class="i-sdc-form-input myClass"
+                     placeholder="new metadata value"
+                     (input)="onChangePropertyMetadataValue(idx, $event.target.value)"
+                     [value]="metadata.value"
+                     [ngClass]="{'disabled': isViewOnly}"
+                     required/>
+            </div>
+            <div class="w-sdc-form-column">
+              <span class="sprite-new delete-btn" [ngClass]="{'disabled': isViewOnly}" (click)="removeFromList(idx)"></span>
+            </div>
+        </div>
+        <div class="w-sdc-form-columns-wrapper">
+          <div class="validation-errors">
+            <ng-container *ngFor="let validation of validationMessages.metadata">
+              <div class="input-error" *ngIf="metadataFormArray.at(idx).hasError(validation.type);">
+                {{ validation.message }}
+              </div>
+            </ng-container>
+          </div>
+        </div>
+      </div>
+      <div class="w-sdc-form-columns-wrapper" *ngIf="!isViewOnly">
+          <div class="add-btn add-list-item w-sdc-form-column" *ngIf="!isViewOnly" [ngClass]="{'disabled': isViewOnly}"
+          (click)="addMetadataField()"> Add Metadata Entry </div>
+      </div>
+    </div>
+  </form>
+</div>
\ No newline at end of file
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.less b/catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.less
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.spec.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.spec.ts
new file mode 100644 (file)
index 0000000..0a0b2c0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 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 { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { PropertyMetadataComponent } from './property-metadata.component';
+
+describe('PropertyMetadataComponent', () => {
+  let component: PropertyMetadataComponent;
+  let fixture: ComponentFixture<PropertyMetadataComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ PropertyMetadataComponent ],
+      imports: [FormsModule, ReactiveFormsModule]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(PropertyMetadataComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.component.ts
new file mode 100644 (file)
index 0000000..dc4c283
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 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, OnInit, Output } from '@angular/core';
+import {
+  AbstractControl, FormArray,
+  FormControl,
+  FormGroup, ValidationErrors,
+  ValidatorFn,
+  Validators
+} from '@angular/forms';
+
+@Component({
+  selector: 'app-property-metadata',
+  templateUrl: './property-metadata.component.html',
+  styleUrls: ['./property-metadata.component.less']
+})
+export class PropertyMetadataComponent implements OnInit {
+
+  @Input() propertyMetadata: any;
+  @Input() isViewOnly: boolean = false;
+  @Output() onPropertyMetadataChange: EventEmitter<any> = new EventEmitter<any>();
+
+  propertyMetadataArray: Metadata[] = [];
+  metadataFormArray: FormArray = new FormArray([]);
+  metadataForm: FormGroup = new FormGroup (
+    {
+      'metadataFormList': this.metadataFormArray
+    }
+  );
+  validationMessages = {
+    metadata: [
+      { type: 'required', message: 'Metadata key and value is required'}
+    ]
+  };
+
+  ngOnInit() {
+    this.initForm();
+  }
+
+  private initForm(): void {
+    this.metadataForm.valueChanges.subscribe(() => {
+      this.emitOnPropertyMetadataChange();
+    });
+    if (this.propertyMetadata) {
+      for (const key in this.propertyMetadata) {
+        const value = this.propertyMetadata[key];
+        let metadata: Metadata = {
+          key: key,
+          value: value
+        }
+        this.propertyMetadataArray.push(metadata);
+        this.metadataFormArray.push(
+          new FormControl(metadata, [Validators.required, Validators.minLength(1)])
+        );
+      }
+    }
+  }
+
+  addMetadataField() {
+    let metadata: Metadata = {
+      key: null,
+      value: null,
+    }
+    this.propertyMetadataArray.push(metadata);
+    this.metadataFormArray.push(
+      new FormControl(metadata, [Validators.required, this.formControlValidator()])
+    );
+  }
+
+  private formControlValidator(): ValidatorFn {
+    return (control: AbstractControl): ValidationErrors | null => {
+      const metadata = control.value;
+
+      if (!metadata || !metadata.key || !metadata.value) {
+        return {required:true};
+      }
+      return null;
+    }
+  }
+
+  removeFromList(index: number) {
+    this.propertyMetadataArray.splice(index, 1);;
+    this.metadataFormArray.removeAt(index);
+  }
+
+  private emitOnPropertyMetadataChange(): void {
+    let newMetadata = new Object;
+    for (const metadata of this.propertyMetadataArray) {
+      const key = metadata.key;
+      const value = metadata.value;
+      newMetadata[key] = value;
+    }
+
+    this.onPropertyMetadataChange.emit({
+      metadata: newMetadata,
+      valid: this.metadataForm.valid
+    });
+  }
+
+  onChangePropertyMetadataValue(index: number, newValue: any) {
+    let metadata = this.metadataFormArray.controls[index].value;
+    metadata.value = newValue;
+    this.metadataFormArray.controls[index].setValue(metadata);
+  }
+
+  onChangePropertyMetadataKey(index: number, newKey: any) {
+    let metadata = this.metadataFormArray.controls[index].value;
+    metadata.key = newKey;
+    this.metadataFormArray.controls[index].setValue(metadata);
+  }
+}
+
+export interface Metadata {
+  key: string,
+  value: string
+}
diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.module.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/property-metadata/property-metadata.module.ts
new file mode 100644 (file)
index 0000000..00324e0
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 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 { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { PropertyMetadataComponent } from './property-metadata.component';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    ReactiveFormsModule
+  ],
+    declarations: [PropertyMetadataComponent],
+    exports: [PropertyMetadataComponent],
+    entryComponents: [
+      PropertyMetadataComponent
+    ],
+})
+
+export class PropertyMetadataModule { }
index 0be5193..faf2a0f 100644 (file)
@@ -537,6 +537,19 @@ export class PropertyFormViewModel {
             this.$scope.editPropertyModel.property.constraints = constraints.constraints;
         }
 
+        this.$scope.onPropertyMetadataChange = (metadata: any): void => {
+            if (!this.$scope.invalidMandatoryFields) {
+                this.$scope.footerButtons[0].disabled = !metadata.valid;
+            } else {
+                this.$scope.footerButtons[0].disabled = this.$scope.invalidMandatoryFields;
+            }
+            if (!metadata.metadata || metadata.metadata.length == 0) {
+                this.$scope.editPropertyModel.property.metadata = null;
+                return;
+            }
+            this.$scope.editPropertyModel.property.metadata = metadata.metadata;
+        }
+
         this.$scope.onGetFunctionValidFunction = (toscaGetFunction: ToscaGetFunction): void => {
             this.$scope.editPropertyModel.property.toscaFunction = toscaGetFunction;
         }
index d7c2af5..bd2126b 100644 (file)
                         </app-constraints>
                     </ng-container>
                 </div>
+                <div class="constraints-section i-sdc-form-item" data-ng-if="editPropertyModel.property.propertyView || editPropertyModel.property.metadata || !(isViewOnly || componentMetadata.isService)">
+                    <label class="i-sdc-form-label">Metadata</label>
+                    <ng-container>
+                        <app-property-metadata [property-metadata]="editPropertyModel.property.metadata"
+                                               [is-view-only]="editPropertyModel.property.propertyView? !editPropertyModel.property.propertyView : (isViewOnly || componentMetadata.isService)"
+                                               (on-property-metadata-change)="onPropertyMetadataChange($event)">
+                        </app-property-metadata>
+                    </ng-container>
+                </div>
                 <span  class="w-sdc-form-note"  data-ng-show="forms.editForm.$invalid && false" translate="LABEL_ALL_FIELDS_ARE_MANDATORY"></span>
             </form>
         </perfect-scrollbar>