Fix path issues 78/102778/4
authorJim Hahn <jrh3@att.com>
Mon, 2 Mar 2020 21:17:36 +0000 (16:17 -0500)
committerJim Hahn <jrh3@att.com>
Tue, 3 Mar 2020 01:12:53 +0000 (01:12 +0000)
A&AI tenant query is prepending the target entity with "/", but it should
not.  Fixed it.
Modified A&AI and SO actors to get path prefixes from parameters.
Fixed a bug in an A&AI simulator response (extra "}" at the end.

Issue-ID: POLICY-2349
Signed-off-by: Jim Hahn <jrh3@att.com>
Change-Id: I71f8b1e5fb8a4bd29b4f616a7757d366c7d58127

models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperation.java
models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiGetOperation.java
models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperationTest.java
models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiGetOperationTest.java
models-interactions/model-actors/actor.aai/src/test/resources/service.yaml
models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/VfModuleCreate.java
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/VfModuleCreateTest.java
models-interactions/model-actors/actor.so/src/test/resources/service.yaml
models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicHttpOperation.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpOperation.java
models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/AaiSimulatorJaxRs.java

index 613b668..5b4aa05 100644 (file)
 package org.onap.policy.controlloop.actor.aai;
 
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.concurrent.CompletableFuture;
 import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation.Builder;
+import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import lombok.Getter;
@@ -54,6 +57,7 @@ public class AaiCustomQueryOperation extends HttpOperation<String> {
     public static final String RESOURCE_LINK = "resource-link";
     public static final String RESULT_DATA = "result-data";
 
+    // TODO make this configurable
     private static final String PREFIX = "/aai/v16";
 
     @Getter
@@ -89,21 +93,38 @@ public class AaiCustomQueryOperation extends HttpOperation<String> {
     @Override
     protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
 
-        Map<String, String> request = makeRequest();
+        final Map<String, String> request = makeRequest();
+        Map<String, Object> headers = makeHeaders();
 
-        Entity<Map<String, String>> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
+        StringBuilder str = new StringBuilder(getClient().getBaseUrl());
 
-        Map<String, Object> headers = makeHeaders();
+        String path = getPath();
+        WebTarget web = getClient().getWebTarget().path(path);
+        str.append(path);
+
+        web = addQuery(web, str, "?", "format", "resource");
+
+        Builder webldr = web.request();
+        for (Entry<String, Object> header : headers.entrySet()) {
+            webldr.header(header.getKey(), header.getValue());
+        }
 
-        headers.put("Accept", MediaType.APPLICATION_JSON);
-        String url = makeUrl();
+        String url = str.toString();
 
         logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
 
-        // @formatter:off
-        return handleResponse(outcome, url,
-            callback -> getClient().put(callback, makePath(), entity, headers));
-        // @formatter:on
+        Entity<Map<String, String>> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
+
+        return handleResponse(outcome, url, callback -> webldr.async().put(entity, callback));
+    }
+
+    private WebTarget addQuery(WebTarget web, StringBuilder str, String separator, String name, String value) {
+        str.append(separator);
+        str.append(name);
+        str.append('=');
+        str.append(value);
+
+        return web.queryParam(name, value);
     }
 
     /**
index 408c1a0..4c41241 100644 (file)
 package org.onap.policy.controlloop.actor.aai;
 
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
+import javax.ws.rs.client.Invocation.Builder;
+import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.onap.policy.aai.AaiConstants;
@@ -92,14 +95,35 @@ public class AaiGetOperation extends HttpOperation<StandardCoderObject> {
         Map<String, Object> headers = makeHeaders();
 
         headers.put("Accept", MediaType.APPLICATION_JSON);
-        String url = makeUrl();
+
+        StringBuilder str = new StringBuilder(getClient().getBaseUrl());
+
+        String path = getPath();
+        WebTarget web = getClient().getWebTarget().path(path);
+        str.append(path);
+
+        web = addQuery(web, str, "?", "search-node-type", "vserver");
+        web = addQuery(web, str, "&", "filter", "vserver-name:EQUALS:" + params.getTargetEntity());
+
+        Builder webldr = web.request();
+        for (Entry<String, Object> header : headers.entrySet()) {
+            webldr.header(header.getKey(), header.getValue());
+        }
+
+        String url = str.toString();
 
         logMessage(EventType.OUT, CommInfrastructure.REST, url, null);
 
-        // @formatter:off
-        return handleResponse(outcome, url,
-            callback -> getClient().get(callback, makePath(), headers));
-        // @formatter:on
+        return handleResponse(outcome, url, callback -> webldr.async().get(callback));
+    }
+
+    private WebTarget addQuery(WebTarget web, StringBuilder str, String separator, String name, String value) {
+        str.append(separator);
+        str.append(name);
+        str.append('=');
+        str.append(value);
+
+        return web.queryParam(name, value);
     }
 
     @Override
@@ -109,7 +133,7 @@ public class AaiGetOperation extends HttpOperation<StandardCoderObject> {
 
     @Override
     public String makePath() {
-        return (getPath() + "/" + params.getTargetEntity());
+        return (getPath() + params.getTargetEntity());
     }
 
     /**
index 78ffc50..c24e45d 100644 (file)
@@ -38,6 +38,7 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.InvocationCallback;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -109,11 +110,12 @@ public class AaiCustomQueryOperationTest extends BasicAaiOperation<Map<String, S
     }
 
     @Test
+    @SuppressWarnings("unchecked")
     public void testStartOperationAsync_testStartPreprocessorAsync_testMakeRequest_testPostProcess() throws Exception {
         // need two responses
         when(rawResponse.readEntity(String.class)).thenReturn(makeTenantReply()).thenReturn(makeCqReply());
-        when(client.get(any(), any(), any())).thenAnswer(provideResponse(rawResponse));
-        when(client.put(any(), any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+        when(webAsync.get(any(InvocationCallback.class))).thenAnswer(provideResponse(rawResponse));
+        when(webAsync.put(any(), any(InvocationCallback.class))).thenAnswer(provideResponse(rawResponse, 1));
 
         CompletableFuture<OperationOutcome> future2 = oper.start();
 
@@ -131,13 +133,14 @@ public class AaiCustomQueryOperationTest extends BasicAaiOperation<Map<String, S
      * Tests when preprocessor step is not needed.
      */
     @Test
+    @SuppressWarnings("unchecked")
     public void testStartOperationAsync_testStartPreprocessorAsyncNotNeeded() throws Exception {
         // pre-load the tenant data
         final StandardCoderObject data = preloadTenantData();
 
         // only need one response
         when(rawResponse.readEntity(String.class)).thenReturn(makeCqReply());
-        when(client.put(any(), any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+        when(webAsync.put(any(), any(InvocationCallback.class))).thenAnswer(provideResponse(rawResponse, 1));
 
         CompletableFuture<OperationOutcome> future2 = oper.start();
 
@@ -157,17 +160,18 @@ public class AaiCustomQueryOperationTest extends BasicAaiOperation<Map<String, S
     }
 
     @Test
+    @SuppressWarnings("unchecked")
     public void testMakeRequest() throws Exception {
         // preload
         preloadTenantData();
 
         when(rawResponse.readEntity(String.class)).thenReturn(makeCqReply());
-        when(client.put(any(), any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+        when(webAsync.put(any(), any(InvocationCallback.class))).thenAnswer(provideResponse(rawResponse, 1));
 
         oper.start();
         executor.runAll(100);
 
-        verify(client).put(any(), any(), entityCaptor.capture(), any());
+        verify(webAsync).put(entityCaptor.capture(), any(InvocationCallback.class));
 
         // sort the request fields so they match the order in cq.json
         Map<String, String> request = new TreeMap<>(entityCaptor.getValue().getEntity());
@@ -176,12 +180,13 @@ public class AaiCustomQueryOperationTest extends BasicAaiOperation<Map<String, S
     }
 
     @Test
+    @SuppressWarnings("unchecked")
     public void testMakeRequestNoResourceLink() throws Exception {
         // pre-load EMPTY tenant data
         preloadTenantData(new StandardCoderObject());
 
         when(rawResponse.readEntity(String.class)).thenReturn(makeCqReply());
-        when(client.put(any(), any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+        when(webAsync.put(any(), any(InvocationCallback.class))).thenAnswer(provideResponse(rawResponse, 1));
 
         CompletableFuture<OperationOutcome> future2 = oper.start();
 
index 4681d9e..13560cc 100644 (file)
@@ -30,6 +30,7 @@ import static org.mockito.Mockito.when;
 
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
+import javax.ws.rs.client.InvocationCallback;
 import org.junit.Before;
 import org.junit.Test;
 import org.onap.policy.aai.AaiConstants;
@@ -69,13 +70,14 @@ public class AaiGetOperationTest extends BasicAaiOperation<Void> {
     }
 
     @Test
+    @SuppressWarnings("unchecked")
     public void testStartOperationAsync_testStartQueryAsync_testPostProcessResponse() throws Exception {
 
         // return a map in the reply
         Map<String, String> reply = Map.of(INPUT_FIELD, TEXT);
         when(rawResponse.readEntity(String.class)).thenReturn(new StandardCoder().encode(reply));
 
-        when(client.get(any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+        when(webAsync.get(any(InvocationCallback.class))).thenAnswer(provideResponse(rawResponse));
 
         CompletableFuture<OperationOutcome> future2 = oper.startOperationAsync(1, outcome);
         assertFalse(future2.isDone());
@@ -95,12 +97,13 @@ public class AaiGetOperationTest extends BasicAaiOperation<Void> {
      * Tests startOperationAsync() when there's a failure.
      */
     @Test
+    @SuppressWarnings("unchecked")
     public void testStartOperationAsyncFailure() throws Exception {
 
         when(rawResponse.getStatus()).thenReturn(500);
         when(rawResponse.readEntity(String.class)).thenReturn("");
 
-        when(client.get(any(), any(), any())).thenAnswer(provideResponse(rawResponse));
+        when(webAsync.get(any(InvocationCallback.class))).thenAnswer(provideResponse(rawResponse));
 
         CompletableFuture<OperationOutcome> future2 = oper.startOperationAsync(1, outcome);
         assertFalse(future2.isDone());
@@ -121,7 +124,7 @@ public class AaiGetOperationTest extends BasicAaiOperation<Void> {
 
     @Test
     public void testMakePath() {
-        assertEquals(PATH + "/" + TARGET_ENTITY, oper.makePath());
+        assertEquals(PATH + TARGET_ENTITY, oper.makePath());
     }
 
     @Test
index 9f6561d..d0c11be 100644 (file)
@@ -21,7 +21,7 @@ httpClients:
 - clientName: my-client
   hostname: localhost
   port: 80
-  basePath: base-url
+  basePath: base-url/
   managed: true
 actors:
   AAI:
index fb8fe60..4c35f9a 100644 (file)
@@ -107,7 +107,7 @@ public class VfModuleCreate extends SoOperation {
         resetGetCount();
 
         Pair<String, SoRequest> pair = makeRequest();
-        String path = pair.getLeft();
+        String path = getPath() + pair.getLeft();
         SoRequest request = pair.getRight();
 
         Entity<SoRequest> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
@@ -115,8 +115,6 @@ public class VfModuleCreate extends SoOperation {
 
         logMessage(EventType.OUT, CommInfrastructure.REST, url, request);
 
-        // TODO should this use "path" or the full "url"?
-
         return handleResponse(outcome, url, callback -> getClient().post(callback, path, entity, null));
     }
 
@@ -208,8 +206,8 @@ public class VfModuleCreate extends SoOperation {
         buildConfigurationParameters().ifPresent(request.getRequestDetails()::setConfigurationParameters);
 
         // compute the path
-        String path = "/serviceInstantiation/v7/serviceInstances/" + vnfServiceItem.getServiceInstanceId() + "/vnfs/"
-                        + vnfItem.getVnfId() + "/vfModules/scaleOut";
+        String path = "/serviceInstances/" + vnfServiceItem.getServiceInstanceId() + "/vnfs/" + vnfItem.getVnfId()
+                        + "/vfModules/scaleOut";
 
         return Pair.of(path, request);
     }
index 98c645b..8bd607f 100644 (file)
@@ -202,7 +202,7 @@ public class VfModuleCreateTest extends BasicSoOperation {
 
         // @formatter:off
         assertEquals(
-            "/serviceInstantiation/v7/serviceInstances/my-service-instance-id/vnfs/my-vnf-id/vfModules/scaleOut",
+            "/serviceInstances/my-service-instance-id/vnfs/my-vnf-id/vfModules/scaleOut",
             pair.getLeft());
         // @formatter:on
 
index b1ac162..4bf074f 100644 (file)
@@ -21,11 +21,11 @@ httpClients:
 - clientName: my-client
   hostname: localhost
   port: 80
-  basePath: base-url
+  basePath: base-url/
   managed: true
 actors:
   SO:
     clientName: my-client
     operations:
       VF Module Create:
-        path: create
\ No newline at end of file
+        path: serviceInstantiation/v7
\ No newline at end of file
index e803df8..6228756 100644 (file)
 
 package org.onap.policy.controlloop.actor.test;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.when;
 
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
+import javax.ws.rs.client.AsyncInvoker;
 import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.Invocation.Builder;
 import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.Response;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
@@ -55,6 +59,12 @@ public class BasicHttpOperation<Q> extends BasicOperation {
     @Mock
     protected HttpConfig config;
     @Mock
+    protected WebTarget webTarget;
+    @Mock
+    protected Builder webBuilder;
+    @Mock
+    protected AsyncInvoker webAsync;
+    @Mock
     protected HttpClient client;
     @Mock
     protected HttpClientFactory factory;
@@ -90,6 +100,14 @@ public class BasicHttpOperation<Q> extends BasicOperation {
 
         when(rawResponse.getStatus()).thenReturn(200);
 
+        when(webBuilder.async()).thenReturn(webAsync);
+
+        when(webTarget.request()).thenReturn(webBuilder);
+        when(webTarget.path(any())).thenReturn(webTarget);
+        when(webTarget.queryParam(any(), any())).thenReturn(webTarget);
+
+        when(client.getWebTarget()).thenReturn(webTarget);
+
         when(client.getBaseUrl()).thenReturn(BASE_URI);
 
         initConfig();
@@ -110,8 +128,19 @@ public class BasicHttpOperation<Q> extends BasicOperation {
      * @return a function that provides the response to the call
      */
     protected Answer<CompletableFuture<Response>> provideResponse(Response response) {
+        return provideResponse(response, 0);
+    }
+
+    /**
+     * Provides a response to an asynchronous HttpClient call.
+     *
+     * @param response response to be provided to the call
+     * @param index index of the callback within the arguments
+     * @return a function that provides the response to the call
+     */
+    protected Answer<CompletableFuture<Response>> provideResponse(Response response, int index) {
         return args -> {
-            InvocationCallback<Response> cb = args.getArgument(0);
+            InvocationCallback<Response> cb = args.getArgument(index);
             cb.completed(response);
             return CompletableFuture.completedFuture(response);
         };
index 4c007ea..aa98c0d 100644 (file)
@@ -116,7 +116,7 @@ public abstract class HttpOperation<T> extends OperationPartial {
     }
 
     /**
-     * Makes the URL to which the "get" request should be posted. This ir primarily used
+     * Makes the URL to which the "get" request should be posted. This is primarily used
      * for logging purposes. This particular method returns the base URL appended with the
      * return value from {@link #makePath()}.
      *
index 3b17723..8c96ee5 100644 (file)
@@ -73,7 +73,7 @@ public class AaiSimulatorJaxRs {
             return "{\"result-data\":[{\"resource-type\": \"vserver\",\"resource-link\":\"/aai/v15/"
                 + "cloud-infrastructure/cloud-regions/cloud-region/CloudOwner/RegionOne/tenants"
                 + "/tenant/3f2aaef74ecb4b19b35e26d0849fe9a2/vservers/vserver/"
-                + "6c3b3714-e36c-45af-9f16-7d3a73d99497\"}]}}";
+                + "6c3b3714-e36c-45af-9f16-7d3a73d99497\"}]}";
         } else {
             return null;
         }