Bug fixes November 5th 05/71905/1
authorBenjamin, Max (mb388a) <mb388a@us.att.com>
Tue, 6 Nov 2018 00:12:02 +0000 (19:12 -0500)
committerBenjamin, Max (mb388a) <mb388a@us.att.com>
Tue, 6 Nov 2018 01:02:06 +0000 (20:02 -0500)
building block validator test now passes
added buildingblockvalidator process instance test
Retrieve actual error from WorkflowExceptionErrorMessage when the error
message is empty, not only when null.
Propagate orchestrationStatusValidationResult values from BB to BB.
corrected use of constructor for AaiUtil
use AaiUtil method to create path for get call
modified how allotted resource urls are constructed
- Removed the findExistingVnfcInstanceGroup method.  It is not required
to check this because the vnfResourceCustomization is always a new
object at this location.
Enable multiStage VF Module Create processing only when aLaCarte flag
is on.

Change-Id: If8cf397a84abc290e67e287d5b2264dd226398bc
Issue-ID: SO-1188
Signed-off-by: Benjamin, Max (mb388a) <mb388a@us.att.com>
15 files changed:
asdc-controller/src/main/java/org/onap/so/asdc/installer/heat/ToscaResourceInstaller.java
bpmn/MSOCommonBPMN/src/main/groovy/org/onap/so/bpmn/common/scripts/AllottedResourceUtils.groovy
bpmn/MSOCommonBPMN/src/main/resources/META-INF/processes.xml [deleted file]
bpmn/MSOCommonBPMN/src/test/groovy/org/onap/so/bpmn/common/scripts/AllottedResourceUtilsTest.groovy [new file with mode: 0644]
bpmn/so-bpmn-building-blocks/src/main/resources/subprocess/BuildingBlock/WorkflowActionBB.bpmn
bpmn/so-bpmn-building-blocks/src/test/java/org/onap/so/bpmn/infrastructure/bpmn/subprocess/BuildingBlockValidatorRunnerTest.java [new file with mode: 0644]
bpmn/so-bpmn-building-blocks/src/test/resources/META-INF/processes.xml [new file with mode: 0644]
bpmn/so-bpmn-building-blocks/src/test/resources/subprocess/BuildingBlock/BuildingBlockValidatorRunnerTest.bpmn [new file with mode: 0644]
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/vcpe/scripts/DoCreateAllottedResourceBRG.groovy
bpmn/so-bpmn-infrastructure-common/src/main/groovy/org/onap/so/bpmn/vcpe/scripts/DoCreateAllottedResourceTXC.groovy
bpmn/so-bpmn-tasks/src/main/java/org/onap/so/bpmn/infrastructure/aai/tasks/AAIUpdateTasks.java
bpmn/so-bpmn-tasks/src/main/java/org/onap/so/bpmn/infrastructure/workflow/tasks/OrchestrationStatusValidator.java
bpmn/so-bpmn-tasks/src/test/java/org/onap/so/bpmn/infrastructure/aai/tasks/AAIUpdateTasksTest.java
bpmn/so-bpmn-tasks/src/test/java/org/onap/so/bpmn/infrastructure/workflow/tasks/OrchestrationStatusValidatorTest.java
common/src/main/java/org/onap/so/client/aai/entities/uri/AAIUriFactory.java

index f77a48a..06da4e7 100644 (file)
@@ -1170,27 +1170,7 @@ public class ToscaResourceInstaller {
                return vfcInstanceGroupCustom;
 
        }
-       
-       protected VFCInstanceGroup findExistingVnfcInstanceGroup(VnfResourceCustomization vnfResourceCustomization,
-                       String modelUUID) {
-               VFCInstanceGroup vfcInstanceGroup = null;
-               List<VnfcInstanceGroupCustomization> vnfInstanceGroupCustomizations = vnfResourceCustomization
-                               .getVnfcInstanceGroupCustomizations();
-               if(vnfInstanceGroupCustomizations != null){
-                       for (VnfcInstanceGroupCustomization vnfcInstanceGroupCustom : vnfResourceCustomization
-                                       .getVnfcInstanceGroupCustomizations()) {
-                               if (vnfcInstanceGroupCustom.getInstanceGroup() != null
-                                               && vnfcInstanceGroupCustom.getInstanceGroup().getModelUUID().equals(modelUUID)) {
-                                       vfcInstanceGroup = (VFCInstanceGroup)vnfcInstanceGroupCustom.getInstanceGroup();
-                               }
-                       }
-               }
-               if (vfcInstanceGroup == null)
-                       vfcInstanceGroup = (VFCInstanceGroup) instanceGroupRepo.findByModelUUID(modelUUID);
-
-               return vfcInstanceGroup;
-       }
-       
+               
        protected VfModuleCustomization createVFModuleResource(Group group, NodeTemplate nodeTemplate,
                        ToscaResourceStructure toscaResourceStructure, VfResourceStructure vfResourceStructure,
                        IVfModuleData vfModuleData, VnfResourceCustomization vnfResource, Service service, Set<CvnfcCustomization> existingCvnfcSet, Set<VnfcCustomization> existingVnfcSet) {
@@ -1606,16 +1586,10 @@ public class ToscaResourceInstaller {
                                
                        for (Group group : groupList) { 
                                
-                               VFCInstanceGroup vfcInstanceGroup = findExistingVnfcInstanceGroup(vnfResourceCustomization,
-                                               group.getMetadata().getValue(SdcPropertyNames.PROPERTY_NAME_UUID));
-                               if(vfcInstanceGroup == null){
-                                       VnfcInstanceGroupCustomization vnfcInstanceGroupCustomization = createVNFCInstanceGroup(
-                                                       vfNodeTemplate, group, vnfResourceCustomization);
-                               
-                                       vnfcInstanceGroupCustomizationRepo.saveAndFlush(vnfcInstanceGroupCustomization);
-                               }
-                       }
-                       
+                                       VnfcInstanceGroupCustomization vnfcInstanceGroupCustomization = createVNFCInstanceGroup(vfNodeTemplate, group, vnfResourceCustomization);
+                                       
+                                       vnfcInstanceGroupCustomizationRepo.saveAndFlush(vnfcInstanceGroupCustomization);                                
+                       }                       
                }
                return vnfResourceCustomization;
        }
