Adding custom headers capability to REST client
authorottero <rodrigo.ottero@est.tech>
Sun, 17 Mar 2019 19:38:32 +0000 (19:38 +0000)
committerottero <rodrigo.ottero@est.tech>
Sun, 17 Mar 2019 19:38:32 +0000 (19:38 +0000)
For YANG PATCH requests to ODL to work, they need to have a Content-
type header of application/yang.patch+json and should not have Accept
as application/json

Current REST client inserts a default header to the requests with this
content:

  Content-Type: application/json
  Accept: application/json

The solution was to add the possibility of sending custom headers alon-
gside the other parameters.

Change-Id: I2cf0cd2ef7b87f4f5a246d427dffafe266cb33f7
Issue-ID: CCSDK-926
Signed-off-by: ottero <rodrigo.ottero@est.tech>
ms/blueprintsprocessor/application/src/main/resources/application-dev.properties
ms/blueprintsprocessor/application/src/main/resources/application.properties
ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BasicAuthRestClientService.kt
ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/BlueprintWebClientService.kt
ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/DME2ProxyRestClientService.kt
ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/SSLBasicAuthRestClientService.kt
ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/TokenAuthRestClientService.kt
ms/blueprintsprocessor/modules/commons/rest-lib/src/main/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/utils/WebClientUtils.kt
ms/blueprintsprocessor/modules/commons/rest-lib/src/test/kotlin/org/onap/ccsdk/apps/blueprintsprocessor/rest/service/RestClientServiceTest.kt

index 380eb20..e64dee2 100755 (executable)
@@ -44,5 +44,5 @@ blueprints.processor.functions.python.executor.modulePaths=./../../../components
 blueprintsprocessor.restconfEnabled=true\r
 blueprintsprocessor.restclient.sdncodl.type=basic-auth\r
 blueprintsprocessor.restclient.sdncodl.url=http://localhost:8282/\r
-blueprintsprocessor.restclient.sdncodl.userId=admin\r
-blueprintsprocessor.restclient.sdncodl.token=Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
\ No newline at end of file
+blueprintsprocessor.restclient.sdncodl.username=admin\r
+blueprintsprocessor.restclient.sdncodl.password=Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
\ No newline at end of file
index 8cafceb..3b97e67 100755 (executable)
@@ -47,4 +47,5 @@ security.user.name: ccsdkapps
 blueprintsprocessor.restconfEnabled=true
 blueprintsprocessor.restclient.sdncodl.type=basic-auth
 blueprintsprocessor.restclient.sdncodl.url=http://sdnc:8282/
-blueprintsprocessor.restclient.sdncodl.userId=admin
+blueprintsprocessor.restclient.sdncodl.username=admin
+blueprintsprocessor.restclient.sdncodl.password=Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
index 0502f67..98a4fd5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2017-2019 AT&T, Bell Canada
+ * Copyright © 2017-2019 AT&T, Bell Canada, Nordix Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,6 +12,8 @@
  * 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
  */
 
 package org.onap.ccsdk.apps.blueprintsprocessor.rest.service
