Add A&AI actor and some operators 35/101635/3
authorJim Hahn <jrh3@att.com>
Wed, 12 Feb 2020 18:01:57 +0000 (13:01 -0500)
committerJim Hahn <jrh3@att.com>
Wed, 12 Feb 2020 19:29:21 +0000 (14:29 -0500)
Added A&AI Actor, as well as operators for the "custom query" and
the "tenant" query (on which the custom query depends).

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

16 files changed:
models-interactions/model-actors/actor.aai/pom.xml [new file with mode: 0644]
models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProvider.java [new file with mode: 0644]
models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperation.java [new file with mode: 0644]
models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiGetOperation.java [new file with mode: 0644]
models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiUtil.java [new file with mode: 0644]
models-interactions/model-actors/actor.aai/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor [new file with mode: 0644]
models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProviderTest.java [new file with mode: 0644]
models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperationTest.java [new file with mode: 0644]
models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiGetOperatorTest.java [new file with mode: 0644]
models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiUtilTest.java [new file with mode: 0644]
models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/BasicAaiOperator.java [new file with mode: 0644]
models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicHttpOperation.java
models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/BasicHttpOperationTest.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/parameters/TopicParams.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/TopicParamsTest.java
models-interactions/model-actors/pom.xml

diff --git a/models-interactions/model-actors/actor.aai/pom.xml b/models-interactions/model-actors/actor.aai/pom.xml
new file mode 100644 (file)
index 0000000..deb0181
--- /dev/null
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<!--
+  ============LICENSE_START=======================================================
+  Copyright (C) 2018 Huawei Intellectual Property. All rights reserved.
+  Modifications Copyright (C) 2019-2020 Nordix Foundation.
+  Copyright (C) 2020 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=========================================================
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+        <artifactId>model-actors</artifactId>
+        <version>2.2.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>actor.aai</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+            <artifactId>actorServiceProvider</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+            <artifactId>aai</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+            <artifactId>events</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+            <artifactId>aai</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.common</groupId>
+            <artifactId>policy-endpoints</artifactId>
+            <version>${policy.common.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId>
+            <artifactId>actor.test</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions</groupId>
+            <artifactId>simulators</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito2</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProvider.java b/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProvider.java
new file mode 100644 (file)
index 0000000..df427c3
--- /dev/null
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.controlloop.actor.aai;
+
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpActor;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
+
+/**
+ * A&AI Actor.
+ */
+public class AaiActorServiceProvider extends HttpActor {
+    public static final String NAME = AaiConstants.ACTOR_NAME;
+
+    /**
+     * Constructs the object.
+     */
+    public AaiActorServiceProvider() {
+        super(NAME);
+
+        addOperator(HttpOperator.makeOperator(NAME, AaiCustomQueryOperation.NAME,
+                        AaiCustomQueryOperation::new));
+
+        // add all "get" operators
+        for (String operation : AaiGetOperation.OPERATIONS) {
+            addOperator(HttpOperator.makeOperator(NAME, operation, AaiGetOperation::new));
+        }
+    }
+}
diff --git a/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperation.java b/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperation.java
new file mode 100644 (file)
index 0000000..5f43270
--- /dev/null
@@ -0,0 +1,127 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.controlloop.actor.aai;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.aai.AaiCqResponse;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A&AI Custom Query. Stores the {@link AaiCqResponse} in the context. In addition, if the
+ * context does not contain the "tenant" data for the vserver, then it will request that,
+ * as well.
+ */
+public class AaiCustomQueryOperation extends HttpOperation<String> {
+    private static final Logger logger = LoggerFactory.getLogger(AaiCustomQueryOperation.class);
+
+    public static final String NAME = "CustomQuery";
+
+    public static final String RESOURCE_LINK = "resource-link";
+    public static final String RESULT_DATA = "result-data";
+
+    private static final String PREFIX = "/aai/v16";
+
+    /**
+     * Constructs the object.
+     *
+     * @param params operation parameters
+     * @param operator operator that created this operation
+     */
+    public AaiCustomQueryOperation(ControlLoopOperationParams params, HttpOperator operator) {
+        super(params, operator, String.class);
+    }
+
+    /**
+     * Queries the vserver, if necessary.
+     */
+    @Override
+    protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
+        String vserver = params.getTargetEntity();
+
+        ControlLoopOperationParams tenantParams = params.toBuilder().actor(AaiConstants.ACTOR_NAME)
+                        .operation(AaiGetOperation.TENANT).payload(null).retry(null).timeoutSec(null).build();
+
+        return params.getContext().obtain(AaiGetOperation.getTenantKey(vserver), tenantParams);
+    }
+
+    @Override
+    protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
+
+        Map<String, String> request = makeRequest();
+
+        Entity<Map<String, String>> entity = Entity.entity(request, MediaType.APPLICATION_JSON);
+
+        Map<String, Object> headers = makeHeaders();
+
+        headers.put("Accept", MediaType.APPLICATION_JSON);
+        String url = makeUrl();
+
+        logRestRequest(url, request);
+
+        // @formatter:off
+        return handleResponse(outcome, url,
+            callback -> operator.getClient().put(callback, makePath(), entity, headers));
+        // @formatter:on
+    }
+
+    /**
+     * Constructs the custom query using the previously retrieved tenant data.
+     */
+    private Map<String, String> makeRequest() {
+        String vserver = params.getTargetEntity();
+        StandardCoderObject tenant = params.getContext().getProperty(AaiGetOperation.getTenantKey(vserver));
+
+        String resourceLink = tenant.getString(RESULT_DATA, 0, RESOURCE_LINK);
+        if (resourceLink == null) {
+            throw new IllegalArgumentException("cannot perform custom query - no resource-link");
+        }
+
+        resourceLink = resourceLink.replace(PREFIX, "");
+
+        return Map.of("start", resourceLink, "query", "query/closed-loop");
+    }
+
+    @Override
+    protected Map<String, Object> makeHeaders() {
+        return AaiUtil.makeHeaders(params);
+    }
+
+    /**
+     * Injects the response into the context.
+     */
+    @Override
+    protected void postProcessResponse(OperationOutcome outcome, String url, Response rawResponse, String response) {
+
+        logger.info("{}: caching response for {}", getFullName(), params.getRequestId());
+        params.getContext().setProperty(AaiCqResponse.CONTEXT_KEY, new AaiCqResponse(response));
+    }
+}
diff --git a/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiGetOperation.java b/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiGetOperation.java
new file mode 100644 (file)
index 0000000..3bc359a
--- /dev/null
@@ -0,0 +1,133 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.controlloop.actor.aai;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Superclass of A&AI operators that use "get" to perform their request and store their
+ * response within the context as a {@link StandardCoderObject}. The property name under
+ * which they are stored is ${actor}.${operation}.${targetEntity}.
+ */
+public class AaiGetOperation extends HttpOperation<StandardCoderObject> {
+    private static final Logger logger = LoggerFactory.getLogger(AaiGetOperation.class);
+
+    public static final int DEFAULT_RETRY = 3;
+
+    // operation names
+    public static final String TENANT = "Tenant";
+
+    // property prefixes
+    private static final String TENANT_KEY_PREFIX = AaiConstants.CONTEXT_PREFIX + TENANT + ".";
+
+    /**
+     * Operation names supported by this operator.
+     */
+    public static final Set<String> OPERATIONS = Set.of(TENANT);
+
+
+    /**
+     * Responses that are retrieved from A&AI are placed in the operation context under
+     * the name "${propertyPrefix}.${targetEntity}".
+     */
+    private final String propertyPrefix;
+
+    /**
+     * Constructs the object.
+     *
+     * @param params operation parameters
+     * @param operator operator that created this operation
+     */
+    public AaiGetOperation(ControlLoopOperationParams params, HttpOperator operator) {
+        super(params, operator, StandardCoderObject.class);
+        this.propertyPrefix = operator.getFullName() + ".";
+    }
+
+    /**
+     * Gets the "context key" for the tenant query response associated with the given
+     * target entity.
+     *
+     * @param targetEntity target entity
+     * @return the "context key" for the response associated with the given target
+     */
+    public static String getTenantKey(String targetEntity) {
+        return (TENANT_KEY_PREFIX + targetEntity);
+    }
+
+    @Override
+    protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) {
+
+        Map<String, Object> headers = makeHeaders();
+
+        headers.put("Accept", MediaType.APPLICATION_JSON);
+        String url = makeUrl();
+
+        logRestRequest(url, null);
+
+        // @formatter:off
+        return handleResponse(outcome, url,
+            callback -> operator.getClient().get(callback, makePath(), headers));
+        // @formatter:on
+    }
+
+    @Override
+    protected Map<String, Object> makeHeaders() {
+        return AaiUtil.makeHeaders(params);
+    }
+
+    @Override
+    public String makePath() {
+        return (operator.getPath() + "/" + params.getTargetEntity());
+    }
+
+    /**
+     * Injects the response into the context.
+     */
+    @Override
+    protected void postProcessResponse(OperationOutcome outcome, String url, Response rawResponse,
+                    StandardCoderObject response) {
+        String entity = params.getTargetEntity();
+
+        logger.info("{}: caching response of {} for {}", getFullName(), entity, params.getRequestId());
+
+        params.getContext().setProperty(propertyPrefix + entity, response);
+    }
+
+    /**
+     * Provides a default retry value, if none specified.
+     */
+    @Override
+    protected int getRetry(Integer retry) {
+        return (retry == null ? DEFAULT_RETRY : retry);
+    }
+}
diff --git a/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiUtil.java b/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiUtil.java
new file mode 100644 (file)
index 0000000..14edc3a
--- /dev/null
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.controlloop.actor.aai;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+
+/**
+ * Utilities used by A&AI classes.
+ */
+public class AaiUtil {
+
+    private AaiUtil() {
+        // do nothing
+    }
+
+    /**
+     * Makes standard request headers for A&AI requests.
+     *
+     * @param params operation parameters
+     * @return new request headers
+     */
+    public static Map<String, Object> makeHeaders(ControlLoopOperationParams params) {
+        Map<String, Object> headers = new HashMap<>();
+
+        headers.put("X-FromAppId", "POLICY");
+        headers.put("X-TransactionId", params.getRequestId().toString());
+
+        return headers;
+    }
+}
diff --git a/models-interactions/model-actors/actor.aai/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor b/models-interactions/model-actors/actor.aai/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor
new file mode 100644 (file)
index 0000000..6a52e3f
--- /dev/null
@@ -0,0 +1 @@
+org.onap.policy.controlloop.actor.aai.AaiActorServiceProvider
diff --git a/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProviderTest.java b/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProviderTest.java
new file mode 100644 (file)
index 0000000..513f339
--- /dev/null
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.controlloop.actor.aai;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+
+public class AaiActorServiceProviderTest {
+
+    @Test
+    public void testAaiActorServiceProvider() {
+        final AaiActorServiceProvider prov = new AaiActorServiceProvider();
+
+        // verify that it has the operators we expect
+        List<String> expected = new LinkedList<>();
+        expected.add(AaiCustomQueryOperation.NAME);
+        expected.addAll(AaiGetOperation.OPERATIONS);
+
+        Collections.sort(expected);
+
+        var actual = prov.getOperationNames().stream().sorted().collect(Collectors.toList());
+
+        assertEquals(expected.toString(), actual.toString());
+    }
+}
diff --git a/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperationTest.java b/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperationTest.java
new file mode 100644 (file)
index 0000000..eca5062
--- /dev/null
@@ -0,0 +1,189 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.controlloop.actor.aai;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.aai.AaiCqResponse;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpParams;
+import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
+import org.onap.policy.controlloop.policy.PolicyResult;
+
+public class AaiCustomQueryOperationTest extends BasicAaiOperator<Map<String, String>> {
+    private static final StandardCoder coder = new StandardCoder();
+
+    private static final String MY_LINK = "my-link";
+
+    @Mock
+    private Actor tenantActor;
+
+    private AaiCustomQueryOperation oper;
+
+    public AaiCustomQueryOperationTest() {
+        super(AaiConstants.ACTOR_NAME, AaiCustomQueryOperation.NAME);
+    }
+
+    /**
+     * Sets up.
+     */
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        MyTenantOperator tenantOperator = new MyTenantOperator();
+
+        when(service.getActor(AaiConstants.ACTOR_NAME)).thenReturn(tenantActor);
+        when(tenantActor.getOperator(AaiGetOperation.TENANT)).thenReturn(tenantOperator);
+
+        oper = new AaiCustomQueryOperation(params, operator);
+    }
+
+    @Test
+    public void testAaiCustomQueryOperation() {
+        assertEquals(AaiConstants.ACTOR_NAME, oper.getActorName());
+        assertEquals(AaiCustomQueryOperation.NAME, oper.getName());
+    }
+
+    @Test
+    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));
+
+        CompletableFuture<OperationOutcome> future2 = oper.start();
+
+        assertEquals(PolicyResult.SUCCESS, future2.get(5, TimeUnit.SECONDS).getResult());
+
+        // tenant response should have been cached within the context
+        assertNotNull(context.getProperty(AaiGetOperation.getTenantKey(TARGET_ENTITY)));
+
+        // custom query response should have been cached within the context
+        AaiCqResponse cqData = context.getProperty(AaiCqResponse.CONTEXT_KEY);
+        assertNotNull(cqData);
+    }
+
+    /**
+     * Tests when preprocessor step is not needed.
+     */
+    @Test
+    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));
+
+        CompletableFuture<OperationOutcome> future2 = oper.start();
+
+        assertEquals(PolicyResult.SUCCESS, future2.get(5, TimeUnit.SECONDS).getResult());
+
+        // should not have replaced tenant response
+        assertSame(data, context.getProperty(AaiGetOperation.getTenantKey(TARGET_ENTITY)));
+
+        // custom query response should have been cached within the context
+        AaiCqResponse cqData = context.getProperty(AaiCqResponse.CONTEXT_KEY);
+        assertNotNull(cqData);
+    }
+
+    @Test
+    public void testMakeHeaders() {
+        verifyHeaders(oper.makeHeaders());
+    }
+
+    @Test
+    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));
+
+        CompletableFuture<OperationOutcome> future2 = oper.start();
+
+        assertEquals(PolicyResult.FAILURE_EXCEPTION, future2.get(5, TimeUnit.SECONDS).getResult());
+    }
+
+    private String makeTenantReply() throws Exception {
+        Map<String, String> links = Map.of(AaiCustomQueryOperation.RESOURCE_LINK, MY_LINK);
+        List<Map<String, String>> data = Arrays.asList(links);
+
+        Map<String, Object> reply = Map.of(AaiCustomQueryOperation.RESULT_DATA, data);
+        return coder.encode(reply);
+    }
+
+    private String makeCqReply() {
+        return "{}";
+    }
+
+    private StandardCoderObject preloadTenantData() throws Exception {
+        StandardCoderObject data = coder.decode(makeTenantReply(), StandardCoderObject.class);
+        preloadTenantData(data);
+        return data;
+    }
+
+    private void preloadTenantData(StandardCoderObject data) {
+        context.setProperty(AaiGetOperation.getTenantKey(TARGET_ENTITY), data);
+    }
+
+    protected class MyTenantOperator extends HttpOperator {
+        public MyTenantOperator() {
+            super(AaiConstants.ACTOR_NAME, AaiGetOperation.TENANT);
+
+            HttpParams http = HttpParams.builder().clientName(MY_CLIENT).path(PATH).build();
+
+            configure(Util.translateToMap(AaiGetOperation.TENANT, http));
+            start();
+        }
+
+        @Override
+        public Operation buildOperation(ControlLoopOperationParams params) {
+            return new AaiGetOperation(params, this);
+        }
+
+        @Override
+        protected HttpClientFactory getClientFactory() {
+            return factory;
+        }
+    }
+}
diff --git a/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiGetOperatorTest.java b/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiGetOperatorTest.java
new file mode 100644 (file)
index 0000000..ca90ce4
--- /dev/null
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.controlloop.actor.aai;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.policy.PolicyResult;
+
+public class AaiGetOperatorTest extends BasicAaiOperator<Void> {
+
+    private static final String INPUT_FIELD = "input";
+    private static final String TEXT = "my-text";
+
+    private AaiGetOperation oper;
+
+    public AaiGetOperatorTest() {
+        super(AaiConstants.ACTOR_NAME, AaiGetOperation.TENANT);
+    }
+
+    /**
+     * Sets up.
+     */
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        oper = new AaiGetOperation(params, operator);
+    }
+
+    @Test
+    public void testGetRetry() {
+        // use default if null retry
+        assertEquals(AaiGetOperation.DEFAULT_RETRY, oper.getRetry(null));
+
+        // otherwise, use specified value
+        assertEquals(0, oper.getRetry(0));
+        assertEquals(10, oper.getRetry(10));
+    }
+
+    @Test
+    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));
+
+        CompletableFuture<OperationOutcome> future2 = oper.startOperationAsync(1, outcome);
+        assertFalse(future2.isDone());
+
+        assertEquals(PolicyResult.SUCCESS, future2.get(5, TimeUnit.SECONDS).getResult());
+
+        // data should have been cached within the context
+        StandardCoderObject data = context.getProperty(AaiGetOperation.getTenantKey(TARGET_ENTITY));
+        assertNotNull(data);
+        assertEquals(TEXT, data.getString(INPUT_FIELD));
+    }
+
+    /**
+     * Tests startOperationAsync() when there's a failure.
+     */
+    @Test
+    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));
+
+        CompletableFuture<OperationOutcome> future2 = oper.startOperationAsync(1, outcome);
+        assertFalse(future2.isDone());
+
+        assertEquals(PolicyResult.FAILURE, future2.get(5, TimeUnit.SECONDS).getResult());
+
+        // data should NOT have been cached within the context
+        assertNull(context.getProperty(AaiGetOperation.getTenantKey(TARGET_ENTITY)));
+    }
+
+    @Test
+    public void testMakeHeaders() {
+        verifyHeaders(oper.makeHeaders());
+    }
+
+    @Test
+    public void testMakePath() {
+        assertEquals(PATH + "/" + TARGET_ENTITY, oper.makePath());
+    }
+
+    @Test
+    public void testAaiGetOperator() {
+        assertEquals(AaiConstants.ACTOR_NAME, oper.getActorName());
+        assertEquals(AaiGetOperation.TENANT, oper.getName());
+    }
+
+    @Test
+    public void testGetTenantKey() {
+        assertEquals("AAI.Tenant." + TARGET_ENTITY, AaiGetOperation.getTenantKey(TARGET_ENTITY));
+    }
+}
diff --git a/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiUtilTest.java b/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiUtilTest.java
new file mode 100644 (file)
index 0000000..39ed6fe
--- /dev/null
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.controlloop.actor.aai;
+
+import java.util.Map;
+import org.junit.Test;
+
+public class AaiUtilTest extends BasicAaiOperator<Void> {
+
+    @Test
+    public void testMakeHeaders() {
+        makeContext();
+
+        Map<String, Object> headers = AaiUtil.makeHeaders(params);
+
+        verifyHeaders(headers);
+    }
+}
diff --git a/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/BasicAaiOperator.java b/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/BasicAaiOperator.java
new file mode 100644 (file)
index 0000000..50b562a
--- /dev/null
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.controlloop.actor.aai;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Map;
+import org.onap.policy.controlloop.actor.test.BasicHttpOperation;
+
+/**
+ * Superclass for various operator tests.
+ */
+public abstract class BasicAaiOperator<Q> extends BasicHttpOperation<Q> {
+
+    /**
+     * Constructs the object using a default actor and operation name.
+     */
+    public BasicAaiOperator() {
+        super();
+    }
+
+    /**
+     * Constructs the object.
+     *
+     * @param actor actor name
+     * @param operation operation name
+     */
+    public BasicAaiOperator(String actor, String operation) {
+        super(actor, operation);
+    }
+
+    protected void verifyHeaders(Map<String, Object> headers) {
+        assertEquals("POLICY", headers.get("X-FromAppId").toString());
+        assertEquals(params.getRequestId().toString(), headers.get("X-TransactionId"));
+    }
+}
index 15e4848..cd9681c 100644 (file)
@@ -33,6 +33,7 @@ import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
 import org.onap.policy.common.endpoints.http.client.HttpClient;
 import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