index e4bc5f8..c337a21 100644 (file)
@@ -137,7 +137,7 @@ class AllottedResourceUtils {
                try {
                        AAIResourcesClient client = new AAIResourcesClient()
                        AAIResourceUri uri = AAIUriFactory.createResourceUri(AAIObjectType.ALLOTTED_RESOURCE, allottedResourceId)
-                       AaiUtil aaiUtil = new AaiUtil()
+                       AaiUtil aaiUtil = new AaiUtil(taskProcessor)
                        arLink = aaiUtil.createAaiUri(uri)
                } catch (NotFoundException e) {
                        msoLogger.debug("GET AR received a Not Found (404) Response")
@@ -323,5 +323,25 @@ class AllottedResourceUtils {
                msoLogger.trace("Exit BuildAAIErrorResponse Process")
                throw new BpmnError("MSOWorkflowException")
        }
+       
+       public String createARUrl(DelegateExecution execution, AAIResourceUri uri, String allottedResourceId) {
+               AaiUtil aaiUriUtil = new AaiUtil(taskProcessor)
+               AAIResourceUri siResourceLink= uri
+
+               String siUri = ""
+
+               if(siResourceLink != null) {
+                       msoLogger.debug("Incoming PSI Resource Link is: " + siResourceLink.build().toString())
+               }
+               else
+               {
+                       String msg = "Parent Service Link in AAI is null"
+                       msoLogger.debug(msg)
+                       exceptionUtil.buildAndThrowWorkflowException(execution, 500, msg)
+               }
+               AAIResourceUri arUri = AAIUriFactory.createResourceFromParentURI(siResourceLink, AAIObjectType.ALLOTTED_RESOURCE, allottedResourceId)
+
+               return aaiUriUtil.createAaiUri(arUri)
+       }
 
 }
diff --git a/bpmn/MSOCommonBPMN/src/main/resources/META-INF/processes.xml b/bpmn/MSOCommonBPMN/src/main/resources/META-INF/processes.xml
deleted file mode 100644 (file)
index 33c8cb1..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
-  ============LICENSE_START=======================================================
-  ONAP SO
-  ================================================================================
-  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.
-  ============LICENSE_END=========================================================
-  -->
-
-<process-application
-    xmlns="http://www.camunda.org/schema/1.0/ProcessApplication"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-
-    <process-archive name="MSOCommonBPMN">
-        <properties>
-            <property name="isDeleteUponUndeploy">false</property>
-            <property name="isScanForProcessDefinitions">true</property>
-        </properties>
-    </process-archive>
-
-</process-application>
diff --git a/bpmn/MSOCommonBPMN/src/test/groovy/org/onap/so/bpmn/common/scripts/AllottedResourceUtilsTest.groovy b/bpmn/MSOCommonBPMN/src/test/groovy/org/onap/so/bpmn/common/scripts/AllottedResourceUtilsTest.groovy
new file mode 100644 (file)
index 0000000..5058961
--- /dev/null
@@ -0,0 +1,39 @@
+package org.onap.so.bpmn.common.scripts
+
+import static org.junit.Assert.assertEquals
+import static org.mockito.Matchers.eq
+import static org.mockito.Mockito.mock
+import static org.mockito.Mockito.when
+
+import org.camunda.bpm.engine.delegate.DelegateExecution
+import org.camunda.bpm.extension.mockito.delegate.DelegateExecutionFake
+import org.junit.Test
+import org.onap.so.bpmn.core.UrnPropertiesReader
+import org.onap.so.client.aai.entities.uri.AAIResourceUri
+import org.springframework.core.env.Environment
+
+class AllottedResourceUtilsTest {
+
+       
+       @Test
+       public void createARUrlTest() {
+               AllottedResourceUtils utils = new AllottedResourceUtils(mock(AbstractServiceTaskProcessor.class))
+               DelegateExecution execution = new DelegateExecutionFake()
+               String allottedResourceId = "my-id"
+               UrnPropertiesReader reader = new UrnPropertiesReader()
+               Environment env = mock(Environment.class);
+               
+               when(env.getProperty(eq("mso.workflow.global.default.aai.version"))).thenReturn("14")
+               when(env.getProperty(eq("aai.endpoint"))).thenReturn("http://localhost:8080")
+
+               
+               reader.setEnvironment(env)
+               
+               
+               AAIResourceUri uri = mock(AAIResourceUri.class)
+               when(uri.build()).thenReturn(new URI("/business/customers/customer/1/service-subscriptions/service-subscription/2/service-instances/service-instance/3"))
+               String actual = utils.createARUrl(execution, uri, allottedResourceId)
+               
+               assertEquals("http://localhost:8080/aai/v14/business/customers/customer/1/service-subscriptions/service-subscription/2/service-instances/service-instance/3/allotted-resources/allotted-resource/my-id", actual)
+       }
+}
index 8cda1d7..3b24ba3 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="1.8.2">
+<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="1.4.0">
   <bpmn:process id="WorkflowActionBB" name="WorkflowActionBB" isExecutable="true">
     <bpmn:startEvent id="Start_WorkflowActionBB" name="start">
       <bpmn:outgoing>SequenceFlow_15s0okp</bpmn:outgoing>
@@ -17,6 +17,9 @@
         <camunda:in source="mso-request-id" target="mso-request-id" />
         <camunda:in source="retryCount" target="retryCount" />
         <camunda:out source="WorkflowExceptionErrorMessage" target="WorkflowExceptionErrorMessage" />
+        <camunda:in source="aLaCarte" target="aLaCarte" />
+        <camunda:in source="orchestrationStatusValidationResult" target="orchestrationStatusValidationResult" />
+        <camunda:out source="orchestrationStatusValidationResult" target="orchestrationStatusValidationResult" />
       </bpmn:extensionElements>
       <bpmn:incoming>SequenceFlow_0mew9im</bpmn:incoming>
       <bpmn:outgoing>SequenceFlow_07h9d4y</bpmn:outgoing>
diff --git a/bpmn/so-bpmn-building-blocks/src/test/java/org/onap/so/bpmn/infrastructure/bpmn/subprocess/BuildingBlockValidatorRunnerTest.java b/bpmn/so-bpmn-building-blocks/src/test/java/org/onap/so/bpmn/infrastructure/bpmn/subprocess/BuildingBlockValidatorRunnerTest.java
new file mode 100644 (file)
index 0000000..ea0fb95
--- /dev/null
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.so.bpmn.infrastructure.bpmn.subprocess;
+
+import static org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareAssertions.assertThat;
+import static org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.job;
+import static org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.execute;
+import org.camunda.bpm.engine.runtime.Job;
+import org.camunda.bpm.engine.runtime.ProcessInstance;
+import org.camunda.bpm.engine.task.Task;
+import org.junit.Test;
+import org.onap.so.bpmn.BaseBPMNTest;
+
+public class BuildingBlockValidatorRunnerTest extends BaseBPMNTest{
+    @Test
+    public void sunnyDayActivateNetwork_Test() throws InterruptedException {
+       variables.put("flowToBeCalled","CreateVolumeGroupBB");
+        ProcessInstance pi = runtimeService.startProcessInstanceByKey("BuildingBlockValidatorRunnerTest", variables);
+        assertThat(pi).isNotNull();
+        
+        execute(job());
+        execute(job());
+        assertThat(pi).isStarted().hasPassedInOrder("ServiceTask_1","ServiceTask_2","ServiceTask_3");     
+        assertThat(pi).isEnded();
+    }
+}
diff --git a/bpmn/so-bpmn-building-blocks/src/test/resources/META-INF/processes.xml b/bpmn/so-bpmn-building-blocks/src/test/resources/META-INF/processes.xml
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/bpmn/so-bpmn-building-blocks/src/test/resources/subprocess/BuildingBlock/BuildingBlockValidatorRunnerTest.bpmn b/bpmn/so-bpmn-building-blocks/src/test/resources/subprocess/BuildingBlock/BuildingBlockValidatorRunnerTest.bpmn
new file mode 100644 (file)
index 0000000..446dfe3
--- /dev/null
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="1.8.2">
+  <bpmn:process id="BuildingBlockValidatorRunnerTest" name="BuildingBlockValidatorRunnerTest" isExecutable="true">
+    <bpmn:startEvent id="StartEvent_1">
+      <bpmn:outgoing>SequenceFlow_0vof4nz</bpmn:outgoing>
+    </bpmn:startEvent>
+    <bpmn:serviceTask id="ServiceTask_1" name="PreValidate" camunda:expression="${BuildingBlockValidatorRunner.preValidate(flowToBeCalled, InjectExecution.execute(execution, execution.getVariable(&#34;gBuildingBlockExecution&#34;)))}">
+      <bpmn:incoming>SequenceFlow_0vof4nz</bpmn:incoming>
+      <bpmn:outgoing>SequenceFlow_14nr57b</bpmn:outgoing>
+    </bpmn:serviceTask>
+    <bpmn:serviceTask id="ServiceTask_2" name="PreValidate" camunda:expression="${BuildingBlockValidatorRunner.preValidate(flowToBeCalled, InjectExecution.execute(execution, execution.getVariable(&#34;gBuildingBlockExecution&#34;)))}">
+      <bpmn:incoming>SequenceFlow_1vsqhv8</bpmn:incoming>
+      <bpmn:outgoing>SequenceFlow_04xg7yh</bpmn:outgoing>
+    </bpmn:serviceTask>
+    <bpmn:sequenceFlow id="SequenceFlow_0vof4nz" sourceRef="StartEvent_1" targetRef="ServiceTask_1" />
+    <bpmn:endEvent id="EndEvent_1wyjfsw">
+      <bpmn:incoming>SequenceFlow_06wt67a</bpmn:incoming>
+    </bpmn:endEvent>
+    <bpmn:intermediateCatchEvent id="IntermediateCatchEvent_1" name="RetryTimer">
+      <bpmn:incoming>SequenceFlow_14nr57b</bpmn:incoming>
+      <bpmn:outgoing>SequenceFlow_1vsqhv8</bpmn:outgoing>
+      <bpmn:timerEventDefinition>
+        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT10S</bpmn:timeDuration>
+      </bpmn:timerEventDefinition>
+    </bpmn:intermediateCatchEvent>
+    <bpmn:sequenceFlow id="SequenceFlow_14nr57b" sourceRef="ServiceTask_1" targetRef="IntermediateCatchEvent_1" />
+    <bpmn:sequenceFlow id="SequenceFlow_1vsqhv8" sourceRef="IntermediateCatchEvent_1" targetRef="ServiceTask_2" />
+    <bpmn:intermediateCatchEvent id="IntermediateCatchEvent_2" name="RetryTimer">
+      <bpmn:incoming>SequenceFlow_04xg7yh</bpmn:incoming>
+      <bpmn:outgoing>SequenceFlow_0w8ajcb</bpmn:outgoing>
+      <bpmn:timerEventDefinition>
+        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT10S</bpmn:timeDuration>
+      </bpmn:timerEventDefinition>
+    </bpmn:intermediateCatchEvent>
+    <bpmn:serviceTask id="ServiceTask_3" name="PreValidate" camunda:expression="${BuildingBlockValidatorRunner.preValidate(flowToBeCalled, InjectExecution.execute(execution, execution.getVariable(&#34;gBuildingBlockExecution&#34;)))}">
+      <bpmn:incoming>SequenceFlow_0w8ajcb</bpmn:incoming>
+      <bpmn:outgoing>SequenceFlow_06wt67a</bpmn:outgoing>
+    </bpmn:serviceTask>
+    <bpmn:sequenceFlow id="SequenceFlow_04xg7yh" sourceRef="ServiceTask_2" targetRef="IntermediateCatchEvent_2" />
+    <bpmn:sequenceFlow id="SequenceFlow_0w8ajcb" sourceRef="IntermediateCatchEvent_2" targetRef="ServiceTask_3" />
+    <bpmn:sequenceFlow id="SequenceFlow_06wt67a" sourceRef="ServiceTask_3" targetRef="EndEvent_1wyjfsw" />
+  </bpmn:process>
+  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
+    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="BuildingBlockValidatorRunnerTest">
+      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
+        <dc:Bounds x="320" y="131" width="36" height="36" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="293" y="167" width="90" height="20" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="ServiceTask_0k9j83h_di" bpmnElement="ServiceTask_1">
+        <dc:Bounds x="435" y="109" width="100" height="80" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="ServiceTask_06izmow_di" bpmnElement="ServiceTask_2">
+        <dc:Bounds x="685" y="109" width="100" height="80" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge id="SequenceFlow_0vof4nz_di" bpmnElement="SequenceFlow_0vof4nz">
+        <di:waypoint xsi:type="dc:Point" x="356" y="149" />
+        <di:waypoint xsi:type="dc:Point" x="435" y="149" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="395.5" y="128" width="0" height="12" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNShape id="EndEvent_1wyjfsw_di" bpmnElement="EndEvent_1wyjfsw">
+        <dc:Bounds x="1106" y="131" width="36" height="36" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="1079" y="171" width="90" height="12" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="IntermediateCatchEvent_1xxf97p_di" bpmnElement="IntermediateCatchEvent_1">
+        <dc:Bounds x="596" y="131" width="36" height="36" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="587" y="106" width="55" height="12" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge id="SequenceFlow_14nr57b_di" bpmnElement="SequenceFlow_14nr57b">
+        <di:waypoint xsi:type="dc:Point" x="535" y="149" />
+        <di:waypoint xsi:type="dc:Point" x="596" y="149" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="565.5" y="128" width="0" height="12" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="SequenceFlow_1vsqhv8_di" bpmnElement="SequenceFlow_1vsqhv8">
+        <di:waypoint xsi:type="dc:Point" x="632" y="149" />
+        <di:waypoint xsi:type="dc:Point" x="685" y="149" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="658.5" y="128" width="0" height="12" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNShape id="IntermediateCatchEvent_1hx48zh_di" bpmnElement="IntermediateCatchEvent_2">
+        <dc:Bounds x="853" y="131" width="36" height="36" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="844" y="106" width="55" height="12" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="ServiceTask_059dhj6_di" bpmnElement="ServiceTask_3">
+        <dc:Bounds x="943" y="109" width="100" height="80" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge id="SequenceFlow_04xg7yh_di" bpmnElement="SequenceFlow_04xg7yh">
+        <di:waypoint xsi:type="dc:Point" x="785" y="149" />
+        <di:waypoint xsi:type="dc:Point" x="853" y="149" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="819" y="128" width="0" height="12" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="SequenceFlow_0w8ajcb_di" bpmnElement="SequenceFlow_0w8ajcb">
+        <di:waypoint xsi:type="dc:Point" x="889" y="149" />
+        <di:waypoint xsi:type="dc:Point" x="943" y="149" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="916" y="128" width="0" height="12" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="SequenceFlow_06wt67a_di" bpmnElement="SequenceFlow_06wt67a">
+        <di:waypoint xsi:type="dc:Point" x="1043" y="149" />
+        <di:waypoint xsi:type="dc:Point" x="1106" y="149" />
+        <bpmndi:BPMNLabel>
+          <dc:Bounds x="1074.5" y="128" width="0" height="12" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</bpmn:definitions>
index 3c08779..1be4989 100644 (file)
@@ -240,11 +240,9 @@ public class DoCreateAllottedResourceBRG extends AbstractServiceTaskProcessor{
                        if(obj.has("result-data")){
                                JSONObject ob = obj.getJSONArray("result-data").getJSONObject(0)
                                String resourceLink = ob.getString("resource-link")
+                               AAIResourceUri siUri = AAIUriFactory.createResourceFromExistingURI(AAIObjectType.SERVICE_INSTANCE, new URI(resourceLink))
 
-                               String[] split = resourceLink.split("/aai/")
-                               String siRelatedLink = "/aai/" + split[1]
-
-                               execution.setVariable("PSI_resourceLink", resourceLink)
+                               execution.setVariable("PSI_resourceLink", siUri)
                        }else{
                                exceptionUtil.buildAndThrowWorkflowException(execution, 2500, "Service instance was not found in aai")
                        }
@@ -277,26 +275,9 @@ public class DoCreateAllottedResourceBRG extends AbstractServiceTaskProcessor{
 
                        //AAI PUT
                        AaiUtil aaiUriUtil = new AaiUtil(this)
-                       String aaiEndpoint = UrnPropertiesReader.getVariable("aai.endpoint", execution)
-                       String siResourceLink= execution.getVariable("PSI_resourceLink")
-
-                       String siUri = ""
-                       msoLogger.debug("PSI_resourceLink:" + siResourceLink)
-
-                       if(!isBlank(siResourceLink)) {
-                               msoLogger.debug("Incoming PSI Resource Link is: " + siResourceLink)
-                               String[] split = siResourceLink.split("/aai/")
-                               siUri = "/aai/" + split[1]
-                       }
-                       else
-                       {
-                               msg = "Parent Service Link in AAI is null"
-                               msoLogger.debug(msg)
-                               exceptionUtil.buildAndThrowWorkflowException(execution, 500, msg)
-                       }
-
-                       arUrl = "${aaiEndpoint}${siUri}"  + "/allotted-resources/allotted-resource/" + UriUtils.encode(allottedResourceId,"UTF-8")
-                       execution.setVariable("aaiARPath", arUrl)
+                       AAIResourceUri siResourceLink= execution.getVariable("PSI_resourceLink")
+                       AllottedResourceUtils arUtils = new AllottedResourceUtils(this)
+                       execution.setVariable("aaiARPath", arUtils.createARUrl(execution, siResourceLink, allottedResourceId))
                        msoLogger.debug("GET AllottedResource AAI URL is:\n" + arUrl)
 
                        String namespace = aaiUriUtil.getNamespaceFromUri(execution, arUrl)
index 5f9b4b8..48eb1c8 100644 (file)
 
 package org.onap.so.bpmn.vcpe.scripts;
 
+import static org.apache.commons.lang3.StringUtils.*
+
+import org.apache.commons.lang3.*
+import org.camunda.bpm.engine.delegate.BpmnError
+import org.camunda.bpm.engine.delegate.DelegateExecution
 import org.onap.so.bpmn.common.scripts.*;
-import org.onap.so.bpmn.common.scripts.AaiUtil
 import org.onap.so.bpmn.core.RollbackData
-import org.onap.so.bpmn.core.WorkflowException
 import org.onap.so.bpmn.core.UrnPropertiesReader
+import org.onap.so.bpmn.core.WorkflowException
 import org.onap.so.bpmn.core.json.JsonUtils
-import org.onap.so.rest.APIResponse
-
-import java.util.UUID;
-import org.camunda.bpm.engine.delegate.BpmnError
-import org.camunda.bpm.engine.delegate.DelegateExecution
-import org.apache.commons.lang3.*
-import org.springframework.web.util.UriUtils;
-import static org.apache.commons.lang3.StringUtils.*
-
+import org.onap.so.client.aai.AAIObjectType
+import org.onap.so.client.aai.entities.uri.AAIResourceUri
+import org.onap.so.client.aai.entities.uri.AAIUriFactory
 import org.onap.so.logger.MessageEnum
 import org.onap.so.logger.MsoLogger
+import org.onap.so.rest.APIResponse
 
 /**
  * This groovy class supports the <class>DoCreateAllottedResourceTXC.bpmn</class> process.
@@ -204,26 +203,9 @@ public class DoCreateAllottedResourceTXC extends AbstractServiceTaskProcessor{
 
                        //AAI PUT
                        AaiUtil aaiUriUtil = new AaiUtil(this)
-                       String aaiEndpoint = UrnPropertiesReader.getVariable("aai.endpoint", execution)
-                       String siResourceLink= execution.getVariable("PSI_resourceLink")
-
-                       String siUri = ""
-                       msoLogger.debug("PSI_resourceLink:" + siResourceLink)
-
-                       if(!isBlank(siResourceLink)) {
-                               msoLogger.debug("Incoming PSI Resource Link is: " + siResourceLink)
-                               String[] split = siResourceLink.split("/aai/")
-                               siUri = "/aai/" + split[1]
-                       }
-                       else
-                       {
-                               msg = "Parent Service Link in AAI is null"
-                               msoLogger.debug(msg)
-                               exceptionUtil.buildAndThrowWorkflowException(execution, 500, msg)
-                       }
-
-                       arUrl = "${aaiEndpoint}${siUri}"  + "/allotted-resources/allotted-resource/" + UriUtils.encode(allottedResourceId,"UTF-8")
-                       execution.setVariable("aaiARPath", arUrl)
+                       AAIResourceUri siResourceLink= execution.getVariable("PSI_resourceLink")
+                       AllottedResourceUtils arUtils = new AllottedResourceUtils(this)
+                       execution.setVariable("aaiARPath", arUtils.createARUrl(execution, siResourceLink, allottedResourceId))
                        msoLogger.debug("GET AllottedResource AAI URL is:\n" + arUrl)
 
                        String namespace = aaiUriUtil.getNamespaceFromUri(execution, arUrl)
index 87c04d7..38261c0 100644 (file)
@@ -54,6 +54,7 @@ import org.springframework.stereotype.Component;
 @Component
 public class AAIUpdateTasks {
        private static final MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL, AAIUpdateTasks.class);
+       private static final String ALACARTE = "aLaCarte";
        private static final String MULTI_STAGE_DESIGN_OFF = "false";
        private static final String MULTI_STAGE_DESIGN_ON = "true";
        @Autowired
@@ -196,7 +197,8 @@ public class AAIUpdateTasks {
                        if (vnf.getModelInfoGenericVnf() != null) {
                                multiStageDesign = vnf.getModelInfoGenericVnf().getMultiStageDesign();
                        }
-                       if (multiStageDesign != null && multiStageDesign.equalsIgnoreCase(MULTI_STAGE_DESIGN_ON)) {
+                       boolean aLaCarte = (boolean) execution.getVariable(ALACARTE);
+                       if (aLaCarte && multiStageDesign != null && multiStageDesign.equalsIgnoreCase(MULTI_STAGE_DESIGN_ON)) {
                                aaiVfModuleResources.updateOrchestrationStatusVfModule(vfModule,vnf,OrchestrationStatus.PENDING_ACTIVATION);
                        }
                        else {
index 88ae374..b0063c1 100644 (file)
@@ -45,6 +45,7 @@ public class OrchestrationStatusValidator {
        private static final String UNKNOWN_RESOURCE_TYPE = "Building Block (%s) not set up correctly in Orchestration_Status_Validation table in CatalogDB. ResourceType=(%s), TargetAction=(%s)";
        private static final String ORCHESTRATION_VALIDATION_FAIL = "Orchestration Status Validation failed. ResourceType=(%s), TargetAction=(%s), OrchestrationStatus=(%s)";
        private static final String ORCHESTRATION_STATUS_VALIDATION_RESULT = "orchestrationStatusValidationResult";
+       private static final String ALACARTE = "aLaCarte";
        private static final String MULTI_STAGE_DESIGN_OFF = "false";
        private static final String MULTI_STAGE_DESIGN_ON = "true";
        
@@ -62,8 +63,10 @@ public class OrchestrationStatusValidator {
                        
                        execution.setVariable(ORCHESTRATION_STATUS_VALIDATION_RESULT, null);
                        
-                       String buildingBlockFlowName = execution.getFlowToBeCalled();
+                       boolean aLaCarte = (boolean) execution.getVariable(ALACARTE);
                        
+                       String buildingBlockFlowName = execution.getFlowToBeCalled();                   
+                                       
                        BuildingBlockDetail buildingBlockDetail = catalogDbClient.getBuildingBlockDetail(buildingBlockFlowName);
                        
                        if (buildingBlockDetail == null) {
@@ -112,7 +115,7 @@ public class OrchestrationStatusValidator {
                        }
                        OrchestrationStatusStateTransitionDirective orchestrationStatusStateTransitionDirective = catalogDbClient.getOrchestrationStatusStateTransitionDirective(buildingBlockDetail.getResourceType(), orchestrationStatus, buildingBlockDetail.getTargetAction());
                        
-                       if(ResourceType.VF_MODULE.equals(buildingBlockDetail.getResourceType()) && OrchestrationAction.CREATE.equals(buildingBlockDetail.getTargetAction()) &&
+                       if(aLaCarte && ResourceType.VF_MODULE.equals(buildingBlockDetail.getResourceType()) && OrchestrationAction.CREATE.equals(buildingBlockDetail.getTargetAction()) &&
                                        OrchestrationStatus.PENDING_ACTIVATION.equals(orchestrationStatus)) {                           
                                org.onap.so.bpmn.servicedecomposition.bbobjects.GenericVnf genericVnf = extractPojosForBB.extractByKey(execution, ResourceKey.GENERIC_VNF_ID, execution.getLookupMap().get(ResourceKey.GENERIC_VNF_ID));
                                orchestrationStatusStateTransitionDirective = processPossibleSecondStageofVfModuleCreate(execution, previousOrchestrationStatusValidationResult,
@@ -138,11 +141,11 @@ public class OrchestrationStatusValidator {
        private OrchestrationStatusStateTransitionDirective processPossibleSecondStageofVfModuleCreate(BuildingBlockExecution execution, OrchestrationStatusValidationDirective previousOrchestrationStatusValidationResult,
                        org.onap.so.bpmn.servicedecomposition.bbobjects.GenericVnf genericVnf, OrchestrationStatusStateTransitionDirective orchestrationStatusStateTransitionDirective) {               
                if (previousOrchestrationStatusValidationResult != null && previousOrchestrationStatusValidationResult.equals(OrchestrationStatusValidationDirective.SILENT_SUCCESS)) {                 
-                       String multiStageDesign = "false";                      
+                       String multiStageDesign = MULTI_STAGE_DESIGN_OFF;                       
                        if (genericVnf.getModelInfoGenericVnf() != null) {
                                multiStageDesign = genericVnf.getModelInfoGenericVnf().getMultiStageDesign();
                        }
-                       if (multiStageDesign != null && multiStageDesign.equalsIgnoreCase("true")) {                            
+                       if (multiStageDesign != null && multiStageDesign.equalsIgnoreCase(MULTI_STAGE_DESIGN_ON)) {                             
                                orchestrationStatusStateTransitionDirective.setFlowDirective(OrchestrationStatusValidationDirective.CONTINUE);                                          
                        }                                       
                }
index 560f2a9..1a302c2 100644 (file)
@@ -169,6 +169,7 @@ public class AAIUpdateTasksTest extends BaseTaskTest{
        
        @Test
        public void updateOrchestrationStatusAssignedOrPendingActivationVfModuleNoMultiStageTest() throws Exception {
+               execution.setVariable("aLaCarte", true);
                ModelInfoGenericVnf modelInfoGenericVnf = new ModelInfoGenericVnf();
                modelInfoGenericVnf.setMultiStageDesign("false");
                genericVnf.setModelInfoGenericVnf(modelInfoGenericVnf);
@@ -178,8 +179,21 @@ public class AAIUpdateTasksTest extends BaseTaskTest{
                assertEquals("", vfModule.getHeatStackId());
        }
        
+       @Test
+       public void updateOrchestrationStatusAssignedOrPendingActivationVfModuleMultiStageButNotAlacarteTest() throws Exception {
+               execution.setVariable("aLaCarte", false);
+               ModelInfoGenericVnf modelInfoGenericVnf = new ModelInfoGenericVnf();
+               modelInfoGenericVnf.setMultiStageDesign("true");
+               genericVnf.setModelInfoGenericVnf(modelInfoGenericVnf);
+               doNothing().when(aaiVfModuleResources).updateOrchestrationStatusVfModule(vfModule, genericVnf, OrchestrationStatus.ASSIGNED);
+               aaiUpdateTasks.updateOrchestrationStatusAssignedOrPendingActivationVfModule(execution);
+               verify(aaiVfModuleResources, times(1)).updateOrchestrationStatusVfModule(vfModule, genericVnf, OrchestrationStatus.ASSIGNED);
+               assertEquals("", vfModule.getHeatStackId());
+       }
+       
        @Test
        public void updateOrchestrationStatusAssignedOrPendingActivationVfModuleWithMultiStageTest() throws Exception {
+               execution.setVariable("aLaCarte", true);
                ModelInfoGenericVnf modelInfoGenericVnf = new ModelInfoGenericVnf();
                modelInfoGenericVnf.setMultiStageDesign("true");
                genericVnf.setModelInfoGenericVnf(modelInfoGenericVnf);
@@ -191,6 +205,7 @@ public class AAIUpdateTasksTest extends BaseTaskTest{
        
        @Test
        public void updateOrchestrationStatusAssignedOrPendingActivationVfModuleExceptionTest() throws Exception {
+               execution.setVariable("aLaCarte", true);
                doThrow(Exception.class).when(aaiVfModuleResources).updateOrchestrationStatusVfModule(vfModule, genericVnf, OrchestrationStatus.ASSIGNED);
                
                expectedException.expect(BpmnError.class);
index fa5081d..5ef020d 100644 (file)
@@ -51,7 +51,7 @@ public class OrchestrationStatusValidatorTest extends BaseTaskTest {
        public void test_validateOrchestrationStatus() throws Exception {
                String flowToBeCalled = "AssignServiceInstanceBB";
                setServiceInstance().setOrchestrationStatus(OrchestrationStatus.PRECREATED);
-               
+               execution.setVariable("aLaCarte", true);
                execution.setVariable("flowToBeCalled", flowToBeCalled);
                
                BuildingBlockDetail buildingBlockDetail = new BuildingBlockDetail();
@@ -91,6 +91,7 @@ public class OrchestrationStatusValidatorTest extends BaseTaskTest {
                si.setConfigurations(configurations);
                
                execution.setVariable("flowToBeCalled", flowToBeCalled);
+               execution.setVariable("aLaCarte", true);
                
                BuildingBlockDetail buildingBlockDetail = new BuildingBlockDetail();
                buildingBlockDetail.setBuildingBlockName("UnassignFabricConfigurationBB");
@@ -188,6 +189,7 @@ public class OrchestrationStatusValidatorTest extends BaseTaskTest {
                String flowToBeCalled = "UnassignServiceInstanceBB";
                
                execution.setVariable("flowToBeCalled", flowToBeCalled);
+               execution.setVariable("aLaCarte", true);
                
                BuildingBlockDetail buildingBlockDetail = new BuildingBlockDetail();
                buildingBlockDetail.setBuildingBlockName("UnassignServiceInstanceBB");
@@ -209,7 +211,7 @@ public class OrchestrationStatusValidatorTest extends BaseTaskTest {
                String flowToBeCalled = "CreateVfModuleBB";
                                
                execution.setVariable("orchestrationStatusValidationResult", OrchestrationStatusValidationDirective.SILENT_SUCCESS);
-               
+               execution.setVariable("aLaCarte", true);
                execution.setVariable("flowToBeCalled", flowToBeCalled);
                
                GenericVnf genericVnf = buildGenericVnf();
@@ -246,7 +248,7 @@ public class OrchestrationStatusValidatorTest extends BaseTaskTest {
                String flowToBeCalled = "CreateVfModuleBB";
                
                execution.setVariable("orchestrationStatusValidationResult", OrchestrationStatusValidationDirective.CONTINUE);
-               
+               execution.setVariable("aLaCarte", true);
                execution.setVariable("flowToBeCalled", flowToBeCalled);
                
                GenericVnf genericVnf = buildGenericVnf();
@@ -282,7 +284,7 @@ public class OrchestrationStatusValidatorTest extends BaseTaskTest {
                String flowToBeCalled = "CreateVfModuleBB";             
                
                execution.setVariable("orchestrationStatusValidationResult", OrchestrationStatusValidationDirective.SILENT_SUCCESS);
-               
+               execution.setVariable("aLaCarte", true);
                execution.setVariable("flowToBeCalled", flowToBeCalled);
                
                GenericVnf genericVnf = buildGenericVnf();
@@ -318,7 +320,7 @@ public class OrchestrationStatusValidatorTest extends BaseTaskTest {
                String flowToBeCalled = "CreateVfModuleBB";
                                
                execution.setVariable("orchestrationStatusValidationResult", OrchestrationStatusValidationDirective.SILENT_SUCCESS);
-               
+               execution.setVariable("aLaCarte", true);
                execution.setVariable("flowToBeCalled", flowToBeCalled);
                
                GenericVnf genericVnf = buildGenericVnf();
@@ -354,7 +356,7 @@ public class OrchestrationStatusValidatorTest extends BaseTaskTest {
                String flowToBeCalled = "CreateVfModuleBB";
                                
                execution.setVariable("orchestrationStatusValidationResult", OrchestrationStatusValidationDirective.SILENT_SUCCESS);
-               
+               execution.setVariable("aLaCarte", true);
                execution.setVariable("flowToBeCalled", flowToBeCalled);
                
                GenericVnf genericVnf = buildGenericVnf();
@@ -384,4 +386,40 @@ public class OrchestrationStatusValidatorTest extends BaseTaskTest {
                
                assertEquals(OrchestrationStatusValidationDirective.SILENT_SUCCESS, execution.getVariable("orchestrationStatusValidationResult"));
        }
+       
+       @Test
+       public void test_validateOrchestrationStatusSecondStageOfMultiStageWrongAlacarteValueVfModule() throws Exception {
+               String flowToBeCalled = "CreateVfModuleBB";
+                               
+               execution.setVariable("orchestrationStatusValidationResult", OrchestrationStatusValidationDirective.SILENT_SUCCESS);
+               execution.setVariable("aLaCarte", false);
+               execution.setVariable("flowToBeCalled", flowToBeCalled);
+               
+               GenericVnf genericVnf = buildGenericVnf();
+               ModelInfoGenericVnf modelInfoGenericVnf = genericVnf.getModelInfoGenericVnf();
+               modelInfoGenericVnf.setMultiStageDesign("true");
+               setGenericVnf().setModelInfoGenericVnf(modelInfoGenericVnf);
+               setVfModule().setOrchestrationStatus(OrchestrationStatus.PENDING_ACTIVATION);
+               
+               BuildingBlockDetail buildingBlockDetail = new BuildingBlockDetail();
+               buildingBlockDetail.setBuildingBlockName("CreateVfModuleBB");
+               buildingBlockDetail.setId(1);
+               buildingBlockDetail.setResourceType(ResourceType.VF_MODULE);
+               buildingBlockDetail.setTargetAction(OrchestrationAction.CREATE);
+               
+               doReturn(buildingBlockDetail).when(catalogDbClient).getBuildingBlockDetail(flowToBeCalled);
+               
+               OrchestrationStatusStateTransitionDirective orchestrationStatusStateTransitionDirective = new OrchestrationStatusStateTransitionDirective();
+               orchestrationStatusStateTransitionDirective.setFlowDirective(OrchestrationStatusValidationDirective.SILENT_SUCCESS);
+               orchestrationStatusStateTransitionDirective.setId(1);
+               orchestrationStatusStateTransitionDirective.setOrchestrationStatus(OrchestrationStatus.PENDING_ACTIVATION);
+               orchestrationStatusStateTransitionDirective.setResourceType(ResourceType.VF_MODULE);
+               orchestrationStatusStateTransitionDirective.setTargetAction(OrchestrationAction.ACTIVATE);
+               
+               doReturn(orchestrationStatusStateTransitionDirective).when(catalogDbClient).getOrchestrationStatusStateTransitionDirective(ResourceType.VF_MODULE, OrchestrationStatus.PENDING_ACTIVATION, OrchestrationAction.CREATE);
+               
+               orchestrationStatusValidator.validateOrchestrationStatus(execution);
+               
+               assertEquals(OrchestrationStatusValidationDirective.SILENT_SUCCESS, execution.getVariable("orchestrationStatusValidationResult"));
+       }
 }
index 3589075..77c6108 100644 (file)
@@ -25,6 +25,7 @@ import java.net.URI;
 import org.onap.so.client.aai.AAIObjectPlurals;
 import org.onap.so.client.aai.AAIObjectType;
 
+
 public class AAIUriFactory {
        
        /**
@@ -53,7 +54,12 @@ public class AAIUriFactory {
                return new NodesUri(type, values);
                
        }
-
+       
+       public static AAIResourceUri createNodesUri(AAIObjectPlurals type) {
+               return new NodesUri(type);
+               
+       }
+       
        /**
         * This method should only be used to wrap a URI retrieved from A&AI contained within an object response
         * 
@@ -64,7 +70,21 @@ public class AAIUriFactory {
        public static AAIResourceUri createResourceFromExistingURI(AAIObjectType type, URI uri) {
                return new AAISimpleUri(type, uri);
        }
-
+       
+       
+       /**
+        * creates an AAIResourceUri from a parentUri
+        * 
+        * @param parentUri
+        * @param childType
+        * @param childValues
+        * @return
+        */
+       public static AAIResourceUri createResourceFromParentURI(AAIResourceUri parentUri, AAIObjectType childType, Object... childValues) {
+               
+               return new AAISimpleUri(parentUri, childType, childValues);
+       }
+       
        /**
         * Creates a uri for a plural type e.g. /cloud-infrastructure/pservers
         * 
@@ -88,4 +108,4 @@ public class AAIUriFactory {
                return new AAISimpleUri(type, values);
        
        }
-}
+}
\ No newline at end of file