Change payload to Map<String,Object> so it's more versatile 73/102173/1
authorJim Hahn <jrh3@att.com>
Fri, 21 Feb 2020 00:08:55 +0000 (19:08 -0500)
committerJim Hahn <jrh3@att.com>
Sat, 22 Feb 2020 01:33:06 +0000 (20:33 -0500)
This was supposed to be two separate commits, but I goofed something.

Added guard query to Operation superclass.  Modified VfModuleCreate
to store the VF count, pass it to the guard, and bump it once the
create completes successfully.
Added code to check Actors for proper plug-in to ActorService.
Renamed "operation" property to "operations", to be more consistent
with other parameters (e.g., TopicParameterGroup).
The META-INF/services files for the actors had mixed case, which
did not match the package name of the Actor class, preventing the
ServiceLoader from recognizing them.  Also modified the ActorService
to skip any that cannot actually be loaded, for whatever reason
(e.g., not in the classpath).

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

63 files changed:
models-interactions/model-actors/actor.aai/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor [moved from models-interactions/model-actors/actor.aai/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor with 100% similarity]
models-interactions/model-actors/actor.aai/src/test/java/org/onap/policy/controlloop/actor/aai/AaiActorServiceProviderTest.java
models-interactions/model-actors/actor.aai/src/test/resources/service.yaml [new file with mode: 0644]
models-interactions/model-actors/actor.appc/src/main/java/org/onap/policy/controlloop/actor/appc/AppcActorServiceProvider.java
models-interactions/model-actors/actor.appc/src/main/java/org/onap/policy/controlloop/actor/appc/AppcOperation.java
models-interactions/model-actors/actor.appc/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor [moved from models-interactions/model-actors/actor.appc/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor with 100% similarity]
models-interactions/model-actors/actor.appc/src/test/java/org/onap/policy/controlloop/actor/appc/AppcOperationTest.java
models-interactions/model-actors/actor.appc/src/test/java/org/onap/policy/controlloop/actor/appc/AppcServiceProviderTest.java
models-interactions/model-actors/actor.appc/src/test/java/org/onap/policy/controlloop/actor/appc/BasicAppcOperation.java
models-interactions/model-actors/actor.appc/src/test/java/org/onap/policy/controlloop/actor/appc/ModifyConfigOperationTest.java
models-interactions/model-actors/actor.appc/src/test/resources/service.yaml [new file with mode: 0644]
models-interactions/model-actors/actor.appclcm/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor [moved from models-interactions/model-actors/actor.appclcm/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor with 100% similarity]
models-interactions/model-actors/actor.guard/src/main/java/org/onap/policy/controlloop/actor/guard/GuardActorServiceProvider.java
models-interactions/model-actors/actor.guard/src/main/java/org/onap/policy/controlloop/actor/guard/GuardOperation.java
models-interactions/model-actors/actor.guard/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor [moved from models-interactions/model-actors/actor.guard/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor with 100% similarity]
models-interactions/model-actors/actor.guard/src/test/java/org/onap/policy/controlloop/actor/guard/GuardActorParamsTest.java
models-interactions/model-actors/actor.guard/src/test/java/org/onap/policy/controlloop/actor/guard/GuardActorServiceProviderTest.java
models-interactions/model-actors/actor.guard/src/test/java/org/onap/policy/controlloop/actor/guard/GuardOperationTest.java
models-interactions/model-actors/actor.guard/src/test/resources/makeReqDefault.json
models-interactions/model-actors/actor.guard/src/test/resources/service.yaml [new file with mode: 0644]
models-interactions/model-actors/actor.sdnc/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor [moved from models-interactions/model-actors/actor.sdnc/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor with 100% similarity]
models-interactions/model-actors/actor.sdnc/src/test/java/org/onap/policy/controlloop/actor/sdnc/SdncActorServiceProviderTest.java
models-interactions/model-actors/actor.sdnc/src/test/java/org/onap/policy/controlloop/actor/sdnc/SdncOperationTest.java
models-interactions/model-actors/actor.sdnc/src/test/resources/service.yaml [new file with mode: 0644]
models-interactions/model-actors/actor.sdnr/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor [moved from models-interactions/model-actors/actor.sdnr/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor with 100% similarity]
models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/SoActorServiceProvider.java
models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/SoConstants.java [new file with mode: 0644]
models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/SoOperation.java
models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/VfModuleCreate.java
models-interactions/model-actors/actor.so/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor [moved from models-interactions/model-actors/actor.so/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor with 100% similarity]
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/BasicSoOperation.java
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/SoActorParamsTest.java
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/SoActorServiceProviderTest.java
models-interactions/model-actors/actor.so/src/test/java/org/onap/policy/controlloop/actor/so/SoOperationTest.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 [new file with mode: 0644]
models-interactions/model-actors/actor.test/pom.xml
models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicActor.java [new file with mode: 0644]
models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicOperation.java
models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/BasicActorTest.java [new file with mode: 0644]
models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/BasicOperationTest.java
models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/DummyActor.java [new file with mode: 0644]
models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/DummyOperator.java [new file with mode: 0644]
models-interactions/model-actors/actor.test/src/test/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor [new file with mode: 0644]
models-interactions/model-actors/actor.test/src/test/resources/service.yaml [new file with mode: 0644]
models-interactions/model-actors/actor.test/src/test/resources/serviceFull.yaml [new file with mode: 0644]
models-interactions/model-actors/actor.test/src/test/resources/serviceInvalidHttp.yaml [new file with mode: 0644]
models-interactions/model-actors/actor.test/src/test/resources/serviceMissingActors.yaml [new file with mode: 0644]
models-interactions/model-actors/actor.vfc/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor [moved from models-interactions/model-actors/actor.vfc/src/main/resources/META-INF/services/org.onap.policy.controlloop.actorServiceProvider.spi.Actor with 100% similarity]
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/ActorService.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/controlloop/ControlLoopEventContext.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/impl/OperationPartial.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/parameters/CommonActorParams.java
models-interactions/model-actors/actorServiceProvider/src/main/java/org/onap/policy/controlloop/actorserviceprovider/parameters/ControlLoopOperationParams.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/controlloop/ControlLoopEventContextTest.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/BidirectionalTopicActorTest.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/HttpActorTest.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/impl/OperationPartialTest.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/BidirectionalTopicActorParamsTest.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/CommonActorParamsTest.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/ControlLoopOperationParamsTest.java
models-interactions/model-actors/actorServiceProvider/src/test/java/org/onap/policy/controlloop/actorserviceprovider/parameters/HttpActorParamsTest.java
models-interactions/model-actors/actorServiceProvider/src/test/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor

index 513f339..12b6ca4 100644 (file)
@@ -27,8 +27,9 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.junit.Test;
+import org.onap.policy.controlloop.actor.test.BasicActor;
 