@@ -166,4 +167,18 @@ public class BasicHttpOperation<Q> {
     protected Map<String, String> makeEnrichment() {
         return new TreeMap<>();
     }
+
+    /**
+     * Provides a response to an asynchronous HttpClient call.
+     *
+     * @param response response to be provided to the call
+     * @return a function that provides the response to the call
+     */
+    protected Answer<CompletableFuture<Response>> provideResponse(Response response) {
+        return args -> {
+            InvocationCallback<Response> cb = args.getArgument(0);
+            cb.completed(response);
+            return CompletableFuture.completedFuture(response);
+        };
+    }
 }
index c33483d..1de2f92 100644 (file)
@@ -24,7 +24,11 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
 
+import javax.ws.rs.client.InvocationCallback;
+import javax.ws.rs.core.Response;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -55,7 +59,7 @@ public class BasicHttpOperationTest {
     }
 
     @Test
-    public void testSetUp() {
+    public void testSetUp() throws Exception {
         assertNotNull(oper.client);
         assertSame(oper.client, oper.factory.get(BasicHttpOperation.MY_CLIENT));
         assertEquals(200, oper.rawResponse.getStatus());
@@ -101,4 +105,23 @@ public class BasicHttpOperationTest {
         assertTrue(oper.makeEnrichment().isEmpty());
     }
 
+    @Test
+    public void testProvideResponse() throws Exception {
+        InvocationCallback<Response> cb = new InvocationCallback<>() {
+            @Override
+            public void completed(Response response) {
+                // do nothing
+            }
+
+            @Override
+            public void failed(Throwable throwable) {
+                // do nothing
+            }
+        };
+
+
+        when(oper.client.get(any(), any(), any())).thenAnswer(oper.provideResponse(oper.rawResponse));
+
+        assertSame(oper.rawResponse, oper.client.get(cb, null, null).get());
+    }
 }
index 4834c98..77c9af3 100644 (file)
@@ -36,7 +36,7 @@ public class TopicParamsTest {
     private static final String CONTAINER = "my-container";
     private static final String TARGET = "my-target";
     private static final String SOURCE = "my-source";
-    private static final long TIMEOUT = 10;
+    private static final int TIMEOUT = 10;
 
     private TopicParams params;
 
index 029ac7f..3089114 100644 (file)
@@ -37,6 +37,7 @@
   <modules>
     <module>actorServiceProvider</module>
     <module>actor.test</module>
+    <module>actor.aai</module>
     <module>actor.appc</module>
     <module>actor.vfc</module>
     <module>actor.sdnc</module>