diff options
9 files changed, 116 insertions, 5 deletions
diff --git a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml index c5b417d725..4d26e08831 100644 --- a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml +++ b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml @@ -2926,3 +2926,11 @@ errors: message: "Error: The following properties:\n%1\nused in the substitution mapping node type in CSAR are missing from the system. Please add these to the node in the SDC catalog prior to importing the template", messageId: "SVC4021" } + + # %1 - Output name + #---------SVC4022----------------------------- + OUTPUT_NAME_ALREADY_EXIST: { + code: 400, + message: "Output name '%1' already exist.", + messageId: "SVC4022" + } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/attribute/DefaultAttributeDeclarator.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/attribute/DefaultAttributeDeclarator.java index 394d6b314b..e494934d4d 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/attribute/DefaultAttributeDeclarator.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/attribute/DefaultAttributeDeclarator.java @@ -119,7 +119,13 @@ public abstract class DefaultAttributeDeclarator<PROPERTYOWNER extends Propertie //Creating input from property create on self using add property..Do not add the prefix generatedInputPrefix = null; } - final String generatedOutputName = generateOutputName(generatedInputPrefix, attribOutput); + String generatedOutputName = null; + if (StringUtils.isNotEmpty(attribOutput.getOutputName())) { + generatedOutputName = attribOutput.getOutputName(); + } else { + generatedOutputName = generateOutputName(generatedInputPrefix, attribOutput); + } + log.debug("createInput: propOwner.uniqueId={}, attribOutput.parentUniqueId={}", propertiesOwner.getUniqueId(), attribOutput.getParentUniqueId()); return createOutputFromAttribute(component.getUniqueId(), propertiesOwner, generatedOutputName, attribOutput, attributeDataDefinition); diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/OutputsBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/OutputsBusinessLogic.java index 62ef98b985..58baad1a58 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/OutputsBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/OutputsBusinessLogic.java @@ -23,10 +23,14 @@ package org.openecomp.sdc.be.components.impl; import fj.data.Either; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import org.apache.commons.collections.MapUtils; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; import org.jetbrains.annotations.NotNull; import org.openecomp.sdc.be.components.attribute.AttributeDeclarationOrchestrator; import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException; @@ -130,6 +134,10 @@ public class OutputsBusinessLogic extends BaseBusinessLogic { try { validateUserExists(userId); component = getAndValidateComponentForCreate(userId, componentId, componentType, shouldLockComp); + ImmutablePair<StorageOperationStatus, String> status = validateOutputName(component, componentInstOutputsMapUi); + if (status.getLeft() != StorageOperationStatus.OK) { + throw new ByResponseFormatComponentException(componentsUtils.getResponseFormat(ActionStatus.OUTPUT_NAME_ALREADY_EXIST, status.getRight())); + } result = attributeDeclarationOrchestrator.declareAttributesToOutputs(component, componentInstOutputsMapUi).left() .bind(outputsToCreate -> prepareOutputsForCreation(userId, componentId, outputsToCreate)).right() .map(componentsUtils::getResponseFormat); @@ -155,6 +163,29 @@ public class OutputsBusinessLogic extends BaseBusinessLogic { } } + private ImmutablePair<StorageOperationStatus, String> validateOutputName(final Component component, + final ComponentInstOutputsMap componentInstOutputsMapUi) { + final Map<String, List<ComponentInstanceAttribOutput>> outputDeclaredProperties = new HashMap<>(); + if (MapUtils.isNotEmpty(componentInstOutputsMapUi.getComponentInstanceOutputsMap())) { + outputDeclaredProperties.putAll(componentInstOutputsMapUi.getComponentInstanceOutputsMap()); + } else if (MapUtils.isNotEmpty(componentInstOutputsMapUi.getComponentInstanceAttributes())) { + outputDeclaredProperties.putAll(componentInstOutputsMapUi.getComponentInstanceAttributes()); + } + if (MapUtils.isNotEmpty(outputDeclaredProperties) && CollectionUtils.isNotEmpty(component.getOutputs())) { + for (final List<ComponentInstanceAttribOutput> componentInstancePropOutputs : outputDeclaredProperties.values()) { + for (final ComponentInstanceAttribOutput componentInstancePropOutput : componentInstancePropOutputs) { + final Optional<OutputDefinition> outputDefinition = component.getOutputs().stream() + .filter(output -> output.getName().equals(componentInstancePropOutput.getOutputName()) + || output.getName().equals(componentInstancePropOutput.getName())).findAny(); + if (outputDefinition.isPresent()) { + return new ImmutablePair<>(StorageOperationStatus.INVALID_VALUE, outputDefinition.get().getName()); + } + } + } + } + return new ImmutablePair<>(StorageOperationStatus.OK, StringUtils.EMPTY); + } + private Component getAndValidateComponentForCreate(final String userId, final String componentId, final ComponentTypeEnum componentType, final boolean shouldLockComp) { diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java index 52162fc272..fbe1c2e376 100644 --- a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java +++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java @@ -94,7 +94,7 @@ public enum ActionStatus { // Inputs INPUT_IS_NOT_CHILD_OF_COMPONENT, CFVC_LOOP_DETECTED, INPUT_ALREADY_EXIST, INPUT_NAME_ALREADY_EXIST, // Outputs - OUTPUT_IS_NOT_CHILD_OF_COMPONENT, OUTPUT_ALREADY_EXIST, + OUTPUT_IS_NOT_CHILD_OF_COMPONENT, OUTPUT_ALREADY_EXIST, OUTPUT_NAME_ALREADY_EXIST, //Forwarding Path related FORWARDING_PATH_NAME_MAXIMUM_LENGTH, FORWARDING_PATH_NAME_ALREADY_IN_USE, FORWARDING_PATH_NAME_EMPTY, FORWARDING_PATH_PROTOCOL_MAXIMUM_LENGTH, FORWARDING_PATH_DESTINATION_PORT_MAXIMUM_LENGTH, MISSING_OLD_COMPONENT_INSTANCE, MISSING_NEW_COMPONENT_INSTANCE, //policies diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceAttribOutput.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceAttribOutput.java index 8531a36843..42ff192ac7 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceAttribOutput.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceAttribOutput.java @@ -28,6 +28,7 @@ public class ComponentInstanceAttribOutput extends ComponentInstanceAttribute { private String attributesName; private AttributeDefinition output; + private String outputName; public ComponentInstanceAttribOutput() { super(); diff --git a/catalog-ui/src/app/models/attributes-outputs/attribute-be-model.ts b/catalog-ui/src/app/models/attributes-outputs/attribute-be-model.ts index a6966cd799..3ec03c5121 100644 --- a/catalog-ui/src/app/models/attributes-outputs/attribute-be-model.ts +++ b/catalog-ui/src/app/models/attributes-outputs/attribute-be-model.ts @@ -51,6 +51,7 @@ export class AttributeBEModel { subAttributeOutputPath: string; outputPath: string; toscaPresentation: ToscaPresentationData; + outputName: string; constructor(attribute?: AttributeBEModel) { if (attribute) { @@ -77,6 +78,7 @@ export class AttributeBEModel { this.subAttributeOutputPath = attribute.subAttributeOutputPath; this.toscaPresentation = attribute.toscaPresentation; this.outputPath = attribute.outputPath; + this.outputName = attribute.outputName; } if (!this.schema || !this.schema.property) { diff --git a/catalog-ui/src/app/ng2/pages/attributes-outputs/attributes-outputs.page.component.html b/catalog-ui/src/app/ng2/pages/attributes-outputs/attributes-outputs.page.component.html index e4f672ce9b..e55fc47007 100644 --- a/catalog-ui/src/app/ng2/pages/attributes-outputs/attributes-outputs.page.component.html +++ b/catalog-ui/src/app/ng2/pages/attributes-outputs/attributes-outputs.page.component.html @@ -57,7 +57,7 @@ </div> </tabs> <div class="header"> - <button class="tlv-btn blue declare-button" [disabled]="!checkedAttributesCount || isReadonly || hasChangedData" (click)="declareAttributes()" data-tests-id="declare-button declare-output">Declare Output</button> + <button class="tlv-btn blue declare-button" [disabled]="!checkedAttributesCount || isReadonly || hasChangedData" (click)="declareOutput()" data-tests-id="declare-button declare-output">Declare Output</button> </div> </div> </div> diff --git a/catalog-ui/src/app/ng2/pages/attributes-outputs/attributes-outputs.page.component.ts b/catalog-ui/src/app/ng2/pages/attributes-outputs/attributes-outputs.page.component.ts index 85d2756da8..48700f2ecb 100644 --- a/catalog-ui/src/app/ng2/pages/attributes-outputs/attributes-outputs.page.component.ts +++ b/catalog-ui/src/app/ng2/pages/attributes-outputs/attributes-outputs.page.component.ts @@ -60,6 +60,7 @@ import {DerivedFEAttribute} from "../../../models/attributes-outputs/derived-fe- import {AttributeBEModel} from "../../../models/attributes-outputs/attribute-be-model"; import {AttributeCreatorComponent} from "app/ng2/pages/attributes-outputs/attribute-creator/attribute-creator.component"; import {AttributeRowSelectedEvent} from "app/ng2/components/logic/attributes-table/attributes-table.component"; +import { DeclareInputComponent } from '../properties-assignment/declare-input/declare-input.component'; const SERVICE_SELF_TITLE = "SELF"; @@ -432,9 +433,62 @@ export class AttributesOutputsComponent { this.searchQuery = ''; }; + declareOutput = (): void => { + if (this.checkedAttributesCount == 1) { + this.openAddOutputNameModal(); + } else if (this.checkedAttributesCount > 1) { + this.declareAttributes(); + } + } + + private openAddOutputNameModal = (): void => { + const modalTitle = 'Enter name of the ouput to be created'; + const modalButtons = []; + const modal = this.ModalService.createCustomModal(new ModalModel( + 'sm', + modalTitle, + null, + modalButtons, + null /* type */ + )); + modalButtons.push(new ButtonModel('Save', 'blue', + () => { + const outputName: string = modal.instance.dynamicContent.instance.inputNameForm.value; + if (outputName) { + this.declareAttributes(outputName); + } else { + this.Notification.warning({ + message: 'Failed to set input name', + title: 'Warning' + }); + } + this.ModalService.closeCurrentModal(); + } + )); + modalButtons.push(new ButtonModel('Cancel', 'outline grey', () => { + this.ModalService.closeCurrentModal(); + })); + this.ModalService.addDynamicContentToModal(modal, DeclareInputComponent, {defaultInputName: this.generateDefaultOutputName()}); + modal.instance.open(); + } + + generateDefaultOutputName = (): string => { + let defaultInputName: string; + let instancesIds = this.keysPipe.transform(this.instanceFeAttributesMap, []); + angular.forEach(instancesIds, (instanceId: string) => { + const selectedOutput : AttributeBEModel = this.attributesService.getCheckedAttributes(this.instanceFeAttributesMap[instanceId])[0]; + let selectedInstanceData: any = this.instances.find(instance => instance.uniqueId == instanceId); + defaultInputName = selectedOutput.name; + if (selectedInstanceData.invariantName) { + defaultInputName = selectedInstanceData.invariantName+'_'+selectedOutput.name; + } + }); + return defaultInputName; + } /*** DECLARE ATTRIBUTES/OUTPUTS ***/ - declareAttributes = (): void => { + declareAttributes = (outputName?: string): void => { + this.loadingAttributes = true; let selectedComponentInstancesAttributes: InstanceBeAttributesMap = new InstanceBeAttributesMap(); let selectedComponentInstancesOutputs: InstanceBeAttributesMap = new InstanceBeAttributesMap(); let instancesIds = this.keysPipe.transform(this.instanceFeAttributesMap, []); @@ -445,6 +499,9 @@ export class AttributesOutputsComponent { if (!this.isOutput(selectedInstanceData.originType)) { // convert Attribute FE model -> Attribute BE model, extract only checked selectedComponentInstancesAttributes[instanceId] = this.attributesService.getCheckedAttributes(this.instanceFeAttributesMap[instanceId]); + if (outputName) { + selectedComponentInstancesAttributes[instanceId][0].outputName = outputName; + } } else { selectedComponentInstancesOutputs[instanceId] = this.attributesService.getCheckedAttributes(this.instanceFeAttributesMap[instanceId]); } @@ -463,7 +520,8 @@ export class AttributesOutputsComponent { this.outputs.push(newOutput); this.updateAttributeValueAfterDeclare(newOutput); }); - }); + this.loadingAttributes = false; + }, error => {this.loadingAttributes = false;}); //ignore error }; saveChangedData = (): Promise<(AttributeBEModel | OutputBEModel)[]> => { diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributesTabComponent.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributesTabComponent.java index e06d702afe..207c79a49e 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributesTabComponent.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributesTabComponent.java @@ -45,6 +45,9 @@ public class AttributesTabComponent extends AbstractPageObject { } waitForElementVisibility(By.xpath(XpathSelector.ATTRIBUTES_CHECKBOX.getXpath(attributeName))).click(); waitToBeClickable(By.xpath(XpathSelector.DECLARE_OUTPUT_BTN.getXpath())).click(); + final By setInputNameLocator = By.xpath(XpathSelector.SET_INPUT_NAME_FIELD.getXpath()); + final WebElement inputNameField = waitForElementVisibility(setInputNameLocator, 5); + waitToBeClickable(By.xpath(XpathSelector.INPUT_NAME_SAVE_BTN.getXpath())).click(); waitForAddedOutputNotification(); } @@ -82,6 +85,8 @@ public class AttributesTabComponent extends AbstractPageObject { ATTRIBUTES_CHECKBOX("//checkbox[@data-tests-id='%s']"), DECLARE_OUTPUT_BTN("declare-button declare-output", "//button[@data-tests-id='%s']"), INSTANCE_SPAN("//div[contains(@class,'table-rows-header')]"), + SET_INPUT_NAME_FIELD("//*[@id=\"myText\"]"), + INPUT_NAME_SAVE_BTN("//button[@data-tests-id='Save']"), ADDED_OUTPUT_NOTIFICATION("tab-indication", "//div[@data-tests-id='Outputs']/div[contains(@class, '%s')]"); @Getter |