2 * Copyright © 2017-2018 AT&T Intellectual Property.
3 * Copyright (C) 2019 Nordix Foundation
4 * Modifications Copyright © 2019 Huawei.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package org.onap.ccsdk.cds.blueprintsprocessor.rest.service
21 import com.fasterxml.jackson.databind.JsonNode
22 import com.fasterxml.jackson.databind.ObjectMapper
23 import kotlinx.coroutines.CoroutineStart
24 import kotlinx.coroutines.async
25 import kotlinx.coroutines.runBlocking
26 import org.junit.After
27 import org.junit.Before
29 import org.junit.runner.RunWith
30 import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertiesService
31 import org.onap.ccsdk.cds.blueprintsprocessor.core.BluePrintPropertyConfiguration
32 import org.onap.ccsdk.cds.blueprintsprocessor.rest.BluePrintRestLibConfiguration
33 import org.springframework.beans.factory.annotation.Autowired
34 import org.springframework.boot.autoconfigure.EnableAutoConfiguration
35 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
36 import org.springframework.boot.test.context.SpringBootTest
37 import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory
38 import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory
39 import org.springframework.boot.web.server.WebServer
40 import org.springframework.context.annotation.Bean
41 import org.springframework.http.HttpMethod
42 import org.springframework.http.server.reactive.HttpHandler
43 import org.springframework.security.config.web.server.ServerHttpSecurity
44 import org.springframework.security.core.userdetails.MapReactiveUserDetailsService
45 import org.springframework.security.core.userdetails.User
46 import org.springframework.security.core.userdetails.UserDetails
47 import org.springframework.security.web.server.SecurityWebFilterChain
48 import org.springframework.test.context.ContextConfiguration
49 import org.springframework.test.context.TestPropertySource
50 import org.springframework.test.context.junit4.SpringRunner
51 import org.springframework.web.bind.annotation.DeleteMapping
52 import org.springframework.web.bind.annotation.GetMapping
53 import org.springframework.web.bind.annotation.PatchMapping
54 import org.springframework.web.bind.annotation.PathVariable
55 import org.springframework.web.bind.annotation.PostMapping
56 import org.springframework.web.bind.annotation.PutMapping
57 import org.springframework.web.bind.annotation.RequestHeader
58 import org.springframework.web.bind.annotation.RequestMapping
59 import org.springframework.web.bind.annotation.RequestParam
60 import org.springframework.web.bind.annotation.RestController
61 import kotlin.test.assertEquals
62 import kotlin.test.assertNotNull
64 @RunWith(SpringRunner::class)
65 @EnableAutoConfiguration(exclude = [DataSourceAutoConfiguration::class])
66 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
67 @ContextConfiguration(
69 BluePrintRestLibConfiguration::class, SampleController::class,
70 SecurityConfiguration::class,
71 BluePrintPropertyConfiguration::class, BluePrintPropertiesService::class
78 "server.ssl.enabled=true",
79 "server.ssl.key-store=classpath:keystore.p12",
80 "server.ssl.key-store-password=changeit",
81 "server.ssl.keyStoreType=PKCS12",
82 "server.ssl.keyAlias=tomcat",
83 "blueprintsprocessor.restclient.sample.type=basic-auth",
84 "blueprintsprocessor.restclient.sample.url=http://127.0.0.1:9081",
85 "blueprintsprocessor.restclient.sample.username=admin",
86 "blueprintsprocessor.restclient.sample.password=jans",
87 "blueprintsprocessor.restclient.test.type=ssl-basic-auth",
88 "blueprintsprocessor.restclient.test.url=https://localhost:8443",
89 "blueprintsprocessor.restclient.test.username=admin",
90 "blueprintsprocessor.restclient.test.password=jans",
91 "blueprintsprocessor.restclient.test.keyStoreInstance=PKCS12",
92 "blueprintsprocessor.restclient.test.sslTrust=src/test/resources/keystore.p12",
93 "blueprintsprocessor.restclient.test.sslTrustPassword=changeit"
96 class RestClientServiceTest {
99 lateinit var bluePrintRestLibPropertyService: BluePrintRestLibPropertyService
102 lateinit var httpHandler: HttpHandler
104 lateinit var http: WebServer
106 fun localPort() = http.port
110 // Second Http server required for non-SSL requests to be processed along with the https server.
111 val factory: ReactiveWebServerFactory = NettyReactiveWebServerFactory(9081)
112 this.http = factory.getWebServer(this.httpHandler)
122 fun testGetQueryParam() {
123 val restClientService = bluePrintRestLibPropertyService
124 .blueprintWebClientService("sample")
125 val response = restClientService.exchangeResource(
126 HttpMethod.GET.name, "/sample/query?id=3", ""
129 "query with id:3", response.body,
130 "failed to get query param response"
135 fun testGetPathParamWithWhitespace() {
136 val restClientService = bluePrintRestLibPropertyService
137 .blueprintWebClientService("sample")
138 val response = restClientService.exchangeResource(
139 HttpMethod.GET.name, "/sample/path/id 3/get", ""
142 "path param id:id 3", response.body,
143 "failed to get query param response"
149 val restClientService = bluePrintRestLibPropertyService
150 .blueprintWebClientService("sample")
151 val response = restClientService.exchangeResource(
152 HttpMethod.PATCH.name, "/sample/name", ""
155 "Patch request successful", response.body,
156 "failed to get patch response"
162 val restClientService = bluePrintRestLibPropertyService
163 .blueprintWebClientService("sample")
164 val headers = mutableMapOf<String, String>()
165 headers["X-Transaction-Id"] = "1234"
166 val response = restClientService.exchangeResource(
170 assertNotNull(response.body, "failed to get response")
174 fun testSimpleBasicAuth() {
175 val json: String = "{\n" +
176 " \"type\" : \"basic-auth\",\n" +
177 " \"url\" : \"http://localhost:9081\",\n" +
178 " \"username\" : \"admin\",\n" +
179 " \"password\" : \"jans\"\n" +
181 val mapper = ObjectMapper()
182 val actualObj: JsonNode = mapper.readTree(json)
183 val restClientService = bluePrintRestLibPropertyService
184 .blueprintWebClientService(actualObj)
185 lateinit var res: String
187 val get = async(start = CoroutineStart.LAZY) {
188 restClientService.exchangeNB(
196 assertNotNull(res, "failed to get response")
197 assertEquals(res, "Basic request arrived successfully")
201 fun testSampleAaiReq() {
202 val restClientService = bluePrintRestLibPropertyService
203 .blueprintWebClientService("test")
204 val headers = mutableMapOf<String, String>()
205 headers["X-TransactionId"] = "9999"
206 headers["X-FromAppId"] = "AAI"
208 " \"customer\": {\n" +
209 " \"global-customer-id\": \"ONSDEMOBJHKCustomer\",\n" +
210 " \"subscriber-name\": \"ONSDEMOBJHKCustomer\",\n" +
211 " \"subscriber-type\": \"CUST\",\n" +
212 " \"resource-version\": \"1552985011163\"\n" +
215 lateinit var res1: Customer
216 lateinit var res2: Customer
217 lateinit var res3: String
218 lateinit var res4: String
219 lateinit var res5: String
220 lateinit var res6: String
222 val get1 = async(start = CoroutineStart.LAZY) {
223 restClientService.exchangeNB(
225 "/sample/aai/v22/business/customers", "", headers,
230 val get2 = async(start = CoroutineStart.LAZY) {
231 restClientService.exchangeNB(
233 "/sample/aai/v22/business/customers", "", headers,
238 val post = async(start = CoroutineStart.LAZY) {
239 restClientService.exchangeNB(
240 HttpMethod.POST.name,
241 "/sample/aai/v22/business/customers", post1, headers,
246 val put = async(start = CoroutineStart.LAZY) {
247 restClientService.exchangeNB(
249 "/sample/aai/v22/business/customers", post1, headers,
254 val patch = async(start = CoroutineStart.LAZY) {
255 restClientService.exchangeNB(
256 HttpMethod.PATCH.name,
257 "/sample/aai/v22/business/customers", post1, headers,
262 val delete = async(start = CoroutineStart.LAZY) {
263 restClientService.exchangeNB(
264 HttpMethod.DELETE.name,
265 "/sample/aai/v22/business/customers", "", headers,
281 res6 = delete.await()
283 assertNotNull(res1, "failed to get response")
284 assertNotNull(res2, "failed to get response")
285 assertEquals(res1.id, "ONSDEMOBJHKCustomer")
286 assertEquals(res1.name, "ONSDEMOBJHKCustomer")
287 assertEquals(res1.type, "CUST")
288 assertEquals(res1.resource, "1552985011163")
289 assertEquals(res2.id, "ONSDEMOBJHKCustomer")
290 assertEquals(res2.name, "ONSDEMOBJHKCustomer")
291 assertEquals(res2.type, "CUST")
292 assertEquals(res2.resource, "1552985011163")
293 assertEquals(res3, "The message is successfully posted")
294 assertEquals(res4, "The put request is success")
295 assertEquals(res5, "The patch request is success")
296 assertEquals(res6, "The message is successfully deleted")
301 * Sample controller code for testing both http and https requests.
304 @RequestMapping("/sample")
305 open class SampleController {
308 fun getName(): String = "Sample Controller"
310 @GetMapping("/query")
311 fun getQuery(@RequestParam("id") id: String): String =
314 @GetMapping("/path/{id}/get")
315 fun getPathParam(@PathVariable("id") id: String): String =
318 @PatchMapping("/name")
319 fun patchName(): String = "Patch request successful"
321 @GetMapping("/basic")
322 fun getBasic(): String = "Basic request arrived successfully"
324 @GetMapping("/aai/v22/business/customers")
326 @RequestHeader(name = "X-TransactionId", required = true)
328 @RequestHeader(name = "X-FromAppId", required = true)
331 if (transId != "9999" || appId != "AAI") {
335 " \"id\": \"ONSDEMOBJHKCustomer\",\n" +
336 " \"name\": \"ONSDEMOBJHKCustomer\",\n" +
337 " \"type\": \"CUST\",\n" +
338 " \"resource\": \"1552985011163\"\n" +
342 @PostMapping("/aai/v22/business/customers")
343 fun postAaiCustomers(
344 @RequestHeader(name = "X-TransactionId", required = true)
346 @RequestHeader(name = "X-FromAppId", required = true)
349 if (transId != "9999" || appId != "AAI") {
352 return "The message is successfully posted"
355 @PutMapping("/aai/v22/business/customers")
357 @RequestHeader(name = "X-TransactionId", required = true)
359 @RequestHeader(name = "X-FromAppId", required = true)
362 if (transId != "9999" || appId != "AAI") {
365 return "The put request is success"
368 @PatchMapping("/aai/v22/business/customers")
369 fun patchAaiCustomers(
370 @RequestHeader(name = "X-TransactionId", required = true)
372 @RequestHeader(name = "X-FromAppId", required = true)
375 if (transId != "9999" || appId != "AAI") {
378 return "The patch request is success"
381 @DeleteMapping("/aai/v22/business/customers")
382 fun deleteAaiCustomers(
383 @RequestHeader(name = "X-TransactionId", required = true)
385 @RequestHeader(name = "X-FromAppId", required = true)
388 if (transId != "9999" || appId != "AAI") {
391 return "The message is successfully deleted"
396 * Security configuration required for basic authentication with username and
397 * password for any request in the server.
399 open class SecurityConfiguration {
402 open fun userDetailsService(): MapReactiveUserDetailsService {
403 val user: UserDetails = User.withDefaultPasswordEncoder()
408 return MapReactiveUserDetailsService(user)
412 open fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
415 .authorizeExchange().anyExchange().authenticated()
422 * Data class required for response