From 349643f7bec1b793e3427e97c79f0a123a20ca37 Mon Sep 17 00:00:00 2001 From: Alexis de Talhouët Date: Fri, 8 Mar 2019 13:41:18 -0500 Subject: Enhance / fix REST resource resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - provide sync web client - add support for VERB - add support for payload - fix URI resolution from input-key-mapping Change-Id: Id4f1973a15778ad4f2ab0f7e2a4b2c61054a234d Issue-ID: CCSDK-1131 Signed-off-by: Alexis de Talhouët --- .../resolution/ResourceSourceProperties.kt | 3 + .../DatabaseResourceAssignmentProcessor.kt | 10 ++- .../processor/RestResourceResolutionProcessor.kt | 52 +++++++++--- .../resolution/utils/ResourceAssignmentUtils.kt | 16 ++-- .../modules/commons/rest-lib/pom.xml | 8 +- .../rest/BluePrintRestLibConfiguration.kt | 1 + .../rest/BluePrintRestLibData.kt | 10 ++- .../rest/service/BasicAuthRestClientService.kt | 76 +++++------------ .../service/BluePrintRestLibPropertyService.kt | 51 ++++++----- .../rest/service/BlueprintWebClientService.kt | 73 ++++++++++++++-- .../rest/service/DME2ProxyRestClientService.kt | 24 +----- .../rest/service/SSLBasicAuthRestClientService.kt | 99 +++++++++------------- .../rest/service/TokenAuthRestClientService.kt | 38 +++++++++ .../rest/utils/WebClientUtils.kt | 25 ++---- .../rest/service/RestClientServiceTest.kt | 13 ++- 15 files changed, 282 insertions(+), 217 deletions(-) create mode 100644 ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/TokenAuthRestClientService.kt (limited to 'ms') diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/ResourceSourceProperties.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/ResourceSourceProperties.kt index 1c357446..25fc8c01 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/ResourceSourceProperties.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/ResourceSourceProperties.kt @@ -48,6 +48,9 @@ open class DatabaseResourceSource : ResourceSourceProperties() { } open class RestResourceSource : ResourceSourceProperties() { + lateinit var verb: String + @get:JsonProperty("payload") + var payload: String? = null lateinit var type: String @get:JsonProperty("endpoint-selector") var endpointSelector: String? = null diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/DatabaseResourceAssignmentProcessor.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/DatabaseResourceAssignmentProcessor.kt index 39be14c9..ca4d631b 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/DatabaseResourceAssignmentProcessor.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/DatabaseResourceAssignmentProcessor.kt @@ -22,7 +22,13 @@ import org.onap.ccsdk.apps.blueprintsprocessor.db.primary.DBLibGenericService import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.DatabaseResourceSource import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.PREFIX_RESOURCE_RESOLUTION_PROCESSOR import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.utils.ResourceAssignmentUtils -import org.onap.ccsdk.apps.controllerblueprints.core.* +import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintProcessorException +import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintTypes +import org.onap.ccsdk.apps.controllerblueprints.core.checkEqualsOrThrow +import org.onap.ccsdk.apps.controllerblueprints.core.checkNotEmpty +import org.onap.ccsdk.apps.controllerblueprints.core.checkNotEmptyOrThrow +import org.onap.ccsdk.apps.controllerblueprints.core.nullToEmpty +import org.onap.ccsdk.apps.controllerblueprints.core.returnNotEmptyOrThrow import org.onap.ccsdk.apps.controllerblueprints.core.utils.JacksonUtils import org.onap.ccsdk.apps.controllerblueprints.resource.dict.ResourceAssignment import org.onap.ccsdk.apps.controllerblueprints.resource.dict.ResourceDictionaryConstants @@ -91,7 +97,7 @@ open class DatabaseResourceAssignmentProcessor(private val dBLibGenericService: } private fun blueprintDBLibService(sourceProperties: DatabaseResourceSource): NamedParameterJdbcTemplate { - return if (checkNotEmpty(sourceProperties.endpointSelector!!)) { + return if (checkNotEmpty(sourceProperties.endpointSelector)) { val dbPropertiesJson = raRuntimeService.resolveDSLExpression(sourceProperties.endpointSelector!!) dBLibGenericService.remoteJdbcTemplate(dbPropertiesJson) } else { diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/RestResourceResolutionProcessor.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/RestResourceResolutionProcessor.kt index 37b4774a..9bb1ea21 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/RestResourceResolutionProcessor.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/processor/RestResourceResolutionProcessor.kt @@ -19,9 +19,8 @@ package org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.pr import com.fasterxml.jackson.databind.node.ArrayNode import com.fasterxml.jackson.databind.node.JsonNodeFactory -import com.fasterxml.jackson.databind.node.MissingNode -import com.fasterxml.jackson.databind.node.NullNode import com.fasterxml.jackson.databind.node.ObjectNode +import org.apache.commons.collections.MapUtils import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.ResourceResolutionConstants.PREFIX_RESOURCE_RESOLUTION_PROCESSOR import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.RestResourceSource import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.utils.ResourceAssignmentUtils @@ -41,6 +40,7 @@ import org.slf4j.LoggerFactory import org.springframework.beans.factory.config.ConfigurableBeanFactory import org.springframework.context.annotation.Scope import org.springframework.stereotype.Service +import java.util.* /** * RestResourceResolutionProcessor @@ -63,11 +63,11 @@ open class RestResourceResolutionProcessor(private val blueprintRestLibPropertyS validate(resourceAssignment) // Check if It has Input - val value = raRuntimeService.getInputValue(resourceAssignment.name) - if (value !is MissingNode && value !is NullNode) { - logger.info("primary-db source template key (${resourceAssignment.name}) found from input and value is ($value)") + try { + val value = raRuntimeService.getInputValue(resourceAssignment.name) + logger.info("rest source template key (${resourceAssignment.name}) found from input and value is ($value)") ResourceAssignmentUtils.setResourceDataValue(resourceAssignment, raRuntimeService, value) - } else { + } catch (e: BluePrintProcessorException) { val dName = resourceAssignment.dictionaryName val dSource = resourceAssignment.dictionarySource val resourceDefinition = resourceDictionaries[dName] @@ -78,17 +78,22 @@ open class RestResourceResolutionProcessor(private val blueprintRestLibPropertyS checkNotNull(resourceSource.properties) { "failed to get source properties for $dName " } val sourceProperties = JacksonUtils.getInstanceFromMap(resourceSourceProperties, RestResourceSource::class.java) - - val urlPath = - checkNotNull(sourceProperties.urlPath) { "failed to get request urlPath for $dName under $dSource properties" } val path = nullToEmpty(sourceProperties.path) val inputKeyMapping = checkNotNull(sourceProperties.inputKeyMapping) { "failed to get input-key-mappings for $dName under $dSource properties" } + val resolvedInputKeyMapping = populateInputKeyMappingVariables(inputKeyMapping) + + // Resolving content Variables + val payload = resolveFromInputKeyMapping(nullToEmpty(sourceProperties.payload), resolvedInputKeyMapping) + val urlPath = + resolveFromInputKeyMapping(checkNotNull(sourceProperties.urlPath), resolvedInputKeyMapping) + val verb = resolveFromInputKeyMapping(nullToEmpty(sourceProperties.verb), resolvedInputKeyMapping) logger.info("$dSource dictionary information : ($urlPath), ($inputKeyMapping), (${sourceProperties.outputKeyMapping})") // Get the Rest Client Service val restClientService = blueprintWebClientService(resourceAssignment, sourceProperties) - val response = restClientService.getResource(urlPath, String::class.java) + + val response = restClientService.exchangeResource(verb, urlPath, payload) if (response.isBlank()) { logger.warn("Failed to get $dSource result for dictionary name ($dName) using urlPath ($urlPath)") } else { @@ -104,8 +109,8 @@ open class RestResourceResolutionProcessor(private val blueprintRestLibPropertyS } } - open fun blueprintWebClientService(resourceAssignment: ResourceAssignment, - restResourceSource: RestResourceSource): BlueprintWebClientService { + private fun blueprintWebClientService(resourceAssignment: ResourceAssignment, + restResourceSource: RestResourceSource): BlueprintWebClientService { return if (checkNotEmpty(restResourceSource.endpointSelector)) { val restPropertiesJson = raRuntimeService.resolveDSLExpression(restResourceSource.endpointSelector!!) blueprintRestLibPropertyService.blueprintWebClientService(restPropertiesJson) @@ -181,6 +186,29 @@ open class RestResourceResolutionProcessor(private val blueprintRestLibPropertyS } } + private fun populateInputKeyMappingVariables(inputKeyMapping: Map): Map { + val resolvedInputKeyMapping = HashMap() + if (MapUtils.isNotEmpty(inputKeyMapping)) { + for ((key, value) in inputKeyMapping) { + val expressionValue = raRuntimeService.getResolutionStore(value).asText() + logger.trace("Reference dictionary key ({}), value ({})", key, expressionValue) + resolvedInputKeyMapping[key] = expressionValue + } + } + return resolvedInputKeyMapping + } + + private fun resolveFromInputKeyMapping(valueToResolve: String, keyMapping: Map): String { + if (valueToResolve.isEmpty() || !valueToResolve.contains("$")) { + return valueToResolve + } + var res = valueToResolve + for (entry in keyMapping.entries) { + res = res.replace(("\\$" + entry.key).toRegex(), entry.value.toString()) + } + return res + } + @Throws(BluePrintProcessorException::class) private fun validate(resourceAssignment: ResourceAssignment) { checkNotEmptyOrThrow(resourceAssignment.name, "resource assignment template key is not defined") diff --git a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtils.kt b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtils.kt index b5b126ab..d55ccacb 100644 --- a/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtils.kt +++ b/ms/blueprintsprocessor/functions/resource-resolution/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/functions/resource/resolution/utils/ResourceAssignmentUtils.kt @@ -24,7 +24,13 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.NullNode import com.fasterxml.jackson.databind.node.ObjectNode import org.onap.ccsdk.apps.blueprintsprocessor.functions.resource.resolution.ResourceAssignmentRuntimeService -import org.onap.ccsdk.apps.controllerblueprints.core.* +import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintConstants +import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintProcessorException +import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintTypes +import org.onap.ccsdk.apps.controllerblueprints.core.checkNotEmpty +import org.onap.ccsdk.apps.controllerblueprints.core.checkNotEmptyOrThrow +import org.onap.ccsdk.apps.controllerblueprints.core.nullToEmpty +import org.onap.ccsdk.apps.controllerblueprints.core.returnNotEmptyOrThrow import org.onap.ccsdk.apps.controllerblueprints.core.service.BluePrintRuntimeService import org.onap.ccsdk.apps.controllerblueprints.core.utils.JacksonUtils import org.onap.ccsdk.apps.controllerblueprints.resource.dict.ResourceAssignment @@ -36,7 +42,6 @@ class ResourceAssignmentUtils { private val logger: EELFLogger = EELFManager.getInstance().getLogger(ResourceAssignmentUtils::class.toString()) // TODO("Modify Value type from Any to JsonNode") - @Synchronized @Throws(BluePrintProcessorException::class) fun setResourceDataValue(resourceAssignment: ResourceAssignment, raRuntimeService: ResourceAssignmentRuntimeService, value: Any?) { @@ -85,7 +90,6 @@ class ResourceAssignmentUtils { } - @Synchronized fun setFailedResourceDataValue(resourceAssignment: ResourceAssignment, message: String?) { if (checkNotEmpty(resourceAssignment.name)) { resourceAssignment.updatedDate = Date() @@ -95,7 +99,6 @@ class ResourceAssignmentUtils { } } - @Synchronized @Throws(BluePrintProcessorException::class) fun assertTemplateKeyValueNotNull(resourceAssignment: ResourceAssignment) { val resourceProp = checkNotNull(resourceAssignment.property) { "Failed to populate mandatory resource resource mapping $resourceAssignment" } @@ -105,7 +108,6 @@ class ResourceAssignmentUtils { } } - @Synchronized @Throws(BluePrintProcessorException::class) fun generateResourceDataForAssignments(assignments: List): String { val result: String @@ -139,10 +141,6 @@ class ResourceAssignmentUtils { return resourceAssignmentRuntimeService } - /* - * Populate the Field property type for the Data type - */ - @Synchronized @Throws(BluePrintProcessorException::class) fun getPropertyType(raRuntimeService: ResourceAssignmentRuntimeService, dataTypeName: String, propertyName: String): String { lateinit var type: String diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/pom.xml b/ms/blueprintsprocessor/modules/commons/rest-lib/pom.xml index f1d97896..009493a6 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/pom.xml +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/pom.xml @@ -15,7 +15,8 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + 4.0.0 org.onap.ccsdk.apps.blueprintsprocessor @@ -36,6 +37,11 @@ org.onap.ccsdk.apps.blueprintsprocessor core + + org.apache.httpcomponents + httpclient + ${apache.httpcomponents.client.version} + diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/BluePrintRestLibConfiguration.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/BluePrintRestLibConfiguration.kt index f63e39f3..7af91c6f 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/BluePrintRestLibConfiguration.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/BluePrintRestLibConfiguration.kt @@ -30,6 +30,7 @@ open class BluePrintRestLibConfiguration class RestLibConstants { companion object { const val SERVICE_BLUEPRINT_REST_LIB_PROPERTY = "blueprint-rest-lib-property-service" + const val TYPE_TOKEN_AUTH = "token-auth" const val TYPE_BASIC_AUTH = "basic-auth" const val TYPE_SSL_BASIC_AUTH = "ssl-basic-auth" const val TYPE_DME2_PROXY = "dme2-proxy" diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/BluePrintRestLibData.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/BluePrintRestLibData.kt index 41e45458..dd4d9f7d 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/BluePrintRestLibData.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/BluePrintRestLibData.kt @@ -19,19 +19,23 @@ package org.onap.ccsdk.apps.blueprintsprocessor.rest open class RestClientProperties { lateinit var type: String lateinit var url: String - lateinit var userId: String } open class BasicAuthRestClientProperties : RestClientProperties() { + lateinit var password: String + lateinit var username: String +} + +open class TokenAuthRestClientProperties : RestClientProperties() { var token: String? = null } open class SSLBasicAuthRestClientProperties : RestClientProperties() { lateinit var keyStoreInstance: String // JKS, PKCS12 lateinit var sslTrust: String - lateinit var sslTrustPasswd: String + lateinit var sslTrustPassword: String lateinit var sslKey: String - lateinit var sslKeyPasswd: String + lateinit var sslKeyPassword: String } open class DME2RestClientProperties : RestClientProperties() { diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BasicAuthRestClientService.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BasicAuthRestClientService.kt index 130706d7..0502f67c 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BasicAuthRestClientService.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BasicAuthRestClientService.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2018 AT&T Intellectual Property. + * Copyright © 2017-2019 AT&T, Bell Canada * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,67 +16,31 @@ package org.onap.ccsdk.apps.blueprintsprocessor.rest.service +import org.apache.http.message.BasicHeader import org.onap.ccsdk.apps.blueprintsprocessor.rest.BasicAuthRestClientProperties -import org.onap.ccsdk.apps.blueprintsprocessor.rest.utils.WebClientUtils import org.springframework.http.HttpHeaders import org.springframework.http.MediaType -import org.springframework.web.reactive.function.BodyInserters -import org.springframework.web.reactive.function.client.ExchangeFilterFunctions -import org.springframework.web.reactive.function.client.WebClient - - -class BasicAuthRestClientService(private val restClientProperties: BasicAuthRestClientProperties) : BlueprintWebClientService { - - private var webClient: WebClient? = null - - override fun webClient(): WebClient { - if (webClient == null) { - webClient = WebClient.builder() - .baseUrl(restClientProperties.url) - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) - .filter(ExchangeFilterFunctions - .basicAuthentication(restClientProperties.userId, restClientProperties.token)) - .filter(WebClientUtils.logRequest()) - .filter(WebClientUtils.logResponse()) - .build() - } - return webClient!! - } - - override fun getResource(path: String, responseType: Class): T { - return getResource(path, null, responseType) - } - - override fun getResource(path: String, headers: Map?, responseType: Class): T { - return webClient().get() - .uri(path) - .headers { httpHeaders -> - headers?.forEach { - httpHeaders.set(it.key, it.value) - } - } - .retrieve() - .bodyToMono(responseType).block()!! - } - - override fun postResource(path: String, request: Any, responseType: Class): T { - return postResource(path, null, request, responseType) +import java.nio.charset.Charset +import java.util.* + +class BasicAuthRestClientService(private val restClientProperties: BasicAuthRestClientProperties) : + BlueprintWebClientService { + + override fun headers(): Array { + val encodedCredentials = setBasicAuth(restClientProperties.username, restClientProperties.password) + val params = arrayListOf() + params.add(BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)) + params.add(BasicHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)) + params.add(BasicHeader(HttpHeaders.AUTHORIZATION, "Basic $encodedCredentials")) + return params.toTypedArray() } - override fun postResource(path: String, headers: Map?, request: Any, responseType: Class): T { - return webClient().post() - .uri(path) - .headers { httpHeaders -> - headers?.forEach { - httpHeaders.set(it.key, it.value) - } - } - .body(BodyInserters.fromObject(request)) - .retrieve().bodyToMono(responseType).block()!! + override fun host(uri: String): String { + return restClientProperties.url + uri } - override fun exchangeResource(methodType: String, path: String, request: Any, responseType: Class): T { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + private fun setBasicAuth(username: String, password: String): String { + val credentialsString = "$username:$password" + return Base64.getEncoder().encodeToString(credentialsString.toByteArray(Charset.defaultCharset())) } } \ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BluePrintRestLibPropertyService.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BluePrintRestLibPropertyService.kt index 705caa2e..3888bb72 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BluePrintRestLibPropertyService.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BluePrintRestLibPropertyService.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2018 AT&T Intellectual Property. + * Copyright © 2017-2019 AT&T, Bell Canada * Modifications Copyright © 2019 IBM. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,13 @@ package org.onap.ccsdk.apps.blueprintsprocessor.rest.service import com.fasterxml.jackson.databind.JsonNode import org.onap.ccsdk.apps.blueprintsprocessor.core.BluePrintProperties -import org.onap.ccsdk.apps.blueprintsprocessor.rest.* +import org.onap.ccsdk.apps.blueprintsprocessor.rest.BasicAuthRestClientProperties +import org.onap.ccsdk.apps.blueprintsprocessor.rest.DME2RestClientProperties +import org.onap.ccsdk.apps.blueprintsprocessor.rest.PolicyManagerRestClientProperties +import org.onap.ccsdk.apps.blueprintsprocessor.rest.RestClientProperties +import org.onap.ccsdk.apps.blueprintsprocessor.rest.RestLibConstants +import org.onap.ccsdk.apps.blueprintsprocessor.rest.SSLBasicAuthRestClientProperties +import org.onap.ccsdk.apps.blueprintsprocessor.rest.TokenAuthRestClientProperties import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintProcessorException import org.onap.ccsdk.apps.controllerblueprints.core.utils.JacksonUtils import org.springframework.stereotype.Service @@ -27,6 +33,17 @@ import org.springframework.stereotype.Service @Service(RestLibConstants.SERVICE_BLUEPRINT_REST_LIB_PROPERTY) open class BluePrintRestLibPropertyService(private var bluePrintProperties: BluePrintProperties) { + fun blueprintWebClientService(jsonNode: JsonNode): BlueprintWebClientService { + val restClientProperties = restClientProperties(jsonNode) + return blueprintWebClientService(restClientProperties) + } + + fun blueprintWebClientService(selector: String): BlueprintWebClientService { + val prefix = "blueprintsprocessor.restclient.$selector" + val restClientProperties = restClientProperties(prefix) + return blueprintWebClientService(restClientProperties) + } + fun restClientProperties(prefix: String): RestClientProperties { val type = bluePrintProperties.propertyBeanType("$prefix.type", String::class.java) return when (type) { @@ -48,9 +65,12 @@ open class BluePrintRestLibPropertyService(private var bluePrintProperties: Blue } } - fun restClientProperties(jsonNode: JsonNode): RestClientProperties { + private fun restClientProperties(jsonNode: JsonNode): RestClientProperties { val type = jsonNode.get("type").textValue() return when (type) { + RestLibConstants.TYPE_TOKEN_AUTH -> { + JacksonUtils.readValue(jsonNode, TokenAuthRestClientProperties::class.java)!! + } RestLibConstants.TYPE_BASIC_AUTH -> { JacksonUtils.readValue(jsonNode, BasicAuthRestClientProperties::class.java)!! } @@ -69,20 +89,11 @@ open class BluePrintRestLibPropertyService(private var bluePrintProperties: Blue } } - - fun blueprintWebClientService(selector: String): BlueprintWebClientService { - val prefix = "blueprintsprocessor.restclient.$selector" - val restClientProperties = restClientProperties(prefix) - return blueprintWebClientService(restClientProperties) - } - - fun blueprintWebClientService(jsonNode: JsonNode): BlueprintWebClientService { - val restClientProperties = restClientProperties(jsonNode) - return blueprintWebClientService(restClientProperties) - } - - fun blueprintWebClientService(restClientProperties: RestClientProperties): BlueprintWebClientService { + private fun blueprintWebClientService(restClientProperties: RestClientProperties): BlueprintWebClientService { when (restClientProperties) { + is TokenAuthRestClientProperties -> { + return TokenAuthRestClientService(restClientProperties) + } is BasicAuthRestClientProperties -> { return BasicAuthRestClientService(restClientProperties) } @@ -98,19 +109,19 @@ open class BluePrintRestLibPropertyService(private var bluePrintProperties: Blue } } - fun basicAuthRestClientProperties(prefix: String): BasicAuthRestClientProperties { + private fun basicAuthRestClientProperties(prefix: String): BasicAuthRestClientProperties { return bluePrintProperties.propertyBeanType(prefix, BasicAuthRestClientProperties::class.java) } - fun sslBasicAuthRestClientProperties(prefix: String): SSLBasicAuthRestClientProperties { + private fun sslBasicAuthRestClientProperties(prefix: String): SSLBasicAuthRestClientProperties { return bluePrintProperties.propertyBeanType(prefix, SSLBasicAuthRestClientProperties::class.java) } - fun dme2ProxyClientProperties(prefix: String): DME2RestClientProperties { + private fun dme2ProxyClientProperties(prefix: String): DME2RestClientProperties { return bluePrintProperties.propertyBeanType(prefix, DME2RestClientProperties::class.java) } - fun policyManagerRestClientProperties(prefix: String): PolicyManagerRestClientProperties { + private fun policyManagerRestClientProperties(prefix: String): PolicyManagerRestClientProperties { return bluePrintProperties.propertyBeanType(prefix, PolicyManagerRestClientProperties::class.java) } } diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BlueprintWebClientService.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BlueprintWebClientService.kt index 8106c077..9c2caad7 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BlueprintWebClientService.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BlueprintWebClientService.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2018 AT&T Intellectual Property. + * Copyright © 2017-2019 AT&T, Bell Canada * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,21 +16,76 @@ package org.onap.ccsdk.apps.blueprintsprocessor.rest.service -import org.springframework.web.reactive.function.client.WebClient +import org.apache.commons.io.IOUtils +import org.apache.http.client.methods.HttpDelete +import org.apache.http.client.methods.HttpGet +import org.apache.http.client.methods.HttpPost +import org.apache.http.client.methods.HttpPut +import org.apache.http.entity.StringEntity +import org.apache.http.impl.client.CloseableHttpClient +import org.apache.http.impl.client.HttpClients +import org.apache.http.message.BasicHeader +import org.onap.ccsdk.apps.blueprintsprocessor.rest.utils.WebClientUtils +import org.onap.ccsdk.apps.controllerblueprints.core.BluePrintProcessorException +import org.springframework.http.HttpMethod +import java.nio.charset.Charset interface BlueprintWebClientService { - fun webClient(): WebClient + fun headers(): Array - fun getResource(path: String, responseType: Class): T + fun host(uri: String): String - fun getResource(path: String, headers: Map?, responseType: Class): T + fun httpClient(): CloseableHttpClient { + return HttpClients.custom() + .addInterceptorFirst(WebClientUtils.logRequest()) + .addInterceptorLast(WebClientUtils.logResponse()) + .build() + } - fun postResource(path: String, request: Any, responseType: Class): T + fun exchangeResource(methodType: String, path: String, request: String): String { + return when (HttpMethod.resolve(methodType)) { + HttpMethod.DELETE -> delete(path) + HttpMethod.GET -> get(path) + HttpMethod.POST -> post(path, request) + HttpMethod.PUT -> put(path, request) + else -> throw BluePrintProcessorException("Unsupported methodType($methodType)") + } + } - fun postResource(path: String, headers: Map?, request: Any, responseType: Class): T + fun delete(path: String): String { + val httpDelete = HttpDelete(host(path)) + httpDelete.setHeaders(headers()) + httpClient().execute(httpDelete).entity.content.use { + return IOUtils.toString(it, Charset.defaultCharset()) + } + } - fun exchangeResource(methodType: String, path: String, request: Any, responseType: Class): T + fun get(path: String): String { + val httpGet = HttpGet(host(path)) + httpGet.setHeaders(headers()) + httpClient().execute(httpGet).entity.content.use { + return IOUtils.toString(it, Charset.defaultCharset()) + } + } -} + fun post(path: String, request: String): String { + val httpPost = HttpPost(host(path)) + val entity = StringEntity(request) + httpPost.entity = entity + httpPost.setHeaders(headers()) + httpClient().execute(httpPost).entity.content.use { + return IOUtils.toString(it, Charset.defaultCharset()) + } + } + fun put(path: String, request: String): String { + val httpPut = HttpPut(host(path)) + val entity = StringEntity(request) + httpPut.entity = entity + httpPut.setHeaders(headers()) + httpClient().execute(httpPut).entity.content.use { + return IOUtils.toString(it, Charset.defaultCharset()) + } + } +} \ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/DME2ProxyRestClientService.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/DME2ProxyRestClientService.kt index 27dbe6f8..2b2578a3 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/DME2ProxyRestClientService.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/DME2ProxyRestClientService.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2018 AT&T Intellectual Property. + * Copyright © 2017-2019 AT&T, Bell Canada * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,31 +16,15 @@ package org.onap.ccsdk.apps.blueprintsprocessor.rest.service +import org.apache.http.message.BasicHeader import org.onap.ccsdk.apps.blueprintsprocessor.rest.RestClientProperties -import org.springframework.web.reactive.function.client.WebClient class DME2ProxyRestClientService(restClientProperties: RestClientProperties) : BlueprintWebClientService { - override fun webClient(): WebClient { + override fun headers(): Array { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } - override fun getResource(path: String, responseType: Class): T { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getResource(path: String, headers: Map?, responseType: Class): T { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun postResource(path: String, request: Any, responseType: Class): T { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun postResource(path: String, headers: Map?, request: Any, responseType: Class): T { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun exchangeResource(methodType: String, path: String, request: Any, responseType: Class): T { + override fun host(uri: String): String { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } } \ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/SSLBasicAuthRestClientService.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/SSLBasicAuthRestClientService.kt index 71727b93..dc2993d9 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/SSLBasicAuthRestClientService.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/SSLBasicAuthRestClientService.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2018 AT&T Intellectual Property. + * Copyright © 2017-2019 AT&T, Bell Canada * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,82 +16,59 @@ package org.onap.ccsdk.apps.blueprintsprocessor.rest.service -import io.netty.handler.ssl.SslContextBuilder +import org.apache.http.conn.ssl.SSLConnectionSocketFactory +import org.apache.http.impl.client.CloseableHttpClient +import org.apache.http.impl.client.HttpClients +import org.apache.http.message.BasicHeader +import org.apache.http.ssl.SSLContextBuilder import org.onap.ccsdk.apps.blueprintsprocessor.rest.SSLBasicAuthRestClientProperties import org.onap.ccsdk.apps.blueprintsprocessor.rest.utils.WebClientUtils import org.springframework.http.HttpHeaders import org.springframework.http.MediaType -import org.springframework.http.client.reactive.ReactorClientHttpConnector -import org.springframework.web.reactive.function.BodyInserters -import org.springframework.web.reactive.function.client.WebClient -import reactor.netty.http.client.HttpClient import java.io.File +import java.io.FileInputStream import java.security.KeyStore import java.security.cert.X509Certificate +class SSLBasicAuthRestClientService(private val restClientProperties: SSLBasicAuthRestClientProperties) : + BlueprintWebClientService { -class SSLBasicAuthRestClientService(private val restClientProperties: SSLBasicAuthRestClientProperties) : BlueprintWebClientService { + override fun headers(): Array { + val params = arrayListOf() + params.add(BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)) + params.add(BasicHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)) + return params.toTypedArray() + } - override fun webClient(): WebClient { + override fun host(uri: String): String { + return restClientProperties.url + uri + } - // Load the Keystore Information - val ketInputStream = File(restClientProperties.sslKey).inputStream() - val ks = KeyStore.getInstance(restClientProperties.keyStoreInstance) - ks.load(ketInputStream, restClientProperties.sslKeyPasswd.toCharArray()) + override fun httpClient(): CloseableHttpClient { - // Manage Trust Store - val trustCertCollection = ks.aliases().toList().map { alias -> - ks.getCertificate(alias) as X509Certificate - }.toTypedArray() - val sslContext = SslContextBuilder - .forClient() - .trustManager(*trustCertCollection) - .build() + val keystoreInstance = restClientProperties.keyStoreInstance + val sslKey = restClientProperties.sslKey + val sslKeyPwd = restClientProperties.sslKeyPassword + val sslTrust = restClientProperties.sslTrust + val sslTrustPwd = restClientProperties.sslTrustPassword - // Create Http Client - val httpClient = HttpClient.create().secure { t -> t.sslContext(sslContext) } + val acceptingTrustStrategy = { chain: Array, authType: String -> true } - return WebClient.builder() - .baseUrl(restClientProperties.url) - .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) - .filter(WebClientUtils.logRequest()) - .clientConnector(ReactorClientHttpConnector(httpClient)).build() - } + FileInputStream(sslKey).use { keyInput -> + val keyStore = KeyStore.getInstance(keystoreInstance) + keyStore.load(keyInput, sslKeyPwd.toCharArray()) - override fun getResource(path: String, responseType: Class): T { - return getResource(path, null, responseType) - } + val sslContext = + SSLContextBuilder.create() + .loadKeyMaterial(keyStore, sslKeyPwd.toCharArray()) + .loadTrustMaterial(File(sslTrust), sslTrustPwd.toCharArray(), acceptingTrustStrategy).build() - override fun getResource(path: String, headers: Map?, responseType: Class): T { - return webClient().get() - .uri(path) - .headers { httpHeaders -> - headers?.forEach { - httpHeaders.set(it.key, it.value) - } - } - .retrieve() - .bodyToMono(responseType).block()!! - } - - override fun postResource(path: String, request: Any, responseType: Class): T { - return postResource(path, null, request, responseType) - } - - override fun postResource(path: String, headers: Map?, request: Any, responseType: Class): T { - return webClient().post() - .uri(path) - .headers { httpHeaders -> - headers?.forEach { - httpHeaders.set(it.key, it.value) - } - } - .body(BodyInserters.fromObject(request)) - .retrieve().bodyToMono(responseType).block()!! - } + val csf = SSLConnectionSocketFactory(sslContext!!) - override fun exchangeResource(methodType: String, path: String, request: Any, responseType: Class): T { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + return HttpClients.custom() + .addInterceptorFirst(WebClientUtils.logRequest()) + .addInterceptorLast(WebClientUtils.logResponse()) + .setSSLSocketFactory(csf).build() + } } } \ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/TokenAuthRestClientService.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/TokenAuthRestClientService.kt new file mode 100644 index 00000000..6e90957d --- /dev/null +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/TokenAuthRestClientService.kt @@ -0,0 +1,38 @@ +/* + * Copyright © 2019 Bell Canada + * + * 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.apps.blueprintsprocessor.rest.service + +import org.apache.http.message.BasicHeader +import org.onap.ccsdk.apps.blueprintsprocessor.rest.TokenAuthRestClientProperties +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType + +class TokenAuthRestClientService(private val restClientProperties: TokenAuthRestClientProperties) : + BlueprintWebClientService { + + override fun headers(): Array { + val params = arrayListOf() + params.add(BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)) + params.add(BasicHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)) + params.add(BasicHeader(HttpHeaders.AUTHORIZATION, restClientProperties.token)) + return params.toTypedArray() + } + + override fun host(uri: String): String { + return restClientProperties.url + uri + } +} diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/utils/WebClientUtils.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/utils/WebClientUtils.kt index 40d6ba63..d6167a87 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/utils/WebClientUtils.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/utils/WebClientUtils.kt @@ -16,29 +16,20 @@ package org.onap.ccsdk.apps.blueprintsprocessor.rest.utils +import org.apache.http.HttpRequestInterceptor +import org.apache.http.HttpResponseInterceptor +import org.slf4j.Logger import org.slf4j.LoggerFactory -import org.springframework.web.reactive.function.client.ExchangeFilterFunction -import reactor.core.publisher.Mono - class WebClientUtils { companion object { - val log = LoggerFactory.getLogger(WebClientUtils::class.java)!! - - fun logRequest(): ExchangeFilterFunction { + val log: Logger = LoggerFactory.getLogger(WebClientUtils::class.java) - return ExchangeFilterFunction.ofRequestProcessor { clientRequest -> - log.info("Rest request method(${clientRequest.method()}), url(${clientRequest.url()})") - Mono.just(clientRequest) - } - } + fun logRequest(): HttpRequestInterceptor = + HttpRequestInterceptor { request, _ -> log.info("Rest request method(${request?.requestLine?.method}), url(${request?.requestLine?.uri})") } - fun logResponse(): ExchangeFilterFunction { - return ExchangeFilterFunction.ofResponseProcessor { clientResponse -> - log.info("Response status(${clientResponse.statusCode()})") - Mono.just(clientResponse) - } - } + fun logResponse(): HttpResponseInterceptor = + HttpResponseInterceptor { response, _ -> log.info("Response status(${response.statusLine.statusCode})") } } } \ No newline at end of file diff --git a/ms/blueprintsprocessor/modules/commons/rest-lib/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/RestClientServiceTest.kt b/ms/blueprintsprocessor/modules/commons/rest-lib/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/RestClientServiceTest.kt index 25821966..4fa82df2 100644 --- a/ms/blueprintsprocessor/modules/commons/rest-lib/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/RestClientServiceTest.kt +++ b/ms/blueprintsprocessor/modules/commons/rest-lib/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/RestClientServiceTest.kt @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.HttpMethod import org.springframework.test.context.ContextConfiguration import org.springframework.test.context.TestPropertySource import org.springframework.test.context.junit4.SpringRunner @@ -39,11 +40,10 @@ import kotlin.test.assertNotNull @ContextConfiguration(classes = [BluePrintRestLibConfiguration::class, BlueprintPropertyConfiguration::class, SampleController::class, BluePrintProperties::class, BluePrintProperties::class]) @TestPropertySource(properties = -["server.port=9111", - "blueprintsprocessor.restclient.sample.type=basic-auth", - "blueprintsprocessor.restclient.sample.url=http://127.0.0.1:9111", - "blueprintsprocessor.restclient.sample.userId=sampleuser", - "blueprintsprocessor.restclient.sample.token=sampletoken"]) +["blueprintsprocessor.restclient.sample.type=basic-auth", + "blueprintsprocessor.restclient.sample.url=http://127.0.0.1:8080", + "blueprintsprocessor.restclient.sample.username=sampleuser", + "blueprintsprocessor.restclient.sample.password=sampletoken"]) class RestClientServiceTest { @Autowired @@ -51,11 +51,10 @@ class RestClientServiceTest { @Test fun testBaseAuth() { - val restClientService = bluePrintRestLibPropertyService.blueprintWebClientService("sample") val headers = mutableMapOf() headers["X-Transaction-Id"] = "1234" - val response = restClientService.getResource("/sample/name", headers, String::class.java) + val response = restClientService.exchangeResource(HttpMethod.GET.name, "/sample/name", "") assertNotNull(response, "failed to get response") } -- cgit 1.2.3-korg