summaryrefslogtreecommitdiffstats
path: root/ms
diff options
context:
space:
mode:
Diffstat (limited to 'ms')
-rwxr-xr-xms/blueprintsprocessor/application/pom.xml6
-rw-r--r--ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTest.kt (renamed from ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTests.kt)138
-rw-r--r--ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt19
-rw-r--r--ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/JsonNormalizer.kt79
-rw-r--r--ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/PathDeserializer.kt52
-rw-r--r--ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/UatDefinition.kt68
-rw-r--r--ms/blueprintsprocessor/functions/ansible-awx-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/ansible/executor/ComponentRemoteAnsibleExecutor.kt113
-rw-r--r--ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BluePrintWorkflowExecutionServiceImpl.kt48
-rw-r--r--ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/ImperativeWorkflowExecutionService.kt148
-rw-r--r--ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionService.kt6
-rw-r--r--ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BluePrintWorkflowExecutionServiceImplTest.kt50
-rw-r--r--ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BlueprintServiceLogicTest.kt7
-rw-r--r--ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/ImperativeWorkflowExecutionServiceTest.kt105
-rw-r--r--ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionServiceTest.kt22
-rw-r--r--ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/mock/MockComponentFunction.kt12
-rw-r--r--ms/blueprintsprocessor/modules/services/workflow-service/src/test/resources/execution-input/imperative-test-input.json18
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/BluePrintConstants.kt3
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/CustomFunctions.kt9
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/GraphExtensionFunctions.kt116
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/data/BluePrintGraph.kt174
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/dsl/BluePrintServiceDSLBuilder.kt2
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintWorkflowService.kt339
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintMetadataUtils.kt14
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/JacksonUtils.kt67
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/WorkflowGraphUtils.kt46
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/GraphExtensionFunctionsTest.kt37
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintRuntimeServiceTest.kt13
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintWorkflowServiceTest.kt175
-rw-r--r--ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/WorkflowGraphUtilsTest.kt42
-rw-r--r--ms/controllerblueprints/modules/service/src/main/java/org/onap/ccsdk/cds/controllerblueprints/service/AutoResourceMappingService.java210
-rw-r--r--ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/AutoResourceMappingService.kt174
31 files changed, 1899 insertions, 413 deletions
diff --git a/ms/blueprintsprocessor/application/pom.xml b/ms/blueprintsprocessor/application/pom.xml
index 314b09c42..120b948be 100755
--- a/ms/blueprintsprocessor/application/pom.xml
+++ b/ms/blueprintsprocessor/application/pom.xml
@@ -132,6 +132,12 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>com.schibsted.spt.data</groupId>
+ <artifactId>jslt</artifactId>
+ <version>0.1.8</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTests.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTest.kt
index 0a57277ea..ad4173c9e 100644
--- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTests.kt
+++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/BlueprintsAcceptanceTest.kt
@@ -19,7 +19,9 @@
*/
package org.onap.ccsdk.cds.blueprintsprocessor
+import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.node.MissingNode
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.argThat
import com.nhaarman.mockitokotlin2.atLeast
@@ -53,15 +55,17 @@ import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.TestPropertySource
import org.springframework.test.context.junit4.rules.SpringClassRule
import org.springframework.test.context.junit4.rules.SpringMethodRule
+import org.springframework.test.web.reactive.server.EntityExchangeResult
import org.springframework.test.web.reactive.server.WebTestClient
-import org.yaml.snakeyaml.Yaml
import reactor.core.publisher.Mono
import java.io.File
-import java.nio.file.Path
+import java.nio.charset.StandardCharsets
import java.nio.file.Paths
import kotlin.test.BeforeTest
import kotlin.test.Test
+// Only one runner can be configured with jUnit 4. We had to replace the SpringRunner by equivalent jUnit rules.
+// See more on https://docs.spring.io/autorepo/docs/spring-framework/current/spring-framework-reference/testing.html#testcontext-junit4-rules
@RunWith(Parameterized::class)
// Set blueprintsprocessor.httpPort=0 to trigger a random port selection
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@@ -71,8 +75,7 @@ import kotlin.test.Test
TestSecuritySettings.ServerContextInitializer::class
])
@TestPropertySource(locations = ["classpath:application-test.properties"])
-@Suppress("UNCHECKED_CAST")
-class BlueprintsAcceptanceTests(private val blueprintName: String, private val filename: String) {
+class BlueprintsAcceptanceTest(private val blueprintName: String, private val filename: String) {
companion object {
const val UAT_BLUEPRINTS_BASE_DIR = "../../../components/model-catalog/blueprint-model/uat-blueprints"
@@ -82,11 +85,15 @@ class BlueprintsAcceptanceTests(private val blueprintName: String, private val f
@JvmField
val springClassRule = SpringClassRule()
- val log: Logger = LoggerFactory.getLogger(BlueprintsAcceptanceTests::class.java)
+ val log: Logger = LoggerFactory.getLogger(BlueprintsAcceptanceTest::class.java)
+ /**
+ * Generates the parameters to create a test instance for every blueprint found under UAT_BLUEPRINTS_BASE_DIR
+ * that contains the proper UAT definition file.
+ */
@Parameterized.Parameters(name = "{index} {0}")
@JvmStatic
- fun filenames(): List<Array<String>> {
+ fun testParameters(): List<Array<String>> {
return File(UAT_BLUEPRINTS_BASE_DIR)
.listFiles { file -> file.isDirectory && File(file, EMBEDDED_UAT_FILE).isFile }
?.map { file -> arrayOf(file.nameWithoutExtension, file.canonicalPath) }
@@ -119,38 +126,31 @@ class BlueprintsAcceptanceTests(private val blueprintName: String, private val f
@Test
fun testBlueprint() {
- val yaml: Map<String, *> = loadYaml(Paths.get(filename, EMBEDDED_UAT_FILE))
+ val uat = UatDefinition.load(mapper, Paths.get(filename, EMBEDDED_UAT_FILE))
uploadBlueprint(blueprintName)
// Configure mocked external services
- val services = yaml["external-services"] as List<Map<String, *>>? ?: emptyList()
- val expectationPerClient = services.map { service ->
- val selector = service["selector"] as String
- val expectations = (service["expectations"] as List<Map<String, *>>).map {
- parseExpectation(it)
- }
- val mockClient = createRestClientMock(selector, expectations)
- mockClient to expectations
- }.toMap()
+ val expectationPerClient = uat.externalServices.associateBy(
+ { service -> createRestClientMock(service.selector, service.expectations) },
+ { service -> service.expectations }
+ )
// Run processes
- for (process in (yaml["processes"] as List<Map<String, *>>)) {
- val processName = process["name"]
- log.info("Executing process '$processName'")
- val request = mapper.writeValueAsString(process["request"])
- val expectedResponse = mapper.writeValueAsString(process["expectedResponse"])
- processBlueprint(request, expectedResponse)
+ for (process in uat.processes) {
+ log.info("Executing process '${process.name}'")
+ processBlueprint(process.request, process.expectedResponse,
+ JsonNormalizer.getNormalizer(mapper, process.responseNormalizerSpec))
}
- // Validate request payloads
+ // Validate request payloads to external services
for ((mockClient, expectations) in expectationPerClient) {
expectations.forEach { expectation ->
verify(mockClient, atLeastOnce()).exchangeResource(
- eq(expectation.method),
- eq(expectation.path),
- argThat { assertJsonEqual(expectation.expectedRequestBody, this) },
- expectation.requestHeadersMatcher())
+ eq(expectation.request.method),
+ eq(expectation.request.path),
+ argThat { assertJsonEqual(expectation.request.body, this) },
+ expectation.request.requestHeadersMatcher())
}
// Don't mind the invocations to the overloaded exchangeResource(String, String, String)
verify(mockClient, atLeast(0)).exchangeResource(any(), any(), any())
@@ -158,7 +158,8 @@ class BlueprintsAcceptanceTests(private val blueprintName: String, private val f
}
}
- private fun createRestClientMock(selector: String, restExpectations: List<RestExpectation>): BlueprintWebClientService {
+ private fun createRestClientMock(selector: String, restExpectations: List<ExpectationDefinition>)
+ : BlueprintWebClientService {
val restClient = mock<BlueprintWebClientService>(verboseLogging = true)
// Delegates to overloaded exchangeResource(String, String, String, Map<String, String>)
@@ -171,11 +172,11 @@ class BlueprintsAcceptanceTests(private val blueprintName: String, private val f
}
for (expectation in restExpectations) {
whenever(restClient.exchangeResource(
- eq(expectation.method),
- eq(expectation.path),
+ eq(expectation.request.method),
+ eq(expectation.request.path),
any(),
any()))
- .thenReturn(WebClientResponse(expectation.statusCode, expectation.responseBody))
+ .thenReturn(WebClientResponse(expectation.response.status, expectation.response.body.toString()))
}
whenever(restClientFactory.blueprintWebClientService(selector))
@@ -194,17 +195,20 @@ class BlueprintsAcceptanceTests(private val blueprintName: String, private val f
.expectStatus().isOk
}
- private fun processBlueprint(request: String, expectedResponse: String) {
+ private fun processBlueprint(request: JsonNode, expectedResponse: JsonNode,
+ responseNormalizer: (String) -> String) {
webTestClient
.post()
.uri("/api/v1/execution-service/process")
.header("Authorization", TestSecuritySettings.clientAuthToken())
.contentType(MediaType.APPLICATION_JSON_UTF8)
- .body(Mono.just(request), String::class.java)
+ .body(Mono.just(request.toString()), String::class.java)
.exchange()
.expectStatus().isOk
.expectBody()
- .json(expectedResponse)
+ .consumeWith { response ->
+ assertJsonEqual(expectedResponse, responseNormalizer(getBodyAsString(response)))
+ }
}
private fun getBlueprintAsResource(blueprintName: String): Resource {
@@ -216,65 +220,21 @@ class BlueprintsAcceptanceTests(private val blueprintName: String, private val f
}
}
- private fun loadYaml(path: Path): Map<String, Any> {
- return path.toFile().reader().use { reader ->
- Yaml().load(reader)
- }
- }
-
- private fun assertJsonEqual(expected: Any, actual: String): Boolean {
- if (actual != expected) {
- // assertEquals throws an exception whenever match fails
- JSONAssert.assertEquals(mapper.writeValueAsString(expected), actual, JSONCompareMode.LENIENT)
+ private fun assertJsonEqual(expected: JsonNode, actual: String): Boolean {
+ if ((actual == "") && (expected is MissingNode)) {
+ return true
}
+ JSONAssert.assertEquals(expected.toString(), actual, JSONCompareMode.LENIENT)
+ // assertEquals throws an exception whenever match fails
return true
}
- private fun parseExpectation(expectation: Map<String, *>): RestExpectation {
- val request = expectation["request"] as Map<String, Any>
- val method = request["method"] as String
- val path = joinPath(request.getValue("path"))
- val contentType = request["content-type"] as String?
- val requestBody = request.getOrDefault("body", "")
-
- val response = expectation["response"] as Map<String, Any>? ?: emptyMap()
- val status = response["status"] as Int? ?: 200
- val responseBody = when (val body = response["body"] ?: "") {
- is String -> body
- else -> mapper.writeValueAsString(body)
- }
-
- return RestExpectation(method, path, contentType, requestBody, status, responseBody)
- }
-
- /**
- * Join a multilevel lists of strings.
- * Example: joinPath(listOf("a", listOf("b", "c"), "d")) will result in "a/b/c/d".
- */
- private fun joinPath(any: Any): String {
- fun recursiveJoin(any: Any, sb: StringBuilder): StringBuilder {
- when (any) {
- is List<*> -> any.filterNotNull().forEach { recursiveJoin(it, sb) }
- is String -> {
- if (sb.isNotEmpty()) {
- sb.append('/')
- }
- sb.append(any)
- }
- else -> throw IllegalArgumentException("Unsupported type: ${any.javaClass}")
- }
- return sb
- }
-
- return recursiveJoin(any, StringBuilder()).toString()
- }
-
- data class RestExpectation(val method: String, val path: String, val contentType: String?,
- val expectedRequestBody: Any,
- val statusCode: Int, val responseBody: String) {
-
- fun requestHeadersMatcher(): Map<String, String> {
- return if (contentType != null) eq(mapOf("Content-Type" to contentType)) else any()
+ private fun getBodyAsString(result: EntityExchangeResult<ByteArray>): String {
+ val body = result.responseBody
+ if ((body == null) || body.isEmpty()) {
+ return ""
}
+ val charset = result.responseHeaders.contentType?.charset ?: StandardCharsets.UTF_8
+ return String(body, charset)
}
} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt
index 4576f2761..3c517e6ac 100644
--- a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt
+++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/ExtendedTemporaryFolder.kt
@@ -1,3 +1,22 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.ccsdk.cds.blueprintsprocessor
import org.junit.rules.TemporaryFolder
diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/JsonNormalizer.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/JsonNormalizer.kt
new file mode 100644
index 000000000..69673f931
--- /dev/null
+++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/JsonNormalizer.kt
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.ccsdk.cds.blueprintsprocessor
+
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.node.ContainerNode
+import com.fasterxml.jackson.databind.node.MissingNode
+import com.fasterxml.jackson.databind.node.ObjectNode
+import com.schibsted.spt.data.jslt.Parser
+
+class JsonNormalizer {
+
+ companion object {
+
+ fun getNormalizer(mapper: ObjectMapper, jsltSpec: JsonNode): (String) -> String {
+ if (jsltSpec is MissingNode) {
+ return { it }
+ }
+ return { s: String ->
+ val input = mapper.readTree(s)
+ val expandedJstlSpec = expandJstlSpec(jsltSpec)
+ val jslt = Parser.compileString(expandedJstlSpec)
+ val output = jslt.apply(input)
+ output.toString()
+ }
+ }
+
+ /**
+ * Creates an extended JSTL spec by appending the "*: ." wildcard pattern to every inner JSON object, and
+ * removing the extra quotes added by the standard YAML/JSON converters on fields prefixed by "?".
+ *
+ * @param jstlSpec the JSTL spec as a structured JSON object.
+ * @return the string representation of the extended JSTL spec.
+ */
+ private fun expandJstlSpec(jstlSpec: JsonNode): String {
+ val extendedJstlSpec = updateObjectNodes(jstlSpec, "*", ".")
+ return extendedJstlSpec.toString()
+ // Handle the "?" as a prefix to literal/non-quoted values
+ .replace("\"\\?([^\"]+)\"".toRegex(), "$1")
+ // Also, remove the quotes added by Jackson for key and value of the wildcard matcher
+ .replace("\"([.*])\"".toRegex(), "$1")
+ }
+
+ /**
+ * Expands a structured JSON object, by adding the given key and value to every nested ObjectNode.
+ *
+ * @param jsonNode the root node.
+ * @param fieldName the fixed field name.
+ * @param fieldValue the fixed field value.
+ */
+ private fun updateObjectNodes(jsonNode: JsonNode, fieldName: String, fieldValue: String): JsonNode {
+ if (jsonNode is ContainerNode<*>) {
+ (jsonNode as? ObjectNode)?.put(fieldName, fieldValue)
+ jsonNode.forEach { child ->
+ updateObjectNodes(child, fieldName, fieldValue)
+ }
+ }
+ return jsonNode
+ }
+ }
+}
diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/PathDeserializer.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/PathDeserializer.kt
new file mode 100644
index 000000000..1a232f2d3
--- /dev/null
+++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/PathDeserializer.kt
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.ccsdk.cds.blueprintsprocessor
+
+import com.fasterxml.jackson.core.JsonParser
+import com.fasterxml.jackson.databind.DeserializationContext
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer
+
+class PathDeserializer : StdDeserializer<String>(String::class.java) {
+ override fun deserialize(jp: JsonParser, ctxt: DeserializationContext?): String {
+ val path = jp.codec.readValue(jp, Any::class.java)
+ return flatJoin(path)
+ }
+
+ /**
+ * Join a multilevel lists of strings.
+ * Example: flatJoin(listOf("a", listOf("b", "c"), "d")) will result in "a/b/c/d".
+ */
+ private fun flatJoin(path: Any): String {
+ fun flatJoinTo(sb: StringBuilder, path: Any): StringBuilder {
+ when (path) {
+ is List<*> -> path.filterNotNull().forEach { flatJoinTo(sb, it) }
+ is String -> {
+ if (sb.isNotEmpty()) {
+ sb.append('/')
+ }
+ sb.append(path)
+ }
+ else -> throw IllegalArgumentException("Unsupported type: ${path.javaClass}")
+ }
+ return sb
+ }
+ return flatJoinTo(StringBuilder(), path).toString()
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/UatDefinition.kt b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/UatDefinition.kt
new file mode 100644
index 000000000..ce2061168
--- /dev/null
+++ b/ms/blueprintsprocessor/application/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/UatDefinition.kt
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * 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.ccsdk.cds.blueprintsprocessor
+
+import com.fasterxml.jackson.annotation.JsonAlias
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
+import com.fasterxml.jackson.databind.node.MissingNode
+import com.nhaarman.mockitokotlin2.any
+import com.nhaarman.mockitokotlin2.eq
+import org.yaml.snakeyaml.Yaml
+import java.nio.file.Path
+
+data class ProcessDefinition(val name: String, val request: JsonNode, val expectedResponse: JsonNode,
+ val responseNormalizerSpec: JsonNode = MissingNode.getInstance())
+
+data class RequestDefinition(val method: String,
+ @JsonDeserialize(using = PathDeserializer::class)
+ val path: String,
+ @JsonAlias("content-type")
+ val contentType: String? = null,
+ val body: JsonNode = MissingNode.getInstance()) {
+ fun requestHeadersMatcher(): Map<String, String> {
+ return if (contentType != null) eq(mapOf("Content-Type" to contentType)) else any()
+ }
+}
+
+data class ResponseDefinition(val status: Int = 200, val body: JsonNode = MissingNode.getInstance()) {
+ companion object {
+ val DEFAULT_RESPONSE = ResponseDefinition()
+ }
+}
+
+data class ExpectationDefinition(val request: RequestDefinition,
+ val response: ResponseDefinition = ResponseDefinition.DEFAULT_RESPONSE)
+
+data class ServiceDefinition(val selector: String, val expectations: List<ExpectationDefinition>)
+
+data class UatDefinition(val processes: List<ProcessDefinition>,
+ @JsonAlias("external-services")
+ val externalServices: List<ServiceDefinition> = emptyList()) {
+
+ companion object {
+ fun load(mapper: ObjectMapper, path: Path): UatDefinition {
+ return path.toFile().reader().use { reader ->
+ mapper.convertValue(Yaml().load(reader), UatDefinition::class.java)
+ }
+ }
+ }
+}
diff --git a/ms/blueprintsprocessor/functions/ansible-awx-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/ansible/executor/ComponentRemoteAnsibleExecutor.kt b/ms/blueprintsprocessor/functions/ansible-awx-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/ansible/executor/ComponentRemoteAnsibleExecutor.kt
index 947a9630d..743aa714b 100644
--- a/ms/blueprintsprocessor/functions/ansible-awx-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/ansible/executor/ComponentRemoteAnsibleExecutor.kt
+++ b/ms/blueprintsprocessor/functions/ansible-awx-executor/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/functions/ansible/executor/ComponentRemoteAnsibleExecutor.kt
@@ -24,10 +24,7 @@ import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInpu
import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BluePrintRestLibPropertyService
import org.onap.ccsdk.cds.blueprintsprocessor.rest.service.BlueprintWebClientService
import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction
-import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
-import org.onap.ccsdk.cds.controllerblueprints.core.asJsonString
-import org.onap.ccsdk.cds.controllerblueprints.core.isNotNull
-import org.onap.ccsdk.cds.controllerblueprints.core.rootFieldsToMap
+import org.onap.ccsdk.cds.controllerblueprints.core.*
import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.config.ConfigurableBeanFactory
@@ -68,6 +65,7 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
// input fields names accepted by this executor
const val INPUT_ENDPOINT_SELECTOR = "endpoint-selector"
const val INPUT_JOB_TEMPLATE_NAME = "job-template-name"
+ const val INPUT_WORKFLOW_JOB_TEMPLATE_NAME = "workflow-job-template-id"
const val INPUT_LIMIT_TO_HOST = "limit"
const val INPUT_INVENTORY = "inventory"
const val INPUT_EXTRA_VARS = "extra-vars"
@@ -85,12 +83,20 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
try {
val restClientService = getAWXRestClient()
- val jobTemplateName = getOperationInput(INPUT_JOB_TEMPLATE_NAME).asText()
- val jtId = lookupJobTemplateIDByName(restClientService, jobTemplateName)
+ // Get either a job template name or a workflow template name property
+ var workflowURIPrefix = ""
+ var jobTemplateName = getOperationInput(INPUT_JOB_TEMPLATE_NAME).returnNullIfMissing()?.textValue() ?: ""
+ val isWorkflowJT = jobTemplateName.isBlank()
+ if (isWorkflowJT) {
+ jobTemplateName = getOperationInput(INPUT_WORKFLOW_JOB_TEMPLATE_NAME).asText()
+ workflowURIPrefix = "workflow_"
+ }
+
+ val jtId = lookupJobTemplateIDByName(restClientService, jobTemplateName, workflowURIPrefix)
if (jtId.isNotEmpty()) {
- runJobTemplateOnAWX(restClientService, jobTemplateName, jtId)
+ runJobTemplateOnAWX(restClientService, jobTemplateName, jtId, workflowURIPrefix)
} else {
- val message = "Job template ${jobTemplateName} does not exists"
+ val message = "Workflow/Job template ${jobTemplateName} does not exists"
log.error(message)
setNodeOutputErrors(ATTRIBUTE_EXEC_CMD_STATUS_ERROR, message)
}
@@ -135,9 +141,10 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
/**
* Finds the job template ID based on the job template name provided in the request
*/
- private fun lookupJobTemplateIDByName(awxClient: BlueprintWebClientService, job_template_name: String?): String {
+ private fun lookupJobTemplateIDByName(awxClient: BlueprintWebClientService, job_template_name: String?,
+ workflowPrefix : String) : String {
val encodedJTName = URI(null, null,
- "/api/v2/job_templates/${job_template_name}/",
+ "/api/v2/${workflowPrefix}job_templates/${job_template_name}/",
null, null).rawPath
// Get Job Template details by name
@@ -152,19 +159,20 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
* its execution. Finally, it retrieves the job results via the stdout api.
* The status and output attributes are populated in the process.
*/
- private fun runJobTemplateOnAWX(awxClient: BlueprintWebClientService, job_template_name: String?, jtId: String) {
+ private fun runJobTemplateOnAWX(awxClient: BlueprintWebClientService, job_template_name: String?, jtId: String,
+ workflowPrefix : String) {
setNodeOutputProperties("preparing".asJsonPrimitive(), "".asJsonPrimitive())
// Get Job Template requirements
- var response = awxClient.exchangeResource(GET, "/api/v2/job_templates/${jtId}/launch/", "")
+ var response = awxClient.exchangeResource(GET, "/api/v2/${workflowPrefix}job_templates/${jtId}/launch/", "")
// FIXME: handle non-successful SC
val jtLaunchReqs: JsonNode = mapper.readTree(response.body)
- val payload = prepareLaunchPayload(awxClient, jtLaunchReqs)
+ val payload = prepareLaunchPayload(awxClient, jtLaunchReqs, workflowPrefix.isBlank())
log.info("Running job with $payload, for requestId $processId.")
// Launch the job for the targeted template
var jtLaunched: JsonNode = JacksonUtils.objectMapper.createObjectNode()
- response = awxClient.exchangeResource(POST, "/api/v2/job_templates/${jtId}/launch/", payload)
+ response = awxClient.exchangeResource(POST, "/api/v2/${workflowPrefix}job_templates/${jtId}/launch/", payload)
if (response.status in HTTP_SUCCESS) {
jtLaunched = mapper.readTree(response.body)
val fieldsIgnored: JsonNode = jtLaunched.at("/ignored_fields")
@@ -180,7 +188,7 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
var jobStatus = "unknown"
var jobEndTime = "null"
while (jobEndTime == "null") {
- response = awxClient.exchangeResource(GET, "/api/v2/jobs/${jobId}/", "")
+ response = awxClient.exchangeResource(GET, "/api/v2/${workflowPrefix}jobs/${jobId}/", "")
val jobLaunched: JsonNode = mapper.readTree(response.body)
jobStatus = jobLaunched.at("/status").asText()
jobEndTime = jobLaunched.at("/finished").asText()
@@ -189,12 +197,10 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
log.info("Execution of job template $job_template_name in job #$jobId finished with status ($jobStatus) for requestId $processId")
- // Get job execution results (stdout)
- val plainTextHeaders = mutableMapOf<String, String>()
- plainTextHeaders["Content-Type"] = "text/plain ;utf-8"
- response = awxClient.exchangeResource(GET, "/api/v2/jobs/${jobId}/stdout/?format=txt", "", plainTextHeaders)
+ // Get workflow/job execution results
+ val collectedOutput = extractJobRunResponse(awxClient, jobId, workflowPrefix)
- setNodeOutputProperties(jobStatus.asJsonPrimitive(), response.body.asJsonPrimitive())
+ setNodeOutputProperties(jobStatus.asJsonPrimitive(), collectedOutput.asJsonPrimitive())
} else {
// The job template requirements were not fulfilled with the values passed in. The message below will
// provide more information via the response, like the ignored_fields, or variables_needed_to_start,
@@ -207,42 +213,77 @@ open class ComponentRemoteAnsibleExecutor(private val blueprintRestLibPropertySe
}
/**
+ * Extracts the response from either a job stdout call OR collects the workflow run output
+ */
+ private fun extractJobRunResponse(awxClient: BlueprintWebClientService, jobId: String, workflowPrefix: String): String {
+
+ // First, collect all job ID from either the job template run or the workflow nodes that ran
+ var jobIds : Array<String>
+ var collectedResponses = StringBuilder()
+ if (workflowPrefix.isNotEmpty()) {
+ var response = awxClient.exchangeResource(GET, "/api/v2/${workflowPrefix}jobs/${jobId}/workflow_nodes/", "")
+ val jobDetails = mapper.readTree(response.body).at("/results")
+ jobIds = emptyArray()
+ for (jobDetail in jobDetails.elements()) {
+ jobIds = jobIds.plus( jobDetail.at("/summary_fields/job/id").asText() )
+ }
+ } else {
+ jobIds = arrayOf(jobId)
+ }
+
+ // Then collect the response text from the corresponding jobIds
+ val plainTextHeaders = mutableMapOf<String, String>()
+ plainTextHeaders["Content-Type"] = "text/plain ;utf-8"
+ for (aJobId in jobIds) {
+ var response = awxClient.exchangeResource(GET, "/api/v2/jobs/${aJobId}/stdout/?format=txt", "", plainTextHeaders)
+ collectedResponses.append("Output for job ${aJobId}:")
+ collectedResponses.append(response.body)
+ }
+ return collectedResponses.toString()
+ }
+
+ /**
* Prepares the JSON payload expected by the job template api,
* by applying the overrides that were provided
* and allowed by the template definition flags in jtLaunchReqs
*/
- private fun prepareLaunchPayload(awxClient: BlueprintWebClientService, jtLaunchReqs: JsonNode): String {
+ private fun prepareLaunchPayload(awxClient: BlueprintWebClientService, jtLaunchReqs: JsonNode,
+ isWorkflow : Boolean): String {
val payload = JacksonUtils.objectMapper.createObjectNode()
// Parameter defaults
- val limitProp = getOptionalOperationInput(INPUT_LIMIT_TO_HOST)
- val tagsProp = getOptionalOperationInput(INPUT_TAGS)
- val skipTagsProp = getOptionalOperationInput(INPUT_SKIP_TAGS)
val inventoryProp = getOptionalOperationInput(INPUT_INVENTORY)
val extraArgs = getOperationInput(INPUT_EXTRA_VARS)
- val askLimitOnLaunch = jtLaunchReqs.at("/ask_limit_on_launch").asBoolean()
- if (askLimitOnLaunch && limitProp.isNotNull()) {
- payload.set(INPUT_LIMIT_TO_HOST, limitProp)
- }
- val askTagsOnLaunch = jtLaunchReqs.at("/ask_tags_on_launch").asBoolean()
- if (askTagsOnLaunch && tagsProp.isNotNull()) {
- payload.set(INPUT_TAGS, tagsProp)
- }
- if (askTagsOnLaunch && skipTagsProp.isNotNull()) {
- payload.set("skip_tags", skipTagsProp)
+ if (!isWorkflow) {
+ val limitProp = getOptionalOperationInput(INPUT_LIMIT_TO_HOST)
+ val tagsProp = getOptionalOperationInput(INPUT_TAGS)
+ val skipTagsProp = getOptionalOperationInput(INPUT_SKIP_TAGS)
+
+ val askLimitOnLaunch = jtLaunchReqs.at("/ask_limit_on_launch").asBoolean()
+ if (askLimitOnLaunch && limitProp.isNotNull()) {
+ payload.set(INPUT_LIMIT_TO_HOST, limitProp)
+ }
+ val askTagsOnLaunch = jtLaunchReqs.at("/ask_tags_on_launch").asBoolean()
+ if (askTagsOnLaunch && tagsProp.isNotNull()) {
+ payload.set(INPUT_TAGS, tagsProp)
+ }
+ if (askTagsOnLaunch && skipTagsProp.isNotNull()) {
+ payload.set("skip_tags", skipTagsProp)
+ }
}
+
val askInventoryOnLaunch = jtLaunchReqs.at("/ask_inventory_on_launch").asBoolean()
if (askInventoryOnLaunch && inventoryProp.isNotNull()) {
var inventoryKeyId = if (inventoryProp is TextNode) {
- resolveInventoryIdByName(awxClient, inventoryProp!!.textValue())?.asJsonPrimitive()
+ resolveInventoryIdByName(awxClient, inventoryProp.textValue())?.asJsonPrimitive()
} else {
inventoryProp
}
payload.set(INPUT_INVENTORY, inventoryKeyId)
}
val askVariablesOnLaunch = jtLaunchReqs.at("/ask_variables_on_launch").asBoolean()
- if (askVariablesOnLaunch && extraArgs != null) {
+ if (askVariablesOnLaunch) {
payload.set("extra_vars", extraArgs)
}
return payload.asJsonString(false)
diff --git a/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BluePrintWorkflowExecutionServiceImpl.kt b/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BluePrintWorkflowExecutionServiceImpl.kt
index fcf0558c7..cde919ce8 100644
--- a/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BluePrintWorkflowExecutionServiceImpl.kt
+++ b/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BluePrintWorkflowExecutionServiceImpl.kt
@@ -29,8 +29,9 @@ import org.springframework.stereotype.Service
@Service("bluePrintWorkflowExecutionService")
open class BluePrintWorkflowExecutionServiceImpl(
- private val componentWorkflowExecutionService: ComponentWorkflowExecutionService,
- private val dgWorkflowExecutionService: DGWorkflowExecutionService
+ private val componentWorkflowExecutionService: ComponentWorkflowExecutionService,
+ private val dgWorkflowExecutionService: DGWorkflowExecutionService,
+ private val imperativeWorkflowExecutionService: ImperativeWorkflowExecutionService
) : BluePrintWorkflowExecutionService<ExecutionServiceInput, ExecutionServiceOutput> {
private val log = LoggerFactory.getLogger(BluePrintWorkflowExecutionServiceImpl::class.java)!!
@@ -51,28 +52,37 @@ open class BluePrintWorkflowExecutionServiceImpl(
val input = executionServiceInput.payload.get("$workflowName-request")
bluePrintRuntimeService.assignWorkflowInputs(workflowName, input)
- // Get the DG Node Template
- val nodeTemplateName = bluePrintContext.workflowFirstStepNodeTemplate(workflowName)
+ val workflow = bluePrintContext.workflowByName(workflowName)
- val derivedFrom = bluePrintContext.nodeTemplateNodeType(nodeTemplateName).derivedFrom
+ val steps = workflow.steps ?: throw BluePrintProcessorException("could't get steps for workflow($workflowName)")
- log.info("Executing workflow($workflowName) NodeTemplate($nodeTemplateName), derived from($derivedFrom)")
-
- val executionServiceOutput: ExecutionServiceOutput = when {
- derivedFrom.startsWith(BluePrintConstants.MODEL_TYPE_NODE_COMPONENT, true) -> {
- componentWorkflowExecutionService
- .executeBluePrintWorkflow(bluePrintRuntimeService, executionServiceInput, properties)
- }
- derivedFrom.startsWith(BluePrintConstants.MODEL_TYPE_NODE_WORKFLOW, true) -> {
- dgWorkflowExecutionService
+ /** If workflow has multiple steps, then it is imperative workflow */
+ val executionServiceOutput: ExecutionServiceOutput = if (steps.size > 1) {
+ imperativeWorkflowExecutionService
.executeBluePrintWorkflow(bluePrintRuntimeService, executionServiceInput, properties)
- }
- else -> {
- throw BluePrintProcessorException("couldn't execute workflow($workflowName) step mapped " +
- "to node template($nodeTemplateName) derived from($derivedFrom)")
+ } else {
+ // Get the DG Node Template
+ val nodeTemplateName = bluePrintContext.workflowFirstStepNodeTemplate(workflowName)
+
+ val derivedFrom = bluePrintContext.nodeTemplateNodeType(nodeTemplateName).derivedFrom
+
+ log.info("Executing workflow($workflowName) NodeTemplate($nodeTemplateName), derived from($derivedFrom)")
+ /** Return ExecutionServiceOutput based on DG node or Component Node */
+ when {
+ derivedFrom.startsWith(BluePrintConstants.MODEL_TYPE_NODE_COMPONENT, true) -> {
+ componentWorkflowExecutionService
+ .executeBluePrintWorkflow(bluePrintRuntimeService, executionServiceInput, properties)
+ }
+ derivedFrom.startsWith(BluePrintConstants.MODEL_TYPE_NODE_WORKFLOW, true) -> {
+ dgWorkflowExecutionService
+ .executeBluePrintWorkflow(bluePrintRuntimeService, executionServiceInput, properties)
+ }
+ else -> {
+ throw BluePrintProcessorException("couldn't execute workflow($workflowName) step mapped " +
+ "to node template($nodeTemplateName) derived from($derivedFrom)")
+ }
}
}
-
executionServiceOutput.commonHeader = executionServiceInput.commonHeader
executionServiceOutput.actionIdentifiers = executionServiceInput.actionIdentifiers
// Resolve Workflow Outputs
diff --git a/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/ImperativeWorkflowExecutionService.kt b/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/ImperativeWorkflowExecutionService.kt
new file mode 100644
index 000000000..2a14be216
--- /dev/null
+++ b/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/ImperativeWorkflowExecutionService.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright © 2019 IBM.
+ *
+ * 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.workflow
+
+import kotlinx.coroutines.CompletableDeferred
+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.core.*
+import org.onap.ccsdk.cds.controllerblueprints.core.data.EdgeLabel
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Graph
+import org.onap.ccsdk.cds.controllerblueprints.core.interfaces.BluePrintWorkflowExecutionService
+import org.onap.ccsdk.cds.controllerblueprints.core.service.*
+import org.springframework.beans.factory.config.ConfigurableBeanFactory
+import org.springframework.context.annotation.Scope
+import org.springframework.stereotype.Service
+
+@Service("imperativeWorkflowExecutionService")
+class ImperativeWorkflowExecutionService(
+ private val imperativeBluePrintWorkflowService: BluePrintWorkFlowService<ExecutionServiceInput, ExecutionServiceOutput>)
+ : BluePrintWorkflowExecutionService<ExecutionServiceInput, ExecutionServiceOutput> {
+
+ override suspend fun executeBluePrintWorkflow(bluePrintRuntimeService: BluePrintRuntimeService<*>,
+ executionServiceInput: ExecutionServiceInput,
+ properties: MutableMap<String, Any>): ExecutionServiceOutput {
+
+ val bluePrintContext = bluePrintRuntimeService.bluePrintContext()
+
+ val workflowName = executionServiceInput.actionIdentifiers.actionName
+
+ val graph = bluePrintContext.workflowByName(workflowName).asGraph()
+
+ val deferredOutput = CompletableDeferred<ExecutionServiceOutput>()
+ imperativeBluePrintWorkflowService.executeWorkflow(graph, bluePrintRuntimeService,
+ executionServiceInput, deferredOutput)
+ return deferredOutput.await()
+ }
+}
+
+@Service
+@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+open class ImperativeBluePrintWorkflowService(private val nodeTemplateExecutionService: NodeTemplateExecutionService)
+ : AbstractBluePrintWorkFlowService<ExecutionServiceInput, ExecutionServiceOutput>() {
+ val log = logger(ImperativeBluePrintWorkflowService::class)
+
+ lateinit var bluePrintRuntimeService: BluePrintRuntimeService<*>
+ lateinit var executionServiceInput: ExecutionServiceInput
+ lateinit var workflowName: String
+ lateinit var deferredExecutionServiceOutput: CompletableDeferred<ExecutionServiceOutput>
+
+ override suspend fun executeWorkflow(graph: Graph, bluePrintRuntimeService: BluePrintRuntimeService<*>,
+ input: ExecutionServiceInput,
+ output: CompletableDeferred<ExecutionServiceOutput>) {
+ this.graph = graph
+ this.bluePrintRuntimeService = bluePrintRuntimeService
+ this.executionServiceInput = input
+ this.workflowName = this.executionServiceInput.actionIdentifiers.actionName
+ this.deferredExecutionServiceOutput = output
+ this.workflowId = bluePrintRuntimeService.id()
+ val startMessage = WorkflowExecuteMessage(input, output)
+ workflowActor().send(startMessage)
+ }
+
+ override suspend fun initializeWorkflow(input: ExecutionServiceInput): EdgeLabel {
+ return EdgeLabel.SUCCESS
+ }
+
+ override suspend fun prepareWorkflowOutput(exception: BluePrintProcessorException?): ExecutionServiceOutput {
+ val wfStatus = if (exception != null) {
+ val status = Status()
+ status.message = BluePrintConstants.STATUS_FAILURE
+ status.errorMessage = exception.message
+ status
+ } else {
+ val status = Status()
+ status.message = BluePrintConstants.STATUS_SUCCESS
+ status
+ }
+ return ExecutionServiceOutput().apply {
+ commonHeader = executionServiceInput.commonHeader
+ actionIdentifiers = executionServiceInput.actionIdentifiers
+ status = wfStatus
+ }
+ }
+
+ override suspend fun prepareNodeExecutionMessage(node: Graph.Node)
+ : NodeExecuteMessage<ExecutionServiceInput, ExecutionServiceOutput> {
+ val nodeOutput = ExecutionServiceOutput().apply {
+ commonHeader = executionServiceInput.commonHeader
+ actionIdentifiers = executionServiceInput.actionIdentifiers
+ }
+ return NodeExecuteMessage(node, executionServiceInput, nodeOutput)
+ }
+
+ override suspend fun prepareNodeSkipMessage(node: Graph.Node)
+ : NodeSkipMessage<ExecutionServiceInput, ExecutionServiceOutput> {
+ val nodeOutput = ExecutionServiceOutput().apply {
+ commonHeader = executionServiceInput.commonHeader
+ actionIdentifiers = executionServiceInput.actionIdentifiers
+ }
+ return NodeSkipMessage(node, executionServiceInput, nodeOutput)
+ }
+
+ override suspend fun executeNode(node: Graph.Node, nodeInput: ExecutionServiceInput,
+ nodeOutput: ExecutionServiceOutput): EdgeLabel {
+ log.info("Executing workflow($workflowName[${this.workflowId}])'s step($${node.id})")
+ val step = bluePrintRuntimeService.bluePrintContext().workflowStepByName(this.workflowName, node.id)
+ checkNotEmpty(step.target) { "couldn't get step target for workflow(${this.workflowName})'s step(${node.id})" }
+ val nodeTemplateName = step.target!!
+ /** execute node template */
+ val executionServiceOutput = nodeTemplateExecutionService
+ .executeNodeTemplate(bluePrintRuntimeService, nodeTemplateName, nodeInput)
+
+ return when (executionServiceOutput.status.message) {
+ BluePrintConstants.STATUS_FAILURE -> EdgeLabel.FAILURE
+ else -> EdgeLabel.SUCCESS
+ }
+ }
+
+ override suspend fun skipNode(node: Graph.Node, nodeInput: ExecutionServiceInput,
+ nodeOutput: ExecutionServiceOutput): EdgeLabel {
+ return EdgeLabel.SUCCESS
+ }
+
+ override suspend fun cancelNode(node: Graph.Node, nodeInput: ExecutionServiceInput,
+ nodeOutput: ExecutionServiceOutput): EdgeLabel {
+ TODO("not implemented")
+ }
+
+ override suspend fun restartNode(node: Graph.Node, nodeInput: ExecutionServiceInput,
+ nodeOutput: ExecutionServiceOutput): EdgeLabel {
+ TODO("not implemented")
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionService.kt b/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionService.kt
index af7846340..89732e300 100644
--- a/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionService.kt
+++ b/ms/blueprintsprocessor/modules/services/workflow-service/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionService.kt
@@ -23,13 +23,13 @@ import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.StepData
import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction
import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
import org.onap.ccsdk.cds.controllerblueprints.core.putJsonElement
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService
import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintRuntimeService
import org.slf4j.LoggerFactory
-import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Service
@Service
-open class NodeTemplateExecutionService(private val applicationContext: ApplicationContext) {
+open class NodeTemplateExecutionService {
private val log = LoggerFactory.getLogger(NodeTemplateExecutionService::class.java)!!
@@ -48,7 +48,7 @@ open class NodeTemplateExecutionService(private val applicationContext: Applicat
"interface($interfaceName) operation($operationName)")
// Get the Component Instance
- val plugin = applicationContext.getBean(componentName, AbstractComponentFunction::class.java)
+ val plugin = BluePrintDependencyService.instance<AbstractComponentFunction>(componentName)
// Set the Blueprint Service
plugin.bluePrintRuntimeService = bluePrintRuntimeService
plugin.stepName = nodeTemplateName
diff --git a/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BluePrintWorkflowExecutionServiceImplTest.kt b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BluePrintWorkflowExecutionServiceImplTest.kt
index c15c054db..436de1b56 100644
--- a/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BluePrintWorkflowExecutionServiceImplTest.kt
+++ b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BluePrintWorkflowExecutionServiceImplTest.kt
@@ -16,14 +16,21 @@
package org.onap.ccsdk.cds.blueprintsprocessor.services.workflow
+import io.mockk.every
+import io.mockk.mockkObject
+import io.mockk.unmockkAll
import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
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.services.workflow.mock.MockComponentFunction
import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
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.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
import org.springframework.beans.factory.annotation.Autowired
@@ -41,21 +48,50 @@ class BluePrintWorkflowExecutionServiceImplTest {
@Autowired
lateinit var bluePrintWorkflowExecutionService: BluePrintWorkflowExecutionService<ExecutionServiceInput, ExecutionServiceOutput>
+ @Before
+ fun init() {
+ mockkObject(BluePrintDependencyService)
+ every { BluePrintDependencyService.applicationContext.getBean(any()) } returns MockComponentFunction()
+ }
+
+ @After
+ fun afterTests() {
+ unmockkAll()
+ }
+
@Test
fun testBluePrintWorkflowExecutionService() {
runBlocking {
val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("1234",
- "./../../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration")
+ "./../../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration")
val executionServiceInput = JacksonUtils.readValueFromClassPathFile("execution-input/resource-assignment-input.json",
- ExecutionServiceInput::class.java)!!
+ ExecutionServiceInput::class.java)!!
val executionServiceOutput = bluePrintWorkflowExecutionService
- .executeBluePrintWorkflow(bluePrintRuntimeService, executionServiceInput, hashMapOf())
+ .executeBluePrintWorkflow(bluePrintRuntimeService, executionServiceInput, hashMapOf())
assertNotNull(executionServiceOutput, "failed to get response")
assertEquals(BluePrintConstants.STATUS_SUCCESS, executionServiceOutput.status.message,
- "failed to get successful response")
+ "failed to get successful response")
+ }
+ }
+
+ @Test
+ fun testImperativeBluePrintWorkflowExecutionService() {
+ runBlocking {
+ val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("1234",
+ "./../../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration")
+
+ val executionServiceInput = JacksonUtils.readValueFromClassPathFile("execution-input/imperative-test-input.json",
+ ExecutionServiceInput::class.java)!!
+
+ val executionServiceOutput = bluePrintWorkflowExecutionService
+ .executeBluePrintWorkflow(bluePrintRuntimeService, executionServiceInput, hashMapOf())
+
+ assertNotNull(executionServiceOutput, "failed to get response")
+ assertEquals(BluePrintConstants.STATUS_SUCCESS, executionServiceOutput.status.message,
+ "failed to get successful response")
}
}
@@ -64,13 +100,13 @@ class BluePrintWorkflowExecutionServiceImplTest {
assertFailsWith(exceptionClass = BluePrintProcessorException::class) {
runBlocking {
val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("1234",
- "./../../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration")
+ "./../../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration")
//service input will have a mislabeled input params, we are expecting to get an error when that happens with a useful error message
val executionServiceInput = JacksonUtils.readValueFromClassPathFile("execution-input/resource-assignment-input-missing-resource_assignment_request.json",
- ExecutionServiceInput::class.java)!!
+ ExecutionServiceInput::class.java)!!
val executionServiceOutput = bluePrintWorkflowExecutionService
- .executeBluePrintWorkflow(bluePrintRuntimeService, executionServiceInput, hashMapOf())
+ .executeBluePrintWorkflow(bluePrintRuntimeService, executionServiceInput, hashMapOf())
}
}
}
diff --git a/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BlueprintServiceLogicTest.kt b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BlueprintServiceLogicTest.kt
index 4352277b7..12fd9c7c4 100644
--- a/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BlueprintServiceLogicTest.kt
+++ b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/BlueprintServiceLogicTest.kt
@@ -18,6 +18,7 @@
package org.onap.ccsdk.cds.blueprintsprocessor.services.workflow
import kotlinx.coroutines.runBlocking
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
@@ -25,6 +26,7 @@ import org.onap.ccsdk.cds.blueprintsprocessor.services.workflow.executor.Compone
import org.onap.ccsdk.cds.blueprintsprocessor.services.workflow.mock.PrototypeComponentFunction
import org.onap.ccsdk.cds.blueprintsprocessor.services.workflow.mock.SingletonComponentFunction
import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService
import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonReactorUtils
import org.springframework.beans.factory.annotation.Autowired
@@ -44,6 +46,11 @@ class BlueprintServiceLogicTest {
@Autowired
lateinit var dgWorkflowExecutionService: DGWorkflowExecutionService
+ @Before
+ fun init() {
+ BluePrintDependencyService.inject(applicationContext)
+ }
+
@Test
fun testExecuteGraphWithSingleComponent() {
runBlocking {
diff --git a/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/ImperativeWorkflowExecutionServiceTest.kt b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/ImperativeWorkflowExecutionServiceTest.kt
new file mode 100644
index 000000000..becd22857
--- /dev/null
+++ b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/ImperativeWorkflowExecutionServiceTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2019 IBM.
+ *
+ * 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.workflow
+
+import io.mockk.every
+import io.mockk.mockkObject
+import io.mockk.unmockkAll
+import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Before
+import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.nodeTypeComponentScriptExecutor
+import org.onap.ccsdk.cds.blueprintsprocessor.services.workflow.mock.MockComponentFunction
+import org.onap.ccsdk.cds.blueprintsprocessor.services.workflow.mock.mockNodeTemplateComponentScriptExecutor
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintTypes
+import org.onap.ccsdk.cds.controllerblueprints.core.data.ServiceTemplate
+import org.onap.ccsdk.cds.controllerblueprints.core.dsl.serviceTemplate
+import org.onap.ccsdk.cds.controllerblueprints.core.logger
+import org.onap.ccsdk.cds.controllerblueprints.core.normalizedPathName
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintContext
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
+import kotlin.test.Test
+import kotlin.test.assertNotNull
+
+class ImperativeWorkflowExecutionServiceTest {
+ val log = logger(ImperativeWorkflowExecutionServiceTest::class)
+
+ @Before
+ fun init() {
+ mockkObject(BluePrintDependencyService)
+ every { BluePrintDependencyService.applicationContext.getBean(any()) } returns MockComponentFunction()
+ }
+
+ @After
+ fun afterTests() {
+ unmockkAll()
+ }
+
+ fun mockServiceTemplate(): ServiceTemplate {
+ return serviceTemplate("imperative-test", "1.0.0",
+ "brindasanth@onap.com", "tosca") {
+
+ topologyTemplate {
+ nodeTemplate(mockNodeTemplateComponentScriptExecutor("resolve-config",
+ "cba.wt.imperative.test.ResolveConfig"))
+ nodeTemplate(mockNodeTemplateComponentScriptExecutor("activate-config",
+ "cba.wt.imperative.test.ActivateConfig"))
+ nodeTemplate(mockNodeTemplateComponentScriptExecutor("activate-config-rollback",
+ "cba.wt.imperative.test.ActivateConfigRollback"))
+ nodeTemplate(mockNodeTemplateComponentScriptExecutor("activate-licence",
+ "cba.wt.imperative.test.ActivateLicence"))
+
+ workflow("imperative-test-wf", "Test Imperative flow") {
+ step("resolve-config", "resolve-config", "") {
+ success("activate-config")
+ }
+ step("activate-config", "activate-config", "") {
+ success("activate-licence")
+ failure("activate-config-rollback")
+ }
+ step("activate-config-rollback", "activate-config-rollback", "")
+ step("activate-licence", "activate-licence", "")
+ }
+ }
+ nodeType(BluePrintTypes.nodeTypeComponentScriptExecutor())
+ }
+ }
+
+ @Test
+ fun testImperativeExecutionService() {
+ runBlocking {
+ val serviceTemplate = mockServiceTemplate()
+ val bluePrintContext = BluePrintContext(serviceTemplate)
+ bluePrintContext.rootPath = normalizedPathName(".")
+ bluePrintContext.entryDefinition = "cba.imperative.test.ImperativeTestDefinitions.kt"
+ val bluePrintRuntimeService = BluePrintMetadataUtils.getBluePrintRuntime("12345", bluePrintContext)
+
+ val executionServiceInput = JacksonUtils
+ .readValueFromClassPathFile("execution-input/imperative-test-input.json",
+ ExecutionServiceInput::class.java)!!
+
+ val bluePrintWorkFlowService = ImperativeBluePrintWorkflowService(NodeTemplateExecutionService())
+ val imperativeWorkflowExecutionService = ImperativeWorkflowExecutionService(bluePrintWorkFlowService)
+ val output = imperativeWorkflowExecutionService
+ .executeBluePrintWorkflow(bluePrintRuntimeService, executionServiceInput, hashMapOf())
+ assertNotNull(output)
+ }
+ }
+} \ No newline at end of file
diff --git a/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionServiceTest.kt b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionServiceTest.kt
index 05cd99785..24d96629e 100644
--- a/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionServiceTest.kt
+++ b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/NodeTemplateExecutionServiceTest.kt
@@ -15,14 +15,20 @@
*/
package org.onap.ccsdk.cds.blueprintsprocessor.services.workflow
+import io.mockk.every
+import io.mockk.mockkObject
+import io.mockk.unmockkAll
import kotlinx.coroutines.runBlocking
+import org.junit.After
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
+import org.onap.ccsdk.cds.blueprintsprocessor.services.workflow.mock.MockComponentFunction
import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
+import org.onap.ccsdk.cds.controllerblueprints.core.service.BluePrintDependencyService
import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
-import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.SpringRunner
import kotlin.test.assertEquals
@@ -32,8 +38,16 @@ import kotlin.test.assertNotNull
@ContextConfiguration(classes = [WorkflowServiceConfiguration::class])
class NodeTemplateExecutionServiceTest {
- @Autowired
- lateinit var nodeTemplateExecutionService: NodeTemplateExecutionService
+ @Before
+ fun init() {
+ mockkObject(BluePrintDependencyService)
+ every { BluePrintDependencyService.applicationContext.getBean(any()) } returns MockComponentFunction()
+ }
+
+ @After
+ fun afterTests() {
+ unmockkAll()
+ }
@Test
fun testExecuteNodeTemplate() {
@@ -49,7 +63,7 @@ class NodeTemplateExecutionServiceTest {
bluePrintRuntimeService.assignWorkflowInputs("resource-assignment", input)
val nodeTemplate = "resource-assignment"
-
+ val nodeTemplateExecutionService = NodeTemplateExecutionService()
val executionServiceOutput = nodeTemplateExecutionService
.executeNodeTemplate(bluePrintRuntimeService, nodeTemplate, executionServiceInput)
diff --git a/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/mock/MockComponentFunction.kt b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/mock/MockComponentFunction.kt
index 5dc5b9dba..44751b5b5 100644
--- a/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/mock/MockComponentFunction.kt
+++ b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/services/workflow/mock/MockComponentFunction.kt
@@ -19,6 +19,8 @@ package org.onap.ccsdk.cds.blueprintsprocessor.services.workflow.mock
import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput
import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.AbstractComponentFunction
+import org.onap.ccsdk.cds.blueprintsprocessor.services.execution.nodeTemplateComponentScriptExecutor
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintTypes
import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.config.ConfigurableBeanFactory
@@ -27,6 +29,16 @@ import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Scope
import org.springframework.stereotype.Component
+fun mockNodeTemplateComponentScriptExecutor(id: String, script: String) = BluePrintTypes.nodeTemplateComponentScriptExecutor(id,
+ "mock($id) component function") {
+ definedOperation("") {
+ inputs {
+ type("kotlin")
+ scriptClassReference(script)
+ }
+ }
+}
+
@Configuration
open class MockComponentConfiguration {
diff --git a/ms/blueprintsprocessor/modules/services/workflow-service/src/test/resources/execution-input/imperative-test-input.json b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/resources/execution-input/imperative-test-input.json
new file mode 100644
index 000000000..d3495c456
--- /dev/null
+++ b/ms/blueprintsprocessor/modules/services/workflow-service/src/test/resources/execution-input/imperative-test-input.json
@@ -0,0 +1,18 @@
+{
+ "commonHeader": {
+ "originatorId": "System",
+ "requestId": "1234",
+ "subRequestId": "1234-12234"
+ },
+ "actionIdentifiers": {
+ "blueprintName": "imperative-test",
+ "blueprintVersion": "1.0.0",
+ "actionName": "imperative-test-wf",
+ "mode": "sync"
+ },
+ "payload": {
+ "imperative-test-wf-request": {
+ "hostname": "localhost"
+ }
+ }
+} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/BluePrintConstants.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/BluePrintConstants.kt
index 67a215ef5..064c196ed 100644
--- a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/BluePrintConstants.kt
+++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/BluePrintConstants.kt
@@ -161,6 +161,9 @@ object BluePrintConstants {
const val TOSCA_SCRIPTS_KOTLIN_DIR: String = "$TOSCA_SCRIPTS_DIR/kotlin"
const val TOSCA_SCRIPTS_JYTHON_DIR: String = "$TOSCA_SCRIPTS_DIR/python"
+ const val GRAPH_START_NODE_NAME = "START"
+ const val GRAPH_END_NODE_NAME = "END"
+
const val PROPERTY_ENV = "ENV"
const val PROPERTY_APP = "APP"
const val PROPERTY_BPP = "BPP"
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/CustomFunctions.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/CustomFunctions.kt
index c77427b01..93ba15e99 100644
--- a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/CustomFunctions.kt
+++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/CustomFunctions.kt
@@ -24,7 +24,6 @@ import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
import org.onap.ccsdk.cds.controllerblueprints.core.utils.JsonParserUtils
import org.slf4j.LoggerFactory
import org.slf4j.helpers.MessageFormatter
-import java.lang.Float
import kotlin.reflect.KClass
/**
@@ -98,10 +97,10 @@ fun <T : Any?> T.asJsonPrimitive(): JsonNode {
fun String.asJsonType(bpDataType: String): JsonNode {
return when (bpDataType.toLowerCase()) {
BluePrintConstants.DATA_TYPE_STRING -> this.asJsonPrimitive()
- BluePrintConstants.DATA_TYPE_BOOLEAN -> java.lang.Boolean.valueOf(this).asJsonPrimitive()
- BluePrintConstants.DATA_TYPE_INTEGER -> Integer.valueOf(this).asJsonPrimitive()
- BluePrintConstants.DATA_TYPE_FLOAT -> Float.valueOf(this).asJsonPrimitive()
- BluePrintConstants.DATA_TYPE_DOUBLE -> java.lang.Double.valueOf(this).asJsonPrimitive()
+ BluePrintConstants.DATA_TYPE_BOOLEAN -> this.toBoolean().asJsonPrimitive()
+ BluePrintConstants.DATA_TYPE_INTEGER -> this.toInt().asJsonPrimitive()
+ BluePrintConstants.DATA_TYPE_FLOAT -> this.toFloat().asJsonPrimitive()
+ BluePrintConstants.DATA_TYPE_DOUBLE -> this.toDouble().asJsonPrimitive()
// For List, Map and Complex Types.
else -> this.jsonAsJsonType()
}
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/GraphExtensionFunctions.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/GraphExtensionFunctions.kt
new file mode 100644
index 000000000..793bdc455
--- /dev/null
+++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/GraphExtensionFunctions.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright © 2019 IBM.
+ *
+ * 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.controllerblueprints.core
+
+import org.onap.ccsdk.cds.controllerblueprints.core.data.EdgeLabel
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Graph
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow
+import org.onap.ccsdk.cds.controllerblueprints.core.utils.WorkflowGraphUtils
+import java.util.regex.Pattern
+
+private val graphTokenSeparators = Pattern.compile("[->/]")
+
+/** Convert Blueprint workflow to graph data structure */
+fun Workflow.asGraph(): Graph {
+ return WorkflowGraphUtils.workFlowToGraph(this)
+}
+
+fun String.toGraph(): Graph {
+ if (!startsWith('[') || !endsWith(']')) {
+ throw IllegalArgumentException("Expected string starting '[' and ending with ']' but it was '$")
+ }
+ val tokens = substring(1, length - 1).split(", ").map { it.split(graphTokenSeparators) }
+ val nodes = tokens.flatMap { it.take(2) }.toCollection(LinkedHashSet())
+ val edges = tokens.filter { it.size == 3 }.map { Graph.TermForm.Term(it[0], it[1], EdgeLabel.valueOf(it[2])) }
+ return Graph.labeledDirectedTerms(Graph.TermForm(nodes, edges))
+}
+
+fun Graph.toAdjacencyList(): Graph.AdjacencyList<String, EdgeLabel> {
+ val entries = nodes.values.map { node ->
+ val links = node.edges.map { Graph.AdjacencyList.Link(it.target(node).id, it.label) }
+ Graph.AdjacencyList.Entry(node = node.id, links = links)
+ }
+ return Graph.AdjacencyList(entries)
+}
+
+fun Graph.findAllPaths(from: String, to: String, path: List<String> = emptyList()): List<List<String>> {
+ if (from == to) return listOf(path + to)
+ return nodes[from]!!.neighbors()
+ .filter { !path.contains(it.id) }
+ .flatMap { findAllPaths(it.id, to, path + from) }
+}
+
+fun Graph.findCycles(node: String): List<List<String>> {
+ fun findCycles(path: List<String>): List<List<String>> {
+ if (path.size > 3 && path.first() == path.last()) return listOf(path)
+ return nodes[path.last()]!!.neighbors()
+ .filterNot { path.tail().contains(it.id) }
+ .flatMap { findCycles(path + it.id) }
+ }
+ return findCycles(listOf(node))
+}
+
+fun Graph.startNodes() = this.nodes.values.filter {
+ val incomingEdges = incomingEdges(it.id)
+ incomingEdges.isEmpty()
+}
+
+fun Graph.endNodes(): Set<Graph.Node> = this.nodes.values.filter {
+ outgoingEdges(it.id).isEmpty()
+}.toSet()
+
+fun Graph.node(node: String) = this.nodes[node]
+
+fun Graph.edge(label: EdgeLabel) =
+ this.edges.filter { it.label == label }
+
+fun Graph.incomingEdges(node: String) =
+ this.edges.filter { it.target.id == node }
+
+fun Graph.incomingNodes(node: String) =
+ this.incomingEdges(node).map { it.source }
+
+fun Graph.outgoingEdges(node: String) =
+ this.edges.filter { it.source.id == node }
+
+fun Graph.outgoingNodes(node: String) =
+ this.outgoingEdges(node).map { it.target }
+
+fun Graph.outgoingEdges(node: String, label: EdgeLabel) =
+ this.edges.filter { it.source.id == node && it.label == label }
+
+fun Graph.outgoingNodes(node: String, label: EdgeLabel) =
+ this.outgoingEdges(node, label).map { it.target }
+
+fun Graph.outgoingNodesNotInEdgeLabels(node: String, labels: List<EdgeLabel>) =
+ this.outgoingEdgesNotInLabels(node, labels).map { it.target }
+
+fun Graph.outgoingEdges(node: String, labels: List<EdgeLabel>) =
+ this.edges.filter { it.source.id == node && labels.contains(it.label) }
+
+fun Graph.outgoingEdgesNotInLabels(node: String, labels: List<EdgeLabel>) =
+ this.edges.filter { it.source.id == node && !labels.contains(it.label) }
+
+fun Graph.outgoingNodes(node: String, labels: List<EdgeLabel>) =
+ this.outgoingEdges(node, labels).map { it.target }
+
+fun Graph.isEndNode(node: Graph.Node): Boolean {
+ return this.endNodes().contains(node)
+}
+
+fun <T> List<T>.tail(): List<T> = drop(1)
+
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/data/BluePrintGraph.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/data/BluePrintGraph.kt
new file mode 100644
index 000000000..9e1b7498e
--- /dev/null
+++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/data/BluePrintGraph.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright © 2019 IBM.
+ *
+ * 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.controllerblueprints.core.data
+
+enum class EdgeLabel(val id: String) {
+ SUCCESS("success"),
+ FAILURE("failure"),
+ DEFAULT("*")
+}
+
+enum class EdgeStatus(val id: String) {
+ NOT_STARTED("not_started"),
+ EXECUTED("executed"),
+ SKIPPED("skipped")
+}
+
+enum class NodeStatus(val id: String) {
+ NOT_STARTED("not_started"),
+ READY("ready"),
+ EXECUTING("executing"),
+ EXECUTED("executed"),
+ SKIPPED("skipped")
+}
+
+class Graph {
+ val nodes: MutableMap<String, Node> = hashMapOf()
+ val edges: MutableSet<Edge> = mutableSetOf()
+
+ fun addNode(value: String): Node {
+ val node = Node(value)
+ nodes[value] = node
+ return node
+ }
+
+ fun addEdge(source: String, destination: String, label: EdgeLabel) {
+ if (!nodes.containsKey(source)) {
+ addNode(source)
+ }
+ if (!nodes.containsKey(destination)) {
+ addNode(destination)
+ }
+ val edge = Edge(nodes[source]!!, nodes[destination]!!, label)
+ if (!edges.contains(edge)) {
+ edges.add(edge)
+ nodes[source]!!.edges.add(edge)
+ }
+ }
+
+ override fun toString(): String {
+ val standaloneNodes = nodes.values.filter { node -> edges.all { it.source != node && it.target != node } }
+ val s = (edges.map { it.toString() } + standaloneNodes.map { it.toString() }).joinToString()
+ return "[$s]"
+ }
+
+ fun print(): String {
+ val buffer = StringBuffer("Nodes :")
+ nodes.values.forEach {
+ buffer.append("\n\t$it")
+ }
+ buffer.append("\nEdges :")
+ edges.forEach {
+ buffer.append("\n\t$it")
+ }
+ return buffer.toString()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other?.javaClass != javaClass) return false
+ other as Graph
+ return nodes == other.nodes && edges == other.edges
+ }
+
+ override fun hashCode() = 31 * nodes.hashCode() + edges.hashCode()
+
+ fun equivalentTo(other: Graph): Boolean {
+ return nodes == other.nodes && edges.all { edge -> other.edges.any { it.equivalentTo(edge) } }
+ }
+
+ data class Node(val id: String, var status: NodeStatus = NodeStatus.NOT_STARTED) {
+ val edges: MutableList<Edge> = ArrayList()
+
+ fun neighbors(): List<Node> = edges.map { edge -> edge.target(this) }
+
+ fun neighbors(label: EdgeLabel): List<Node> = edges.filter { it.label == label }
+ .map { edge -> edge.target(this) }
+
+ fun labelEdges(label: EdgeLabel): List<Edge> = edges.filter { it.label == label }
+
+ override fun toString() = "$id, Status($status)"
+ }
+
+ data class Edge(
+ val source: Node,
+ val target: Node,
+ val label: EdgeLabel,
+ var status: EdgeStatus = EdgeStatus.NOT_STARTED) {
+
+ fun target(node: Node): Node = target
+
+ fun equivalentTo(other: Edge) =
+ (source == other.source && target == other.target)
+ || (source == other.target && target == other.source)
+
+ override fun toString() =
+ "${source.id}>${target.id}/$label($status)"
+ }
+
+ data class TermForm(val nodes: Collection<String>, val edges: List<Term>) {
+
+ data class Term(val source: String, val target: String, val label: EdgeLabel) {
+ override fun toString() = "Term($source, $target, $label)"
+ }
+ }
+
+ data class AdjacencyList<String, out EdgeLabel>(val entries: List<Entry<String, EdgeLabel>>) {
+ constructor(vararg entries: Entry<String, EdgeLabel>) : this(entries.asList())
+
+ override fun toString() = "AdjacencyList(${entries.joinToString()})"
+
+ data class Entry<out String, out EdgeLabel>(val node: String, val links: List<Link<String, EdgeLabel>> = emptyList<Nothing>()) {
+ constructor(node: String, vararg links: Link<String, EdgeLabel>) : this(node, links.asList())
+
+ override fun toString() = "Entry($node, links[${links.joinToString()}])"
+ }
+
+ data class Link<out String, out EdgeLabel>(val node: String, val label: EdgeLabel) {
+ override fun toString() = if (label == null) "$node" else "$node/$label"
+ }
+ }
+
+ companion object {
+
+ fun labeledDirectedTerms(termForm: TermForm): Graph =
+ createFromTerms(termForm) { graph, n1, n2, value -> graph.addEdge(n1, n2, value) }
+
+ fun labeledDirectedAdjacent(adjacencyList: AdjacencyList<String, EdgeLabel>): Graph =
+ fromAdjacencyList(adjacencyList) { graph, n1, n2, value ->
+ graph.addEdge(n1, n2, value)
+ }
+
+ private fun createFromTerms(termForm: TermForm,
+ addFunction: (Graph, String, String, EdgeLabel) -> Unit): Graph {
+ val graph = Graph()
+ termForm.nodes.forEach { graph.addNode(it) }
+ termForm.edges.forEach { addFunction(graph, it.source, it.target, it.label) }
+ return graph
+ }
+
+ private fun fromAdjacencyList(adjacencyList: AdjacencyList<String, EdgeLabel>,
+ addFunction: (Graph, String, String, EdgeLabel) -> Unit): Graph {
+ val graph = Graph()
+ adjacencyList.entries.forEach { graph.addNode(it.node) }
+ adjacencyList.entries.forEach { (node, links) ->
+ links.forEach { addFunction(graph, node, it.node, it.label) }
+ }
+ return graph
+ }
+ }
+} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/dsl/BluePrintServiceDSLBuilder.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/dsl/BluePrintServiceDSLBuilder.kt
index 06d3421c0..259efbf0b 100644
--- a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/dsl/BluePrintServiceDSLBuilder.kt
+++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/dsl/BluePrintServiceDSLBuilder.kt
@@ -26,7 +26,7 @@ class ServiceTemplateBuilder(private val name: String,
private val author: String,
private val tags: String) {
private var serviceTemplate = ServiceTemplate()
- private lateinit var topologyTemplate: TopologyTemplate
+ private var topologyTemplate: TopologyTemplate? = null
private var metadata: MutableMap<String, String> = hashMapOf()
private var dslDefinitions: MutableMap<String, JsonNode>? = null
private var imports: MutableList<ImportDefinition> = mutableListOf()
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintWorkflowService.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintWorkflowService.kt
new file mode 100644
index 000000000..905150213
--- /dev/null
+++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintWorkflowService.kt
@@ -0,0 +1,339 @@
+/*
+ * Copyright © 2019 IBM.
+ *
+ * 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.controllerblueprints.core.service
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.actor
+import kotlinx.coroutines.channels.consumeEach
+import org.onap.ccsdk.cds.controllerblueprints.core.*
+import org.onap.ccsdk.cds.controllerblueprints.core.data.EdgeLabel
+import org.onap.ccsdk.cds.controllerblueprints.core.data.EdgeStatus
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Graph
+import org.onap.ccsdk.cds.controllerblueprints.core.data.NodeStatus
+import kotlin.coroutines.CoroutineContext
+
+interface BluePrintWorkFlowService<In, Out> {
+
+ /** Executes imperative workflow graph [graph] for the bluePrintRuntimeService [bluePrintRuntimeService]
+ * and workflow input [input], response will be retrieve from output [output]*/
+ suspend fun executeWorkflow(graph: Graph, bluePrintRuntimeService: BluePrintRuntimeService<*>,
+ input: In, output: CompletableDeferred<Out>)
+
+ suspend fun initializeWorkflow(input: In): EdgeLabel
+
+ suspend fun prepareWorkflowOutput(exception: BluePrintProcessorException?): Out
+
+ /** Prepare the message for the Node */
+ suspend fun prepareNodeExecutionMessage(node: Graph.Node): NodeExecuteMessage<In, Out>
+
+ suspend fun prepareNodeSkipMessage(node: Graph.Node): NodeSkipMessage<In, Out>
+
+ suspend fun executeNode(node: Graph.Node, nodeInput: In, nodeOutput: Out): EdgeLabel
+
+ suspend fun skipNode(node: Graph.Node, nodeInput: In, nodeOutput: Out): EdgeLabel
+
+ suspend fun cancelNode(node: Graph.Node, nodeInput: In, nodeOutput: Out): EdgeLabel
+
+ suspend fun restartNode(node: Graph.Node, nodeInput: In, nodeOutput: Out): EdgeLabel
+
+}
+
+/** Workflow Message Types */
+sealed class WorkflowMessage<In, Out>
+
+class WorkflowExecuteMessage<In, Out>(val input: In, val output: CompletableDeferred<Out>) : WorkflowMessage<In, Out>()
+
+class WorkflowCancelMessage<In, Out>(val input: In, val output: CompletableDeferred<Out>) : WorkflowMessage<In, Out>()
+
+class WorkflowRestartMessage<In, Out>(val input: In, val output: CompletableDeferred<Out>) : WorkflowMessage<In, Out>()
+
+/** Node Message Types */
+sealed class NodeMessage<In, Out>
+
+class NodeReadyMessage<In, Out>(val fromEdge: Graph.Edge, val edgeAction: EdgeAction) : NodeMessage<In, Out>()
+
+class NodeExecuteMessage<In, Out>(val node: Graph.Node, val nodeInput: In, val nodeOutput: Out) : NodeMessage<In, Out>()
+
+class NodeRestartMessage<In, Out>(val node: Graph.Node, val nodeInput: In, val nodeOutput: Out) : NodeMessage<In, Out>()
+
+class NodeSkipMessage<In, Out>(val node: Graph.Node, val nodeInput: In, val nodeOutput: Out) : NodeMessage<In, Out>()
+
+class NodeCancelMessage<In, Out>(val node: Graph.Node, val nodeInput: In, val nodeOutput: Out) : NodeMessage<In, Out>()
+
+enum class EdgeAction(val id: String) {
+ EXECUTE("execute"),
+ SKIP("skip")
+}
+
+/** Abstract workflow service implementation */
+abstract class AbstractBluePrintWorkFlowService<In, Out> : CoroutineScope, BluePrintWorkFlowService<In, Out> {
+
+ lateinit var graph: Graph
+
+ private val log = logger(AbstractBluePrintWorkFlowService::class)
+
+ private val job = Job()
+
+ lateinit var workflowId: String
+
+ final override val coroutineContext: CoroutineContext
+ get() = job + CoroutineName("Wf")
+
+ fun cancel() {
+ log.info("Received workflow($workflowId) cancel request")
+ job.cancel()
+ throw CancellationException("Workflow($workflowId) cancelled as requested")
+ }
+
+ fun workflowActor() = actor<WorkflowMessage<In, Out>>(coroutineContext, Channel.UNLIMITED) {
+ /** Process the workflow execution message */
+ suspend fun executeMessageActor(workflowExecuteMessage: WorkflowExecuteMessage<In, Out>) {
+
+ val nodeActor = nodeActor()
+ // Prepare Workflow and Populate the Initial store
+ initializeWorkflow(workflowExecuteMessage.input)
+
+ val startNode = graph.startNodes().first()
+ // Prepare first node message and Send NodeExecuteMessage
+ // Start node doesn't wait for any nodes, so we can pass Execute message directly
+ val nodeExecuteMessage = prepareNodeExecutionMessage(startNode)
+ /** Send message from workflow actor to node actor */
+ launch {
+ nodeActor.send(nodeExecuteMessage)
+ }
+ // Wait for workflow completion or Error
+ nodeActor.invokeOnClose { exception ->
+ launch {
+ log.info("End Node Completed, processing completion message")
+ val bluePrintProcessorException: BluePrintProcessorException? =
+ if (exception != null) BluePrintProcessorException(exception) else null
+
+ val workflowOutput = prepareWorkflowOutput(bluePrintProcessorException)
+ workflowExecuteMessage.output.complete(workflowOutput)
+ channel.close(exception)
+ }
+ }
+ }
+
+ /** Process each actor message received based on type */
+ consumeEach { message ->
+ when (message) {
+ is WorkflowExecuteMessage<In, Out> -> {
+ launch {
+ executeMessageActor(message)
+ }
+ }
+ is WorkflowRestartMessage<In, Out> -> {
+ launch {
+ TODO("")
+ }
+ }
+ is WorkflowCancelMessage<In, Out> -> {
+ launch {
+ TODO("")
+ }
+ }
+ }
+ }
+ }
+
+
+ private fun nodeActor() = actor<NodeMessage<In, Out>>(coroutineContext, Channel.UNLIMITED) {
+
+ /** Send message to process from one state to other state */
+ fun sendNodeMessage(nodeMessage: NodeMessage<In, Out>) = launch {
+ channel.send(nodeMessage)
+ }
+
+ /** Process the cascade node processing, based on the previous state of the node */
+ fun processNextNodes(node: Graph.Node, nodeState: EdgeLabel) {
+ // Process only Next Success Node
+ val stateEdges = graph.outgoingEdges(node.id, arrayListOf(nodeState))
+ log.debug("Next Edges :$stateEdges")
+ if (stateEdges.isNotEmpty()) {
+ stateEdges.forEach { stateEdge ->
+ // Prepare next node ready message and Send NodeReadyMessage
+ val nodeReadyMessage = NodeReadyMessage<In, Out>(stateEdge, EdgeAction.EXECUTE)
+ sendNodeMessage(nodeReadyMessage)
+ }
+ }
+ }
+
+ suspend fun triggerToExecuteOrSkip(message: NodeReadyMessage<In, Out>) {
+ val edge = message.fromEdge
+ val node = edge.target
+ // Check if current edge action is Skip or Execute
+ when (message.edgeAction) {
+ EdgeAction.SKIP -> {
+ val skipMessage = prepareNodeSkipMessage(node)
+ sendNodeMessage(skipMessage)
+ }
+ EdgeAction.EXECUTE -> {
+ val nodeExecuteMessage = prepareNodeExecutionMessage(node)
+ sendNodeMessage(nodeExecuteMessage)
+ }
+ }
+ }
+
+ suspend fun readyNodeWorker(message: NodeReadyMessage<In, Out>) {
+ val edge = message.fromEdge
+ val node = edge.target
+ log.debug("@@@@@ Ready workflow($workflowId), node($node) from edge($edge) for action(${message.edgeAction}) @@@@@")
+ // Update the current incoming edge status to executed or skipped
+ when (message.edgeAction) {
+ EdgeAction.SKIP -> message.fromEdge.status = EdgeStatus.SKIPPED
+ EdgeAction.EXECUTE -> message.fromEdge.status = EdgeStatus.EXECUTED
+ }
+ val incomingEdges = graph.incomingEdges(node.id)
+ if (incomingEdges.size > 1) {
+ // Check all incoming edges executed or skipped
+ val notCompletedEdges = incomingEdges.filter { it.status == EdgeStatus.NOT_STARTED }
+ if (notCompletedEdges.isEmpty()) {
+ // Possibility of skip edge action performed at last, but other edges have execute action.
+ val executePresent = incomingEdges.filter { it.status == EdgeStatus.EXECUTED }
+ val newMessage = if (executePresent.isNotEmpty()) {
+ NodeReadyMessage(message.fromEdge, EdgeAction.EXECUTE)
+ } else {
+ message
+ }
+ triggerToExecuteOrSkip(newMessage)
+ } else {
+ log.info("node(${node.id}) waiting for not completed edges($notCompletedEdges)")
+ }
+ } else {
+ triggerToExecuteOrSkip(message)
+ }
+ }
+
+ suspend fun executeNodeWorker(message: NodeExecuteMessage<In, Out>) {
+ val node = message.node
+ node.status = NodeStatus.EXECUTING
+ val nodeState = if (node.id == BluePrintConstants.GRAPH_START_NODE_NAME
+ || node.id == BluePrintConstants.GRAPH_END_NODE_NAME) {
+ EdgeLabel.SUCCESS
+ } else {
+ log.debug("##### Processing workflow($workflowId) node($node) #####")
+ // Call the Extension function and get the next Edge state.
+ executeNode(node, message.nodeInput, message.nodeOutput)
+ }
+ // Update Node Completed
+ node.status = NodeStatus.EXECUTED
+ log.info("Execute Node($node) -> Executed State($nodeState)")
+
+ // If End Node, Send End Message
+ if (graph.isEndNode(node)) {
+ // Close the current channel
+ channel.close()
+ } else {
+ val skippingEdges = graph.outgoingEdgesNotInLabels(node.id, arrayListOf(nodeState))
+ log.debug("Skipping node($node) outgoing Edges($skippingEdges)")
+ // Process Skip Edges
+ skippingEdges.forEach { skippingEdge ->
+ // Prepare next node ready message and Send NodeReadyMessage
+ val nodeReadyMessage = NodeReadyMessage<In, Out>(skippingEdge, EdgeAction.SKIP)
+ sendNodeMessage(nodeReadyMessage)
+ }
+ // Process Success Node
+ processNextNodes(node, nodeState)
+ }
+ }
+
+ suspend fun skipNodeWorker(message: NodeSkipMessage<In, Out>) {
+ val node = message.node
+ val incomingEdges = graph.incomingEdges(node.id)
+ // Check All Incoming Nodes Skipped
+ val nonSkippedEdges = incomingEdges.filter {
+ it.status == EdgeStatus.NOT_STARTED
+ }
+ log.debug("Node($node) incoming edges ($incomingEdges), not skipped incoming edges ($nonSkippedEdges)")
+
+ if (nonSkippedEdges.isEmpty()) {
+ log.debug("$$$$$ Skipping workflow($workflowId) node($node) $$$$$")
+ // Call the Extension Function
+ val nodeState = skipNode(node, message.nodeInput, message.nodeOutput)
+ log.info("Skip Node($node) -> Executed State($nodeState)")
+ // Mark the Current node as Skipped
+ node.status = NodeStatus.SKIPPED
+ // Look for next possible skip nodes
+ graph.outgoingEdges(node.id).forEach { outgoingEdge ->
+ val nodeReadyMessage = NodeReadyMessage<In, Out>(outgoingEdge, EdgeAction.SKIP)
+ sendNodeMessage(nodeReadyMessage)
+ }
+ }
+ }
+
+ fun restartNodeWorker(message: NodeRestartMessage<In, Out>) = launch {
+ TODO()
+ }
+
+ fun cancelNodeWorker(messageWorkflow: WorkflowCancelMessage<In, Out>) = launch {
+ channel.close()
+ throw CancellationException("Workflow($workflowId) actor cancelled as requested ...")
+ }
+
+ /** Process each actor message received based on type **/
+ consumeEach { nodeMessage ->
+ when (nodeMessage) {
+ is NodeReadyMessage<In, Out> -> {
+ // Blocking call
+ try {
+ readyNodeWorker(nodeMessage)
+ } catch (e: Exception) {
+ channel.close(e)
+ }
+ }
+ is NodeExecuteMessage<In, Out> -> {
+ launch {
+ try {
+ executeNodeWorker(nodeMessage)
+ } catch (e: Exception) {
+ channel.close(e)
+ }
+ }
+ }
+ is NodeSkipMessage<In, Out> -> {
+ launch {
+ try {
+ skipNodeWorker(nodeMessage)
+ } catch (e: Exception) {
+ channel.close(e)
+ }
+ }
+ }
+ is NodeRestartMessage<In, Out> -> {
+ launch {
+ try {
+ restartNodeWorker(nodeMessage)
+ } catch (e: Exception) {
+ channel.close(e)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ override suspend fun executeWorkflow(graph: Graph, bluePrintRuntimeService: BluePrintRuntimeService<*>,
+ input: In, output: CompletableDeferred<Out>) {
+ log.info("Executing Graph : $graph")
+ this.graph = graph
+ this.workflowId = bluePrintRuntimeService.id()
+ val startMessage = WorkflowExecuteMessage(input, output)
+ workflowActor().send(startMessage)
+ }
+} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintMetadataUtils.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintMetadataUtils.kt
index 669ab3fef..55424ada8 100644
--- a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintMetadataUtils.kt
+++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/BluePrintMetadataUtils.kt
@@ -61,7 +61,7 @@ class BluePrintMetadataUtils {
// Verify if the environment directory exists
if (envDir.exists() && envDir.isDirectory) {
//Find all available environment files
- envDir.listFiles()
+ envDir.listFiles()!!
.filter { it.name.endsWith(".properties") }
.forEach {
val istream = it.inputStream()
@@ -96,14 +96,20 @@ class BluePrintMetadataUtils {
return toscaMetaData
}
- fun getBluePrintRuntime(id: String, blueprintBasePath: String): BluePrintRuntimeService<MutableMap<String, JsonNode>> {
-
+ fun getBluePrintRuntime(id: String, blueprintBasePath: String)
+ : BluePrintRuntimeService<MutableMap<String, JsonNode>> {
val bluePrintContext: BluePrintContext = getBluePrintContext(blueprintBasePath)
+ return getBluePrintRuntime(id, bluePrintContext)
+ }
+ fun getBluePrintRuntime(id: String, bluePrintContext: BluePrintContext)
+ : BluePrintRuntimeService<MutableMap<String, JsonNode>> {
+ checkNotEmpty(bluePrintContext.rootPath) { "blueprint context root path is missing." }
+ checkNotEmpty(bluePrintContext.entryDefinition) { "blueprint context entry definition is missing." }
+ val blueprintBasePath = bluePrintContext.rootPath
val bluePrintRuntimeService = DefaultBluePrintRuntimeService(id, bluePrintContext)
bluePrintRuntimeService.put(BluePrintConstants.PROPERTY_BLUEPRINT_BASE_PATH, blueprintBasePath.asJsonPrimitive())
bluePrintRuntimeService.put(BluePrintConstants.PROPERTY_BLUEPRINT_PROCESS_ID, id.asJsonPrimitive())
-
return bluePrintRuntimeService
}
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/JacksonUtils.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/JacksonUtils.kt
index 768f8753f..73dff9379 100644
--- a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/JacksonUtils.kt
+++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/JacksonUtils.kt
@@ -236,51 +236,55 @@ class JacksonUtils {
}
}
- fun populatePrimitiveValues(key: String, value: Any, primitiveType: String, objectNode: ObjectNode) {
+ fun populatePrimitiveValues(key: String, value: JsonNode, primitiveType: String, objectNode: ObjectNode) {
when (primitiveType.toLowerCase()) {
- BluePrintConstants.DATA_TYPE_STRING,
BluePrintConstants.DATA_TYPE_BOOLEAN,
BluePrintConstants.DATA_TYPE_INTEGER,
BluePrintConstants.DATA_TYPE_FLOAT,
BluePrintConstants.DATA_TYPE_DOUBLE,
- BluePrintConstants.DATA_TYPE_TIMESTAMP ->
- objectNode.set(key, value.asJsonPrimitive())
- else -> objectNode.set(key, value.asJsonType())
+ BluePrintConstants.DATA_TYPE_TIMESTAMP,
+ BluePrintConstants.DATA_TYPE_STRING ->
+ objectNode.set(key, value)
+ else -> throw BluePrintException("populatePrimitiveValues expected only primitive values! Received: ($value)")
}
}
- fun populatePrimitiveValues(value: Any, primitiveType: String, arrayNode: ArrayNode) {
+ fun populatePrimitiveValues(value: JsonNode, primitiveType: String, arrayNode: ArrayNode) {
when (primitiveType.toLowerCase()) {
- BluePrintConstants.DATA_TYPE_BOOLEAN -> arrayNode.add(value as Boolean)
- BluePrintConstants.DATA_TYPE_INTEGER -> arrayNode.add(value as Int)
- BluePrintConstants.DATA_TYPE_FLOAT -> arrayNode.add(value as Float)
- BluePrintConstants.DATA_TYPE_DOUBLE -> arrayNode.add(value as Double)
- BluePrintConstants.DATA_TYPE_TIMESTAMP -> arrayNode.add(value as String)
- else -> arrayNode.add(value as String)
+ BluePrintConstants.DATA_TYPE_BOOLEAN,
+ BluePrintConstants.DATA_TYPE_INTEGER,
+ BluePrintConstants.DATA_TYPE_FLOAT,
+ BluePrintConstants.DATA_TYPE_DOUBLE,
+ BluePrintConstants.DATA_TYPE_TIMESTAMP,
+ BluePrintConstants.DATA_TYPE_STRING -> arrayNode.add(value)
+ else -> throw BluePrintException("populatePrimitiveValues expected only primitive values! Received: ($value)")
}
}
fun populatePrimitiveDefaultValues(key: String, primitiveType: String, objectNode: ObjectNode) {
- when (primitiveType.toLowerCase()) {
- BluePrintConstants.DATA_TYPE_BOOLEAN -> objectNode.put(key, false)
- BluePrintConstants.DATA_TYPE_INTEGER -> objectNode.put(key, 0)
- BluePrintConstants.DATA_TYPE_FLOAT -> objectNode.put(key, 0.0)
- BluePrintConstants.DATA_TYPE_DOUBLE -> objectNode.put(key, 0.0)
- else -> objectNode.put(key, "")
- }
+ val defaultValue = getDefaultValueOfPrimitiveAsJsonNode(primitiveType) ?:
+ throw BluePrintException("populatePrimitiveDefaultValues expected only primitive values! Received type ($primitiveType)")
+ objectNode.set(key, defaultValue)
}
fun populatePrimitiveDefaultValuesForArrayNode(primitiveType: String, arrayNode: ArrayNode) {
- when (primitiveType.toLowerCase()) {
- BluePrintConstants.DATA_TYPE_BOOLEAN -> arrayNode.add(false)
- BluePrintConstants.DATA_TYPE_INTEGER -> arrayNode.add(0)
- BluePrintConstants.DATA_TYPE_FLOAT -> arrayNode.add(0.0)
- BluePrintConstants.DATA_TYPE_DOUBLE -> arrayNode.add(0.0)
- else -> arrayNode.add("")
+ val defaultValue = getDefaultValueOfPrimitiveAsJsonNode(primitiveType) ?:
+ throw BluePrintException("populatePrimitiveDefaultValuesForArrayNode expected only primitive values! Received type ($primitiveType)")
+ arrayNode.add(defaultValue)
+ }
+
+ private fun getDefaultValueOfPrimitiveAsJsonNode(primitiveType: String): JsonNode? {
+ return when (primitiveType.toLowerCase()) {
+ BluePrintConstants.DATA_TYPE_BOOLEAN -> BooleanNode.valueOf(false)
+ BluePrintConstants.DATA_TYPE_INTEGER -> IntNode.valueOf(0)
+ BluePrintConstants.DATA_TYPE_FLOAT -> FloatNode.valueOf(0.0f)
+ BluePrintConstants.DATA_TYPE_DOUBLE -> DoubleNode.valueOf(0.0)
+ BluePrintConstants.DATA_TYPE_STRING -> MissingNode.getInstance()
+ else -> null
}
}
- fun populateJsonNodeValues(key: String, nodeValue: JsonNode?, type: String, objectNode: ObjectNode) {
+ fun populateJsonNodeValues(key: String, nodeValue: JsonNode, type: String, objectNode: ObjectNode) {
if (nodeValue == null || nodeValue is NullNode) {
objectNode.set(key, nodeValue)
} else if (BluePrintTypes.validPrimitiveTypes().contains(type)) {
@@ -292,12 +296,13 @@ class JacksonUtils {
fun convertPrimitiveResourceValue(type: String, value: String): JsonNode {
return when (type.toLowerCase()) {
- BluePrintConstants.DATA_TYPE_BOOLEAN -> jsonNodeFromObject(java.lang.Boolean.valueOf(value))
- BluePrintConstants.DATA_TYPE_INTEGER -> jsonNodeFromObject(Integer.valueOf(value))
- BluePrintConstants.DATA_TYPE_FLOAT -> jsonNodeFromObject(java.lang.Float.valueOf(value))
- BluePrintConstants.DATA_TYPE_DOUBLE -> jsonNodeFromObject(java.lang.Double.valueOf(value))
+ BluePrintConstants.DATA_TYPE_BOOLEAN -> jsonNodeFromObject(value.toBoolean())
+ BluePrintConstants.DATA_TYPE_INTEGER -> jsonNodeFromObject(value.toInt())
+ BluePrintConstants.DATA_TYPE_FLOAT -> jsonNodeFromObject(value.toFloat())
+ BluePrintConstants.DATA_TYPE_DOUBLE -> jsonNodeFromObject(value.toDouble())
+ BluePrintConstants.DATA_TYPE_STRING -> jsonNodeFromObject(value)
else -> getJsonNode(value)
}
}
}
-} \ No newline at end of file
+}
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/WorkflowGraphUtils.kt b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/WorkflowGraphUtils.kt
new file mode 100644
index 000000000..ef765ab86
--- /dev/null
+++ b/ms/controllerblueprints/modules/blueprint-core/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/WorkflowGraphUtils.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2019 IBM.
+ *
+ * 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.controllerblueprints.core.utils
+
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
+import org.onap.ccsdk.cds.controllerblueprints.core.data.EdgeLabel
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Graph
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Workflow
+import org.onap.ccsdk.cds.controllerblueprints.core.endNodes
+import org.onap.ccsdk.cds.controllerblueprints.core.startNodes
+
+object WorkflowGraphUtils {
+
+ fun workFlowToGraph(workflow: Workflow): Graph {
+ val graph = Graph()
+ workflow.steps?.forEach { (stepName, step) ->
+ step.onSuccess?.forEach { successTarget ->
+ graph.addEdge(stepName, successTarget, EdgeLabel.SUCCESS)
+ }
+ step.onFailure?.forEach { failureTarget ->
+ graph.addEdge(stepName, failureTarget, EdgeLabel.FAILURE)
+ }
+ }
+ graph.startNodes().forEach { rootNode ->
+ graph.addEdge(BluePrintConstants.GRAPH_START_NODE_NAME, rootNode.id, EdgeLabel.SUCCESS)
+ }
+ graph.endNodes().forEach { endNode ->
+ graph.addEdge(endNode.id, BluePrintConstants.GRAPH_END_NODE_NAME, EdgeLabel.SUCCESS)
+ }
+ return graph
+ }
+} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/GraphExtensionFunctionsTest.kt b/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/GraphExtensionFunctionsTest.kt
new file mode 100644
index 000000000..86cb473ae
--- /dev/null
+++ b/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/GraphExtensionFunctionsTest.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2019 IBM.
+ *
+ * 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.controllerblueprints.core
+
+import org.junit.Test
+import org.onap.ccsdk.cds.controllerblueprints.core.data.EdgeLabel
+import kotlin.test.assertNotNull
+
+class GraphExtensionFunctionsTest {
+
+ @Test
+ fun testGraph() {
+ val graph = "[p>q/SUCCESS, m>q/SUCCESS, k, p>m/FAILURE, o>p/SUCCESS]".toGraph()
+ assertNotNull(graph, "failed to create graph")
+ assertNotNull(graph.toAdjacencyList(), "failed to adjacency list from graph")
+
+ val neighbors = graph.nodes["p"]!!.neighbors()
+ assertNotNull(neighbors, "failed to neighbors from graph for 'p' node")
+
+ val nodePath = graph.nodes["p"]!!.neighbors(EdgeLabel.SUCCESS)
+ assertNotNull(nodePath, "failed to nodePath from graph for 'p' node 'SUCCESS' label")
+ }
+}
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintRuntimeServiceTest.kt b/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintRuntimeServiceTest.kt
index 4c207fbe1..9103af3fa 100644
--- a/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintRuntimeServiceTest.kt
+++ b/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintRuntimeServiceTest.kt
@@ -17,16 +17,17 @@
package org.onap.ccsdk.cds.controllerblueprints.core.service
-import org.slf4j.LoggerFactory
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.NullNode
import org.junit.Test
import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants
import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive
import org.onap.ccsdk.cds.controllerblueprints.core.data.PropertyDefinition
+import org.onap.ccsdk.cds.controllerblueprints.core.normalizedPathName
import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintMetadataUtils
import org.onap.ccsdk.cds.controllerblueprints.core.utils.BluePrintRuntimeUtils
import org.onap.ccsdk.cds.controllerblueprints.core.utils.JacksonUtils
+import org.slf4j.LoggerFactory
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
@@ -36,7 +37,7 @@ import kotlin.test.assertNotNull
* @author Brinda Santh
*/
class BluePrintRuntimeServiceTest {
- private val log= LoggerFactory.getLogger(this::class.toString())
+ private val log = LoggerFactory.getLogger(this::class.toString())
@Test
fun `test Resolve NodeTemplate Properties`() {
@@ -167,11 +168,15 @@ class BluePrintRuntimeServiceTest {
}
private fun getBluePrintRuntimeService(): BluePrintRuntimeService<MutableMap<String, JsonNode>> {
- val blueprintBasePath: String = ("./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration")
+ val blueprintBasePath = normalizedPathName("./../../../../components/model-catalog/blueprint-model/test-blueprint/baseconfiguration")
val blueprintRuntime = BluePrintMetadataUtils.getBluePrintRuntime("1234", blueprintBasePath)
+ val checkProcessId = blueprintRuntime.get(BluePrintConstants.PROPERTY_BLUEPRINT_PROCESS_ID)
val checkBasePath = blueprintRuntime.get(BluePrintConstants.PROPERTY_BLUEPRINT_BASE_PATH)
- assertEquals(blueprintBasePath.asJsonPrimitive(), checkBasePath, "Failed to get base path after runtime creation")
+ assertEquals("1234".asJsonPrimitive(),
+ checkProcessId, "Failed to get process id after runtime creation")
+ assertEquals(blueprintBasePath.asJsonPrimitive(),
+ checkBasePath, "Failed to get base path after runtime creation")
return blueprintRuntime
}
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintWorkflowServiceTest.kt b/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintWorkflowServiceTest.kt
new file mode 100644
index 000000000..b8d8cea3e
--- /dev/null
+++ b/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/service/BluePrintWorkflowServiceTest.kt
@@ -0,0 +1,175 @@
+/*
+ * Copyright © 2019 IBM.
+ *
+ * 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.controllerblueprints.core.service
+
+import io.mockk.every
+import io.mockk.mockk
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException
+import org.onap.ccsdk.cds.controllerblueprints.core.data.EdgeLabel
+import org.onap.ccsdk.cds.controllerblueprints.core.data.Graph
+import org.onap.ccsdk.cds.controllerblueprints.core.toGraph
+import kotlin.test.assertNotNull
+
+class BluePrintWorkflowServiceTest {
+ @Test
+ fun testSimpleFlow() {
+ runBlocking {
+ val graph = "[START>A/SUCCESS, A>B/SUCCESS, B>C/SUCCESS, C>D/SUCCESS, D>E/SUCCESS, E>END/SUCCESS]"
+ .toGraph()
+ val simpleWorkflow = TestBluePrintWorkFlowService()
+ simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "B", "C", "D", "E"), null)
+ val deferredOutput = CompletableDeferred<String>()
+ val input = "123456"
+ simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input, deferredOutput)
+ val response = deferredOutput.await()
+ assertNotNull(response, "failed to get response")
+ }
+ }
+
+ @Test
+ fun testConditionalFlow() {
+ runBlocking {
+ val graph = "[START>A/SUCCESS, A>B/SUCCESS, A>C/FAILURE, B>D/SUCCESS, C>D/SUCCESS, D>END/SUCCESS]"
+ .toGraph()
+ val simpleWorkflow = TestBluePrintWorkFlowService()
+ simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "B", "C", "D", "E"), null)
+ val deferredOutput = CompletableDeferred<String>()
+ val input = "123456"
+ simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input, deferredOutput)
+ val response = deferredOutput.await()
+ assertNotNull(response, "failed to get response")
+ }
+ }
+
+ @Test
+ fun testBothConditionalFlow() {
+ runBlocking {
+ // Failure Flow
+ val failurePatGraph = "[START>A/SUCCESS, A>B/SUCCESS, A>C/FAILURE, B>D/SUCCESS, C>D/SUCCESS, D>END/SUCCESS]"
+ .toGraph()
+ val failurePathWorkflow = TestBluePrintWorkFlowService()
+ failurePathWorkflow.simulatedState = prepareSimulation(arrayListOf("B", "C", "D", "E"),
+ arrayListOf("A"))
+ val failurePathWorkflowDeferredOutput = CompletableDeferred<String>()
+ val failurePathWorkflowInput = "123456"
+ failurePathWorkflow.executeWorkflow(failurePatGraph, mockBluePrintRuntimeService(), failurePathWorkflowInput, failurePathWorkflowDeferredOutput)
+ val failurePathResponse = failurePathWorkflowDeferredOutput.await()
+ assertNotNull(failurePathResponse, "failed to get response")
+ }
+ }
+
+ @Test
+ fun testMultipleSkipFlow() {
+ runBlocking {
+ val graph = "[START>A/SUCCESS, A>B/SUCCESS, A>C/FAILURE, C>D/SUCCESS, D>E/SUCCESS, B>E/SUCCESS, E>END/SUCCESS]"
+ .toGraph()
+ val simpleWorkflow = TestBluePrintWorkFlowService()
+ simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "B", "C", "D", "E"), null)
+ val deferredOutput = CompletableDeferred<String>()
+ val input = "123456"
+ simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input, deferredOutput)
+ val response = deferredOutput.await()
+ assertNotNull(response, "failed to get response")
+ }
+ }
+
+ @Test
+ fun testParallelFlow() {
+ runBlocking {
+ val graph = "[START>A/SUCCESS, A>B/SUCCESS, A>C/SUCCESS, B>D/SUCCESS, C>D/SUCCESS, D>END/SUCCESS]"
+ .toGraph()
+ val simpleWorkflow = TestBluePrintWorkFlowService()
+ simpleWorkflow.simulatedState = prepareSimulation(arrayListOf("A", "B", "C", "D"), null)
+ val deferredOutput = CompletableDeferred<String>()
+ val input = "123456"
+ simpleWorkflow.executeWorkflow(graph, mockBluePrintRuntimeService(), input, deferredOutput)
+ val response = deferredOutput.await()
+ assertNotNull(response, "failed to get response")
+ }
+ }
+
+ private fun mockBluePrintRuntimeService(): BluePrintRuntimeService<*> {
+ val bluePrintRuntimeService = mockk<BluePrintRuntimeService<*>>()
+ every { bluePrintRuntimeService.id() } returns "123456"
+ return bluePrintRuntimeService
+ }
+
+ private fun prepareSimulation(successes: List<String>?, failures: List<String>?): MutableMap<String, EdgeLabel> {
+ val simulatedState: MutableMap<String, EdgeLabel> = hashMapOf()
+ successes?.forEach {
+ simulatedState[it] = EdgeLabel.SUCCESS
+ }
+ failures?.forEach {
+ simulatedState[it] = EdgeLabel.FAILURE
+ }
+ return simulatedState
+ }
+}
+
+class TestBluePrintWorkFlowService
+ : AbstractBluePrintWorkFlowService<String, String>() {
+
+ lateinit var simulatedState: MutableMap<String, EdgeLabel>
+
+ override suspend fun initializeWorkflow(input: String): EdgeLabel {
+ return EdgeLabel.SUCCESS
+ }
+
+ override suspend fun prepareNodeExecutionMessage(node: Graph.Node)
+ : NodeExecuteMessage<String, String> {
+ return NodeExecuteMessage(node, "$node Input", "")
+ }
+
+ override suspend fun executeNode(node: Graph.Node, nodeInput: String,
+ nodeOutput: String): EdgeLabel {
+// val random = (1..10).random() * 1000
+// println("will reply in $random ms")
+// kotlinx.coroutines.delay(random.toLong())
+ val status = simulatedState[node.id] ?: throw BluePrintException("failed to get status for the node($node)")
+ return status
+ }
+
+ override suspend fun prepareNodeSkipMessage(node: Graph.Node): NodeSkipMessage<String, String> {
+ val nodeOutput = ""
+ val nodeSkipMessage = NodeSkipMessage(node, "$node Skip Input", nodeOutput)
+ return nodeSkipMessage
+ }
+
+ override suspend fun skipNode(node: Graph.Node, nodeInput: String,
+ nodeOutput: String): EdgeLabel {
+ val status = simulatedState[node.id] ?: throw BluePrintException("failed to get status for the node($node)")
+ return status
+ }
+
+ override suspend fun cancelNode(node: Graph.Node, nodeInput: String,
+ nodeOutput: String): EdgeLabel {
+ TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+ }
+
+ override suspend fun restartNode(node: Graph.Node, nodeInput: String,
+ nodeOutput: String): EdgeLabel {
+ TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+ }
+
+ override suspend fun prepareWorkflowOutput(exception: BluePrintProcessorException?): String {
+ return "Final Response"
+ }
+} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/WorkflowGraphUtilsTest.kt b/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/WorkflowGraphUtilsTest.kt
new file mode 100644
index 000000000..fb0a1a63d
--- /dev/null
+++ b/ms/controllerblueprints/modules/blueprint-core/src/test/kotlin/org/onap/ccsdk/cds/controllerblueprints/core/utils/WorkflowGraphUtilsTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2019 IBM.
+ *
+ * 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.controllerblueprints.core.utils
+
+import org.onap.ccsdk.cds.controllerblueprints.core.dsl.workflow
+import kotlin.test.Test
+import kotlin.test.assertNotNull
+
+class WorkflowGraphUtilsTest {
+
+ @Test
+ fun testWorkFlowToGraph() {
+
+ val workflow = workflow("sample", "") {
+ step("A", "A", "") {
+ success("B")
+ }
+ step("B", "B", "") {
+ success("C")
+ failure("D")
+ }
+ step("C", "C", "")
+ step("D", "D", "")
+ }
+ val graph = WorkflowGraphUtils.workFlowToGraph(workflow)
+ assertNotNull(graph, "failed to create graph")
+ }
+} \ No newline at end of file
diff --git a/ms/controllerblueprints/modules/service/src/main/java/org/onap/ccsdk/cds/controllerblueprints/service/AutoResourceMappingService.java b/ms/controllerblueprints/modules/service/src/main/java/org/onap/ccsdk/cds/controllerblueprints/service/AutoResourceMappingService.java
deleted file mode 100644
index b9eff7624..000000000
--- a/ms/controllerblueprints/modules/service/src/main/java/org/onap/ccsdk/cds/controllerblueprints/service/AutoResourceMappingService.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright © 2017-2018 AT&T Intellectual Property.
- * Modifications Copyright © 2018 IBM.
- *
- * 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.controllerblueprints.service;
-
-import com.google.common.base.Preconditions;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException;
-import org.onap.ccsdk.cds.controllerblueprints.core.data.PropertyDefinition;
-import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment;
-import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceDefinition;
-import org.onap.ccsdk.cds.controllerblueprints.resource.dict.utils.ResourceDictionaryUtils;
-import org.onap.ccsdk.cds.controllerblueprints.service.domain.ResourceDictionary;
-import org.onap.ccsdk.cds.controllerblueprints.service.model.AutoMapResponse;
-import org.onap.ccsdk.cds.controllerblueprints.service.repository.ResourceDictionaryRepository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * AutoResourceMappingService.java Purpose: Provide Automapping of Resource Assignments AutoResourceMappingService
- *
- * @author Brinda Santh
- * @version 1.0
- */
-
-@Service
-@SuppressWarnings("unused")
-public class AutoResourceMappingService {
-
- private static Logger log = LoggerFactory.getLogger(AutoResourceMappingService.class);
-
- private ResourceDictionaryRepository dataDictionaryRepository;
-
- /**
- * This is a AutoResourceMappingService constructor
- *
- * @param dataDictionaryRepository dataDictionaryRepository
- */
- public AutoResourceMappingService(ResourceDictionaryRepository dataDictionaryRepository) {
- this.dataDictionaryRepository = dataDictionaryRepository;
- }
-
- /**
- * This is a autoMap service to map the template keys automatically to Dictionary fields.
- *
- * @param resourceAssignments resourceAssignments
- * @return AutoMapResponse
- */
- public AutoMapResponse autoMap(List<ResourceAssignment> resourceAssignments) throws BluePrintException {
- AutoMapResponse autoMapResponse = new AutoMapResponse();
- try {
- if (CollectionUtils.isNotEmpty(resourceAssignments)) {
-
- // Create the Dictionary definitions for the ResourceAssignment Names
- Map<String, ResourceDictionary> dictionaryMap = getDictionaryDefinitions(resourceAssignments);
-
- for (ResourceAssignment resourceAssignment : resourceAssignments) {
- if (resourceAssignment != null && StringUtils.isNotBlank(resourceAssignment.getName())
- && StringUtils.isBlank(resourceAssignment.getDictionaryName())) {
-
- populateDictionaryMapping(dictionaryMap, resourceAssignment);
-
- log.info("Mapped Resource : {}", resourceAssignment);
-
- }
- }
- }
- List<ResourceDictionary> dictionaries = getDictionaryDefinitionsList(resourceAssignments);
- List<ResourceAssignment> resourceAssignmentsFinal = getAllAutomapResourceAssignments(resourceAssignments);
- autoMapResponse.setDataDictionaries(dictionaries);
- autoMapResponse.setResourceAssignments(resourceAssignmentsFinal);
- } catch (Exception e) {
- log.error(String.format("Failed in auto process %s", e.getMessage()));
- throw new BluePrintException(e.getMessage(), e);
- }
- return autoMapResponse;
- }
-
- private void populateDictionaryMapping(Map<String, ResourceDictionary> dictionaryMap, ResourceAssignment resourceAssignment) {
- ResourceDictionary dbDataDictionary = dictionaryMap.get(resourceAssignment.getName());
- if (dbDataDictionary != null && dbDataDictionary.getDefinition() != null) {
-
- ResourceDefinition dictionaryDefinition = dbDataDictionary.getDefinition();
-
- if (dictionaryDefinition != null && StringUtils.isNotBlank(dictionaryDefinition.getName())
- && StringUtils.isBlank(resourceAssignment.getDictionaryName())) {
-
- resourceAssignment.setDictionaryName(dbDataDictionary.getName());
- ResourceDictionaryUtils.populateSourceMapping(resourceAssignment, dictionaryDefinition);
- }
- }
- }
-
- private Map<String, ResourceDictionary> getDictionaryDefinitions(List<ResourceAssignment> resourceAssignments) {
- Map<String, ResourceDictionary> dictionaryMap = new HashMap<>();
- List<String> names = new ArrayList<>();
- for (ResourceAssignment resourceAssignment : resourceAssignments) {
- if (resourceAssignment != null && StringUtils.isNotBlank(resourceAssignment.getName())) {
- names.add(resourceAssignment.getName());
- }
- }
- if (CollectionUtils.isNotEmpty(names)) {
-
- List<ResourceDictionary> dictionaries = dataDictionaryRepository.findByNameIn(names);
- if (CollectionUtils.isNotEmpty(dictionaries)) {
- for (ResourceDictionary dataDictionary : dictionaries) {
- if (dataDictionary != null && StringUtils.isNotBlank(dataDictionary.getName())) {
- dictionaryMap.put(dataDictionary.getName(), dataDictionary);
- }
- }
- }
- }
- return dictionaryMap;
-
- }
-
- private List<ResourceDictionary> getDictionaryDefinitionsList(List<ResourceAssignment> resourceAssignments) {
- List<ResourceDictionary> dictionaries = null;
- List<String> names = new ArrayList<>();
- for (ResourceAssignment resourceAssignment : resourceAssignments) {
- if (resourceAssignment != null && StringUtils.isNotBlank(resourceAssignment.getDictionaryName())) {
-
- if (!names.contains(resourceAssignment.getDictionaryName())) {
- names.add(resourceAssignment.getDictionaryName());
- }
-
- if (resourceAssignment.getDependencies() != null && !resourceAssignment.getDependencies().isEmpty()) {
- List<String> dependencyNames = resourceAssignment.getDependencies();
- for (String dependencyName : dependencyNames) {
- if (StringUtils.isNotBlank(dependencyName) && !names.contains(dependencyName)) {
- names.add(dependencyName);
- }
- }
- }
- }
- }
- if (CollectionUtils.isNotEmpty(names)) {
- dictionaries = dataDictionaryRepository.findByNameIn(names);
- }
- return dictionaries;
-
- }
-
- private List<ResourceAssignment> getAllAutomapResourceAssignments(List<ResourceAssignment> resourceAssignments) {
- List<ResourceDictionary> dictionaries = null;
- List<String> names = new ArrayList<>();
- for (ResourceAssignment resourceAssignment : resourceAssignments) {
- if (resourceAssignment != null && StringUtils.isNotBlank(resourceAssignment.getDictionaryName())) {
- if (resourceAssignment.getDependencies() != null && !resourceAssignment.getDependencies().isEmpty()) {
- List<String> dependencieNames = resourceAssignment.getDependencies();
- for (String dependencieName : dependencieNames) {
- if (StringUtils.isNotBlank(dependencieName) && !names.contains(dependencieName)
- && !checkAssignmentsExists(resourceAssignments, dependencieName)) {
- names.add(dependencieName);
- }
- }
- }
- }
- }
-
- if (!names.isEmpty()) {
- dictionaries = dataDictionaryRepository.findByNameIn(names);
- }
- if (dictionaries != null) {
- for (ResourceDictionary resourcedictionary : dictionaries) {
- ResourceDefinition dictionaryDefinition = resourcedictionary.getDefinition();
- Preconditions.checkNotNull(dictionaryDefinition, "failed to get Resource Definition from dictionary definition");
- PropertyDefinition property = new PropertyDefinition();
- property.setRequired(true);
- ResourceAssignment resourceAssignment = new ResourceAssignment();
- resourceAssignment.setName(resourcedictionary.getName());
- resourceAssignment.setDictionaryName(resourcedictionary
- .getName());
- resourceAssignment.setVersion(0);
- resourceAssignment.setProperty(property);
- ResourceDictionaryUtils.populateSourceMapping(resourceAssignment, dictionaryDefinition);
- resourceAssignments.add(resourceAssignment);
- }
- }
- return resourceAssignments;
-
- }
-
-
- private boolean checkAssignmentsExists(List<ResourceAssignment> resourceAssignmentsWithDepencies, String resourceName) {
- return resourceAssignmentsWithDepencies.stream().anyMatch(names -> names.getName().equalsIgnoreCase(resourceName));
- }
-
-}
diff --git a/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/AutoResourceMappingService.kt b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/AutoResourceMappingService.kt
new file mode 100644
index 000000000..3ab9fee58
--- /dev/null
+++ b/ms/controllerblueprints/modules/service/src/main/kotlin/org/onap/ccsdk/cds/controllerblueprints/service/AutoResourceMappingService.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright © 2017-2018 AT&T Intellectual Property.
+ * Modifications Copyright © 2019 Huawei.
+ *
+ * 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.controllerblueprints.service
+
+import com.google.common.base.Preconditions
+import org.apache.commons.collections.CollectionUtils
+import org.apache.commons.lang3.StringUtils
+import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintException
+import org.onap.ccsdk.cds.controllerblueprints.core.data.PropertyDefinition
+import org.onap.ccsdk.cds.controllerblueprints.resource.dict.ResourceAssignment
+import org.onap.ccsdk.cds.controllerblueprints.resource.dict.utils.ResourceDictionaryUtils
+import org.onap.ccsdk.cds.controllerblueprints.service.domain.ResourceDictionary
+import org.onap.ccsdk.cds.controllerblueprints.service.model.AutoMapResponse
+import org.onap.ccsdk.cds.controllerblueprints.service.repository.ResourceDictionaryRepository
+import org.slf4j.LoggerFactory
+import org.springframework.stereotype.Service
+import java.util.*
+
+@Service
+open class AutoResourceMappingService(private val dataDictionaryRepository: ResourceDictionaryRepository) {
+
+ private val log = LoggerFactory.getLogger(AutoResourceMappingService::class.java)
+
+ @Throws(BluePrintException::class)
+ fun autoMap(resourceAssignments: MutableList<ResourceAssignment>):
+ AutoMapResponse {
+ val autoMapResponse = AutoMapResponse()
+ try {
+ if (CollectionUtils.isNotEmpty(resourceAssignments)) {
+ // Create the Dictionary definitions for the ResourceAssignment Names
+ val dictionaryMap = getDictionaryDefinitions(resourceAssignments)
+
+ for (resourceAssignment in resourceAssignments) {
+ if (StringUtils.isNotBlank(resourceAssignment.name)
+ && StringUtils.isBlank(resourceAssignment.dictionaryName)) {
+ populateDictionaryMapping(dictionaryMap, resourceAssignment)
+ log.info("Mapped Resource : {}", resourceAssignment)
+ }
+ }
+ }
+ val dictionaries = getDictionaryDefinitionsList(resourceAssignments)
+ val resourceAssignmentsFinal = getAllAutoMapResourceAssignments(resourceAssignments)
+ autoMapResponse.dataDictionaries = dictionaries
+ autoMapResponse.resourceAssignments = resourceAssignmentsFinal
+ } catch (e: Exception) {
+ log.error(String.format("Failed in auto process %s", e.message))
+ throw BluePrintException(e, e.message!!)
+ }
+
+ return autoMapResponse
+ }
+
+ private fun populateDictionaryMapping(dictionaryMap: Map<String, ResourceDictionary>, resourceAssignment: ResourceAssignment) {
+ val dbDataDictionary = dictionaryMap[resourceAssignment.name]
+ if (dbDataDictionary != null && dbDataDictionary.definition != null) {
+
+ val dictionaryDefinition = dbDataDictionary.definition
+
+ if (dictionaryDefinition != null && StringUtils.isNotBlank(dictionaryDefinition.name)
+ && StringUtils.isBlank(resourceAssignment.dictionaryName)) {
+
+ resourceAssignment.dictionaryName = dbDataDictionary.name
+ ResourceDictionaryUtils.populateSourceMapping(resourceAssignment, dictionaryDefinition)
+ }
+ }
+ }
+
+ private fun getDictionaryDefinitions(resourceAssignments: List<ResourceAssignment>): Map<String, ResourceDictionary> {
+ val dictionaryMap = HashMap<String, ResourceDictionary>()
+ val names = ArrayList<String>()
+ for (resourceAssignment in resourceAssignments) {
+ if (StringUtils.isNotBlank(resourceAssignment.name)) {
+ names.add(resourceAssignment.name)
+ }
+ }
+ if (CollectionUtils.isNotEmpty(names)) {
+
+ val dictionaries = dataDictionaryRepository.findByNameIn(names)
+ if (CollectionUtils.isNotEmpty(dictionaries)) {
+ for (dataDictionary in dictionaries) {
+ if (StringUtils.isNotBlank(dataDictionary.name)) {
+ dictionaryMap[dataDictionary.name] = dataDictionary
+ }
+ }
+ }
+ }
+ return dictionaryMap
+
+ }
+ private fun getDictionaryDefinitionsList(resourceAssignments: List<ResourceAssignment>): List<ResourceDictionary>? {
+ var dictionaries: List<ResourceDictionary>? = null
+ val names = ArrayList<String>()
+ for (resourceAssignment in resourceAssignments) {
+ if (StringUtils.isNotBlank(resourceAssignment.dictionaryName)) {
+
+ if (!names.contains(resourceAssignment.dictionaryName)) {
+ names.add(resourceAssignment.dictionaryName!!)
+ }
+
+ if (resourceAssignment.dependencies != null && !resourceAssignment.dependencies!!.isEmpty()) {
+ val dependencyNames = resourceAssignment.dependencies
+ for (dependencyName in dependencyNames!!) {
+ if (StringUtils.isNotBlank(dependencyName) && !names.contains(dependencyName)) {
+ names.add(dependencyName)
+ }
+ }
+ }
+ }
+ }
+ if (CollectionUtils.isNotEmpty(names)) {
+ dictionaries = dataDictionaryRepository.findByNameIn(names)
+ }
+ return dictionaries
+
+ }
+
+ private fun getAllAutoMapResourceAssignments(resourceAssignments: MutableList<ResourceAssignment>): List<ResourceAssignment> {
+ var dictionaries: List<ResourceDictionary>? = null
+ val names = ArrayList<String>()
+ for (resourceAssignment in resourceAssignments) {
+ if (StringUtils.isNotBlank(resourceAssignment.dictionaryName)) {
+ if (resourceAssignment.dependencies != null && !resourceAssignment.dependencies!!.isEmpty()) {
+ val dependencyNames = resourceAssignment.dependencies
+ for (dependencyName in dependencyNames!!) {
+ if (StringUtils.isNotBlank(dependencyName) && !names.contains(dependencyName)
+ && !checkAssignmentsExists(resourceAssignments, dependencyName)) {
+ names.add(dependencyName)
+ }
+ }
+ }
+ }
+ }
+
+ if (!names.isEmpty()) {
+ dictionaries = dataDictionaryRepository.findByNameIn(names)
+ }
+ if (dictionaries != null) {
+ for (rscDictionary in dictionaries) {
+ val dictionaryDefinition = rscDictionary.definition
+ Preconditions.checkNotNull(dictionaryDefinition, "failed to get Resource Definition from dictionary definition")
+ val property = PropertyDefinition()
+ property.required = true
+ val resourceAssignment = ResourceAssignment()
+ resourceAssignment.name = rscDictionary.name
+ resourceAssignment.dictionaryName = rscDictionary.name
+ resourceAssignment.version = 0
+ resourceAssignment.property = property
+ ResourceDictionaryUtils.populateSourceMapping(resourceAssignment, dictionaryDefinition)
+ resourceAssignments.add(resourceAssignment)
+ }
+ }
+ return resourceAssignments
+ }
+
+
+ private fun checkAssignmentsExists(resourceAssignmentsWithDepencies: List<ResourceAssignment>, resourceName: String): Boolean {
+ return resourceAssignmentsWithDepencies.stream().anyMatch { names -> names.name.equals(resourceName, ignoreCase = true) }
+ }
+} \ No newline at end of file