@@ -26,21 +28,31 @@ import java.util.*
 class BasicAuthRestClientService(private val restClientProperties: BasicAuthRestClientProperties) :
     BlueprintWebClientService {
 
-    override fun headers(): Array<BasicHeader> {
+    override fun defaultHeaders(): Map<String, String> {
         val encodedCredentials = setBasicAuth(restClientProperties.username, restClientProperties.password)
-        val params = arrayListOf<BasicHeader>()
-        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()
+        return mapOf(
+                HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
+                HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE,
+                HttpHeaders.AUTHORIZATION to "Basic $encodedCredentials")
     }
 
     override fun host(uri: String): String {
         return restClientProperties.url + uri
     }
 
+    override fun convertToBasicHeaders(headers: Map<String, String>): Array<BasicHeader> {
+        val customHeaders: MutableMap<String, String> = headers.toMutableMap()
+        if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
+            val encodedCredentials = setBasicAuth(restClientProperties.username, restClientProperties.password)
+            customHeaders[HttpHeaders.AUTHORIZATION] = "Basic $encodedCredentials"
+        }
+        return super.convertToBasicHeaders(customHeaders)
+    }
+
     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
index 9c2caad..0629909 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2017-2019 AT&T, Bell Canada
+ * Copyright © 2017-2019 AT&T, Bell Canada, Nordix Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * 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
  */
 
 package org.onap.ccsdk.apps.blueprintsprocessor.rest.service
 
 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.client.methods.*
 import org.apache.http.entity.StringEntity
 import org.apache.http.impl.client.CloseableHttpClient
 import org.apache.http.impl.client.HttpClients
@@ -32,7 +31,7 @@ import java.nio.charset.Charset
 
 interface BlueprintWebClientService {
 
-    fun headers(): Array<BasicHeader>
+    fun defaultHeaders(): Map<String, String>
 
     fun host(uri: String): String
 
@@ -44,48 +43,73 @@ interface BlueprintWebClientService {
     }
 
     fun exchangeResource(methodType: String, path: String, request: String): String {
+        return this.exchangeResource(methodType, path, request, defaultHeaders())
+    }
+
+    fun exchangeResource(methodType: String, path: String, request: String, headers: Map<String, String>): String {
+        val convertedHeaders: Array<BasicHeader> = convertToBasicHeaders(headers)
         return when (HttpMethod.resolve(methodType)) {
-            HttpMethod.DELETE -> delete(path)
-            HttpMethod.GET -> get(path)
-            HttpMethod.POST -> post(path, request)
-            HttpMethod.PUT -> put(path, request)
+            HttpMethod.DELETE -> delete(path, convertedHeaders)
+            HttpMethod.GET -> get(path, convertedHeaders)
+            HttpMethod.POST -> post(path, request, convertedHeaders)
+            HttpMethod.PUT -> put(path, request, convertedHeaders)
+            HttpMethod.PATCH -> patch(path, request, convertedHeaders)
             else -> throw BluePrintProcessorException("Unsupported methodType($methodType)")
         }
     }
 
-    fun delete(path: String): String {
+    fun convertToBasicHeaders(headers: Map<String, String>): Array<BasicHeader> {
+        val convertedHeaders = Array<BasicHeader>(headers.size){ BasicHeader("","") }
+        var currentElement = 0
+        for ((name, value) in headers) {
+            convertedHeaders[currentElement++] = BasicHeader(name, value)
+        }
+        return convertedHeaders
+    }
+
+    fun delete(path: String, headers: Array<BasicHeader>): String {
         val httpDelete = HttpDelete(host(path))
-        httpDelete.setHeaders(headers())
+        httpDelete.setHeaders(headers)
         httpClient().execute(httpDelete).entity.content.use {
             return IOUtils.toString(it, Charset.defaultCharset())
         }
     }
 
-    fun get(path: String): String {
+    fun get(path: String, headers: Array<BasicHeader>): String {
         val httpGet = HttpGet(host(path))
-        httpGet.setHeaders(headers())
+        httpGet.setHeaders(headers)
         httpClient().execute(httpGet).entity.content.use {
             return IOUtils.toString(it, Charset.defaultCharset())
         }
     }
 
-    fun post(path: String, request: String): String {
+    fun post(path: String, request: String, headers: Array<BasicHeader>): String {
         val httpPost = HttpPost(host(path))
         val entity = StringEntity(request)
         httpPost.entity = entity
-        httpPost.setHeaders(headers())
+        httpPost.setHeaders(headers)
         httpClient().execute(httpPost).entity.content.use {
             return IOUtils.toString(it, Charset.defaultCharset())
         }
     }
 
-    fun put(path: String, request: String): String {
+    fun put(path: String, request: String, headers: Array<BasicHeader>): String {
         val httpPut = HttpPut(host(path))
         val entity = StringEntity(request)
         httpPut.entity = entity
-        httpPut.setHeaders(headers())
+        httpPut.setHeaders(headers)
         httpClient().execute(httpPut).entity.content.use {
             return IOUtils.toString(it, Charset.defaultCharset())
         }
     }
+
+    fun patch(path: String, request: String, headers: Array<BasicHeader>): String {
+        val httpPatch = HttpPatch(host(path))
+        val entity = StringEntity(request)
+        httpPatch.entity = entity
+        httpPatch.setHeaders(headers)
+        httpClient().execute(httpPatch).entity.content.use {
+            return IOUtils.toString(it, Charset.defaultCharset())
+        }
+    }
 }
\ No newline at end of file
index 2b2578a..94e146d 100644 (file)
 
 package org.onap.ccsdk.apps.blueprintsprocessor.rest.service
 
-import org.apache.http.message.BasicHeader
 import org.onap.ccsdk.apps.blueprintsprocessor.rest.RestClientProperties
 
 class DME2ProxyRestClientService(restClientProperties: RestClientProperties) : BlueprintWebClientService {
-    override fun headers(): Array<BasicHeader> {
+    override fun defaultHeaders(): Map<String, String> {
         TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
     }
 
index dc2993d..2bfacf4 100644 (file)
@@ -19,7 +19,6 @@ package org.onap.ccsdk.apps.blueprintsprocessor.rest.service
 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
@@ -33,11 +32,10 @@ import java.security.cert.X509Certificate
 class SSLBasicAuthRestClientService(private val restClientProperties: SSLBasicAuthRestClientProperties) :
     BlueprintWebClientService {
 
-    override fun headers(): Array<BasicHeader> {
-        val params = arrayListOf<BasicHeader>()
-        params.add(BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
-        params.add(BasicHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE))
-        return params.toTypedArray()
+    override fun defaultHeaders(): Map<String, String> {
+        return mapOf(
+                HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
+                HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE)
     }
 
     override fun host(uri: String): String {
index 6e90957..d5dec3a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2019 Bell Canada
+ * Copyright © 2019 Bell Canada, Nordix Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,6 +12,8 @@
  * 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
  */
 
 package org.onap.ccsdk.apps.blueprintsprocessor.rest.service
@@ -24,12 +26,19 @@ import org.springframework.http.MediaType
 class TokenAuthRestClientService(private val restClientProperties: TokenAuthRestClientProperties) :
     BlueprintWebClientService {
 
-    override fun headers(): Array<BasicHeader> {
-        val params = arrayListOf<BasicHeader>()
-        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 defaultHeaders(): Map<String, String> {
+        return mapOf(
+                HttpHeaders.CONTENT_TYPE to MediaType.APPLICATION_JSON_VALUE,
+                HttpHeaders.ACCEPT to MediaType.APPLICATION_JSON_VALUE,
+                HttpHeaders.AUTHORIZATION to restClientProperties.token!!)
+    }
+
+    override fun convertToBasicHeaders(headers: Map<String, String>): Array<BasicHeader> {
+        val customHeaders: MutableMap<String, String> = headers.toMutableMap()
+        if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
+            customHeaders[HttpHeaders.AUTHORIZATION] = restClientProperties.token!!
+        }
+        return super.convertToBasicHeaders(customHeaders)
     }
 
     override fun host(uri: String): String {
index d6167a8..b9a014e 100644 (file)
@@ -30,6 +30,6 @@ class WebClientUtils {
             HttpRequestInterceptor { request, _ -> log.info("Rest request method(${request?.requestLine?.method}), url(${request?.requestLine?.uri})") }
 
         fun logResponse(): HttpResponseInterceptor =
-            HttpResponseInterceptor { response, _ -> log.info("Response status(${response.statusLine.statusCode})") }
+            HttpResponseInterceptor { response, _ -> log.info("Response status(${response.statusLine.statusCode} - ${response.statusLine.reasonPhrase})") }
     }
 }
\ No newline at end of file
index 4fa82df..0390550 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2017-2018 AT&T Intellectual Property.
+ * 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.
@@ -12,6 +13,8 @@
  * 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
  */
 
 package org.onap.ccsdk.apps.blueprintsprocessor.rest.service
@@ -29,9 +32,11 @@ import org.springframework.test.context.ContextConfiguration
 import org.springframework.test.context.TestPropertySource
 import org.springframework.test.context.junit4.SpringRunner
 import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.PatchMapping
 import org.springframework.web.bind.annotation.RequestMapping
 import org.springframework.web.bind.annotation.RestController
 import kotlin.test.Test
+import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
 
 @RunWith(SpringRunner::class)
@@ -58,6 +63,13 @@ class RestClientServiceTest {
         assertNotNull(response, "failed to get response")
     }
 
+    @Test
+    fun testPatch() {
+        val restClientService = bluePrintRestLibPropertyService.blueprintWebClientService("sample")
+        val response = restClientService.exchangeResource(HttpMethod.PATCH.name, "/sample/name", "")
+        assertEquals("Patch request successful", response, "failed to get patch response")
+    }
+
 }
 
 @RestController
@@ -65,5 +77,7 @@ class RestClientServiceTest {
 open class SampleController {
     @GetMapping("/name")
     fun getName(): String = "Sample Controller"
+    @PatchMapping("/name")
+    fun patchName(): String = "Patch request successful"
 }