Adding type safety to the service dependency editor. 30/124130/5
authordavsad <david.sadlier@est.tech>
Tue, 8 Jun 2021 06:17:07 +0000 (07:17 +0100)
committerMichael Morris <michael.morris@est.tech>
Wed, 6 Oct 2021 11:31:51 +0000 (11:31 +0000)
Issue-ID: SDC-3725

Signed-off-by: davsad <david.sadlier@est.tech>
Change-Id: I63d77837fb0df24f5ee12baa5b852a76ce5f55e3

catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.html
catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.less
catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.ts
catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.module.ts
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/datatypes/ServiceDependencyProperty.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ServiceTemplateDesignUiTests.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourcePropertiesPage.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ServiceDependenciesEditor.java

index 4b23568..f84214e 100644 (file)
                     <label class="i-sdc-form-label required" >Source</label>
                     <ui-element-dropdown class="i-sdc-form-select" data-tests-id="sourceType" [values]="sourceTypes" [(value)]="currentRule.sourceName" (change)="onSelectSourceType($event)"></ui-element-dropdown>
                 </div>
-
-                <div class="rule-input-field assigned-value-field">
+                <div  [ngClass]="isComplexListMapType() && isStaticSource() ? 'complex-input-field' : ''"
+                    class="rule-input-field assigned-value-field">
                     <label class="i-sdc-form-label required" >{{assignedValueLabel}}</label>
+                    <dynamic-property
+                        *ngIf="isStaticSource() && isComplexListMapType()"
+                        [selectedPropertyId]="selectedPropertyObj.uniqueId"
+                        [property]="selectedPropertyObj"
+                        [expandedChildId]="selectedPropertyObj.expandedChildPropertyId ?
+                            selectedPropertyObj.expandedChildPropertyId : selectedPropertyObj.name"
+                        [canBeDeclared]="true"
+                        (propertyChanged)="updateComplexListMapTypeRuleValue()"
+                        [rootProperty]="selectedPropertyObj"
+                        (expandChild)="selectedPropertyObj.updateExpandedChildPropertyId($event)">
+                    </dynamic-property>
                     <dynamic-element
-                            *ngIf="currentRule.sourceType === SOURCE_TYPES.STATIC.value"
+                            *ngIf="isStaticSource() && !isComplexListMapType()"
                             [(value)]="currentRule.value"
                             class="rule-assigned-value"
                             data-tests-id="ruleAssignedValue"
                             (elementChanged)="onValueChange($event.isValid)"
                             [type]="selectedPropertyObj ? selectedPropertyObj.type : 'string'">
                     </dynamic-element>
-                    <ui-element-dropdown *ngIf="currentRule.sourceType !== SOURCE_TYPES.STATIC.value"
+                    <ui-element-dropdown *ngIf="!isStaticSource()"
                                          class="rule-assigned-value"
                                          data-tests-id="ruleAssignedValue"
                                          [(value)]="currentRule.value"
index e03b73c..b475ed2 100644 (file)
@@ -21,6 +21,7 @@
   .rule-builder-content {
     display: flex;
     align-items: flex-end;
+    flex-wrap: wrap;
     .rule-input-field {
       flex: 1;
       &:not(:last-of-type) {
@@ -38,6 +39,9 @@
         height: 30px;
       }
     }
-
+    .complex-input-field {
+      flex-basis: 100%;
+      display: block;
+    }
   }
 }
\ No newline at end of file
index 084ab32..cb3e87c 100644 (file)
@@ -14,7 +14,7 @@
  * permissions and limitations under the License.
  */
 import {Component} from '@angular/core';