-public class AaiActorServiceProviderTest {
+public class AaiActorServiceProviderTest extends BasicActor {
 
     @Test
     public void testAaiActorServiceProvider() {
@@ -44,5 +45,8 @@ public class AaiActorServiceProviderTest {
         var actual = prov.getOperationNames().stream().sorted().collect(Collectors.toList());
 
         assertEquals(expected.toString(), actual.toString());
+
+        // verify that it all plugs into the ActorService
+        verifyActorService(AaiActorServiceProvider.NAME, "service.yaml");
     }
 }
diff --git a/models-interactions/model-actors/actor.aai/src/test/resources/service.yaml b/models-interactions/model-actors/actor.aai/src/test/resources/service.yaml
new file mode 100644 (file)
index 0000000..9f6561d
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# ============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========================================================
+#
+httpClients:
+- clientName: my-client
+  hostname: localhost
+  port: 80
+  basePath: base-url
+  managed: true
+actors:
+  AAI:
+    clientName: my-client
+    operations:
+      CustomQuery:
+        path: cq
+      Tenant:
+        path: tenant
\ No newline at end of file
index f6a2045..76aa828 100644 (file)
@@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory;
 
 
 public class AppcActorServiceProvider extends BidirectionalTopicActor<BidirectionalTopicActorParams> {
-    private static final String NAME = "APPC";
+    public static final String NAME = "APPC";
 
     private static final Logger logger = LoggerFactory.getLogger(AppcActorServiceProvider.class);
 
index 7d4af80..dc46f12 100644 (file)
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
 import org.onap.policy.appc.CommonHeader;
 import org.onap.policy.appc.Request;
 import org.onap.policy.appc.Response;
@@ -66,6 +67,14 @@ public abstract class AppcOperation extends BidirectionalTopicOperation<Request,
         super(params, config, Response.class);
     }
 
+    /**
+     * Starts the GUARD.
+     */
+    @Override
+    protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
+        return startGuardAsync();
+    }
+
     /**
      * Makes a request, given the target VNF. This is a support function for
      * {@link #makeRequest(int)}.
@@ -104,10 +113,16 @@ public abstract class AppcOperation extends BidirectionalTopicOperation<Request,
      * @param source source from which to get the values
      * @param target where to place the decoded values
      */
-    private static void convertPayload(Map<String, String> source, Map<String, Object> target) {
-        for (Entry<String, String> ent : source.entrySet()) {
+    private static void convertPayload(Map<String, Object> source, Map<String, Object> target) {
+        for (Entry<String, Object> ent : source.entrySet()) {
+            Object value = ent.getValue();
+            if (value == null) {
+                target.put(ent.getKey(), null);
+                continue;
+            }
+
             try {
-                target.put(ent.getKey(), coder.decode(ent.getValue(), Object.class));
+                target.put(ent.getKey(), coder.decode(value.toString(), Object.class));
 
             } catch (CoderException e) {
                 logger.warn("cannot decode JSON value {}: {}", ent.getKey(), ent.getValue(), e);
index 218a4e5..8b71b61 100644 (file)
@@ -62,6 +62,11 @@ public class AppcOperationTest extends BasicAppcOperation {
         assertEquals(DEFAULT_OPERATION, oper.getName());
     }
 
+    @Test
+    public void testStartPreprocessorAsync() {
+        assertNotNull(oper.startPreprocessorAsync());
+    }
+
     @Test
     public void testMakeRequest() {
         Request request = oper.makeRequest(2, MY_VNF);
@@ -106,7 +111,7 @@ public class AppcOperationTest extends BasicAppcOperation {
         /*
          * insert invalid json text into the payload.
          */
-        Map<String, String> payload = new TreeMap<>(params.getPayload());
+        Map<String, Object> payload = new TreeMap<>(params.getPayload());
         payload.put("invalid-key", "{invalid json");
 
         params = params.toBuilder().payload(payload).build();
@@ -126,6 +131,31 @@ public class AppcOperationTest extends BasicAppcOperation {
                     KEY2, Map.of("output", "world")),
             request.getPayload());
         // @formatter:on
+
+
+        /*
+         * insert null item into the payload.
+         */
+        payload = new TreeMap<>();
+        payload.put(KEY1, "abc");
+        payload.put(KEY2, null);
+        payload.put(KEY3, "def");
+        params = params.toBuilder().payload(payload).build();
+
+        oper = new AppcOperation(params, config) {
+            @Override
+            protected Request makeRequest(int attempt) {
+                return oper.makeRequest(attempt, MY_VNF);
+            }
+        };
+        request = oper.makeRequest(2, MY_VNF);
+
+        payload.put(AppcOperation.VNF_ID_KEY, MY_VNF);
+        payload.put(KEY1, "abc");
+        payload.put(KEY2, null);
+        payload.put(KEY3, "def");
+
+        assertEquals(payload, request.getPayload());
     }
 
     @Test
index 305c6d7..99e9d82 100644 (file)
@@ -43,6 +43,7 @@ import org.onap.policy.controlloop.ControlLoopEventStatus;
 import org.onap.policy.controlloop.ControlLoopOperation;
 import org.onap.policy.controlloop.ControlLoopTargetType;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.test.BasicActor;
 import org.onap.policy.controlloop.policy.Policy;
 import org.onap.policy.controlloop.policy.Target;
 import org.onap.policy.controlloop.policy.TargetType;
@@ -50,7 +51,7 @@ import org.onap.policy.simulators.Util;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class AppcServiceProviderTest {
+public class AppcServiceProviderTest extends BasicActor {
 
     private static final String GENERIC_VNF_ID = "generic-vnf.vnf-id";
 
@@ -74,7 +75,8 @@ public class AppcServiceProviderTest {
 
     static {
         /*
-         * Construct an onset with an AAI subtag containing generic-vnf.vnf-id and a target type of VM.
+         * Construct an onset with an AAI subtag containing generic-vnf.vnf-id and a
+         * target type of VM.
          */
         onsetEvent = new VirtualControlLoopEvent();
         onsetEvent.setClosedLoopControlName("closedLoopControlName-Test");
@@ -139,6 +141,12 @@ public class AppcServiceProviderTest {
         assertEquals(expected.toString(), actual.toString());
     }
 
+    @Test
+    public void testActorService() {
+        // verify that it all plugs into the ActorService
+        verifyActorService(AppcActorServiceProvider.NAME, "service.yaml");
+    }
+
     @Test
     public void testConstructModifyConfigRequest() {
         policy.setPayload(new HashMap<>());
index ed3e7a7..ecba919 100644 (file)
@@ -54,6 +54,7 @@ public abstract class BasicAppcOperation extends BasicBidirectionalTopicOperatio
     protected static final String MY_VNF = "my-vnf";
     protected static final String KEY1 = "my-key-A";
     protected static final String KEY2 = "my-key-B";
+    protected static final String KEY3 = "my-key-C";
     protected static final String VALUE1 = "{\"input\":\"hello\"}";
     protected static final String VALUE2 = "{\"output\":\"world\"}";
     protected static final String RESOURCE_ID = "my-resource";
@@ -162,7 +163,7 @@ public abstract class BasicAppcOperation extends BasicBidirectionalTopicOperatio
     }
 
     @Override
-    protected Map<String, String> makePayload() {
+    protected Map<String, Object> makePayload() {
         return Map.of(KEY1, VALUE1, KEY2, VALUE2);
     }
 }
index 5ff7897..460f2c9 100644 (file)
@@ -24,11 +24,11 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 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.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.util.Arrays;
@@ -42,6 +42,7 @@ import org.onap.policy.appc.Request;
 import org.onap.policy.common.utils.coder.CoderException;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
 import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
+import org.onap.policy.controlloop.policy.PolicyResult;
 
 public class ModifyConfigOperationTest extends BasicAppcOperation {
 
@@ -64,10 +65,11 @@ public class ModifyConfigOperationTest extends BasicAppcOperation {
     }
 
     @Test
-    public void testStartPreprocessorAsync() {
-        CompletableFuture<OperationOutcome> future = new CompletableFuture<>();
+    public void testStartPreprocessorAsync() throws Exception {
+        CompletableFuture<OperationOutcome> future2 = new CompletableFuture<>();
         context = mock(ControlLoopEventContext.class);
-        when(context.obtain(eq(AaiCqResponse.CONTEXT_KEY), any())).thenReturn(future);
+        when(context.obtain(eq(AaiCqResponse.CONTEXT_KEY), any())).thenReturn(future2);
+        when(context.getEvent()).thenReturn(event);
         params = params.toBuilder().context(context).build();
 
         AtomicBoolean guardStarted = new AtomicBoolean();
@@ -80,9 +82,16 @@ public class ModifyConfigOperationTest extends BasicAppcOperation {
             }
         };
 
-        assertSame(future, oper.startPreprocessorAsync());
+        CompletableFuture<OperationOutcome> future3 = oper.startPreprocessorAsync();
+        assertNotNull(future3);
         assertFalse(future.isDone());
         assertTrue(guardStarted.get());
+        verify(context).obtain(eq(AaiCqResponse.CONTEXT_KEY), any());
+
+        future2.complete(params.makeOutcome());
+        assertTrue(executor.runAll(100));
+        assertTrue(future3.isDone());
+        assertEquals(PolicyResult.SUCCESS, future3.get().getResult());
     }
 
     @Test
diff --git a/models-interactions/model-actors/actor.appc/src/test/resources/service.yaml b/models-interactions/model-actors/actor.appc/src/test/resources/service.yaml
new file mode 100644 (file)
index 0000000..ab9ad98
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# ============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========================================================
+#
+topics:
+  topicSources:
+  - topicCommInfrastructure: NOOP
+    topic: my-source
+    servers:
+    - localhost
+    managed: true
+  topicSinks:
+  - topicCommInfrastructure: NOOP
+    topic: my-sink
+    servers:
+    - localhost
+    managed: true
+actors:
+  APPC:
+    sinkTopic: my-sink
+    sourceTopic: my-source
+    operations:
+      ModifyConfig: {}
\ No newline at end of file
index ea08d13..e37de57 100644 (file)
@@ -24,10 +24,11 @@ package org.onap.policy.controlloop.actor.guard;
 
 import org.onap.policy.controlloop.actorserviceprovider.impl.HttpActor;
 import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperator;
+import org.onap.policy.controlloop.actorserviceprovider.impl.OperationPartial;
 
 public class GuardActorServiceProvider extends HttpActor<GuardActorParams> {
     // actor name
-    public static final String NAME = "GUARD";
+    public static final String NAME = OperationPartial.GUARD_ACTOR_NAME;
 
     /**
      * Constructs the object.
index 453a3e3..a5459f6 100644 (file)
@@ -20,9 +20,7 @@
 
 package org.onap.policy.controlloop.actor.guard;
 
-import java.util.LinkedHashMap;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import javax.ws.rs.client.Entity;
@@ -33,13 +31,12 @@ import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
 import org.onap.policy.controlloop.actorserviceprovider.Util;
 import org.onap.policy.controlloop.actorserviceprovider.impl.HttpOperation;
+import org.onap.policy.controlloop.actorserviceprovider.impl.OperationPartial;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.HttpConfig;
 import org.onap.policy.controlloop.policy.PolicyResult;
 import org.onap.policy.models.decisions.concepts.DecisionRequest;
 import org.onap.policy.models.decisions.concepts.DecisionResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Guard Operation. The outcome message is set to the guard response. If the guard is
@@ -57,17 +54,14 @@ import org.slf4j.LoggerFactory;
  * </dl>
  */
 public class GuardOperation extends HttpOperation<DecisionResponse> {
-    private static final Logger logger = LoggerFactory.getLogger(GuardOperation.class);
 
     // operation name
-    public static final String NAME = "Decision";
+    public static final String NAME = OperationPartial.GUARD_OPERATION_NAME;
 
     public static final String PERMIT = "Permit";
     public static final String DENY = "Deny";
     public static final String INDETERMINATE = "Indeterminate";
 
-    private static final String RESOURCE = "resource";
-
     /**
      * Prefix for properties in the payload that should be copied to the "resource" field
      * of the request.
@@ -118,34 +112,9 @@ public class GuardOperation extends HttpOperation<DecisionResponse> {
             throw new IllegalArgumentException("missing payload");
         }
 
-        /*
-         * This code could be easily modified to allow the context and/or resource to be
-         * an encoded JSON string, that is decoded into a Map and stuffed into the
-         * appropriate field.
-         */
-
         Map<String, Object> req = config.makeRequest();
-        Map<String, Object> resource = new LinkedHashMap<>();
-
-        for (Entry<String, String> ent : params.getPayload().entrySet()) {
-            String key = ent.getKey();
-
-            if (key.startsWith(RESOURCE_PREFIX)) {
-                // it's a resource property - put into the resource map
-                key = key.substring(RESOURCE_PREFIX.length());
-                resource.put(key, ent.getValue());
-
-            } else if (key.indexOf('.') < 0) {
-                // it's a normal property - put into the request map
-                req.put(key, ent.getValue());
-
-            } else {
-                logger.warn("{}: unused key {} in payload for {}", getFullName(), key, params.getRequestId());
-            }
-        }
-
+        req.putAll(params.getPayload());
         req.computeIfAbsent("requestId", key -> UUID.randomUUID().toString());
-        req.put(RESOURCE, resource);
 
         return req;
     }
index cd9f4e3..1c25609 100644 (file)
@@ -32,6 +32,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.onap.policy.common.parameters.ValidationResult;
 import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.CommonActorParams;
 
 public class GuardActorParamsTest {
     private static final String CONTAINER = "my-container";
@@ -68,13 +69,14 @@ public class GuardActorParamsTest {
         assertTrue(params.validate(CONTAINER).isValid());
 
         // only a few fields are required
-        GuardActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operations), GuardActorParams.class);
+        GuardActorParams sparse = Util.translate(CONTAINER, Map.of(CommonActorParams.OPERATIONS_FIELD, operations),
+                        GuardActorParams.class);
         assertTrue(sparse.validate(CONTAINER).isValid());
 
         assertEquals(GuardActorParams.DEFAULT_ACTION, sparse.getAction());
 
         // check fields from superclass
-        testValidateField("operation", "null", params2 -> params2.setOperation(null));
+        testValidateField(CommonActorParams.OPERATIONS_FIELD, "null", params2 -> params2.setOperations(null));
         testValidateField("timeoutSec", "minimum", params2 -> params2.setTimeoutSec(-1));
     }
 
@@ -96,7 +98,7 @@ public class GuardActorParamsTest {
         GuardActorParams params2 = new GuardActorParams();
         params2.setClientName(CLIENT);
         params2.setTimeoutSec(TIMEOUT);
-        params2.setOperation(operations);
+        params2.setOperations(operations);
 
         params2.setOnapName(ONAP_NAME);
         params2.setOnapComponent(ONAP_COMP);
index f4ab606..bb5d4dd 100644 (file)
@@ -25,8 +25,9 @@ import static org.junit.Assert.assertEquals;
 import java.util.Arrays;
 import java.util.stream.Collectors;
 import org.junit.Test;
+import org.onap.policy.controlloop.actor.test.BasicActor;
 
-public class GuardActorServiceProviderTest {
+public class GuardActorServiceProviderTest extends BasicActor {
 
     @Test
     public void test() {
@@ -37,5 +38,8 @@ public class GuardActorServiceProviderTest {
         var actual = prov.getOperationNames().stream().sorted().collect(Collectors.toList());
 
         assertEquals(expected.toString(), actual.toString());
+
+        // verify that it all plugs into the ActorService
+        verifyActorService(GuardActorServiceProvider.NAME, "service.yaml");
     }
 }
index 406cdd0..bd23a81 100644 (file)
@@ -92,14 +92,16 @@ public class GuardOperationTest extends BasicHttpOperation<DecisionRequest> {
         verifyPayload("makeReqStd.json", makePayload());
         verifyPayload("makeReqDefault.json", new TreeMap<>());
 
-        Map<String, String> payload = new TreeMap<>();
+        Map<String, Object> payload = new TreeMap<>();
         payload.put("action", "some action");
         payload.put("hello", "world");
         payload.put("r u there?", "yes");
         payload.put("requestId", "some request id");
-        payload.put("resource.abc", "def");
-        payload.put("resource.ghi", "jkl");
-        payload.put("some.other", "unused");
+
+        Map<String, Object> resource = new TreeMap<>();
+        payload.put("resource", resource);
+        resource.put("abc", "def");
+        resource.put("ghi", "jkl");
 
         verifyPayload("makeReq.json", payload);
 
@@ -109,7 +111,7 @@ public class GuardOperationTest extends BasicHttpOperation<DecisionRequest> {
         assertThatIllegalArgumentException().isThrownBy(() -> oper.makeRequest());
     }
 
-    private void verifyPayload(String expectedJsonFile, Map<String, String> payload) throws CoderException {
+    private void verifyPayload(String expectedJsonFile, Map<String, Object> payload) throws CoderException {
         params.getPayload().clear();
         params.getPayload().putAll(payload);
 
@@ -150,7 +152,7 @@ public class GuardOperationTest extends BasicHttpOperation<DecisionRequest> {
     }
 
     @Override
-    protected Map<String, String> makePayload() {
+    protected Map<String, Object> makePayload() {
         DecisionRequest req = new DecisionRequest();
         req.setAction("my-action");
         req.setOnapComponent("my-onap-component");
@@ -158,12 +160,14 @@ public class GuardOperationTest extends BasicHttpOperation<DecisionRequest> {
         req.setOnapName("my-onap-name");
         req.setRequestId("my-request-id");
 
-        @SuppressWarnings("unchecked")
-        Map<String, String> map = Util.translate("", req, TreeMap.class);
-
         // add resources
-        map.put(GuardOperation.RESOURCE_PREFIX + "actor", "resource-actor");
-        map.put(GuardOperation.RESOURCE_PREFIX + "operation", "resource-operation");
+        Map<String, Object> resource = new TreeMap<>();
+        req.setResource(resource);
+        resource.put("actor", "resource-actor");
+        resource.put("operation", "resource-operation");
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> map = Util.translate("", req, TreeMap.class);
 
         return map;
     }
diff --git a/models-interactions/model-actors/actor.guard/src/test/resources/service.yaml b/models-interactions/model-actors/actor.guard/src/test/resources/service.yaml
new file mode 100644 (file)
index 0000000..131cf78
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# ============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========================================================
+#
+httpClients:
+- clientName: my-client
+  hostname: localhost
+  port: 80
+  basePath: base-url
+  managed: true
+actors:
+  GUARD:
+    clientName: my-client
+    operations:
+      Decision:
+        path: decide
\ No newline at end of file
index ac81d49..752cb91 100644 (file)
@@ -36,10 +36,11 @@ import org.junit.Test;
 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
 import org.onap.policy.controlloop.ControlLoopOperation;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.test.BasicActor;
 import org.onap.policy.controlloop.policy.Policy;
 import org.onap.policy.sdnc.SdncRequest;
 
-public class SdncActorServiceProviderTest {
+public class SdncActorServiceProviderTest extends BasicActor {
 
     private static final String REROUTE = RerouteOperation.NAME;
 
@@ -68,6 +69,9 @@ public class SdncActorServiceProviderTest {
         var actual = prov.getOperationNames().stream().sorted().collect(Collectors.toList());
 
         assertEquals(expected.toString(), actual.toString());
+
+        // verify that it all plugs into the ActorService
+        verifyActorService(SdncActorServiceProvider.NAME, "service.yaml");
     }
 
     @Test
index b87ed2c..b7f7171 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.policy.controlloop.actor.sdnc;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Map;
@@ -56,6 +57,11 @@ public class SdncOperationTest extends BasicSdncOperation {
         assertEquals(DEFAULT_OPERATION, oper.getName());
     }
 
+    @Test
+    public void testStartPreprocessorAsync() {
+        assertNotNull(oper.startPreprocessorAsync());
+    }
+
     @Test
     public void testStartOperationAsync_testStartRequestAsync() throws Exception {
         verifyOperation(oper);
diff --git a/models-interactions/model-actors/actor.sdnc/src/test/resources/service.yaml b/models-interactions/model-actors/actor.sdnc/src/test/resources/service.yaml
new file mode 100644 (file)
index 0000000..71d9dae
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# ============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========================================================
+#
+httpClients:
+- clientName: my-client
+  hostname: localhost
+  port: 80
+  basePath: base-url
+  managed: true
+actors:
+  SDNC:
+    clientName: my-client
+    operations:
+      BandwidthOnDemand:
+        path: bod
+      Reroute:
+        path: reroute
\ No newline at end of file
index 1dbad62..9c9e6dc 100644 (file)
@@ -35,7 +35,7 @@ import org.onap.aai.domain.yang.Tenant;
 import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.controlloop.ControlLoopOperation;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
-import org.onap.policy.controlloop.actorserviceprovider.impl.ActorImpl;
+import org.onap.policy.controlloop.actorserviceprovider.impl.HttpActor;
 import org.onap.policy.controlloop.policy.Policy;
 import org.onap.policy.so.SoCloudConfiguration;
 import org.onap.policy.so.SoManager;
@@ -51,7 +51,7 @@ import org.onap.policy.so.util.Serialization;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class SoActorServiceProvider extends ActorImpl {
+public class SoActorServiceProvider extends HttpActor<SoActorParams> {
     private static final Logger logger = LoggerFactory.getLogger(SoActorServiceProvider.class);
 
     public static final String NAME = "SO";
@@ -96,7 +96,7 @@ public class SoActorServiceProvider extends ActorImpl {
      * Constructs the object.
      */
     public SoActorServiceProvider() {
-        super(NAME);
+        super(NAME, SoActorParams.class);
 
         addOperator(new SoOperator(NAME, VfModuleCreate.NAME, VfModuleCreate::new));
     }
diff --git a/models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/SoConstants.java b/models-interactions/model-actors/actor.so/src/main/java/org/onap/policy/controlloop/actor/so/SoConstants.java
new file mode 100644 (file)
index 0000000..faafb43
--- /dev/null
@@ -0,0 +1,29 @@
+/*-
+ * ============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.so;
+
+public class SoConstants {
+    public static final String CONTEXT_KEY_VF_COUNT = "SO.VFCount";
+
+    private SoConstants() {
+        // do nothing
+    }
+}
index d8d960e..53fb973 100644 (file)
@@ -96,6 +96,23 @@ public abstract class SoOperation extends HttpOperation<SoResponse> {
         getCount = 0;
     }
 
+    /**
+     * Validates that the parameters contain the required target information to extract
+     * the VF count from the custom query.
+     */
+    protected void validateTarget() {
+        verifyNotNull("Target information", params.getTarget());
+        verifyNotNull("model-customization-id", params.getTarget().getModelCustomizationId());
+        verifyNotNull("model-invariant-id", params.getTarget().getModelInvariantId());
+        verifyNotNull("model-version-id", params.getTarget().getModelVersionId());
+    }
+
+    private void verifyNotNull(String type, Object value) {
+        if (value == null) {
+            throw new IllegalArgumentException("missing " + type + " for guard payload");
+        }
+    }
+
     /**
      * Starts the GUARD.
      */
@@ -104,6 +121,24 @@ public abstract class SoOperation extends HttpOperation<SoResponse> {
         return startGuardAsync();
     }
 
+    /**
+     * Stores the VF count and then runs the guard.
+     *
+     * @return a future to cancel or await the guard response
+     */
+    protected CompletableFuture<OperationOutcome> storeVfCountRunGuard() {
+        String custId = params.getTarget().getModelCustomizationId();
+        String invId = params.getTarget().getModelInvariantId();
+        String verId = params.getTarget().getModelVersionId();
+
+        AaiCqResponse cq = params.getContext().getProperty(AaiCqResponse.CONTEXT_KEY);
+        int vfcount = cq.getVfModuleCount(custId, invId, verId);
+
+        params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, vfcount);
+
+        return startGuardAsync();
+    }
+
     /**
      * If the response does not indicate that the request has been completed, then sleep a
      * bit and issue a "get".
@@ -116,6 +151,7 @@ public abstract class SoOperation extends HttpOperation<SoResponse> {
         if (rawResponse.getStatus() == 200) {
             String requestState = getRequestState(response);
             if (COMPLETE.equalsIgnoreCase(requestState)) {
+                successfulCompletion();
                 return CompletableFuture
                                 .completedFuture(setOutcome(outcome, PolicyResult.SUCCESS, rawResponse, response));
             }
@@ -146,6 +182,13 @@ public abstract class SoOperation extends HttpOperation<SoResponse> {
         return sleep(getWaitMsGet(), TimeUnit.MILLISECONDS).thenComposeAsync(doGet);
     }
 
+    /**
+     * Invoked when a request completes successfully.
+     */
+    protected void successfulCompletion() {
+        // do nothing
+    }
+
     /**
      * Issues a "get" request to see if the original request is complete yet.
      *
@@ -251,13 +294,13 @@ public abstract class SoOperation extends HttpOperation<SoResponse> {
             return null;
         }
 
-        String json = params.getPayload().get(REQ_PARAM_NM);
-        if (json == null) {
+        Object data = params.getPayload().get(REQ_PARAM_NM);
+        if (data == null) {
             return null;
         }
 
         try {
-            return coder.decode(json, SoRequestParameters.class);
+            return coder.decode(data.toString(), SoRequestParameters.class);
         } catch (CoderException e) {
             throw new IllegalArgumentException("invalid payload value: " + REQ_PARAM_NM);
         }
@@ -271,14 +314,14 @@ public abstract class SoOperation extends HttpOperation<SoResponse> {
             return null;
         }
 
-        String json = params.getPayload().get(CONFIG_PARAM_NM);
-        if (json == null) {
+        Object data = params.getPayload().get(CONFIG_PARAM_NM);
+        if (data == null) {
             return null;
         }
 
         try {
             @SuppressWarnings("unchecked")
-            List<Map<String, String>> result = coder.decode(json, ArrayList.class);
+            List<Map<String, String>> result = coder.decode(data.toString(), ArrayList.class);
             return result;
         } catch (CoderException | RuntimeException e) {
             throw new IllegalArgumentException("invalid payload value: " + CONFIG_PARAM_NM);
index c17d252..e88a10c 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.policy.controlloop.actor.so;
 
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.core.MediaType;
@@ -45,24 +46,61 @@ import org.onap.policy.so.SoRequest;
 import org.onap.policy.so.SoRequestDetails;
 import org.onap.policy.so.SoRequestParameters;
 
+/**
+ * Operation to create a VF Module. This gets the VF count from the A&AI Custom Query
+ * response and stores it in the context. It also passes the count+1 to the guard. Once
+ * the "create" completes successfully, it bumps the VF count that's stored in the
+ * context.
+ * <p/>
+ * Note: currently, this only supports storing the count for a single target VF.
+ */
 public class VfModuleCreate extends SoOperation {
     public static final String NAME = "VF Module Create";
 
+    public static final String PAYLOAD_KEY_VF_COUNT = "vfCount";
+
+    /**
+     * Constructs the object.
+     *
+     * @param params operation parameters
+     * @param config configuration for this operation
+     */
     public VfModuleCreate(ControlLoopOperationParams params, HttpConfig config) {
         super(params, config);
+
+        // ensure we have the necessary parameters
+        validateTarget();
     }
 
     /**
-     * Ensures that A&AI customer query has been performed, and then runs the guard query.
+     * Ensures that A&AI customer query has been performed, and then runs the guard.
      */
     @Override
     @SuppressWarnings("unchecked")
     protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
+        if (params.getContext().contains(SoConstants.CONTEXT_KEY_VF_COUNT)) {
+            return startGuardAsync();
+        }
+
+        // need the VF count
         ControlLoopOperationParams cqParams = params.toBuilder().actor(AaiConstants.ACTOR_NAME)
                         .operation(AaiCustomQueryOperation.NAME).payload(null).retry(null).timeoutSec(null).build();
 
-        // run Custom Query and Guard, in parallel
-        return allOf(() -> params.getContext().obtain(AaiCqResponse.CONTEXT_KEY, cqParams), this::startGuardAsync);
+        // run Custom Query, extract the VF count, and then run the Guard
+        return sequence(() -> params.getContext().obtain(AaiCqResponse.CONTEXT_KEY, cqParams),
+                        this::storeVfCountRunGuard);
+    }
+
+    @Override
+    protected Map<String, Object> makeGuardPayload() {
+        Map<String, Object> payload = super.makeGuardPayload();
+
+        int vfcount = params.getContext().getProperty(SoConstants.CONTEXT_KEY_VF_COUNT);
+
+        // run guard with the proposed vf count
+        payload.put(PAYLOAD_KEY_VF_COUNT, vfcount + 1);
+
+        return payload;
     }
 
     @Override
@@ -85,6 +123,15 @@ public class VfModuleCreate extends SoOperation {
         return handleResponse(outcome, url, callback -> getClient().post(callback, path, entity, null));
     }
 
+    /**
+     * Increments the VF count that's stored in the context.
+     */
+    @Override
+    protected void successfulCompletion() {
+        int vfcount = params.getContext().getProperty(SoConstants.CONTEXT_KEY_VF_COUNT);
+        params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, vfcount + 1);
+    }
+
     /**
      * Makes a request.
      *
index 3a2aaf8..35f1ef8 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.policy.controlloop.actor.so;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.when;
 
 import java.util.Collections;
@@ -28,6 +29,7 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import org.mockito.Mock;
+import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.controlloop.actor.test.BasicHttpOperation;
 import org.onap.policy.controlloop.actorserviceprovider.Util;
 import org.onap.policy.controlloop.policy.Target;
@@ -52,6 +54,7 @@ public abstract class BasicSoOperation extends BasicHttpOperation<SoRequest> {
     public static final String PATH_GET = "my-path-get/";
     public static final int MAX_GETS = 3;
     public static final int WAIT_SEC_GETS = 20;
+    public static final Integer VF_COUNT = 10;
 
     @Mock
     protected SoConfig config;
@@ -125,8 +128,8 @@ public abstract class BasicSoOperation extends BasicHttpOperation<SoRequest> {
     }
 
     @Override
-    protected Map<String, String> makePayload() {
-        Map<String, String> payload = new HashMap<>();
+    protected Map<String, Object> makePayload() {
+        Map<String, Object> payload = new HashMap<>();
 
         // request parameters
         SoRequestParameters reqParams = new SoRequestParameters();
@@ -140,4 +143,9 @@ public abstract class BasicSoOperation extends BasicHttpOperation<SoRequest> {
 
         return payload;
     }
+
+    protected AaiCqResponse makeCqResponse() {
+        when(cqResponse.getVfModuleCount(any(), any(), any())).thenReturn(VF_COUNT);
+        return cqResponse;
+    }
 }
index f463fcb..5fb6472 100644 (file)
@@ -31,6 +31,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.onap.policy.common.parameters.ValidationResult;
 import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.CommonActorParams;
 
 
 public class SoActorParamsTest {
@@ -67,14 +68,15 @@ public class SoActorParamsTest {
         assertTrue(params.validate(CONTAINER).isValid());
 
         // only a few fields are required
-        SoActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operations), SoActorParams.class);
+        SoActorParams sparse = Util.translate(CONTAINER, Map.of(CommonActorParams.OPERATIONS_FIELD, operations),
+                        SoActorParams.class);
         assertTrue(sparse.validate(CONTAINER).isValid());
 
         testValidateField("maxGets", "minimum", params2 -> params2.setMaxGets(-1));
         testValidateField("waitSecGet", "minimum", params2 -> params2.setWaitSecGet(0));
 
         // check fields from superclass
-        testValidateField("operation", "null", params2 -> params2.setOperation(null));
+        testValidateField(CommonActorParams.OPERATIONS_FIELD, "null", params2 -> params2.setOperations(null));
         testValidateField("timeoutSec", "minimum", params2 -> params2.setTimeoutSec(-1));
 
         // check edge cases
@@ -105,7 +107,7 @@ public class SoActorParamsTest {
         SoActorParams params2 = new SoActorParams();
         params2.setClientName(CLIENT);
         params2.setTimeoutSec(TIMEOUT);
-        params2.setOperation(operations);
+        params2.setOperations(operations);
 
         params2.setWaitSecGet(WAIT_SEC_GETS);
         params2.setMaxGets(MAX_GETS);
index a9d5b81..b73a65e 100644 (file)
@@ -23,6 +23,7 @@
 
 package org.onap.policy.controlloop.actor.so;
 
+import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -41,6 +42,7 @@ import org.junit.Test;
 import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.controlloop.ControlLoopOperation;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actor.test.BasicActor;
 import org.onap.policy.controlloop.policy.Policy;
 import org.onap.policy.controlloop.policy.Target;
 import org.onap.policy.so.SoOperationType;
@@ -48,7 +50,7 @@ import org.onap.policy.so.SoRequest;
 import org.onap.policy.so.SoRequestParameters;
 import org.onap.policy.so.util.Serialization;
 
-public class SoActorServiceProviderTest {
+public class SoActorServiceProviderTest extends BasicActor {
 
     private static final String C_VALUE = "cvalue";
     private static final String A_VALUE = "avalue";
@@ -78,9 +80,16 @@ public class SoActorServiceProviderTest {
         assertEquals(expected.toString(), actual.toString());
     }
 
+    @Test
+    public void testActorService() {
+        // verify that it all plugs into the ActorService
+        verifyActorService(SoActorServiceProvider.NAME, "service.yaml");
+    }
+
     @Test
     public void testSendRequest() {
-        SoActorServiceProvider.sendRequest(UUID.randomUUID().toString(), null, null, null, null, null);
+        assertThatCode(() -> SoActorServiceProvider.sendRequest(UUID.randomUUID().toString(), null, null, null, null,
+                        null)).doesNotThrowAnyException();
     }
 
     @Test
index 871d370..b2ae572 100644 (file)
 
 package org.onap.policy.controlloop.actor.so;
 
+import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
@@ -35,7 +37,6 @@ import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 import org.junit.Before;
@@ -81,20 +82,56 @@ public class SoOperationTest extends BasicSoOperation {
         assertEquals(1000 * WAIT_SEC_GETS, oper.getWaitMsGet());
     }
 
+    @Test
+    public void testValidateTarget() {
+        // check when various fields are null
+        verifyNotNull("model-customization-id", target::getModelCustomizationId, target::setModelCustomizationId);
+        verifyNotNull("model-invariant-id", target::getModelInvariantId, target::setModelInvariantId);
+        verifyNotNull("model-version-id", target::getModelVersionId, target::setModelVersionId);
+
+        // verify it's still valid
+        assertThatCode(() -> new VfModuleCreate(params, config)).doesNotThrowAnyException();
+
+        // check when Target, itself, is null
+        params = params.toBuilder().target(null).build();
+        assertThatIllegalArgumentException().isThrownBy(() -> new VfModuleCreate(params, config))
+                        .withMessageContaining("Target information");
+    }
+
+    private void verifyNotNull(String expectedText, Supplier<String> getter, Consumer<String> setter) {
+        String originalValue = getter.get();
+
+        // try with null
+        setter.accept(null);
+        assertThatIllegalArgumentException().isThrownBy(() -> new VfModuleCreate(params, config))
+                        .withMessageContaining(expectedText);
+
+        setter.accept(originalValue);
+    }
+
     @Test
     public void testStartPreprocessorAsync() {
-        AtomicBoolean guardStarted = new AtomicBoolean();
+        assertNotNull(oper.startPreprocessorAsync());
+    }
 
-        oper = new SoOperation(params, config) {
-            @Override
-            protected CompletableFuture<OperationOutcome> startGuardAsync() {
-                guardStarted.set(true);
-                return super.startGuardAsync();
-            }
-        };
+    @Test
+    public void testStoreVfCountRunGuard() throws Exception {
+        // insert CQ data so it's there for the guard
+        context.setProperty(AaiCqResponse.CONTEXT_KEY, makeCqResponse());
+
+        // cause guard to fail
+        OperationOutcome outcome2 = params.makeOutcome();
+        outcome2.setResult(PolicyResult.FAILURE);
+        when(guardOperation.start()).thenReturn(CompletableFuture.completedFuture(outcome2));
+
+        CompletableFuture<OperationOutcome> future2 = oper.storeVfCountRunGuard();
+        assertTrue(executor.runAll(100));
+        assertTrue(future2.isDone());
+        assertEquals(PolicyResult.FAILURE, future2.get().getResult());
 
-        assertNull(oper.startPreprocessorAsync());
-        assertTrue(guardStarted.get());
+        // verify that the count was stored
+        Integer vfcount = context.getProperty(SoConstants.CONTEXT_KEY_VF_COUNT);
+        assertEquals(VF_COUNT, vfcount);
     }
 
     @Test
index 6c3cfbf..63cf744 100644 (file)
 
 package org.onap.policy.controlloop.actor.so;
 
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.TimeUnit;
@@ -36,6 +38,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.commons.lang3.tuple.Pair;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.onap.aai.domain.yang.CloudRegion;
 import org.onap.aai.domain.yang.GenericVnf;
 import org.onap.aai.domain.yang.ModelVer;
@@ -44,7 +47,7 @@ import org.onap.aai.domain.yang.Tenant;
 import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.common.utils.coder.CoderException;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
-import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
 import org.onap.policy.controlloop.policy.PolicyResult;
 import org.onap.policy.so.SoRequest;
 
@@ -71,14 +74,17 @@ public class VfModuleCreateTest extends BasicSoOperation {
     public void testConstructor() {
         assertEquals(DEFAULT_ACTOR, oper.getActorName());
         assertEquals(VfModuleCreate.NAME, oper.getName());
+
+        // verify that target validation is done
+        params = params.toBuilder().target(null).build();
+        assertThatIllegalArgumentException().isThrownBy(() -> new VfModuleCreate(params, config))
+                        .withMessageContaining("Target information");
     }
 
     @Test
-    public void testStartPreprocessorAsync() {
-        CompletableFuture<OperationOutcome> future = new CompletableFuture<>();
-        context = mock(ControlLoopEventContext.class);
-        when(context.obtain(eq(AaiCqResponse.CONTEXT_KEY), any())).thenReturn(future);
-        params = params.toBuilder().context(context).build();
+    public void testStartPreprocessorAsync() throws Exception {
+        // put the count in the context so that it will skip the custom query
+        params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, 20);
 
         AtomicBoolean guardStarted = new AtomicBoolean();
 
@@ -90,13 +96,60 @@ public class VfModuleCreateTest extends BasicSoOperation {
             }
         };
 
-        assertSame(future, oper.startPreprocessorAsync());
-        assertFalse(future.isDone());
+        CompletableFuture<OperationOutcome> future3 = oper.startPreprocessorAsync();
+        assertNotNull(future3);
         assertTrue(guardStarted.get());
     }
 
     @Test
-    public void testStartOperationAsync() throws Exception {
+    public void testStartGuardAsync() throws Exception {
+        // remove CQ data so it's forced to query
+        context.removeProperty(AaiCqResponse.CONTEXT_KEY);
+
+        CompletableFuture<OperationOutcome> future2 = oper.startPreprocessorAsync();
+        assertTrue(executor.runAll(100));
+        assertFalse(future2.isDone());
+
+        provideCqResponse(makeCqResponse());
+        assertTrue(executor.runAll(100));
+        assertTrue(future2.isDone());
+        assertEquals(PolicyResult.SUCCESS, future2.get().getResult());
+    }
+
+    @Test
+    public void testMakeGuardPayload() {
+        final int origCount = 30;
+        params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, origCount);
+
+        CompletableFuture<OperationOutcome> future2 = oper.startPreprocessorAsync();
+        assertTrue(executor.runAll(100));
+        assertTrue(future2.isDone());
+
+        // get the payload from the request
+        ArgumentCaptor<ControlLoopOperationParams> captor = ArgumentCaptor.forClass(ControlLoopOperationParams.class);
+        verify(guardOperator).buildOperation(captor.capture());
+
+        Map<String, Object> payload = captor.getValue().getPayload();
+        assertNotNull(payload);
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> resource = (Map<String, Object>) payload.get("resource");
+        assertNotNull(resource);
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> guard = (Map<String, Object>) resource.get("guard");
+        assertNotNull(guard);
+
+        Integer newCount = (Integer) guard.get(VfModuleCreate.PAYLOAD_KEY_VF_COUNT);
+        assertNotNull(newCount);
+        assertEquals(origCount + 1, newCount.intValue());
+    }
+
+    @Test
+    public void testStartOperationAsync_testSuccessfulCompletion() throws Exception {
+        final int origCount = 30;
+        params.getContext().setProperty(SoConstants.CONTEXT_KEY_VF_COUNT, origCount);
+
         when(client.post(any(), any(), any(), any())).thenAnswer(provideResponse(rawResponse));
 
         // use a real executor
@@ -113,6 +166,9 @@ public class VfModuleCreateTest extends BasicSoOperation {
 
         outcome = future2.get(500, TimeUnit.SECONDS);
         assertEquals(PolicyResult.SUCCESS, outcome.getResult());
+
+        Integer newCount = (Integer) params.getContext().getProperty(SoConstants.CONTEXT_KEY_VF_COUNT);
+        assertEquals(origCount + 1, newCount.intValue());
     }
 
     /**
diff --git a/models-interactions/model-actors/actor.so/src/test/resources/service.yaml b/models-interactions/model-actors/actor.so/src/test/resources/service.yaml
new file mode 100644 (file)
index 0000000..b1ac162
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# ============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========================================================
+#
+httpClients:
+- clientName: my-client
+  hostname: localhost
+  port: 80
+  basePath: base-url
+  managed: true
+actors:
+  SO:
+    clientName: my-client
+    operations:
+      VF Module Create:
+        path: create
\ No newline at end of file
index 3df35c3..86e34fa 100644 (file)
     <description>Utilities for testing actors</description>
 
     <dependencies>
+        <dependency>
+            <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
+            <artifactId>aai</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId>
             <artifactId>events</artifactId>
             <scope>compile</scope>
         </dependency>
     </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>src/test/**</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicActor.java b/models-interactions/model-actors/actor.test/src/main/java/org/onap/policy/controlloop/actor/test/BasicActor.java
new file mode 100644 (file)
index 0000000..1b11d0d
--- /dev/null
@@ -0,0 +1,182 @@
+/*-
+ * ============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.test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileNotFoundException;
+import java.util.Map;
+import lombok.Getter;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
+import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
+import org.onap.policy.common.endpoints.http.client.HttpClientConfigException;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
+import org.onap.policy.common.endpoints.parameters.TopicParameterGroup;
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.common.parameters.BeanValidator;
+import org.onap.policy.common.parameters.annotations.NotNull;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardYamlCoder;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.controlloop.actorserviceprovider.ActorService;
+import org.onap.policy.controlloop.actorserviceprovider.Operator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Superclass for various Actor tests.
+ */
+public class BasicActor {
+    private static final Logger logger = LoggerFactory.getLogger(BasicActor.class);
+    private static final Coder yamlCoder = new StandardYamlCoder();
+
+    /**
+     * Reads a YAML configuration file, configures the specified topics and HTTP clients,
+     * and then runs the specified actor through its paces: configure(), start(), stop(),
+     * and shutdown(). Finally, it destroys the topics and HTTP clients.
+     *
+     * @param actorName name of the actor to be tested.
+     * @param yamlConfigFile YAML configuration file name
+     * @throws IllegalArgumentException if an error occurs
+     */
+    protected void verifyActorService(String actorName, String yamlConfigFile) {
+        ActorService service = new ActorService() {};
+
+        // ensure the actor was loaded
+        assertNotNull(service.getActor(actorName));
+
+        try {
+            MyConfig config = readConfig(yamlConfigFile);
+            config.validate();
+
+            startOtherServices(config);
+
+            // configure and verify
+            service.configure(config.getActors());
+            for (Operator operator : service.getActor(actorName).getOperators()) {
+                assertTrue(operator.isConfigured());
+            }
+
+            // start and verify
+            service.start();
+            for (Operator operator : service.getActor(actorName).getOperators()) {
+                assertTrue(operator.isAlive());
+            }
+
+            // stop and verify
+            service.stop();
+            for (Operator operator : service.getActor(actorName).getOperators()) {
+                assertFalse(operator.isAlive());
+            }
+
+            // shut down and verify
+            service.shutdown();
+            for (Operator operator : service.getActor(actorName).getOperators()) {
+                assertFalse(operator.isAlive());
+            }
+
+        } catch (HttpClientConfigException e) {
+            logger.error("failed to configure HTTP client(s) for actor: {}", actorName);
+            throw new IllegalArgumentException(e);
+
+        } finally {
+            stopOtherServices();
+        }
+    }
+
+    /**
+     * Reads a YAML configuration from a file.
+     *
+     * @param yamlConfigFile YAML configuration file name
+     * @return the configuration that was read from the file
+     * @throws AssertionError if an error occurs
+     * @throws CoderException if an error occurs
+     */
+    private MyConfig readConfig(String yamlConfigFile) {
+        try {
+            String yaml = ResourceUtils.getResourceAsString(yamlConfigFile);
+            if (yaml == null) {
+                throw new FileNotFoundException(yamlConfigFile);
+            }
+
+            return yamlCoder.decode(yaml, MyConfig.class);
+
+        } catch (CoderException | FileNotFoundException e) {
+            logger.error("cannot decode YAML file {}", yamlConfigFile);
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    /**
+     * Starts the Topic and HTTP clients.
+     *
+     * @param config configuration
+     * @throws HttpClientConfigException if an error occurs
+     */
+    private void startOtherServices(MyConfig config) throws HttpClientConfigException {
+        stopOtherServices();
+
+        if (config.getHttpClients() != null) {
+            HttpClientFactory factory = HttpClientFactoryInstance.getClientFactory();
+            for (BusTopicParams params : config.getHttpClients()) {
+                factory.build(params);
+            }
+        }
+
+        if (config.getTopics() != null) {
+            TopicEndpointManager.getManager().addTopics(config.getTopics());
+        }
+    }
+
+    /**
+     * Stops the Topic and HTTP clients.
+     */
+    private void stopOtherServices() {
+        TopicEndpointManager.getManager().shutdown();
+        HttpClientFactoryInstance.getClientFactory().destroy();
+    }
+
+    @Getter
+    public static class MyConfig {
+        private BusTopicParams[] httpClients;
+        private TopicParameterGroup topics;
+
+        @NotNull
+        private Map<String, Map<String, Object>> actors;
+
+        /**
+         * Validates the config.
+         */
+        public void validate() {
+            BeanValidationResult result = new BeanValidator().validateTop(BasicActor.class.getSimpleName(), this);
+            if (topics != null) {
+                result.addResult(topics.validate());
+            }
+            if (!result.isValid()) {
+                throw new IllegalArgumentException(result.getResult());
+            }
+        }
+    }
+}
index f027c1c..989f6a7 100644 (file)
@@ -21,6 +21,8 @@
 package org.onap.policy.controlloop.actor.test;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
 
 import java.util.Map;
 import java.util.TreeMap;
@@ -29,6 +31,8 @@ import java.util.concurrent.CompletableFuture;
 import javax.ws.rs.core.Response;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.onap.policy.aai.AaiConstants;
+import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.common.utils.coder.Coder;
 import org.onap.policy.common.utils.coder.CoderException;
 import org.onap.policy.common.utils.coder.StandardCoder;
@@ -36,9 +40,14 @@ import org.onap.policy.common.utils.resources.ResourceUtils;
 import org.onap.policy.common.utils.time.PseudoExecutor;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
 import org.onap.policy.controlloop.actorserviceprovider.ActorService;
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.Operator;
 import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
+import org.onap.policy.controlloop.actorserviceprovider.impl.OperationPartial;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
+import org.onap.policy.controlloop.policy.PolicyResult;
 
 /**
  * Superclass for various Operation tests.
@@ -55,7 +64,22 @@ public class BasicOperation {
 
     @Mock
     protected ActorService service;
+    @Mock
+    protected Actor guardActor;
+    @Mock
+    protected Operator guardOperator;
+    @Mock
+    protected Operation guardOperation;
+    @Mock
+    protected Actor cqActor;
+    @Mock
+    protected Operator cqOperator;
+    @Mock
+    protected Operation cqOperation;
+    @Mock
+    protected AaiCqResponse cqResponse;
 
+    protected CompletableFuture<OperationOutcome> cqFuture;
     protected CompletableFuture<Response> future;
     protected ControlLoopOperationParams params;
     protected Map<String, String> enrichment;
@@ -89,12 +113,28 @@ public class BasicOperation {
     public void setUpBasic() {
         MockitoAnnotations.initMocks(this);
 
+        cqFuture = new CompletableFuture<>();
         future = new CompletableFuture<>();
 
         executor = new PseudoExecutor();
 
         makeContext();
 
+        when(service.getActor(OperationPartial.GUARD_ACTOR_NAME)).thenReturn(guardActor);
+        when(guardActor.getOperator(OperationPartial.GUARD_OPERATION_NAME)).thenReturn(guardOperator);
+        when(guardOperator.buildOperation(any())).thenReturn(guardOperation);
+
+        outcome = params.makeOutcome();
+        outcome.setResult(PolicyResult.SUCCESS);
+        when(guardOperation.start()).thenReturn(CompletableFuture.completedFuture(outcome));
+
+        when(service.getActor(AaiConstants.ACTOR_NAME)).thenReturn(cqActor);
+        when(cqActor.getOperator("CustomQuery")).thenReturn(cqOperator);
+        when(cqOperator.buildOperation(any())).thenReturn(cqOperation);
+
+        when(cqOperation.start()).thenReturn(cqFuture);
+
+        // get a fresh outcome
         outcome = params.makeOutcome();
     }
 
@@ -133,7 +173,7 @@ public class BasicOperation {
      *
      * @return payload data
      */
-    protected Map<String, String> makePayload() {
+    protected Map<String, Object> makePayload() {
         return null;
     }
 
@@ -162,4 +202,16 @@ public class BasicOperation {
 
         assertEquals(expected, json);
     }
+
+    /**
+     * Provides a response to a custom query.
+     *
+     * @param cq response to provide
+     */
+    protected void provideCqResponse(AaiCqResponse cq) {
+        context.setProperty(AaiCqResponse.CONTEXT_KEY, cq);
+        OperationOutcome outcome2 = params.makeOutcome();
+        outcome2.setResult(PolicyResult.SUCCESS);
+        cqFuture.complete(outcome2);
+    }
 }
diff --git a/models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/BasicActorTest.java b/models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/BasicActorTest.java
new file mode 100644 (file)
index 0000000..ef9b37b
--- /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.test;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.Test;
+
+public class BasicActorTest extends BasicActor {
+
+    @Test
+    public void testVerifyActorService_testStartOtherServices_testStopOtherServices() {
+        // mostly empty service
+        verifyActorService(DummyActor.NAME, "service.yaml");
+
+        // service with Topics and HTTP Clients
+        verifyActorService(DummyActor.NAME, "serviceFull.yaml");
+
+        assertThatIllegalArgumentException()
+                        .isThrownBy(() -> verifyActorService(DummyActor.NAME, "serviceInvalidHttp.yaml"));
+
+        assertThatIllegalArgumentException()
+                        .isThrownBy(() -> verifyActorService(DummyActor.NAME, "serviceMissingActors.yaml"));
+
+        // config file not found
+        assertThatThrownBy(() -> verifyActorService(DummyActor.NAME, "file-not-found.yaml"));
+    }
+}
index 60bb008..5eb35e9 100644 (file)
@@ -27,11 +27,16 @@ import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 import org.junit.Before;
 import org.junit.Test;
+import org.onap.policy.aai.AaiCqResponse;
 import org.onap.policy.common.utils.coder.CoderException;
 import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
 import org.onap.policy.controlloop.actorserviceprovider.Util;
+import org.onap.policy.controlloop.actorserviceprovider.impl.OperationPartial;
+import org.onap.policy.controlloop.policy.PolicyResult;
 
 public class BasicOperationTest {
     private static final String ACTOR = "my-actor";
@@ -65,6 +70,12 @@ public class BasicOperationTest {
         assertNotNull(oper.context);
         assertNotNull(oper.outcome);
         assertNotNull(oper.executor);
+        assertNotNull(oper.guardOperation);
+
+        CompletableFuture<OperationOutcome> future = oper.service.getActor(OperationPartial.GUARD_ACTOR_NAME)
+                        .getOperator(OperationPartial.GUARD_OPERATION_NAME).buildOperation(null).start();
+        assertTrue(future.isDone());
+        assertEquals(PolicyResult.SUCCESS, future.get().getResult());
     }
 
     @Test
@@ -97,4 +108,14 @@ public class BasicOperationTest {
         Map<String, Object> map = Util.translateToMap("", ResourceUtils.getResourceAsString("actual.json"));
         oper.verifyRequest("expected.json", map, "svc-request-id", "vnf-id");
     }
+
+    @Test
+    public void testProvideCqResponse() throws Exception {
+        AaiCqResponse cq = new AaiCqResponse("{}");
+        oper.provideCqResponse(cq);
+
+        assertSame(cq, oper.context.getProperty(AaiCqResponse.CONTEXT_KEY));
+        assertTrue(oper.cqFuture.isDone());
+        assertEquals(PolicyResult.SUCCESS, oper.cqFuture.get().getResult());
+    }
 }
diff --git a/models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/DummyActor.java b/models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/DummyActor.java
new file mode 100644 (file)
index 0000000..c862a18
--- /dev/null
@@ -0,0 +1,39 @@
+/*-
+ * ============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.test;
+
+import org.onap.policy.controlloop.actorserviceprovider.impl.ActorImpl;
+
+public class DummyActor extends ActorImpl {
+    public static final String NAME = "MyActor";
+    public static final String MY_OPERATION1 = "MyOperationA";
+    public static final String MY_OPERATION2 = "MyOperationB";
+
+    /**
+     * Constructs the object.
+     */
+    public DummyActor() {
+        super(NAME);
+
+        addOperator(new DummyOperator(NAME, MY_OPERATION1));
+        addOperator(new DummyOperator(NAME, MY_OPERATION2));
+    }
+}
diff --git a/models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/DummyOperator.java b/models-interactions/model-actors/actor.test/src/test/java/org/onap/policy/controlloop/actor/test/DummyOperator.java
new file mode 100644 (file)
index 0000000..efd3b65
--- /dev/null
@@ -0,0 +1,37 @@
+/*-
+ * ============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.test;
+
+import org.onap.policy.controlloop.actorserviceprovider.Operation;
+import org.onap.policy.controlloop.actorserviceprovider.impl.OperatorPartial;
+import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
+
+public class DummyOperator extends OperatorPartial {
+
+    public DummyOperator(String actorName, String name) {
+        super(actorName, name);
+    }
+
+    @Override
+    public Operation buildOperation(ControlLoopOperationParams params) {
+        return null;
+    }
+}
diff --git a/models-interactions/model-actors/actor.test/src/test/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor b/models-interactions/model-actors/actor.test/src/test/resources/META-INF/services/org.onap.policy.controlloop.actorserviceprovider.spi.Actor
new file mode 100644 (file)
index 0000000..f9c3bdd
--- /dev/null
@@ -0,0 +1 @@
+org.onap.policy.controlloop.actor.test.DummyActor
\ No newline at end of file
diff --git a/models-interactions/model-actors/actor.test/src/test/resources/service.yaml b/models-interactions/model-actors/actor.test/src/test/resources/service.yaml
new file mode 100644 (file)
index 0000000..49de7da
--- /dev/null
@@ -0,0 +1,23 @@
+#
+# ============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========================================================
+#
+actors:
+  MyActor:
+    MyOperationA: {}
+    MyOperationB: {}
\ No newline at end of file
diff --git a/models-interactions/model-actors/actor.test/src/test/resources/serviceFull.yaml b/models-interactions/model-actors/actor.test/src/test/resources/serviceFull.yaml
new file mode 100644 (file)
index 0000000..9818215
--- /dev/null
@@ -0,0 +1,42 @@
+#
+# ============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========================================================
+#
+httpClients:
+- clientName: my-client
+  hostname: localhost
+  port: 80
+  basePath: base-url
+  managed: true
+topics:
+  topicSources:
+  - topicCommInfrastructure: NOOP
+    topic: my-source
+    servers:
+    - localhost
+    managed: true
+  topicSinks:
+  - topicCommInfrastructure: NOOP
+    topic: my-sink
+    servers:
+    - localhost
+    managed: true
+actors:
+  MyActor:
+    MyOperationA: {}
+    MyOperationB: {}
\ No newline at end of file
diff --git a/models-interactions/model-actors/actor.test/src/test/resources/serviceInvalidHttp.yaml b/models-interactions/model-actors/actor.test/src/test/resources/serviceInvalidHttp.yaml
new file mode 100644 (file)
index 0000000..b31e8e8
--- /dev/null
@@ -0,0 +1,29 @@
+#
+# ============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========================================================
+#
+httpClients:
+- clientName: my-client
+  hostname: localhost
+  port: 80
+  serializationProvider: unknown.class.name
+  managed: true
+actors:
+  MyActor:
+    MyOperationA: {}
+    MyOperationB: {}
\ No newline at end of file
diff --git a/models-interactions/model-actors/actor.test/src/test/resources/serviceMissingActors.yaml b/models-interactions/model-actors/actor.test/src/test/resources/serviceMissingActors.yaml
new file mode 100644 (file)
index 0000000..0eec84e
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# ============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========================================================
+#
+httpClients:
+- clientName: my-client
+  hostname: localhost
+  port: 80
+  basePath: base-url
+  managed: true
\ No newline at end of file
index 24c2cfc..22c7d33 100644 (file)
@@ -24,7 +24,9 @@ package org.onap.policy.controlloop.actorserviceprovider;
 import com.google.common.collect.ImmutableMap;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.ServiceConfigurationError;
 import java.util.ServiceLoader;
 import java.util.Set;
 import org.onap.policy.common.parameters.BeanValidationResult;
@@ -57,7 +59,17 @@ public class ActorService extends StartConfigPartial<Map<String, Map<String, Obj
 
         Map<String, Actor> map = new HashMap<>();
 
-        for (Actor newActor : loadActors()) {
+        Iterator<Actor> iter = loadActors().iterator();
+        while (iter.hasNext()) {
+
+            Actor newActor;
+            try {
+                newActor = iter.next();
+            } catch (ServiceConfigurationError e) {
+                logger.warn("unable to load actor", e);
+                continue;
+            }
+
             map.compute(newActor.getName(), (name, existingActor) -> {
                 if (existingActor == null) {
                     return newActor;
@@ -168,8 +180,7 @@ public class ActorService extends StartConfigPartial<Map<String, Map<String, Obj
     @Override
     protected void doStop() {
         logger.info("stopping actors");
-        name2actor.values()
-                        .forEach(actor -> Util.runFunction(actor::stop, "failed to stop actor {}", actor.getName()));
+        name2actor.values().forEach(actor -> Util.runFunction(actor::stop, "failed to stop actor {}", actor.getName()));
     }
 
     @Override
index 8099ea7..f7b58c1 100644 (file)
@@ -116,6 +116,14 @@ public class ControlLoopEventContext implements Serializable {
         properties.put(name, value);
     }
 
+    /**
+     * Removes a property.
+     * @param name property name
+     */
+    public void removeProperty(String name) {
+        properties.remove(name);
+    }
+
     /**
      * Obtains the given property.
      *
index e636228..c998209 100644 (file)
@@ -23,8 +23,10 @@ package org.onap.policy.controlloop.actorserviceprovider.impl;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Queue;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
@@ -71,6 +73,8 @@ public abstract class OperationPartial implements Operation {
     private static final Logger logger = LoggerFactory.getLogger(OperationPartial.class);
     private static final Coder coder = new StandardCoder();
 
+    public static final String GUARD_ACTOR_NAME = "GUARD";
+    public static final String GUARD_OPERATION_NAME = "Decision";
     public static final long DEFAULT_RETRY_WAIT_MS = 1000L;
 
     private final OperatorConfig config;
@@ -187,7 +191,7 @@ public abstract class OperationPartial implements Operation {
 
     /**
      * Invokes the operation's preprocessor step(s) as a "future". This method simply
-     * invokes {@link #startGuardAsync()}.
+     * returns {@code null}.
      * <p/>
      * This method assumes the following:
      * <ul>
@@ -199,12 +203,11 @@ public abstract class OperationPartial implements Operation {
      *         {@code null} if this operation needs no preprocessor
      */
     protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
-        return startGuardAsync();
+        return null;
     }
 
     /**
-     * Invokes the operation's guard step(s) as a "future". This method simply returns
-     * {@code null}.
+     * Invokes the operation's guard step(s) as a "future".
      * <p/>
      * This method assumes the following:
      * <ul>
@@ -216,7 +219,42 @@ public abstract class OperationPartial implements Operation {
      *         {@code null} if this operation has no guard
      */
     protected CompletableFuture<OperationOutcome> startGuardAsync() {
-        return null;
+        // get the guard payload
+        Map<String,Object> guardPayload = makeGuardPayload();
+
+        // wrap it in a "resource"
+        Map<String,Object> resource = new LinkedHashMap<>();
+        resource.put("guard", guardPayload);
+
+        Map<String,Object> payload = new LinkedHashMap<>();
+        payload.put("resource", resource);
+
+        /*
+         * Note: can't use constants from actor.guard, because that would create a
+         * circular dependency.
+         */
+        return params.toBuilder().actor(GUARD_ACTOR_NAME).operation(GUARD_OPERATION_NAME).retry(null).timeoutSec(null)
+                        .payload(payload).build().start();
+    }
+
+    /**
+     * Creates a payload to execute a guard operation.
+     *
+     * @return a new guard payload
+     */
+    protected Map<String, Object> makeGuardPayload() {
+        Map<String, Object> guard = new LinkedHashMap<>();
+        guard.put("actor", params.getActor());
+        guard.put("recipe", params.getOperation());
+        guard.put("target", params.getTargetEntity());
+        guard.put("requestId", params.getRequestId());
+
+        String clname = params.getContext().getEvent().getClosedLoopControlName();
+        if (clname != null) {
+            guard.put("clname", clname);
+        }
+
+        return guard;
     }
 
     /**
index dc6f2b6..b8218e5 100644 (file)
@@ -33,18 +33,22 @@ import org.onap.policy.controlloop.actorserviceprovider.Util;
 
 /**
  * Superclass for Actor parameters that have default values in "this" object, and
- * operation-specific values in {@link #operation}.
+ * operation-specific values in {@link #operations}.
  */
 @Getter
 @Setter
 @EqualsAndHashCode
 public class CommonActorParams {
+    /**
+     * Name of the "operations" field contained within actor parameters.
+     */
+    public static final String OPERATIONS_FIELD = "operations";
 
     /**
      * Maps the operation name to its parameters.
      */
     @NotNull
-    protected Map<String, Map<String, Object>> operation;
+    protected Map<String, Map<String, Object>> operations;
 
 
     /**
@@ -57,10 +61,10 @@ public class CommonActorParams {
     public Function<String, Map<String, Object>> makeOperationParameters(String name) {
 
         Map<String, Object> defaultParams = Util.translateToMap(name, this);
-        defaultParams.remove("operation");
+        defaultParams.remove(OPERATIONS_FIELD);
 
         return operationName -> {
-            Map<String, Object> specificParams = operation.get(operationName);
+            Map<String, Object> specificParams = operations.get(operationName);
             if (specificParams == null) {
                 return null;
             }
index cf24262..0f44f4f 100644 (file)
@@ -41,6 +41,7 @@ import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
 
 public class ControlLoopEventContextTest {
+    private static final String MY_KEY = "def";
     private static final UUID REQ_ID = UUID.randomUUID();
     private static final String ITEM_KEY = "obtain-C";
 
@@ -53,7 +54,7 @@ public class ControlLoopEventContextTest {
      */
     @Before
     public void setUp() {
-        enrichment = Map.of("abc", "one", "def", "two");
+        enrichment = Map.of("abc", "one", MY_KEY, "two");
 
         event = new VirtualControlLoopEvent();
         event.setRequestId(REQ_ID);
@@ -81,17 +82,21 @@ public class ControlLoopEventContextTest {
     }
 
     @Test
-    public void testContains_testGetProperty_testSetProperty() {
+    public void testContains_testGetProperty_testSetProperty_testRemoveProperty() {
         context.setProperty("abc", "a string");
-        context.setProperty("def", 100);
+        context.setProperty(MY_KEY, 100);
 
+        assertTrue(context.contains(MY_KEY));
         assertFalse(context.contains("ghi"));
 
         String strValue = context.getProperty("abc");
         assertEquals("a string", strValue);
 
-        int intValue = context.getProperty("def");
+        int intValue = context.getProperty(MY_KEY);
         assertEquals(100, intValue);
+
+        context.removeProperty(MY_KEY);
+        assertFalse(context.contains(MY_KEY));
     }
 
     @Test
index 4a43541..dfd3f8b 100644 (file)
@@ -172,7 +172,7 @@ public class BidirectionalTopicActorTest {
                         new TreeMap<>(maker.apply("operB")).toString());
 
         // with invalid actor parameters
-        params.setOperation(null);
+        params.setOperations(null);
         assertThatThrownBy(() -> prov.makeOperatorParameters(Util.translateToMap(prov.getName(), params)))
                         .isInstanceOf(ParameterValidationRuntimeException.class);
     }
@@ -212,7 +212,7 @@ public class BidirectionalTopicActorTest {
         params.setTimeoutSec(TIMEOUT);
 
         // @formatter:off
-        params.setOperation(Map.of(
+        params.setOperations(Map.of(
                         "operA", Map.of(),
                         "operB", Map.of("sourceTopic", "topicB")));
         // @formatter:on
index dacd3a5..a28b98c 100644 (file)
@@ -54,7 +54,7 @@ public class HttpActorTest {
         params.setTimeoutSec(TIMEOUT);
 
         // @formatter:off
-        params.setOperation(Map.of(
+        params.setOperations(Map.of(
                         "operA", Map.of("path", "urlA"),
                         "operB", Map.of("path", "urlB")));
         // @formatter:on
@@ -73,7 +73,7 @@ public class HttpActorTest {
                         new TreeMap<>(maker.apply("operB")).toString());
 
         // with invalid actor parameters
-        params.setOperation(null);
+        params.setOperations(null);
         assertThatThrownBy(() -> prov.makeOperatorParameters(Util.translateToMap(prov.getName(), params)))
                         .isInstanceOf(ParameterValidationRuntimeException.class);
     }
index 39564a4..2893cb6 100644 (file)
@@ -28,6 +28,9 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import ch.qos.logback.classic.Logger;
 import java.time.Instant;
@@ -44,7 +47,6 @@ import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -55,6 +57,9 @@ import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
 import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
 import org.onap.policy.common.utils.coder.Coder;
@@ -64,11 +69,14 @@ import org.onap.policy.common.utils.test.log.logback.ExtractAppender;
 import org.onap.policy.common.utils.time.PseudoExecutor;
 import org.onap.policy.controlloop.ControlLoopOperation;
 import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.actorserviceprovider.ActorService;
 import org.onap.policy.controlloop.actorserviceprovider.Operation;
 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
+import org.onap.policy.controlloop.actorserviceprovider.Operator;
 import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOperationParams;
 import org.onap.policy.controlloop.actorserviceprovider.parameters.OperatorConfig;
+import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
 import org.onap.policy.controlloop.policy.PolicyResult;
 import org.slf4j.LoggerFactory;
 
@@ -82,6 +90,7 @@ public class OperationPartialTest {
     private static final String OPERATION = "my-operation";
     private static final String MY_SINK = "my-sink";
     private static final String MY_SOURCE = "my-source";
+    private static final String MY_TARGET_ENTITY = "my-entity";
     private static final String TEXT = "my-text";
     private static final int TIMEOUT = 1000;
     private static final UUID REQ_ID = UUID.randomUUID();
@@ -95,6 +104,15 @@ public class OperationPartialTest {
     private static final Logger logger = (Logger) LoggerFactory.getLogger(OperationPartial.class);
     private static final ExtractAppender appender = new ExtractAppender();
 
+    @Mock
+    private ActorService service;
+    @Mock
+    private Actor guardActor;
+    @Mock
+    private Operator guardOperator;
+    @Mock
+    private Operation guardOperation;
+
     private VirtualControlLoopEvent event;
     private ControlLoopEventContext context;
     private PseudoExecutor executor;
@@ -139,6 +157,8 @@ public class OperationPartialTest {
      */
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
         event = new VirtualControlLoopEvent();
         event.setRequestId(REQ_ID);
 
@@ -146,8 +166,13 @@ public class OperationPartialTest {
         executor = new PseudoExecutor();
 
         params = ControlLoopOperationParams.builder().completeCallback(this::completer).context(context)
-                        .executor(executor).actor(ACTOR).operation(OPERATION).timeoutSec(TIMEOUT)
-                        .startCallback(this::starter).targetEntity(MY_SINK).build();
+                        .executor(executor).actorService(service).actor(ACTOR).operation(OPERATION).timeoutSec(TIMEOUT)
+                        .startCallback(this::starter).targetEntity(MY_TARGET_ENTITY).build();
+
+        when(service.getActor(OperationPartial.GUARD_ACTOR_NAME)).thenReturn(guardActor);
+        when(guardActor.getOperator(OperationPartial.GUARD_OPERATION_NAME)).thenReturn(guardOperator);
+        when(guardOperator.buildOperation(any())).thenReturn(guardOperation);
+        when(guardOperation.start()).thenReturn(CompletableFuture.completedFuture(makeSuccess()));
 
         config = new OperatorConfig(executor);
 
@@ -188,25 +213,6 @@ public class OperationPartialTest {
         verifyRun("testStart", 1, 1, PolicyResult.SUCCESS);
     }
 
-    /**
-     * Tests startOperation() when the operation has a preprocessor.
-     */
-    @Test
-    public void testStartWithPreprocessor() {
-        AtomicInteger count = new AtomicInteger();
-
-        CompletableFuture<OperationOutcome> preproc = CompletableFuture.supplyAsync(() -> {
-            count.incrementAndGet();
-            return makeSuccess();
-        }, executor);
-
-        oper.setGuard(preproc);
-
-        verifyRun("testStartWithPreprocessor_testStartPreprocessor", 1, 1, PolicyResult.SUCCESS);
-
-        assertEquals(1, count.get());
-    }
-
     /**
      * Tests start() with multiple running requests.
      */
@@ -232,7 +238,7 @@ public class OperationPartialTest {
      */
     @Test
     public void testStartPreprocessorFailure() {
-        oper.setGuard(CompletableFuture.completedFuture(makeFailure()));
+        oper.setPreProc(CompletableFuture.completedFuture(makeFailure()));
 
         verifyRun("testStartPreprocessorFailure", 1, 0, PolicyResult.FAILURE_GUARD);
     }
@@ -243,7 +249,7 @@ public class OperationPartialTest {
     @Test
     public void testStartPreprocessorException() {
         // arrange for the preprocessor to throw an exception
-        oper.setGuard(CompletableFuture.failedFuture(new IllegalStateException(EXPECTED_EXCEPTION)));
+        oper.setPreProc(CompletableFuture.failedFuture(new IllegalStateException(EXPECTED_EXCEPTION)));
 
         verifyRun("testStartPreprocessorException", 1, 0, PolicyResult.FAILURE_GUARD);
     }
@@ -254,7 +260,7 @@ public class OperationPartialTest {
     @Test
     public void testStartPreprocessorNotRunning() {
         // arrange for the preprocessor to return success, which will be ignored
-        oper.setGuard(CompletableFuture.completedFuture(makeSuccess()));
+        // oper.setGuard(CompletableFuture.completedFuture(makeSuccess()));
 
         oper.start().cancel(false);
         assertTrue(executor.runAll(MAX_REQUESTS));
@@ -291,8 +297,49 @@ public class OperationPartialTest {
     }
 
     @Test
-    public void testStartGuardAsync() {
-        assertNull(oper.startGuardAsync());
+    public void testStartGuardAsync() throws Exception {
+        CompletableFuture<OperationOutcome> future = oper.startGuardAsync();
+        assertTrue(future.isDone());
+        assertEquals(PolicyResult.SUCCESS, future.get().getResult());
+
+        // verify the parameters that were passed
+        ArgumentCaptor<ControlLoopOperationParams> paramsCaptor =
+                        ArgumentCaptor.forClass(ControlLoopOperationParams.class);
+        verify(guardOperator).buildOperation(paramsCaptor.capture());
+
+        params = paramsCaptor.getValue();
+        assertEquals(OperationPartial.GUARD_ACTOR_NAME, params.getActor());
+        assertEquals(OperationPartial.GUARD_OPERATION_NAME, params.getOperation());
+        assertNull(params.getRetry());
+        assertNull(params.getTimeoutSec());
+
+        Map<String, Object> payload = params.getPayload();
+        assertNotNull(payload);
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> resource = (Map<String, Object>) payload.get("resource");
+        assertNotNull(resource);
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> guard = (Map<String, Object>) resource.get("guard");
+        assertEquals(oper.makeGuardPayload(), guard);
+    }
+
+    @Test
+    public void testMakeGuardPayload() {
+        Map<String, Object> payload = oper.makeGuardPayload();
+        assertSame(REQ_ID, payload.get("requestId"));
+
+        // request id changes, so remove it
+        payload.remove("requestId");
+
+        assertEquals("{actor=my-actor, recipe=my-operation, target=my-entity}", payload.toString());
+
+        // repeat, but with closed loop name
+        event.setClosedLoopControlName("my-loop");
+        payload = oper.makeGuardPayload();
+        payload.remove("requestId");
+        assertEquals("{actor=my-actor, recipe=my-operation, target=my-entity, clname=my-loop}", payload.toString());
     }
 
     @Test
@@ -541,7 +588,7 @@ public class OperationPartialTest {
      */
     @Test
     public void testHandlePreprocessorFailureTrue() {
-        oper.setGuard(CompletableFuture.completedFuture(makeSuccess()));
+        oper.setPreProc(CompletableFuture.completedFuture(makeSuccess()));
         verifyRun("testHandlePreprocessorFailureTrue", 1, 1, PolicyResult.SUCCESS);
     }
 
@@ -550,7 +597,7 @@ public class OperationPartialTest {
      */
     @Test
     public void testHandlePreprocessorFailureFalse() throws Exception {
-        oper.setGuard(CompletableFuture.completedFuture(makeFailure()));
+        oper.setPreProc(CompletableFuture.completedFuture(makeFailure()));
         verifyRun("testHandlePreprocessorFailureFalse", 1, 0, PolicyResult.FAILURE_GUARD);
     }
 
@@ -559,9 +606,8 @@ public class OperationPartialTest {
      */
     @Test
     public void testHandlePreprocessorFailureNull() throws Exception {
-        // arrange to return null from the preprocessor
-        oper.setGuard(CompletableFuture.completedFuture(null));
-
+        // arrange to return a null outcome from the preprocessor
+        oper.setPreProc(CompletableFuture.completedFuture(null));
         verifyRun("testHandlePreprocessorFailureNull", 1, 0, PolicyResult.FAILURE_GUARD);
     }
 
@@ -1211,12 +1257,10 @@ public class OperationPartialTest {
 
         @Setter
         private boolean genException;
-
         @Setter
         private int maxFailures = 0;
-
         @Setter
-        private CompletableFuture<OperationOutcome> guard;
+        private CompletableFuture<OperationOutcome> preProc;
 
 
         public MyOper() {
@@ -1241,11 +1285,6 @@ public class OperationPartialTest {
             return operation;
         }
 
-        @Override
-        protected CompletableFuture<OperationOutcome> startGuardAsync() {
-            return (guard != null ? guard : super.startGuardAsync());
-        }
-
         @Override
         protected long getRetryWaitMs() {
             /*
@@ -1254,5 +1293,10 @@ public class OperationPartialTest {
              */
             return 0L;
         }
+
+        @Override
+        protected CompletableFuture<OperationOutcome> startPreprocessorAsync() {
+            return (preProc != null ? preProc : super.startPreprocessorAsync());
+        }
     }
 }
index 1f38ad3..1276950 100644 (file)
@@ -76,11 +76,12 @@ public class BidirectionalTopicActorParamsTest {
         assertTrue(params.validate(CONTAINER).isValid());
 
         // only a few fields are required
-        BidirectionalTopicActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operMap, "timeoutSec", 1),
-                        BidirectionalTopicActorParams.class);
+        BidirectionalTopicActorParams sparse =
+                        Util.translate(CONTAINER, Map.of(CommonActorParams.OPERATIONS_FIELD, operMap, "timeoutSec", 1),
+                                        BidirectionalTopicActorParams.class);
         assertTrue(sparse.validate(CONTAINER).isValid());
 
-        testValidateField("operation", "null", params2 -> params2.setOperation(null));
+        testValidateField(CommonActorParams.OPERATIONS_FIELD, "null", params2 -> params2.setOperations(null));
         testValidateField("timeoutSec", "minimum", params2 -> params2.setTimeoutSec(-1));
 
         // check edge cases
@@ -111,7 +112,7 @@ public class BidirectionalTopicActorParamsTest {
         params2.setSinkTopic(DFLT_SINK);
         params2.setSourceTopic(DFLT_SOURCE);
         params2.setTimeoutSec(DFLT_TIMEOUT);
-        params2.setOperation(operMap);
+        params2.setOperations(operMap);
 
         return params2;
     }
index 9014203..99b4410 100644 (file)
@@ -86,7 +86,7 @@ public class CommonActorParamsTest {
         assertThatCode(() -> params.doValidation(CONTAINER)).doesNotThrowAnyException();
 
         // invalid param
-        params.setOperation(null);
+        params.setOperations(null);
         assertThatThrownBy(() -> params.doValidation(CONTAINER))
                         .isInstanceOf(ParameterValidationRuntimeException.class);
     }
@@ -96,11 +96,12 @@ public class CommonActorParamsTest {
         assertTrue(params.validate(CONTAINER).isValid());
 
         // only a few fields are required
-        CommonActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operations, "timeoutSec", 1),
+        CommonActorParams sparse = Util.translate(CONTAINER,
+                        Map.of(CommonActorParams.OPERATIONS_FIELD, operations, "timeoutSec", 1),
                         CommonActorParams.class);
         assertTrue(sparse.validate(CONTAINER).isValid());
 
-        testValidateField("operation", "null", params2 -> params2.setOperation(null));
+        testValidateField(CommonActorParams.OPERATIONS_FIELD, "null", params2 -> params2.setOperations(null));
     }
 
     private void testValidateField(String fieldName, String expected, Consumer<CommonActorParams> makeInvalid) {
@@ -119,7 +120,7 @@ public class CommonActorParamsTest {
 
     private CommonActorParams makeCommonActorParams() {
         MyParams params2 = new MyParams();
-        params2.setOperation(operations);
+        params2.setOperations(operations);
         params2.setText1(TEXT1);
         params2.setText2(TEXT2);
 
index 9e70853..99bf633 100644 (file)
@@ -64,11 +64,11 @@ public class HttpActorParamsTest {
         assertTrue(params.validate(CONTAINER).isValid());
 
         // only a few fields are required
-        HttpActorParams sparse = Util.translate(CONTAINER, Map.of("operation", operations, "timeoutSec", 1),
-                        HttpActorParams.class);
+        HttpActorParams sparse = Util.translate(CONTAINER,
+                        Map.of(CommonActorParams.OPERATIONS_FIELD, operations, "timeoutSec", 1), HttpActorParams.class);
         assertTrue(sparse.validate(CONTAINER).isValid());
 
-        testValidateField("operation", "null", params2 -> params2.setOperation(null));
+        testValidateField(CommonActorParams.OPERATIONS_FIELD, "null", params2 -> params2.setOperations(null));
         testValidateField("timeoutSec", "minimum", params2 -> params2.setTimeoutSec(-1));
 
         // check edge cases
@@ -97,7 +97,7 @@ public class HttpActorParamsTest {
         HttpActorParams params2 = new HttpActorParams();
         params2.setClientName(CLIENT);
         params2.setTimeoutSec(TIMEOUT);
-        params2.setOperation(operations);
+        params2.setOperations(operations);
 
         return params2;
     }