Optimizing Imports and Formatting code
[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.BluePrintProperties
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.*
52 import kotlin.test.assertEquals
53 import kotlin.test.assertNotNull
54
55 @RunWith(SpringRunner::class)
56 @EnableAutoConfiguration(exclude = [DataSourceAutoConfiguration::class])
57 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
58 @ContextConfiguration(classes = [BluePrintRestLibConfiguration::class, SampleController::class,
59     SecurityConfiguration::class,
60     BlueprintPropertyConfiguration::class, BluePrintProperties::class])
61 @TestPropertySource(properties =
62 [
63     "server.port=8443",
64     "server.ssl.enabled=true",
65     "server.ssl.key-store=classpath:keystore.p12",
66     "server.ssl.key-store-password=changeit",
67     "server.ssl.keyStoreType=PKCS12",
68     "server.ssl.keyAlias=tomcat",
69     "blueprintsprocessor.restclient.sample.type=basic-auth",
70     "blueprintsprocessor.restclient.sample.url=http://127.0.0.1:8081",
71     "blueprintsprocessor.restclient.sample.username=admin",
72     "blueprintsprocessor.restclient.sample.password=jans",
73     "blueprintsprocessor.restclient.test.type=ssl-basic-auth",
74     "blueprintsprocessor.restclient.test.url=https://localhost:8443",
75     "blueprintsprocessor.restclient.test.username=admin",
76     "blueprintsprocessor.restclient.test.password=jans",
77     "blueprintsprocessor.restclient.test.keyStoreInstance=PKCS12",
78     "blueprintsprocessor.restclient.test.sslTrust=src/test/resources/keystore.p12",
79     "blueprintsprocessor.restclient.test.sslTrustPassword=changeit"
80 ])
81 class RestClientServiceTest {
82
83     @Autowired
84     lateinit var bluePrintRestLibPropertyService: BluePrintRestLibPropertyService
85
86     @Autowired
87     lateinit var httpHandler : HttpHandler
88
89     lateinit var http : WebServer
90
91     fun localPort() = http.port
92
93     @Before
94     fun start() {
95         // Second Http server required for non-SSL requests to be processed along with the https server.
96         val factory: ReactiveWebServerFactory = NettyReactiveWebServerFactory(8081)
97         this.http = factory.getWebServer(this.httpHandler)
98         this.http.start()
99     }
100
101     @After
102     fun stop() {
103         this.http.stop()
104     }
105
106     @Test
107     fun testPatch() {
108         val restClientService = bluePrintRestLibPropertyService
109                 .blueprintWebClientService("sample")
110         val response = restClientService.exchangeResource(
111                 HttpMethod.PATCH.name, "/sample/name", "")
112         assertEquals("Patch request successful", response.body,
113                 "failed to get patch response")
114     }
115
116     @Test
117     fun testBaseAuth() {
118         val restClientService = bluePrintRestLibPropertyService
119                 .blueprintWebClientService("sample")
120         val headers = mutableMapOf<String, String>()
121         headers["X-Transaction-Id"] = "1234"
122         val response = restClientService.exchangeResource(HttpMethod.GET.name,
123                 "/sample/name", "")
124         assertNotNull(response.body, "failed to get response")
125     }
126
127     @Test
128     fun testSimpleBasicAuth() {
129         val json: String = "{\n" +
130                 "  \"type\" : \"basic-auth\",\n" +
131                 "  \"url\" : \"http://localhost:8081\",\n" +
132                 "  \"username\" : \"admin\",\n" +
133                 "  \"password\" : \"jans\"\n" +
134                 "}"
135         val mapper = ObjectMapper()
136         val actualObj: JsonNode = mapper.readTree(json)
137         val restClientService = bluePrintRestLibPropertyService
138                 .blueprintWebClientService(actualObj)
139         lateinit var res:String
140         runBlocking {
141             val get = async(start = CoroutineStart.LAZY) {
142                 restClientService.exchangeNB(HttpMethod.GET.name,
143                         "/sample/basic", "").body}
144             get.start()
145             res = get.await()
146         }
147         assertNotNull(res, "failed to get response")
148         assertEquals(res, "Basic request arrived successfully")
149     }
150
151     @Test
152     fun testSampleAaiReq() {
153         val restClientService = bluePrintRestLibPropertyService
154                 .blueprintWebClientService("test")
155         val headers = mutableMapOf<String, String>()
156         headers["X-TransactionId"] = "9999"
157         headers["X-FromAppId"] = "AAI"
158         val post1 = "{\n" +
159                 "  \"customer\": {\n" +
160                 "    \"global-customer-id\": \"ONSDEMOBJHKCustomer\",\n" +
161                 "    \"subscriber-name\": \"ONSDEMOBJHKCustomer\",\n" +
162                 "    \"subscriber-type\": \"CUST\",\n" +
163                 "    \"resource-version\": \"1552985011163\"\n" +
164                 "  }\n" +
165                 "}"
166         lateinit var res1: Customer
167         lateinit var res2: Customer
168         lateinit var res3: String
169         lateinit var res4: String
170         lateinit var res5: String
171         lateinit var res6: String
172         runBlocking {
173             val get1 = async(start = CoroutineStart.LAZY) {
174                 restClientService.exchangeNB(HttpMethod.GET.name,
175                         "/sample/aai/v14/business/customers", "", headers,
176                         Customer::class.java).body}
177
178             val get2 = async(start = CoroutineStart.LAZY) {
179                 restClientService.exchangeNB(HttpMethod.GET.name,
180                         "/sample/aai/v14/business/customers", "", headers,
181                         Customer::class.java).body}
182
183             val post = async(start = CoroutineStart.LAZY) {
184                 restClientService.exchangeNB(HttpMethod.POST.name,
185                         "/sample/aai/v14/business/customers", post1, headers,
186                         String::class.java).body}
187
188             val put = async(start = CoroutineStart.LAZY) {
189                 restClientService.exchangeNB(HttpMethod.PUT.name,
190                         "/sample/aai/v14/business/customers", post1, headers,
191                         String::class.java).body}
192
193             val patch = async(start = CoroutineStart.LAZY) {
194                 restClientService.exchangeNB(HttpMethod.PATCH.name,
195                         "/sample/aai/v14/business/customers", post1, headers,
196                         String::class.java).body}
197
198             val delete = async(start = CoroutineStart.LAZY) {
199                 restClientService.exchangeNB(HttpMethod.DELETE.name,
200                         "/sample/aai/v14/business/customers", "", headers,
201                         String::class.java).body}
202
203             get1.start()
204             get2.start()
205             post.start()
206             put.start()
207             patch.start()
208             delete.start()
209             res1 = get1.await()
210             res2 = get2.await()
211             res3 = post.await()
212             res4 = put.await()
213             res5 = patch.await()
214             res6 = delete.await()
215         }
216         assertNotNull(res1, "failed to get response")
217         assertNotNull(res2, "failed to get response")
218         assertEquals(res1.id, "ONSDEMOBJHKCustomer")
219         assertEquals(res1.name, "ONSDEMOBJHKCustomer")
220         assertEquals(res1.type, "CUST")
221         assertEquals(res1.resource, "1552985011163")
222         assertEquals(res2.id, "ONSDEMOBJHKCustomer")
223         assertEquals(res2.name, "ONSDEMOBJHKCustomer")
224         assertEquals(res2.type, "CUST")
225         assertEquals(res2.resource, "1552985011163")
226         assertEquals(res3, "The message is successfully posted")
227         assertEquals(res4, "The put request is success")
228         assertEquals(res5, "The patch request is success")
229         assertEquals(res6, "The message is successfully deleted")
230     }
231 }
232
233 /**
234  * Sample controller code for testing both http and https requests.
235  */
236 @RestController
237 @RequestMapping("/sample")
238 open class SampleController {
239
240     @GetMapping("/name")
241     fun getName(): String = "Sample Controller"
242
243     @PatchMapping("/name")
244     fun patchName(): String = "Patch request successful"
245
246     @GetMapping("/basic")
247     fun getBasic(): String = "Basic request arrived successfully"
248
249
250     @GetMapping("/aai/v14/business/customers")
251     fun getAaiCustomers(
252             @RequestHeader(name = "X-TransactionId", required = true)
253             transId: String,
254             @RequestHeader(name = "X-FromAppId", required = true)
255             appId: String) : String {
256         if (transId != "9999" || appId != "AAI") {
257             return ""
258         }
259         return "{\n" +
260                 "  \"id\": \"ONSDEMOBJHKCustomer\",\n" +
261                 "  \"name\": \"ONSDEMOBJHKCustomer\",\n" +
262                 "  \"type\": \"CUST\",\n" +
263                 "  \"resource\": \"1552985011163\"\n" +
264                 "}"
265     }
266
267     @PostMapping("/aai/v14/business/customers")
268     fun postAaiCustomers(
269             @RequestHeader(name = "X-TransactionId", required = true)
270             transId: String,
271             @RequestHeader(name = "X-FromAppId", required = true)
272             appId: String) : String {
273         if (transId != "9999" || appId != "AAI") {
274             return ""
275         }
276         return "The message is successfully posted"
277     }
278
279
280     @PutMapping("/aai/v14/business/customers")
281     fun putAaiCustomers(
282             @RequestHeader(name = "X-TransactionId", required = true)
283             transId: String,
284             @RequestHeader(name = "X-FromAppId", required = true)
285             appId: String) : String {
286         if (transId != "9999" || appId != "AAI") {
287             return ""
288         }
289         return "The put request is success"
290     }
291
292     @PatchMapping("/aai/v14/business/customers")
293     fun patchAaiCustomers(
294             @RequestHeader(name = "X-TransactionId", required = true)
295             transId: String,
296             @RequestHeader(name = "X-FromAppId", required = true)
297             appId: String) : String {
298         if (transId != "9999" || appId != "AAI") {
299             return ""
300         }
301         return "The patch request is success"
302     }
303
304     @DeleteMapping("/aai/v14/business/customers")
305     fun deleteAaiCustomers(
306             @RequestHeader(name = "X-TransactionId", required = true)
307             transId: String,
308             @RequestHeader(name = "X-FromAppId", required = true)
309             appId: String) : String {
310         if (transId != "9999" || appId != "AAI") {
311             return ""
312         }
313         return "The message is successfully deleted"
314     }
315 }
316
317 /**
318  * Security configuration required for basic authentication with username and
319  * password for any request in the server.
320  */
321 open class SecurityConfiguration {
322
323     @Bean
324     open fun userDetailsService(): MapReactiveUserDetailsService {
325         val user: UserDetails = User.withDefaultPasswordEncoder()
326                 .username("admin")
327                 .password("jans")
328                 .roles("USER")
329                 .build()
330         return MapReactiveUserDetailsService(user)
331     }
332
333     @Bean
334     open fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
335         return http
336                 .csrf().disable()
337                 .authorizeExchange().anyExchange().authenticated()
338                 .and().httpBasic()
339                 .and().build()
340     }
341 }
342
343 /**
344  * Data class required for response
345  */
346 data class Customer(
347        val id: String,
348        val name: String,
349        val type: String,
350        val resource: String)