-import {InputBEModel, PropertyBEModel} from 'app/models';
+import {InputBEModel, PropertyBEModel, PropertyFEModel} from 'app/models';
 import {
   ConstraintObjectUI,
   OPERATOR_TYPES
@@ -23,6 +23,7 @@ import {DropdownValue} from 'app/ng2/components/ui/form-components/dropdown/ui-e
 import {ServiceServiceNg2} from 'app/ng2/services/component-services/service.service';
 import {PROPERTY_DATA} from 'app/utils';
 import {ServiceInstanceObject} from '../../../models/service-instance-properties-and-interfaces';
+import { PropertiesUtils } from '../properties-assignment/services/properties.utils';
 
 export class UIDropDownSourceTypesElement extends DropdownValue {
   options: any[];
@@ -61,7 +62,7 @@ export class ServiceDependenciesEditorComponent {
   };
   currentServiceName: string;
   selectedServiceProperties: PropertyBEModel[];
-  selectedPropertyObj: PropertyBEModel;
+  selectedPropertyObj: PropertyFEModel;
   ddValueSelectedServicePropertiesNames: DropdownValue[];
   operatorTypes: DropdownValue[];
   sourceTypes: UIDropDownSourceTypesElement[] = [];
@@ -77,6 +78,8 @@ export class ServiceDependenciesEditorComponent {
     SERVICE_PROPERTY: {label: 'Service Property', value: 'property'}
   };
 
+  constructor(private propertiesUtils: PropertiesUtils) {}
+
   ngOnInit() {
     this.currentIndex = this.input.serviceRuleIndex;
     this.serviceRulesList = this.input.serviceRules;
@@ -124,10 +127,11 @@ export class ServiceDependenciesEditorComponent {
   }
 
   syncRuleData() {
-    if (!this.currentRule.sourceName && this.currentRule.sourceType === this.SOURCE_TYPES.STATIC.value) {
+    if (!this.currentRule.sourceName || this.currentRule.sourceType === this.SOURCE_TYPES.STATIC.value) {
       this.currentRule.sourceName = this.SOURCE_TYPES.STATIC.value;
+      this.currentRule.sourceType = this.SOURCE_TYPES.STATIC.value;
     }
-    this.selectedPropertyObj = _.find(this.selectedServiceProperties, (prop) => prop.name === this.currentRule.servicePropertyName);
+    this.updateSelectedPropertyObj();
     this.updateOperatorTypesList();
     this.updateSourceTypesRelatedValues();
   }
@@ -154,7 +158,7 @@ export class ServiceDependenciesEditorComponent {
     }
   }
 
-  onChangePage(newIndex) {
+  onChangePage(newIndex:any) {
     if (newIndex >= 0 && newIndex < this.input.serviceRules.length) {
       this.currentIndex = newIndex;
       this.currentRule = this.serviceRulesList[newIndex];
@@ -163,18 +167,18 @@ export class ServiceDependenciesEditorComponent {
   }
 
   onServicePropertyChanged() {
-    this.selectedPropertyObj = _.find(this.selectedServiceProperties, (prop) => prop.name === this.currentRule.servicePropertyName);
+    this.currentRule.value = '';
+    this.updateSelectedPropertyObj();
     this.updateOperatorTypesList();
     this.filterOptionsByType();
-    this.currentRule.value = '';
   }
 
   onSelectSourceType() {
+    this.currentRule.value = '';
     this.currentRule.sourceType = this.currentRule.sourceName === this.SOURCE_TYPES.STATIC.value ?
         this.SOURCE_TYPES.STATIC.value :
         this.SOURCE_TYPES.SERVICE_PROPERTY.value;
     this.updateSourceTypesRelatedValues();
-    this.currentRule.value = '';
   }
 
   filterOptionsByType() {
@@ -190,7 +194,7 @@ export class ServiceDependenciesEditorComponent {
     }, []);
   }
 
-  onValueChange(isValidValue) {
+  onValueChange(isValidValue:any) {
     this.currentRule.updateValidity(isValidValue);
   }
 
@@ -202,4 +206,29 @@ export class ServiceDependenciesEditorComponent {
     // for update all rules
     return this.serviceRulesList.every((rule) => rule.isValidRule(rule.sourceName === this.SOURCE_TYPES.STATIC.value));
   }
+
+  updateSelectedPropertyObj(): void {
+    this.selectedPropertyObj = null;
+    if (this.currentRule.servicePropertyName) {
+      let newProp = new PropertyFEModel(_.find(this.selectedServiceProperties, (prop) => prop.name === this.currentRule.servicePropertyName));
+      newProp.value = JSON.stringify(this.currentRule.value);
+      this.propertiesUtils.initValueObjectRef(newProp);
+      console.log("TEST" + newProp.value);
+      setTimeout(() => {this.selectedPropertyObj = newProp})
+    }
+  }
+
+  isStaticSource(): boolean {
+    return this.currentRule.sourceType === this.SOURCE_TYPES.STATIC.value
+  }
+
+  isComplexListMapType(): boolean {
+    return this.selectedPropertyObj && this.selectedPropertyObj.derivedDataType > 0;
+  }
+
+  updateComplexListMapTypeRuleValue(): void {
+    let value = PropertyFEModel.cleanValueObj(this.selectedPropertyObj.valueObj);
+    this.currentRule.value = JSON.stringify(value);
+    this.onValueChange(this.selectedPropertyObj.valueObjIsValid);
+  }
 }
index 7b128f4..cfa466f 100644 (file)
@@ -4,6 +4,7 @@ import { FormsModule } from '@angular/forms';
 import { FormElementsModule } from 'app/ng2/components/ui/form-components/form-elements.module';
 import { UiElementsModule } from 'app/ng2/components/ui/ui-elements.module';
 import { ServiceDependenciesEditorComponent } from './service-dependencies-editor.component';
+import { PropertyTableModule } from 'app/ng2/components/logic/properties-table/property-table.module';
 
 @NgModule({
     declarations: [
@@ -13,7 +14,8 @@ import { ServiceDependenciesEditorComponent } from './service-dependencies-edito
         CommonModule,
         FormsModule,
         FormElementsModule,
-        UiElementsModule
+        UiElementsModule,
+        PropertyTableModule
     ],
     exports: [],
     entryComponents: [
index 6afbdc2..47b8df2 100644 (file)
@@ -32,12 +32,14 @@ public class ServiceDependencyProperty {
     private final String name;
     private final String value;
     private final String source;
+    private final String type;
     private final LogicalOperator logicalOperator;
 
-    public ServiceDependencyProperty(String name, String value, LogicalOperator logicalOperator) {
+    public ServiceDependencyProperty(String name, String type, String value, LogicalOperator logicalOperator) {
         this.name = name;
         this.value = value;
         this.source = "Static";
+        this.type = type;
         this.logicalOperator = logicalOperator;
     }
 }
index e944ab3..8bec835 100644 (file)
@@ -284,8 +284,10 @@ public class ServiceTemplateDesignUiTests extends SetupCDTest {
         componentPage.isLoaded();
         final ResourcePropertiesPage vfcPropertiesPage = componentPage.goToProperties();
         vfcPropertiesPage.isLoaded();
-        final List<String> propertyNames = vfcPropertiesPage.getPropertyNames();
-        final ServiceDependencyProperty serviceDependencyProperty = new ServiceDependencyProperty(propertyNames.get(0), value, operator);
+        final Map<String, String> propertyNamesAndTypes = vfcPropertiesPage.getPropertyNamesAndTypes();
+        final List<String> propertyNames = propertyNamesAndTypes.keySet().stream().collect(Collectors.toList());
+        final ServiceDependencyProperty serviceDependencyProperty =
+                new ServiceDependencyProperty(propertyNames.get(0), propertyNamesAndTypes.get(propertyNames.get(0)), value, operator);
 
         homePage.getTopNavComponent().clickOnHome();
         homePage.isLoaded();
@@ -881,7 +883,7 @@ public class ServiceTemplateDesignUiTests extends SetupCDTest {
         assertThat(String.format("The Component '%s' should have properties", vfResourceCreateData.getName()), propertyNamesAndTypes,
             not(anEmptyMap()));
         propertyNamesAndTypes.forEach((name, type)
-            -> substitutionFilterProperties.add(new ServiceDependencyProperty(name, getPropertyValueByType(type), LogicalOperator.EQUALS)));
+            -> substitutionFilterProperties.add(new ServiceDependencyProperty(name, type, getPropertyValueByType(type), LogicalOperator.EQUALS)));
     }
 
     private String getPropertyValueByType(final String type) {
@@ -895,9 +897,9 @@ public class ServiceTemplateDesignUiTests extends SetupCDTest {
             case "boolean":
                 return "TRUE";
             case "list":
-                return "[value1, value2]";
+                return "[\"value1\", \"value2\"]";
             case "map":
-                return "MyKey: MyValue";
+                return "{\"MyKey\": \"MyValue\"}";
             default:
                 throw new UnsupportedOperationException("Not yet implemented for " + type);
         }
@@ -925,7 +927,7 @@ public class ServiceTemplateDesignUiTests extends SetupCDTest {
                 substitutionFilterMap.containsKey(substitutionFilterProperty.getName()));
             final Map<?, ?> substitutionFilterValue = (Map<?, ?>) ((List<?>) substitutionFilterMap.get(substitutionFilterProperty.getName())).get(0);
             assertThat("Substitution Filter Value should not be empty", substitutionFilterMap, not(anEmptyMap()));
-            final String expectedSubstitutionPropertyValue = substitutionFilterProperty.getValue();
+            final String expectedSubstitutionPropertyValue = substitutionFilterProperty.getValue().replaceAll("[\"{}]", "");
             final String actualSubstitutionPropertyValue = substitutionFilterValue.values().stream().findFirst().get() instanceof Map
                 ? substitutionFilterValue.values().stream().findFirst().get().toString().replace("=", ": ")
                 .replaceAll("\\{(.*?)\\}", "$1").trim()
index ace3041..2d45c7c 100644 (file)
 
 package org.onap.sdc.frontend.ci.tests.pages;
 
+import java.util.HashMap;
 import java.util.List;
-import java.util.stream.Collectors;
+import java.util.Map;
 
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
 
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -52,13 +54,19 @@ public class ResourcePropertiesPage extends AbstractPageObject {
     }
 
     /**
-     * Returns a list based on property names
-     * @return list of names from the properties table
+     * Creates a map based on property names and data types
      */
-    public List<String> getPropertyNames() {
+    public Map<String, String> getPropertyNamesAndTypes() {
         waitPropertiesToLoad();
-        return findElements(By.xpath(XpathSelector.PROPERTY_NAMES.getXpath())).stream()
-                .map(ele -> ele.getAttribute("innerText")).collect(Collectors.toList());
+        final Map<String, String> namesAndTypes = new HashMap<>();
+        final List<WebElement> names = findElements(By.xpath(XpathSelector.PROPERTY_NAMES.getXpath()));
+        final List<WebElement> types = findElements(By.xpath(XpathSelector.PROPERTY_TYPES.getXpath()));
+
+        for (int i = 0;i < names.size();i++) {
+            namesAndTypes.put(names.get(i).getAttribute("innerText"), types.get(i).getAttribute("innerText"));
+        }
+
+        return namesAndTypes;
     }
 
     @AllArgsConstructor
index cc3e284..969854b 100644 (file)
 
 package org.onap.sdc.frontend.ci.tests.pages;
 
+import static org.junit.jupiter.api.Assertions.fail;
+
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.stream.Collectors;
 
 import org.onap.sdc.frontend.ci.tests.datatypes.ServiceDependencyProperty;
@@ -28,6 +32,8 @@ import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.ui.Select;
 
+import com.fasterxml.jackson.databind.json.JsonMapper;
+
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
@@ -61,16 +67,56 @@ public class ServiceDependenciesEditor extends AbstractPageObject {
         logicalOperator.selectByVisibleText(property.getLogicalOperator().getOperator());
         final Select sourceType = new Select(webDriver.findElement(By.xpath(XpathSelector.SOURCE_TYPE.xPath)));
         sourceType.selectByVisibleText(property.getSource());
-        addRuleAssignedValue(webDriver.findElement(
-                By.xpath(XpathSelector.RULE_ASSIGNED_VALUE.xPath)), property.getValue());
+        try {
+            addRuleAssignedValue(property);
+        } catch (Exception e) {
+            fail("Failed to add property due to exception while adding rule value :: {}", e);
+        }
         webDriver.findElement(By.xpath(XpathSelector.CREATE_BUTTON.xPath)).click();
     }
 
-    private void addRuleAssignedValue(final WebElement element, final String value) {
+    private void addRuleAssignedValue(final ServiceDependencyProperty property) throws Exception {
+        final var type = property.getType();
+        final var value = property.getValue();
+        switch (type) {
+            case "list":
+                addListInput(property.getName(), value);
+                break;
+            case "map":
+                addMapInput(property.getName(), value);
+                break;
+            default:
+                addStringInput(waitForElementVisibility(By.xpath(XpathSelector.RULE_ASSIGNED_VALUE.xPath)), value);
+                break;
+        }
+    }
+
+    private void addStringInput(WebElement element, Object value) {
         if ("select".equals(element.getTagName())) {
-            new Select(element).selectByVisibleText(value);
+            new Select(element).selectByVisibleText(value.toString());
         } else {
-            element.sendKeys(value);
+            element.sendKeys(value.toString());
+        }
+    }
+
+    private void addListInput(final String name, final String value) throws Exception {
+        final List<?> values = new JsonMapper().readValue(value, List.class);
+        final WebElement addToListElement = waitForElementVisibility(By.xpath(XpathSelector.RULE_ASSIGNED_VALUE_ADD_TO_LIST.formatXpath(name)));
+        for (int i=0;i<values.size();i++) {
+            addToListElement.click();
+            addStringInput(waitForElementVisibility(By.xpath(XpathSelector.RULE_ASSIGNED_LIST_VALUE.formatXpath(name, i))), values.get((i)));
+        }
+    }
+
+    private void addMapInput(final String name, final String value) throws Exception {
+        final Map<?, ?> values = new JsonMapper().readValue(value, Map.class);
+        int i = 0;
+        final WebElement addToListElement = waitForElementVisibility(By.xpath(XpathSelector.RULE_ASSIGNED_VALUE_ADD_TO_LIST.formatXpath(name)));
+        for(Entry<?, ?> entry : values.entrySet()) {
+            addToListElement.click();
+            final List<WebElement> KeyValueInputs = waitForAllElementsVisibility(By.xpath(XpathSelector.RULE_ASSIGNED_MAP_KEY_VALUE.formatXpath(name, i++)));
+            addStringInput(KeyValueInputs.get(0), entry.getKey());
+            addStringInput(KeyValueInputs.get(1), entry.getValue());
         }
     }
 
@@ -82,9 +128,15 @@ public class ServiceDependenciesEditor extends AbstractPageObject {
         CONSTRAINT_OPERATOR("//*[@data-tests-id='constraintOperator']/select"),
         SOURCE_TYPE("//*[@data-tests-id='sourceType']/select"),
         RULE_ASSIGNED_VALUE("//*[@data-tests-id='ruleAssignedValue']//*[self::input or self::select]"),
+        RULE_ASSIGNED_VALUE_ADD_TO_LIST("//a[@data-tests-id = 'add-to-list-%s']"),
+        RULE_ASSIGNED_LIST_VALUE("//*[@data-tests-id='value-prop-%s.%d']"),
+        RULE_ASSIGNED_MAP_KEY_VALUE("//*[contains(@data-tests-id, 'value-prop') and contains(@data-tests-id, '%s.%d')]"),
         CREATE_BUTTON("//button[text()='Create']");
 
         private final String xPath;
 
+        public String formatXpath(Object... values) {
+            return String.format(xPath, values);
+        }
     }
 }