Revert "Renaming Files having BluePrint to have Blueprint"
[ccsdk/cds.git] / ms / blueprintsprocessor / modules / commons / rest-lib / src / test / kotlin / org / onap / ccsdk / cds / blueprintsprocessor / rest / service / RestClientServiceTest.kt
1 /*
2  * Copyright © 2017-2018 AT&T Intellectual Property.
3  * Copyright (C) 2019 Nordix Foundation
4  * Modifications Copyright © 2019 Huawei.
5  *
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
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18
19 package org.onap.ccsdk.cds.blueprintsprocessor.rest.service
20
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
28 import org.junit.Test
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
63
64 @RunWith(SpringRunner::class)
65 @EnableAutoConfiguration(exclude = [DataSourceAutoConfiguration::class])
66 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
67 @ContextConfiguration(
68     classes = [
69         BluePrintRestLibConfiguration::class, SampleController::class,
70         SecurityConfiguration::class,
71         BluePrintPropertyConfiguration::class, BluePrintPropertiesService::class
72     ]
73 )
74 @TestPropertySource(
75     properties =
76         [
77             "server.port=8443",
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"
94         ]
95 )
96 class RestClientServiceTest {
97
98     @Autowired
99     lateinit var bluePrintRestLibPropertyService: BluePrintRestLibPropertyService
100
101     @Autowired
102     lateinit var httpHandler: HttpHandler
103
104     lateinit var http: WebServer
105
106     fun localPort() = http.port
107
108     @Before
109     fun start() {
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)
113         this.http.start()
114     }
115
116     @After
117     fun stop() {
118         this.http.stop()
119     }
120
121     @Test
122     fun testGetQueryParam() {
123         val restClientService = bluePrintRestLibPropertyService
124             .blueprintWebClientService("sample")
125         val response = restClientService.exchangeResource(
126             HttpMethod.GET.name, "/sample/query?id=3", ""
127         )
128         assertEquals(
129             "query with id:3", response.body,
130             "failed to get query param response"
131         )
132     }
133
134     @Test
135     fun testPatch() {
136         val restClientService = bluePrintRestLibPropertyService
137             .blueprintWebClientService("sample")
138         val response = restClientService.exchangeResource(
139             HttpMethod.PATCH.name, "/sample/name", ""
140         )
141         assertEquals(
142             "Patch request successful", response.body,
143             "failed to get patch response"
144         )
145     }
146
147     @Test
148     fun testBaseAuth() {
149         val restClientService = bluePrintRestLibPropertyService
150             .blueprintWebClientService("sample")
151         val headers = mutableMapOf<String, String>()
152         headers["X-Transaction-Id"] = "1234"
153         val response = restClientService.exchangeResource(
154             HttpMethod.GET.name,
155             "/sample/name", ""
156         )
157         assertNotNull(response.body, "failed to get response")
158     }
159
160     @Test
161     fun testSimpleBasicAuth() {
162         val json: String = "{\n" +
163             "  \"type\" : \"basic-auth\",\n" +
164             "  \"url\" : \"http://localhost:9081\",\n" +
165             "  \"username\" : \"admin\",\n" +
166             "  \"password\" : \"jans\"\n" +
167             "}"
168         val mapper = ObjectMapper()
169         val actualObj: JsonNode = mapper.readTree(json)
170         val restClientService = bluePrintRestLibPropertyService
171             .blueprintWebClientService(actualObj)
172         lateinit var res: String
173         runBlocking {
174             val get = async(start = CoroutineStart.LAZY) {
175                 restClientService.exchangeNB(
176                     HttpMethod.GET.name,
177                     "/sample/basic", ""
178                 ).body
179             }
180             get.start()
181             res = get.await()
182         }
183         assertNotNull(res, "failed to get response")
184         assertEquals(res, "Basic request arrived successfully")
185     }
186
187     // @Test
188     fun testSampleAaiReq() {
189         val restClientService = bluePrintRestLibPropertyService
190             .blueprintWebClientService("test")
191         val headers = mutableMapOf<String, String>()
192         headers["X-TransactionId"] = "9999"
193         headers["X-FromAppId"] = "AAI"
194         val post1 = "{\n" +
195             "  \"customer\": {\n" +
196             "    \"global-customer-id\": \"ONSDEMOBJHKCustomer\",\n" +
197             "    \"subscriber-name\": \"ONSDEMOBJHKCustomer\",\n" +
198             "    \"subscriber-type\": \"CUST\",\n" +
199             "    \"resource-version\": \"1552985011163\"\n" +
200             "  }\n" +
201             "}"
202         lateinit var res1: Customer
203         lateinit var res2: Customer
204         lateinit var res3: String
205         lateinit var res4: String
206         lateinit var res5: String
207         lateinit var res6: String
208         runBlocking {
209             val get1 = async(start = CoroutineStart.LAZY) {
210                 restClientService.exchangeNB(
211                     HttpMethod.GET.name,
212                     "/sample/aai/v22/business/customers", "", headers,
213                     Customer::class.java
214                 ).body
215             }
216
217             val get2 = async(start = CoroutineStart.LAZY) {
218                 restClientService.exchangeNB(
219                     HttpMethod.GET.name,
220                     "/sample/aai/v22/business/customers", "", headers,
221                     Customer::class.java
222                 ).body
223             }
224
225             val post = async(start = CoroutineStart.LAZY) {
226                 restClientService.exchangeNB(
227                     HttpMethod.POST.name,
228                     "/sample/aai/v22/business/customers", post1, headers,
229                     String::class.java
230                 ).body
231             }
232
233             val put = async(start = CoroutineStart.LAZY) {
234                 restClientService.exchangeNB(
235                     HttpMethod.PUT.name,
236                     "/sample/aai/v22/business/customers", post1, headers,
237                     String::class.java
238                 ).body
239             }
240
241             val patch = async(start = CoroutineStart.LAZY) {
242                 restClientService.exchangeNB(
243                     HttpMethod.PATCH.name,
244                     "/sample/aai/v22/business/customers", post1, headers,
245                     String::class.java
246                 ).body
247             }
248
249             val delete = async(start = CoroutineStart.LAZY) {
250                 restClientService.exchangeNB(
251                     HttpMethod.DELETE.name,
252                     "/sample/aai/v22/business/customers", "", headers,
253                     String::class.java
254                 ).body
255             }
256
257             get1.start()
258             get2.start()
259             post.start()
260             put.start()
261             patch.start()
262             delete.start()
263             res1 = get1.await()
264             res2 = get2.await()
265             res3 = post.await()
266             res4 = put.await()
267             res5 = patch.await()
268             res6 = delete.await()
269         }
270         assertNotNull(res1, "failed to get response")
271         assertNotNull(res2, "failed to get response")
272         assertEquals(res1.id, "ONSDEMOBJHKCustomer")
273         assertEquals(res1.name, "ONSDEMOBJHKCustomer")
274         assertEquals(res1.type, "CUST")
275         assertEquals(res1.resource, "1552985011163")
276         assertEquals(res2.id, "ONSDEMOBJHKCustomer")
277         assertEquals(res2.name, "ONSDEMOBJHKCustomer")
278         assertEquals(res2.type, "CUST")
279         assertEquals(res2.resource, "1552985011163")
280         assertEquals(res3, "The message is successfully posted")
281         assertEquals(res4, "The put request is success")
282         assertEquals(res5, "The patch request is success")
283         assertEquals(res6, "The message is successfully deleted")
284     }
285 }
286
287 /**
288  * Sample controller code for testing both http and https requests.
289  */
290 @RestController
291 @RequestMapping("/sample")
292 open class SampleController {
293
294     @GetMapping("/name")
295     fun getName(): String = "Sample Controller"
296
297     @GetMapping("/query")
298     fun getQuery(@RequestParam("id") id: String): String =
299         "query with id:$id"
300
301     @GetMapping("/path/{id}/get")
302     fun getPathParam(@PathVariable("id") id: String): String =
303         "path param id:$id"
304
305     @PatchMapping("/name")
306     fun patchName(): String = "Patch request successful"
307
308     @GetMapping("/basic")
309     fun getBasic(): String = "Basic request arrived successfully"
310
311     @GetMapping("/aai/v22/business/customers")
312     fun getAaiCustomers(
313         @RequestHeader(name = "X-TransactionId", required = true)
314         transId: String,
315         @RequestHeader(name = "X-FromAppId", required = true)
316         appId: String
317     ): String {
318         if (transId != "9999" || appId != "AAI") {
319             return ""
320         }
321         return "{\n" +
322             "  \"id\": \"ONSDEMOBJHKCustomer\",\n" +
323             "  \"name\": \"ONSDEMOBJHKCustomer\",\n" +
324             "  \"type\": \"CUST\",\n" +
325             "  \"resource\": \"1552985011163\"\n" +
326             "}"
327     }
328
329     @PostMapping("/aai/v22/business/customers")
330     fun postAaiCustomers(
331         @RequestHeader(name = "X-TransactionId", required = true)
332         transId: String,
333         @RequestHeader(name = "X-FromAppId", required = true)
334         appId: String
335     ): String {
336         if (transId != "9999" || appId != "AAI") {
337             return ""
338         }
339         return "The message is successfully posted"
340     }
341
342     @PutMapping("/aai/v22/business/customers")
343     fun putAaiCustomers(
344         @RequestHeader(name = "X-TransactionId", required = true)
345         transId: String,
346         @RequestHeader(name = "X-FromAppId", required = true)
347         appId: String
348     ): String {
349         if (transId != "9999" || appId != "AAI") {
350             return ""
351         }
352         return "The put request is success"
353     }
354
355     @PatchMapping("/aai/v22/business/customers")
356     fun patchAaiCustomers(
357         @RequestHeader(name = "X-TransactionId", required = true)
358         transId: String,
359         @RequestHeader(name = "X-FromAppId", required = true)
360         appId: String
361     ): String {
362         if (transId != "9999" || appId != "AAI") {
363             return ""
364         }
365         return "The patch request is success"
366     }
367
368     @DeleteMapping("/aai/v22/business/customers")
369     fun deleteAaiCustomers(
370         @RequestHeader(name = "X-TransactionId", required = true)
371         transId: String,
372         @RequestHeader(name = "X-FromAppId", required = true)
373         appId: String
374     ): String {
375         if (transId != "9999" || appId != "AAI") {
376             return ""
377         }
378         return "The message is successfully deleted"
379     }
380 }
381
382 /**
383  * Security configuration required for basic authentication with username and
384  * password for any request in the server.
385  */
386 open class SecurityConfiguration {
387
388     @Bean
389     open fun userDetailsService(): MapReactiveUserDetailsService {
390         val user: UserDetails = User.withDefaultPasswordEncoder()
391             .username("admin")
392             .password("jans")
393             .roles("USER")
394             .build()
395         return MapReactiveUserDetailsService(user)
396     }
397
398     @Bean
399     open fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
400         return http
401             .csrf().disable()
402             .authorizeExchange().anyExchange().authenticated()
403             .and().httpBasic()
404             .and().build()
405     }
406 }
407
408 /**
409  * Data class required for response
410  */
411 data class Customer(
412     val id: String,
413     val name: String,
414     val type: String,
415     val resource: String
416 )