diff options
10 files changed, 446 insertions, 20 deletions
diff --git a/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/GrpcOperation.java b/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/GrpcOperation.java index 820f4de34..0a882ce93 100644 --- a/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/GrpcOperation.java +++ b/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/GrpcOperation.java @@ -51,6 +51,7 @@ 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.actorserviceprovider.parameters.ControlLoopOperationParams; +import org.onap.policy.controlloop.actorserviceprovider.pipeline.PipelineControllerFuture; import org.onap.policy.controlloop.policy.TargetType; /** @@ -202,13 +203,26 @@ public class GrpcOperation extends OperationPartial { @Override protected CompletableFuture<OperationOutcome> startOperationAsync(int attempt, OperationOutcome outcome) { + /* + * construct the request first so that we don't have to clean up the "client" if + * an exception is thrown + */ + ExecutionServiceInput request = constructRequest(params); + CompletableFuture<OperationOutcome> future = new CompletableFuture<>(); + client = new CdsProcessorGrpcClient(new CdsActorServiceManager(outcome, future), config.getCdsServerProperties()); - ExecutionServiceInput request = constructRequest(params); client.sendRequest(request); - return future; + + // arrange to shutdown the client when the request completes + PipelineControllerFuture<OperationOutcome> controller = new PipelineControllerFuture<>(); + + controller.wrap(future).whenCompleteAsync(controller.delayedComplete(), params.getExecutor()) + .whenCompleteAsync((arg1, arg2) -> client.close(), getBlockingExecutor()); + + return controller; } /** diff --git a/models-interactions/model-actors/actor.cds/src/test/java/org/onap/policy/controlloop/actor/cds/GrpcOperationTest.java b/models-interactions/model-actors/actor.cds/src/test/java/org/onap/policy/controlloop/actor/cds/GrpcOperationTest.java index 9477a1585..fe8d42814 100644 --- a/models-interactions/model-actors/actor.cds/src/test/java/org/onap/policy/controlloop/actor/cds/GrpcOperationTest.java +++ b/models-interactions/model-actors/actor.cds/src/test/java/org/onap/policy/controlloop/actor/cds/GrpcOperationTest.java @@ -35,16 +35,20 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.onap.aai.domain.yang.GenericVnf; import org.onap.aai.domain.yang.ServiceInstance; import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput; +import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceOutput; import org.onap.policy.aai.AaiCqResponse; import org.onap.policy.cds.client.CdsProcessorGrpcClient; import org.onap.policy.cds.properties.CdsServerProperties; @@ -63,6 +67,8 @@ import org.onap.policy.controlloop.actorserviceprovider.parameters.ControlLoopOp import org.onap.policy.controlloop.policy.PolicyResult; import org.onap.policy.controlloop.policy.Target; import org.onap.policy.controlloop.policy.TargetType; +import org.onap.policy.simulators.CdsSimulator; +import org.onap.policy.simulators.Util; public class GrpcOperationTest { private static final String TARGET_ENTITY = "entity"; @@ -74,6 +80,14 @@ public class GrpcOperationTest { private static final UUID REQUEST_ID = UUID.randomUUID(); private static final Coder coder = new StandardCoder(); + protected static final Executor blockingExecutor = command -> { + Thread thread = new Thread(command); + thread.setDaemon(true); + thread.start(); + }; + + private static CdsSimulator sim; + @Mock private CdsProcessorGrpcClient cdsClient; private CdsServerProperties cdsProps; @@ -82,6 +96,16 @@ public class GrpcOperationTest { private Target target; private GrpcOperation operation; + @BeforeClass + public static void setUpBeforeClass() throws Exception { + sim = Util.buildCdsSim(); + } + + @AfterClass + public static void tearDownAfterClass() { + sim.stop(); + } + /** * Sets up the fields. */ @@ -112,6 +136,40 @@ public class GrpcOperationTest { target.setResourceID(RESOURCE_ID); } + /** + * Tests "success" case with simulator. + */ + @Test + public void testSuccess() throws Exception { + ControlLoopEventContext context = new ControlLoopEventContext(onset); + loadCqData(context); + + Map<String, Object> payload = Map.of("artifact_name", "my_artifact", "artifact_version", "1.0"); + + final ControlLoopOperationParams params = ControlLoopOperationParams.builder() + .actor(CdsActorConstants.CDS_ACTOR).operation("subscribe").context(context) + .actorService(new ActorService()).targetEntity(TARGET_ENTITY).target(target).retry(0) + .timeoutSec(5).executor(blockingExecutor).payload(payload).build(); + + cdsProps.setHost("localhost"); + cdsProps.setPort(sim.getPort()); + cdsProps.setTimeout(3); + + GrpcConfig config = new GrpcConfig(blockingExecutor, cdsProps); + + operation = new GrpcOperation(params, config) { + @Override + protected CompletableFuture<OperationOutcome> startGuardAsync() { + // indicate that guard completed successfully + return CompletableFuture.completedFuture(params.makeOutcome()); + } + }; + + OperationOutcome outcome = operation.start().get(); + assertEquals(PolicyResult.SUCCESS, outcome.getResult()); + assertTrue(outcome.getResponse() instanceof ExecutionServiceOutput); + } + @Test public void testStartPreprocessorAsync() throws InterruptedException, ExecutionException, TimeoutException { diff --git a/models-interactions/model-simulators/pom.xml b/models-interactions/model-simulators/pom.xml index 92c26c2b7..f0afa54c0 100644 --- a/models-interactions/model-simulators/pom.xml +++ b/models-interactions/model-simulators/pom.xml @@ -56,6 +56,11 @@ </dependency> <dependency> <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>cds</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> <artifactId>appclcm</artifactId> <version>${project.version}</version> </dependency> diff --git a/models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/CdsSimulator.java b/models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/CdsSimulator.java new file mode 100644 index 000000000..dbbaa1681 --- /dev/null +++ b/models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/CdsSimulator.java @@ -0,0 +1,104 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nordix Foundation. + * Modifications 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.simulators; + +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import io.grpc.Server; +import io.grpc.netty.NettyServerBuilder; +import io.grpc.stub.StreamObserver; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import lombok.Getter; +import org.apache.commons.io.IOUtils; +import org.onap.ccsdk.cds.controllerblueprints.processing.api.BluePrintProcessingServiceGrpc.BluePrintProcessingServiceImplBase; +import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput; +import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceOutput; +import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceOutput.Builder; + +public class CdsSimulator { + @Getter + private final int port; + + private final Server server; + + /** + * Constructs the object, but does not start it. + * + * @param host host name of the server + * @param port port of the server + */ + public CdsSimulator(String host, int port) { + this.port = port; + + BluePrintProcessingServiceImplBase testCdsBlueprintServerImpl = new BluePrintProcessingServiceImplBase() { + + @Override + public StreamObserver<ExecutionServiceInput> process( + final StreamObserver<ExecutionServiceOutput> responseObserver) { + + return new StreamObserver<ExecutionServiceInput>() { + + @Override + public void onNext(final ExecutionServiceInput executionServiceInput) { + try { + String responseString = IOUtils.toString( + getClass().getResource("cds/CreateSubscriptionResponseEvent.json"), + StandardCharsets.UTF_8); + Builder builder = ExecutionServiceOutput.newBuilder(); + JsonFormat.parser().ignoringUnknownFields().merge(responseString, builder); + responseObserver.onNext(builder.build()); + + } catch (InvalidProtocolBufferException e) { + throw new SimulatorRuntimeException("Cannot convert ExecutionServiceOutput output", e); + + } catch (IOException e) { + throw new SimulatorRuntimeException("Cannot read ExecutionServiceOutput from file", e); + } + } + + @Override + public void onError(final Throwable throwable) { + responseObserver.onError(throwable); + } + + @Override + public void onCompleted() { + responseObserver.onCompleted(); + } + }; + } + }; + + server = NettyServerBuilder.forAddress(new InetSocketAddress(host, port)).addService(testCdsBlueprintServerImpl) + .build(); + } + + public void start() throws IOException { + server.start(); + } + + public void stop() { + server.shutdown(); + } +} diff --git a/models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/SimulatorRuntimeException.java b/models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/SimulatorRuntimeException.java new file mode 100644 index 000000000..b97e4d9f7 --- /dev/null +++ b/models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/SimulatorRuntimeException.java @@ -0,0 +1,41 @@ +/*- + * ============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.simulators; + +public class SimulatorRuntimeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public SimulatorRuntimeException() { + super(); + } + + public SimulatorRuntimeException(String message) { + super(message); + } + + public SimulatorRuntimeException(Throwable cause) { + super(cause); + } + + public SimulatorRuntimeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/Util.java b/models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/Util.java index 5cb518f4e..efc40367b 100644 --- a/models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/Util.java +++ b/models-interactions/model-simulators/src/main/java/org/onap/policy/simulators/Util.java @@ -21,6 +21,7 @@ package org.onap.policy.simulators; +import java.io.IOException; import java.util.Properties; import org.onap.policy.common.endpoints.http.server.HttpServletServer; import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance; @@ -46,6 +47,7 @@ public class Util { public static final int VFCSIM_SERVER_PORT = 6668; public static final int GUARDSIM_SERVER_PORT = 6669; public static final int SDNCSIM_SERVER_PORT = 6670; + public static final int CDSSIM_SERVER_PORT = 6671; public static final int DMAAPSIM_SERVER_PORT = 3904; private static final String CANNOT_PROCESS_PARAMETERS = "cannot parse parameters "; @@ -67,9 +69,21 @@ public class Util { .build(AAISIM_SERVER_NAME, LOCALHOST, AAISIM_SERVER_PORT, "/", false, true); testServer.addServletClass("/*", AaiSimulatorJaxRs.class.getName()); testServer.waitedStart(5000); - if (!NetworkUtil.isTcpPortOpen(LOCALHOST, testServer.getPort(), 5, 10000L)) { - throw new IllegalStateException(CANNOT_CONNECT + testServer.getPort()); - } + waitForServerToListen(testServer.getPort()); + return testServer; + } + + /** + * Build a CDS simulator. + * + * @return the simulator + * @throws InterruptedException if a thread is interrupted + * @throws IOException if an I/O error occurs + */ + public static CdsSimulator buildCdsSim() throws InterruptedException, IOException { + final CdsSimulator testServer = new CdsSimulator(LOCALHOST, CDSSIM_SERVER_PORT); + testServer.start(); + waitForServerToListen(testServer.getPort()); return testServer; } @@ -84,9 +98,7 @@ public class Util { .build(SDNCSIM_SERVER_NAME, LOCALHOST, SDNCSIM_SERVER_PORT, "/", false, true); testServer.addServletClass("/*", SdncSimulatorJaxRs.class.getName()); testServer.waitedStart(5000); - if (!NetworkUtil.isTcpPortOpen(LOCALHOST, testServer.getPort(), 5, 10000L)) { - throw new IllegalStateException(CANNOT_CONNECT + testServer.getPort()); - } + waitForServerToListen(testServer.getPort()); return testServer; } @@ -102,9 +114,7 @@ public class Util { .build(SOSIM_SERVER_NAME, LOCALHOST, SOSIM_SERVER_PORT, "/", false, true); testServer.addServletClass("/*", SoSimulatorJaxRs.class.getName()); testServer.waitedStart(5000); - if (!NetworkUtil.isTcpPortOpen(LOCALHOST, testServer.getPort(), 5, 10000L)) { - throw new IllegalStateException(CANNOT_CONNECT + testServer.getPort()); - } + waitForServerToListen(testServer.getPort()); return testServer; } @@ -119,9 +129,7 @@ public class Util { .build(VFCSIM_SERVER_NAME, LOCALHOST, VFCSIM_SERVER_PORT, "/", false, true); testServer.addServletClass("/*", VfcSimulatorJaxRs.class.getName()); testServer.waitedStart(5000); - if (!NetworkUtil.isTcpPortOpen(LOCALHOST, testServer.getPort(), 5, 10000L)) { - throw new IllegalStateException(CANNOT_CONNECT + testServer.getPort()); - } + waitForServerToListen(testServer.getPort()); return testServer; } @@ -136,9 +144,7 @@ public class Util { LOCALHOST, GUARDSIM_SERVER_PORT, "/", false, true); testServer.addServletClass("/*", GuardSimulatorJaxRs.class.getName()); testServer.waitedStart(5000); - if (!NetworkUtil.isTcpPortOpen(LOCALHOST, testServer.getPort(), 5, 10000L)) { - throw new IllegalStateException(CANNOT_CONNECT + testServer.getPort()); - } + waitForServerToListen(testServer.getPort()); return testServer; } @@ -170,9 +176,13 @@ public class Util { HttpServletServer testServer = HttpServletServerFactoryInstance.getServerFactory().build(props).get(0); testServer.waitedStart(5000); - if (!NetworkUtil.isTcpPortOpen(LOCALHOST, testServer.getPort(), 50, 1000L)) { - throw new IllegalStateException(CANNOT_CONNECT + testServer.getPort()); - } + waitForServerToListen(testServer.getPort()); return testServer; } + + private static void waitForServerToListen(int port) throws InterruptedException { + if (!NetworkUtil.isTcpPortOpen(LOCALHOST, port, 200, 250L)) { + throw new IllegalStateException(CANNOT_CONNECT + port); + } + } } diff --git a/models-interactions/model-simulators/src/main/resources/org/onap/policy/simulators/cds/CreateSubscriptionResponseEvent.json b/models-interactions/model-simulators/src/main/resources/org/onap/policy/simulators/cds/CreateSubscriptionResponseEvent.json new file mode 100644 index 000000000..adb51adcb --- /dev/null +++ b/models-interactions/model-simulators/src/main/resources/org/onap/policy/simulators/cds/CreateSubscriptionResponseEvent.json @@ -0,0 +1,29 @@ +{ + "commonHeader": { + "timestamp": "2020-03-20T14:00:25.217Z", + "requestId": "123456-1000", + "subRequestId": "sub-123456-1000", + "flag": { + }, + "originatorId": "sdnc" + }, + "actionIdentifiers": { + "blueprintName": "pm_control", + "blueprintVersion": "1.0.0", + "actionName": "create-subscription", + "mode": "sync" + }, + "status": { + "code": 200, + "message": "success", + "eventType": "EVENT_COMPONENT_EXECUTED", + "timestamp": "Fri Mar 20 14:00:26 GMT 2020" + }, + "payload": { + "create-subscription-response": { + "odl-response": { + "status": "success" + } + } + } +}
\ No newline at end of file diff --git a/models-interactions/model-simulators/src/test/java/org/onap/policy/simulators/CdsSimulatorTest.java b/models-interactions/model-simulators/src/test/java/org/onap/policy/simulators/CdsSimulatorTest.java new file mode 100644 index 000000000..5f82b7e94 --- /dev/null +++ b/models-interactions/model-simulators/src/test/java/org/onap/policy/simulators/CdsSimulatorTest.java @@ -0,0 +1,114 @@ +/*- + * ============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.simulators; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import io.grpc.ManagedChannel; +import io.grpc.internal.DnsNameResolverProvider; +import io.grpc.internal.PickFirstLoadBalancerProvider; +import io.grpc.netty.NettyChannelBuilder; +import io.grpc.stub.StreamObserver; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.apache.commons.io.IOUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onap.ccsdk.cds.controllerblueprints.processing.api.BluePrintProcessingServiceGrpc; +import org.onap.ccsdk.cds.controllerblueprints.processing.api.BluePrintProcessingServiceGrpc.BluePrintProcessingServiceStub; +import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceInput; +import org.onap.ccsdk.cds.controllerblueprints.processing.api.ExecutionServiceOutput; +import org.onap.policy.common.utils.coder.StandardCoder; + +public class CdsSimulatorTest { + private static final StandardCoder coder = new StandardCoder(); + + private CdsSimulator sim; + + @Before + public void setUp() throws Exception { + sim = Util.buildCdsSim(); + } + + @After + public void tearDown() { + sim.stop(); + } + + @Test + public void test() throws Exception { + String reqstr = IOUtils.toString(getClass().getResource("cds/cds.request.json"), StandardCharsets.UTF_8); + ExecutionServiceInput request = coder.decode(reqstr, ExecutionServiceInput.class); + + ManagedChannel channel = NettyChannelBuilder.forAddress("localhost", sim.getPort()) + .nameResolverFactory(new DnsNameResolverProvider()) + .loadBalancerFactory(new PickFirstLoadBalancerProvider()).usePlaintext().build(); + + try { + final CompletableFuture<ExecutionServiceOutput> future = new CompletableFuture<>(); + final CountDownLatch completed = new CountDownLatch(1); + + BluePrintProcessingServiceStub asyncStub = BluePrintProcessingServiceGrpc.newStub(channel); + + StreamObserver<ExecutionServiceOutput> responseObserver = new StreamObserver<ExecutionServiceOutput>() { + @Override + public void onNext(ExecutionServiceOutput output) { + future.complete(output); + } + + @Override + public void onError(Throwable throwable) { + future.completeExceptionally(throwable); + } + + @Override + public void onCompleted() { + completed.countDown(); + } + }; + + StreamObserver<ExecutionServiceInput> requestObserver = asyncStub.process(responseObserver); + try { + // publish the message + requestObserver.onNext(request); + + // indicate that the request is done + requestObserver.onCompleted(); + + } catch (RuntimeException e) { + requestObserver.onError(e); + } + + // wait for it to complete + assertTrue(completed.await(5, TimeUnit.SECONDS)); + + ExecutionServiceOutput result = future.get(); + assertEquals(200, result.getStatus().getCode()); + + } finally { + channel.shutdown(); + } + } +} diff --git a/models-interactions/model-simulators/src/test/java/org/onap/policy/simulators/ExceptionsTest.java b/models-interactions/model-simulators/src/test/java/org/onap/policy/simulators/ExceptionsTest.java new file mode 100644 index 000000000..e3d91f0f2 --- /dev/null +++ b/models-interactions/model-simulators/src/test/java/org/onap/policy/simulators/ExceptionsTest.java @@ -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.simulators; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.onap.policy.common.utils.test.ExceptionsTester; + +/** + * Tests XxxException classes. + */ +public class ExceptionsTest { + + @Test + public void testSimulatorRuntimeException() { + assertEquals(4, new ExceptionsTester().test(SimulatorRuntimeException.class)); + } +} diff --git a/models-interactions/model-simulators/src/test/resources/org/onap/policy/simulators/cds/cds.request.json b/models-interactions/model-simulators/src/test/resources/org/onap/policy/simulators/cds/cds.request.json new file mode 100644 index 000000000..37e8b05ce --- /dev/null +++ b/models-interactions/model-simulators/src/test/resources/org/onap/policy/simulators/cds/cds.request.json @@ -0,0 +1,14 @@ +{ + "commonHeader": { + "originatorId": "POLICY", + "requestId": "c7c6a4aa-bb61-4a15-b831-ba1472dd4a65", + "subRequestId": "111be3d2-6c12-4f4b-a3e7-c349acced200" + }, + "actionIdentifiers": { + "blueprintName": "pm_control", + "blueprintVersion": "1.0.0", + "actionName": "create-subscription", + "mode": "sync" + }, + "payload": {} +} |