From 3b4884e5688a71116b3935e3a4ad243ab6287c80 Mon Sep 17 00:00:00 2001 From: Jim Hahn Date: Wed, 12 Feb 2020 13:01:57 -0500 Subject: [PATCH] Add A&AI actor and some operators 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 Change-Id: Ic9dadc07a6057cb1abb9698da86eb9431fc9ed3e --- models-interactions/model-actors/actor.aai/pom.xml | 89 ++++++++++ .../actor/aai/AaiActorServiceProvider.java | 47 +++++ .../actor/aai/AaiCustomQueryOperation.java | 127 ++++++++++++++ .../controlloop/actor/aai/AaiGetOperation.java | 133 +++++++++++++++ .../onap/policy/controlloop/actor/aai/AaiUtil.java | 50 ++++++ ...licy.controlloop.actorServiceProvider.spi.Actor | 1 + .../actor/aai/AaiActorServiceProviderTest.java | 48 ++++++ .../actor/aai/AaiCustomQueryOperationTest.java | 189 +++++++++++++++++++++ .../controlloop/actor/aai/AaiGetOperatorTest.java | 131 ++++++++++++++ .../policy/controlloop/actor/aai/AaiUtilTest.java | 36 ++++ .../controlloop/actor/aai/BasicAaiOperator.java | 54 ++++++ .../controlloop/actor/test/BasicHttpOperation.java | 15 ++ .../actor/test/BasicHttpOperationTest.java | 25 ++- .../parameters/TopicParams.java | 2 +- .../parameters/TopicParamsTest.java | 2 +- models-interactions/model-actors/pom.xml | 1 + 16 files changed, 947 insertions(+), 3 deletions(-) create mode 100644 models-interactions/model-actors/actor.aai/pom.xml create mode 100644 models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProvider.java create mode 100644 models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperation.java create mode 100644 models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiGetOperation.java create mode 100644 models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiUtil.java create mode 100644 models-interactions/model-actors/actor.aai/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor create mode 100644 models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProviderTest.java create mode 100644 models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperationTest.java create mode 100644 models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiGetOperatorTest.java create mode 100644 models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiUtilTest.java create mode 100644 models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/BasicAaiOperator.java diff --git a/models-interactions/model-actors/actor.aai/pom.xml b/models-interactions/model-actors/actor.aai/pom.xml new file mode 100644 index 000000000..deb0181be --- /dev/null +++ b/models-interactions/model-actors/actor.aai/pom.xml @@ -0,0 +1,89 @@ + + + + + 4.0.0 + + + org.onap.policy.models.policy-models-interactions.model-actors + model-actors + 2.2.1-SNAPSHOT + + + actor.aai + + + + org.onap.policy.models.policy-models-interactions.model-actors + actorServiceProvider + ${project.version} + provided + + + org.onap.policy.models.policy-models-interactions.model-impl + aai + ${project.version} + provided + + + org.onap.policy.models.policy-models-interactions.model-impl + events + ${project.version} + provided + + + org.onap.policy.models.policy-models-interactions.model-impl + aai + ${project.version} + provided + + + org.onap.policy.common + policy-endpoints + ${policy.common.version} + provided + + + junit + junit + test + + + org.onap.policy.models.policy-models-interactions.model-actors + actor.test + ${project.version} + test + + + org.onap.policy.models.policy-models-interactions + simulators + ${project.version} + test + + + org.powermock + powermock-api-mockito2 + test + + + 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 index 000000000..df427c32c --- /dev/null +++ b/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProvider.java @@ -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 index 000000000..5f432704c --- /dev/null +++ b/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperation.java @@ -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 { + 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 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 startOperationAsync(int attempt, OperationOutcome outcome) { + + Map request = makeRequest(); + + Entity> entity = Entity.entity(request, MediaType.APPLICATION_JSON); + + Map 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 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 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 index 000000000..3bc359af2 --- /dev/null +++ b/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiGetOperation.java @@ -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 { + 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 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 startOperationAsync(int attempt, OperationOutcome outcome) { + + Map 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 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 index 000000000..14edc3aa1 --- /dev/null +++ b/models-interactions/model-actors/actor.aai/src/main/java/org/onap/policy/controlloop/actor/aai/AaiUtil.java @@ -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 makeHeaders(ControlLoopOperationParams params) { + Map 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 index 000000000..6a52e3f17 --- /dev/null +++ b/models-interactions/model-actors/actor.aai/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor @@ -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 index 000000000..513f339fb --- /dev/null +++ b/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProviderTest.java @@ -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 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 index 000000000..eca5062f1 --- /dev/null +++ b/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiCustomQueryOperationTest.java @@ -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> { + 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 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 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 future2 = oper.start(); + + assertEquals(PolicyResult.FAILURE_EXCEPTION, future2.get(5, TimeUnit.SECONDS).getResult()); + } + + private String makeTenantReply() throws Exception { + Map links = Map.of(AaiCustomQueryOperation.RESOURCE_LINK, MY_LINK); + List> data = Arrays.asList(links); + + Map 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 index 000000000..ca90ce476 --- /dev/null +++ b/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiGetOperatorTest.java @@ -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 { + + 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 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 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 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 index 000000000..39ed6fe88 --- /dev/null +++ b/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiUtilTest.java @@ -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 { + + @Test + public void testMakeHeaders() { + makeContext(); + + Map 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 index 000000000..50b562afb --- /dev/null +++ b/models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/BasicAaiOperator.java @@ -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 extends BasicHttpOperation { + + /** + * 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 headers) { + assertEquals("POLICY", headers.get("X-FromAppId").toString()); + assertEquals(params.getRequestId().toString(), headers.get("X-TransactionId")); + } +} diff --git a/models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicHttpOperation.java b/models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicHttpOperation.java index 15e4848c6..cd9681c8e 100644 --- a/models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicHttpOperation.java +++ b/models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicHttpOperation.java @@ -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 { protected Map 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> provideResponse(Response response) { + return args -> { + InvocationCallback cb = args.getArgument(0); + cb.completed(response); + return CompletableFuture.completedFuture(response); + }; + } } diff --git a/models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/BasicHttpOperationTest.java b/models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/BasicHttpOperationTest.java index c33483d26..1de2f923d 100644 --- a/models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/BasicHttpOperationTest.java +++ b/models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/BasicHttpOperationTest.java @@ -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 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()); + } } diff --git a/models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/parameters/TopicParams.java b/models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/parameters/TopicParams.java index 9e6d8a15e..e6ba7298a 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/parameters/TopicParams.java +++ b/models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/parameters/TopicParams.java @@ -53,7 +53,7 @@ public class TopicParams { */ @Min(0) @Builder.Default - private long timeoutSec = 0; + private int timeoutSec = 0; /** * Validates both the publisher and the subscriber parameters. diff --git a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/TopicParamsTest.java b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/TopicParamsTest.java index 4834c98d2..77c9af362 100644 --- a/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/TopicParamsTest.java +++ b/models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/TopicParamsTest.java @@ -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; diff --git a/models-interactions/model-actors/pom.xml b/models-interactions/model-actors/pom.xml index 029ac7f40..30891142d 100644 --- a/models-interactions/model-actors/pom.xml +++ b/models-interactions/model-actors/pom.xml @@ -37,6 +37,7 @@ actorServiceProvider actor.test + actor.aai actor.appc actor.vfc actor.sdnc -- 2.16.6