Add remote service function execution. 12/97712/1
authorBrinda Santh <bs2796@att.com>
Tue, 29 Oct 2019 21:10:32 +0000 (17:10 -0400)
committerBrinda Santh <bs2796@att.com>
Tue, 29 Oct 2019 21:10:32 +0000 (17:10 -0400)
Issue-ID: CCSDK-1875
Signed-off-by: Brinda Santh <bs2796@att.com>
Change-Id: I18279b1d2ff1a0b2962afa74fc616bfd99e93c91

ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceHandler.kt
ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceHandlerTest.kt [new file with mode: 0644]
ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/AbstractServiceFunction.kt [new file with mode: 0644]

index 20af589..ade47cf 100644 (file)
@@ -23,11 +23,13 @@ import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.launch
 import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.*
 import org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api.utils.toProto
+import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractServiceFunction
 import org.onap.ccsdk.cds.controllerblueprints.common.api.EventType
 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
 import org.onap.ccsdk.cds.controllerblueprints.core.config.BluePrintLoadConfiguration
 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintCatalogService
 import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintWorkflowExecutionService
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService
 import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
 import org.slf4j.LoggerFactory
 import org.springframework.stereotype.Service
@@ -70,26 +72,45 @@ class ExecutionServiceHandler(private val bluePrintLoadConfiguration: BluePrintL
         val blueprintName = actionIdentifiers.blueprintName
         val blueprintVersion = actionIdentifiers.blueprintVersion
         try {
-            val basePath = blueprintsProcessorCatalogService.getFromDatabase(blueprintName, blueprintVersion)
-            log.info("blueprint base path $basePath")
+            /** Check Blueprint is needed for this request */
+            if (checkServiceFunction(executionServiceInput)) {
+                return executeServiceFunction(executionServiceInput)
+            } else {
+                val basePath = blueprintsProcessorCatalogService.getFromDatabase(blueprintName, blueprintVersion)
+                log.info("blueprint base path $basePath")
 
-            val blueprintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime(requestId, basePath.toString())
+                val blueprintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime(requestId, basePath.toString())
 
-            val output = bluePrintWorkflowExecutionService.executeBluePrintWorkflow(blueprintRuntimeService,
-                    executionServiceInput, hashMapOf())
+                val output = bluePrintWorkflowExecutionService.executeBluePrintWorkflow(blueprintRuntimeService,
+                        executionServiceInput, hashMapOf())
 
-            val errors = blueprintRuntimeService.getBluePrintError().errors
-            if (errors.isNotEmpty()) {
-                val errorMessage = errors.stream().map { it.toString() }.collect(Collectors.joining(", "))
-                setErrorStatus(errorMessage, output.status)
+                val errors = blueprintRuntimeService.getBluePrintError().errors
+                if (errors.isNotEmpty()) {
+                    val errorMessage = errors.stream().map { it.toString() }.collect(Collectors.joining(", "))
+                    setErrorStatus(errorMessage, output.status)
+                }
+                return output
             }
-            return output
         } catch (e: Exception) {
             log.error("fail processing request id $requestId", e)
             return response(executionServiceInput, e.localizedMessage ?: e.message ?: e.toString(), true)
         }
     }
 
+    /** If the blueprint name is default, It means no blueprint is needed for the execution */
+    fun checkServiceFunction(executionServiceInput: ExecutionServiceInput): Boolean {
+        return executionServiceInput.actionIdentifiers.blueprintName == "default"
+    }
+
+    /** If no blueprint is needed, then get the Service function instance mapping to the action name and execute it */
+    suspend fun executeServiceFunction(executionServiceInput: ExecutionServiceInput): ExecutionServiceOutput {
+        val actionName = executionServiceInput.actionIdentifiers.actionName
+        val instance = BluePrintDependencyService.instance<AbstractServiceFunction>(actionName)
+        checkNotNull(instance) { "failed to initialize service function($actionName)" }
+        instance.actionName = actionName
+        return instance.applyNB(executionServiceInput)
+    }
+
     private fun setErrorStatus(errorMessage: String, status: Status) {
         status.errorMessage = errorMessage
         status.eventType = EventType.EVENT_COMPONENT_FAILURE.name
diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceHandlerTest.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/ExecutionServiceHandlerTest.kt
new file mode 100644 (file)
index 0000000..293da0d
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2018-2019 AT&T Intellectual Property.
+ *
+ * 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.
+ */
+
+package org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api
+
+import io.mockk.mockk
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ActionIdentifiers
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.CommonHeader
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractServiceFunction
+import org.onap.ccsdk.cds.controllerblueprints.core.jsonAsJsonType
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.context.ApplicationContext
+import org.springframework.stereotype.Service
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.test.context.junit4.SpringRunner
+import kotlin.test.Test
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+
+@RunWith(SpringRunner::class)
+@ContextConfiguration(classes = [MockServiceAction::class])
+class ExecutionServiceHandlerTest {
+
+    @Autowired
+    lateinit var applicationContext: ApplicationContext
+
+    @Before
+    fun init() {
+        BluePrintDependencyService.inject(applicationContext)
+    }
+
+    @Test
+    fun testExecuteServiceFunction() {
+        val executionServiceInput = ExecutionServiceInput().apply {
+            commonHeader = CommonHeader().apply {
+                requestId = "1234"
+                subRequestId = "1234-12"
+                originatorId = "cds-test"
+            }
+            actionIdentifiers = ActionIdentifiers().apply {
+                blueprintName = "default"
+                blueprintVersion = "1.0.0"
+                actionName = "mock-service-action"
+            }
+        }
+        runBlocking {
+            val executionServiceHandler = ExecutionServiceHandler(mockk(), mockk(), mockk())
+            val isServiceFunction = executionServiceHandler.checkServiceFunction(executionServiceInput)
+            assertTrue(isServiceFunction, "failed to checkServiceFunction")
+            val executionServiceOutput = executionServiceHandler.executeServiceFunction(executionServiceInput)
+            assertNotNull(executionServiceOutput, "failed to get executionServiceOutput")
+        }
+    }
+}
+
+@Service("mock-service-action")
+class MockServiceAction : AbstractServiceFunction() {
+    override suspend fun processNB(executionRequest: ExecutionServiceInput) {
+        val responsePayload = """{"answer" : "correct"}""".jsonAsJsonType()
+        setResponsePayloadForAction(responsePayload)
+    }
+
+    override suspend fun recoverNB(runtimeException: RuntimeException, executionRequest: ExecutionServiceInput) {
+
+    }
+}
\ No newline at end of file
diff --git a/ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/AbstractServiceFunction.kt b/ms/blueprintsprocessor/modules/services/execution-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/execution/AbstractServiceFunction.kt
new file mode 100644 (file)
index 0000000..67ab9c4
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2018-2019 AT&T Intellectual Property.
+ *
+ * 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.
+ */
+
+package org.onap.ccsdk.cds.blueprintsprocessor.services.execution
+
+import com.fasterxml.jackson.databind.JsonNode
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceOutput
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.Status
+import org.onap.ccsdk.cds.controllerblueprints.common.api.EventType
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
+import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BlueprintFunctionNode
+import org.onap.ccsdk.cds.controllerblueprints.core.jsonPathParse
+import org.onap.ccsdk.cds.controllerblueprints.core.logger
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
+
+/** This implementation is used to build services, which doesn't need blueprints */
+abstract class AbstractServiceFunction : BlueprintFunctionNode<ExecutionServiceInput, ExecutionServiceOutput> {
+
+    @Transient
+    private val log = logger(AbstractServiceFunction::class)
+
+    lateinit var executionServiceInput: ExecutionServiceInput
+    var executionServiceOutput = ExecutionServiceOutput()
+    lateinit var processId: String
+    lateinit var actionName: String
+    lateinit var responseActionPayload: JsonNode
+
+    override fun getName(): String {
+        return actionName
+    }
+
+    override suspend fun prepareRequestNB(executionRequest: ExecutionServiceInput): ExecutionServiceInput {
+
+        this.executionServiceInput = executionRequest
+
+        actionName = executionRequest.actionIdentifiers.actionName
+        check(actionName.isNotEmpty()) { "couldn't get action name" }
+
+        processId = executionRequest.commonHeader.requestId
+        check(processId.isNotEmpty()) { "couldn't get process id for service action($actionName)" }
+
+        return executionRequest
+    }
+
+    override suspend fun applyNB(executionServiceInput: ExecutionServiceInput): ExecutionServiceOutput {
+        try {
+            prepareRequestNB(executionServiceInput)
+            processNB(executionServiceInput)
+        } catch (runtimeException: RuntimeException) {
+            log.error("failed in ${getName()} : ${runtimeException.message}", runtimeException)
+            recoverNB(runtimeException, executionServiceInput)
+        }
+        return prepareResponseNB()
+    }
+
+    override suspend fun prepareResponseNB(): ExecutionServiceOutput {
+        log.debug("Preparing Response...")
+        executionServiceOutput.commonHeader = executionServiceInput.commonHeader
+        executionServiceOutput.actionIdentifiers = executionServiceInput.actionIdentifiers
+        var status = Status()
+        try {
+            // Set the Response Payload
+            executionServiceOutput.payload = JacksonUtils.objectMapper.createObjectNode()
+            executionServiceOutput.payload.set("$actionName-response", responseActionPayload)
+            // Set the Default Step Status
+            status.eventType = EventType.EVENT_COMPONENT_EXECUTED.name
+        } catch (e: Exception) {
+            status.message = BluePrintConstants.STATUS_FAILURE
+            status.eventType = EventType.EVENT_COMPONENT_FAILURE.name
+        }
+        executionServiceOutput.status = status
+        return this.executionServiceOutput
+    }
+
+    fun setResponsePayloadForAction(actionPayload: JsonNode) {
+        this.responseActionPayload = actionPayload
+    }
+
+    /**
+     * Get Execution Input Payload data
+     */
+    fun requestPayload(): JsonNode? {
+        return executionServiceInput.payload
+    }
+
+    /**
+     * Get Execution Input payload action property with [expression]
+     * ex: requestPayloadActionProperty("data") will look for path "payload/<action-name>-request/data"
+     */
+    fun requestPayloadActionProperty(expression: String?): JsonNode? {
+        val requestExpression = if (expression.isNullOrBlank()) {
+            "$actionName-request"
+        } else {
+            "$actionName-request.$expression"
+        }
+        return executionServiceInput.payload.jsonPathParse(".$requestExpression")
+    }
+}
\ No newline at end of file