Merge "Change payload to Map<String,Object> so it's more versatile"
authorRam Krishna Verma <ram_krishna.verma@bell.ca>
Mon, 24 Feb 2020 19:01:23 +0000 (19:01 +0000)
committerGerrit Code Review <gerrit@onap.org>
Mon, 24 Feb 2020 19:01:23 +0000 (19:01 +0000)
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;
     }