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(
68 classes = [BluePrintRestLibConfiguration::class, SampleController::class,
69 SecurityConfiguration::class,
70 BluePrintPropertyConfiguration::class, BluePrintPropertiesService::class]
76 "server.ssl.enabled=true",
77 "server.ssl.key-store=classpath:keystore.p12",
78 "server.ssl.key-store-password=changeit",
79 "server.ssl.keyStoreType=PKCS12",
80 "server.ssl.keyAlias=tomcat",
81 "blueprintsprocessor.restclient.sample.type=basic-auth",
82 "blueprintsprocessor.restclient.sample.url=http://127.0.0.1:9081",
83 "blueprintsprocessor.restclient.sample.username=admin",
84 "blueprintsprocessor.restclient.sample.password=jans",
85 "blueprintsprocessor.restclient.test.type=ssl-basic-auth",
86 "blueprintsprocessor.restclient.test.url=https://localhost:8443",
87 "blueprintsprocessor.restclient.test.username=admin",
88 "blueprintsprocessor.restclient.test.password=jans",
89 "blueprintsprocessor.restclient.test.keyStoreInstance=PKCS12",
90 "blueprintsprocessor.restclient.test.sslTrust=src/test/resources/keystore.p12",
91 "blueprintsprocessor.restclient.test.sslTrustPassword=changeit"
94 class RestClientServiceTest {
97 lateinit var bluePrintRestLibPropertyService: BluePrintRestLibPropertyService
100 lateinit var httpHandler: HttpHandler
102 lateinit var http: WebServer
104 fun localPort() = http.port
108 // Second Http server required for non-SSL requests to be processed along with the https server.
109 val factory: ReactiveWebServerFactory = NettyReactiveWebServerFactory(9081)
110 this.http = factory.getWebServer(this.httpHandler)
120 fun testGetQueryParam() {
121 val restClientService = bluePrintRestLibPropertyService
122 .blueprintWebClientService("sample")
123 val response = restClientService.exchangeResource(
124 HttpMethod.GET.name, "/sample/query?id=3", ""
127 "query with id:3", response.body,
128 "failed to get query param response"
133 fun testGetPathParamWithWhitespace() {
134 val restClientService = bluePrintRestLibPropertyService
135 .blueprintWebClientService("sample")
136 val response = restClientService.exchangeResource(
137 HttpMethod.GET.name, "/sample/path/id 3/get", ""
140 "path param id:id 3", response.body,
141 "failed to get query param response"
147 val restClientService = bluePrintRestLibPropertyService
148 .blueprintWebClientService("sample")
149 val response = restClientService.exchangeResource(
150 HttpMethod.PATCH.name, "/sample/name", ""
153 "Patch request successful", response.body,
154 "failed to get patch response"
160 val restClientService = bluePrintRestLibPropertyService
161 .blueprintWebClientService("sample")
162 val headers = mutableMapOf<String, String>()
163 headers["X-Transaction-Id"] = "1234"
164 val response = restClientService.exchangeResource(
168 assertNotNull(response.body, "failed to get response")
172 fun testSimpleBasicAuth() {
173 val json: String = "{\n" +
174 " \"type\" : \"basic-auth\",\n" +
175 " \"url\" : \"http://localhost:9081\",\n" +
176 " \"username\" : \"admin\",\n" +
177 " \"password\" : \"jans\"\n" +
179 val mapper = ObjectMapper()
180 val actualObj: JsonNode = mapper.readTree(json)
181 val restClientService = bluePrintRestLibPropertyService
182 .blueprintWebClientService(actualObj)
183 lateinit var res: String
185 val get = async(start = CoroutineStart.LAZY) {
186 restClientService.exchangeNB(
194 assertNotNull(res, "failed to get response")
195 assertEquals(res, "Basic request arrived successfully")
199 fun testSampleAaiReq() {
200 val restClientService = bluePrintRestLibPropertyService
201 .blueprintWebClientService("test")
202 val headers = mutableMapOf<String, String>()
203 headers["X-TransactionId"] = "9999"
204 headers["X-FromAppId"] = "AAI"
206 " \"customer\": {\n" +
207 " \"global-customer-id\": \"ONSDEMOBJHKCustomer\",\n" +
208 " \"subscriber-name\": \"ONSDEMOBJHKCustomer\",\n" +
209 " \"subscriber-type\": \"CUST\",\n" +
210 " \"resource-version\": \"1552985011163\"\n" +
213 lateinit var res1: Customer
214 lateinit var res2: Customer
215 lateinit var res3: String
216 lateinit var res4: String
217 lateinit var res5: String
218 lateinit var res6: String
220 val get1 = async(start = CoroutineStart.LAZY) {
221 restClientService.exchangeNB(
223 "/sample/aai/v14/business/customers", "", headers,
228 val get2 = async(start = CoroutineStart.LAZY) {
229 restClientService.exchangeNB(
231 "/sample/aai/v14/business/customers", "", headers,
236 val post = async(start = CoroutineStart.LAZY) {
237 restClientService.exchangeNB(
238 HttpMethod.POST.name,
239 "/sample/aai/v14/business/customers", post1, headers,
244 val put = async(start = CoroutineStart.LAZY) {
245 restClientService.exchangeNB(
247 "/sample/aai/v14/business/customers", post1, headers,
252 val patch = async(start = CoroutineStart.LAZY) {
253 restClientService.exchangeNB(
254 HttpMethod.PATCH.name,
255 "/sample/aai/v14/business/customers", post1, headers,
260 val delete = async(start = CoroutineStart.LAZY) {
261 restClientService.exchangeNB(
262 HttpMethod.DELETE.name,
263 "/sample/aai/v14/business/customers", "", headers,
279 res6 = delete.await()
281 assertNotNull(res1, "failed to get response")
282 assertNotNull(res2, "failed to get response")
283 assertEquals(res1.id, "ONSDEMOBJHKCustomer")
284 assertEquals(res1.name, "ONSDEMOBJHKCustomer")
285 assertEquals(res1.type, "CUST")
286 assertEquals(res1.resource, "1552985011163")
287 assertEquals(res2.id, "ONSDEMOBJHKCustomer")
288 assertEquals(res2.name, "ONSDEMOBJHKCustomer")
289 assertEquals(res2.type, "CUST")
290 assertEquals(res2.resource, "1552985011163")
291 assertEquals(res3, "The message is successfully posted")
292 assertEquals(res4, "The put request is success")
293 assertEquals(res5, "The patch request is success")
294 assertEquals(res6, "The message is successfully deleted")
299 * Sample controller code for testing both http and https requests.
302 @RequestMapping("/sample")
303 open class SampleController {
306 fun getName(): String = "Sample Controller"
308 @GetMapping("/query")
309 fun getQuery(@RequestParam("id") id: String): String =
312 @GetMapping("/path/{id}/get")
313 fun getPathParam(@PathVariable("id") id: String): String =
316 @PatchMapping("/name")
317 fun patchName(): String = "Patch request successful"
319 @GetMapping("/basic")
320 fun getBasic(): String = "Basic request arrived successfully"
322 @GetMapping("/aai/v14/business/customers")
324 @RequestHeader(name = "X-TransactionId", required = true)
326 @RequestHeader(name = "X-FromAppId", required = true)
329 if (transId != "9999" || appId != "AAI") {
333 " \"id\": \"ONSDEMOBJHKCustomer\",\n" +
334 " \"name\": \"ONSDEMOBJHKCustomer\",\n" +
335 " \"type\": \"CUST\",\n" +
336 " \"resource\": \"1552985011163\"\n" +
340 @PostMapping("/aai/v14/business/customers")
341 fun postAaiCustomers(
342 @RequestHeader(name = "X-TransactionId", required = true)
344 @RequestHeader(name = "X-FromAppId", required = true)
347 if (transId != "9999" || appId != "AAI") {
350 return "The message is successfully posted"
353 @PutMapping("/aai/v14/business/customers")
355 @RequestHeader(name = "X-TransactionId", required = true)
357 @RequestHeader(name = "X-FromAppId", required = true)
360 if (transId != "9999" || appId != "AAI") {
363 return "The put request is success"
366 @PatchMapping("/aai/v14/business/customers")
367 fun patchAaiCustomers(
368 @RequestHeader(name = "X-TransactionId", required = true)
370 @RequestHeader(name = "X-FromAppId", required = true)
373 if (transId != "9999" || appId != "AAI") {
376 return "The patch request is success"
379 @DeleteMapping("/aai/v14/business/customers")
380 fun deleteAaiCustomers(
381 @RequestHeader(name = "X-TransactionId", required = true)
383 @RequestHeader(name = "X-FromAppId", required = true)
386 if (transId != "9999" || appId != "AAI") {
389 return "The message is successfully deleted"
394 * Security configuration required for basic authentication with username and
395 * password for any request in the server.
397 open class SecurityConfiguration {
400 open fun userDetailsService(): MapReactiveUserDetailsService {
401 val user: UserDetails = User.withDefaultPasswordEncoder()
406 return MapReactiveUserDetailsService(user)
410 open fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
413 .authorizeExchange().anyExchange().authenticated()
420 * Data class required for response