diff --git a/pom.xml b/pom.xml index e0edad8..a94ef10 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,17 @@ com.fasterxml.jackson.module jackson-module-kotlin + + + jakarta.json + jakarta.json-api + 2.1.3 + + + org.eclipse.parsson + jakarta.json + 1.1.5 + diff --git a/src/main/kotlin/no/digdir/service_catalog/controller/PublicServiceController.kt b/src/main/kotlin/no/digdir/service_catalog/controller/PublicServiceController.kt index c65538d..d9e6309 100644 --- a/src/main/kotlin/no/digdir/service_catalog/controller/PublicServiceController.kt +++ b/src/main/kotlin/no/digdir/service_catalog/controller/PublicServiceController.kt @@ -1,5 +1,6 @@ package no.digdir.service_catalog.controller +import no.digdir.service_catalog.model.JsonPatchOperation import no.digdir.service_catalog.model.PublicService import no.digdir.service_catalog.security.EndpointPermissions import no.digdir.service_catalog.service.PublicServiceService @@ -11,7 +12,9 @@ import org.springframework.security.oauth2.jwt.Jwt import org.springframework.stereotype.Controller import org.springframework.web.bind.annotation.CrossOrigin import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestMapping @Controller @@ -39,4 +42,19 @@ class PublicServiceController(private val publicServiceService: PublicServiceSer } else { ResponseEntity(HttpStatus.FORBIDDEN) } + + @PatchMapping(value = ["/{id}"], produces = [MediaType.APPLICATION_JSON_VALUE]) + fun patchCodeList( + @AuthenticationPrincipal jwt: Jwt, + @PathVariable catalogId: String, + @PathVariable id: String, + @RequestBody patchOperations: List + ): ResponseEntity = + if (endpointPermissions.hasOrgWritePermission(jwt, catalogId)) { + publicServiceService.patchPublicService(id, catalogId, patchOperations) + ?.let { ResponseEntity(it, HttpStatus.OK) } + ?: ResponseEntity(HttpStatus.NOT_FOUND) + } else { + ResponseEntity(HttpStatus.FORBIDDEN) + } } diff --git a/src/main/kotlin/no/digdir/service_catalog/model/JsonPatchOperation.kt b/src/main/kotlin/no/digdir/service_catalog/model/JsonPatchOperation.kt new file mode 100644 index 0000000..d4d5420 --- /dev/null +++ b/src/main/kotlin/no/digdir/service_catalog/model/JsonPatchOperation.kt @@ -0,0 +1,23 @@ +package no.digdir.service_catalog.model + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonValue + +@JsonIgnoreProperties(ignoreUnknown = true) +data class JsonPatchOperation ( + val op: OpEnum, + val path: String, + val value: Any? = null, + val from: String? = null +) + +enum class OpEnum(val value: String) { + ADD("add"), + REMOVE("remove"), + REPLACE("replace"), + MOVE("move"), + COPY("copy"); + + @JsonValue + fun jsonValue(): String = value +} diff --git a/src/main/kotlin/no/digdir/service_catalog/service/JsonPatchUtils.kt b/src/main/kotlin/no/digdir/service_catalog/service/JsonPatchUtils.kt new file mode 100644 index 0000000..37289c0 --- /dev/null +++ b/src/main/kotlin/no/digdir/service_catalog/service/JsonPatchUtils.kt @@ -0,0 +1,46 @@ +package no.digdir.service_catalog.service + +import com.fasterxml.jackson.core.JsonProcessingException +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import jakarta.json.Json +import jakarta.json.JsonException +import no.digdir.service_catalog.model.JsonPatchOperation +import org.springframework.http.HttpStatus +import org.springframework.web.server.ResponseStatusException +import java.io.StringReader + +inline fun patchOriginal(original: T, operations: List): T { + validateOperations(operations) + try { + return applyPatch(original, operations) + } catch (ex: Exception) { + when (ex) { + is JsonException -> throw ResponseStatusException(HttpStatus.BAD_REQUEST, ex.message) + is JsonProcessingException -> throw ResponseStatusException(HttpStatus.BAD_REQUEST, ex.message) + is IllegalArgumentException -> throw ResponseStatusException(HttpStatus.BAD_REQUEST, ex.message) + else -> throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ex.message) + } + } +} + +inline fun applyPatch(originalObject: T, operations: List): T { + if (operations.isNotEmpty()) { + with(jacksonObjectMapper()) { + val changes = Json.createReader(StringReader(writeValueAsString(operations))).readArray() + val original = Json.createReader(StringReader(writeValueAsString(originalObject))).readObject() + + return Json.createPatch(changes).apply(original) + .let { readValue(it.toString()) } + } + } + return originalObject +} + +fun validateOperations(operations: List) { + val invalidPaths = listOf("/id", "/catalogId") + if (operations.any { it.path in invalidPaths }) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Patch of paths $invalidPaths is not permitted") + } +} \ No newline at end of file diff --git a/src/main/kotlin/no/digdir/service_catalog/service/PublicServiceService.kt b/src/main/kotlin/no/digdir/service_catalog/service/PublicServiceService.kt index 08886f0..5204310 100644 --- a/src/main/kotlin/no/digdir/service_catalog/service/PublicServiceService.kt +++ b/src/main/kotlin/no/digdir/service_catalog/service/PublicServiceService.kt @@ -1,12 +1,16 @@ package no.digdir.service_catalog.service +import no.digdir.service_catalog.model.JsonPatchOperation import no.digdir.service_catalog.model.PublicService import no.digdir.service_catalog.mongodb.PublicServiceRepository +import org.slf4j.LoggerFactory import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service @Service class PublicServiceService(private val publicServiceRepository: PublicServiceRepository) { + private val logger = LoggerFactory.getLogger(PublicServiceService::class.java) + fun findPublicServicesByCatalogId(catalogId: String) = publicServiceRepository.getByCatalogId(catalogId) @@ -14,4 +18,14 @@ class PublicServiceService(private val publicServiceRepository: PublicServiceRep publicServiceRepository .findByIdOrNull(id) ?.takeIf { it.catalogId == catalogId } + + fun patchPublicService(id: String, catalogId: String, operations: List): PublicService? = + try { + findPublicServiceById(id, catalogId) + ?.let { patchOriginal(it, operations)} + ?.let { publicServiceRepository.save(it) } + } catch (ex: Exception) { + logger.error("Failed to update code-list with id $id in catalog $catalogId", ex) + throw ex + } } diff --git a/src/test/kotlin/no/digdir/service_catalog/integration/GetServices.kt b/src/test/kotlin/no/digdir/service_catalog/integration/GetServices.kt index b7f516b..7c2a0f5 100644 --- a/src/test/kotlin/no/digdir/service_catalog/integration/GetServices.kt +++ b/src/test/kotlin/no/digdir/service_catalog/integration/GetServices.kt @@ -6,7 +6,6 @@ import no.digdir.service_catalog.model.Service import no.digdir.service_catalog.utils.ApiTestContext import no.digdir.service_catalog.utils.SERVICES import no.digdir.service_catalog.utils.apiAuthorizedRequest -import no.digdir.service_catalog.utils.apiGet import no.digdir.service_catalog.utils.jwt.Access import no.digdir.service_catalog.utils.jwt.JwtToken import org.junit.jupiter.api.Assertions @@ -14,6 +13,7 @@ import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus import org.springframework.test.context.ContextConfiguration @@ -28,7 +28,7 @@ class GetServices: ApiTestContext() { @Test fun `able to get all services`() { - val response = apiAuthorizedRequest("/catalogs/910244132/services", port, null, JwtToken(Access.ORG_READ).toString(), "GET") + val response = apiAuthorizedRequest("/catalogs/910244132/services", port, null, JwtToken(Access.ORG_READ).toString(), HttpMethod.GET) Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) val result: List = mapper.readValue(response["body"] as String) @@ -37,13 +37,13 @@ class GetServices: ApiTestContext() { @Test fun `unauthorized when missing token`() { - val response = apiAuthorizedRequest("/catalogs/910244132/services", port, null, null, "GET") + val response = apiAuthorizedRequest("/catalogs/910244132/services", port, null, null, HttpMethod.GET) Assertions.assertEquals(HttpStatus.UNAUTHORIZED.value(), response["status"]) } @Test fun `forbidden when authorized for other catalog`() { - val response = apiAuthorizedRequest("/catalogs/910244132/services", port, null, JwtToken(Access.WRONG_ORG_READ).toString(), "GET") + val response = apiAuthorizedRequest("/catalogs/910244132/services", port, null, JwtToken(Access.WRONG_ORG_READ).toString(), HttpMethod.GET) Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response["status"]) } } diff --git a/src/test/kotlin/no/digdir/service_catalog/integration/PublicServices.kt b/src/test/kotlin/no/digdir/service_catalog/integration/PublicServices.kt index 8613362..5ea9c63 100644 --- a/src/test/kotlin/no/digdir/service_catalog/integration/PublicServices.kt +++ b/src/test/kotlin/no/digdir/service_catalog/integration/PublicServices.kt @@ -2,18 +2,25 @@ package no.digdir.service_catalog.integration import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue +import no.digdir.service_catalog.model.JsonPatchOperation +import no.digdir.service_catalog.model.LocalizedStrings +import no.digdir.service_catalog.model.OpEnum import no.digdir.service_catalog.model.PublicService import no.digdir.service_catalog.utils.ApiTestContext import no.digdir.service_catalog.utils.PUBLIC_SERVICES +import no.digdir.service_catalog.utils.PUBLIC_SERVICE_0 import no.digdir.service_catalog.utils.PUBLIC_SERVICE_1 +import no.digdir.service_catalog.utils.PUBLIC_SERVICE_2 import no.digdir.service_catalog.utils.apiAuthorizedRequest import no.digdir.service_catalog.utils.jwt.Access import no.digdir.service_catalog.utils.jwt.JwtToken import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus import org.springframework.test.context.ContextConfiguration @@ -26,63 +33,275 @@ import org.springframework.test.context.ContextConfiguration class PublicServices: ApiTestContext() { private val mapper = jacksonObjectMapper() - @Test - fun `able to get all public services`() { - val response = apiAuthorizedRequest( - "/catalogs/910244132/public-services", - port, - null, - JwtToken(Access.ORG_READ).toString(), - "GET") - Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) - - val result: List = mapper.readValue(response["body"] as String) - Assertions.assertEquals(PUBLIC_SERVICES, result) - } + @Nested + internal inner class GetPublicServices { + @Test + fun `able to get all public services`() { + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services", + port, + null, + JwtToken(Access.ORG_READ).toString(), + HttpMethod.GET) + Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) - @Test - fun `unauthorized when missing token`() { - val response = apiAuthorizedRequest( - "/catalogs/910244132/public-services", - port, - null, - null, - "GET") - Assertions.assertEquals(HttpStatus.UNAUTHORIZED.value(), response["status"]) - } + val result: List = mapper.readValue(response["body"] as String) + Assertions.assertEquals(PUBLIC_SERVICES, result) + } - @Test - fun `forbidden when authorized for other catalog`() { - val response = apiAuthorizedRequest( - "/catalogs/910244132/public-services", - port, - null, - JwtToken(Access.WRONG_ORG_READ).toString(), - "GET") - Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response["status"]) + @Test + fun `unauthorized when missing token`() { + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services", + port, + null, + null, + HttpMethod.GET) + Assertions.assertEquals(HttpStatus.UNAUTHORIZED.value(), response["status"]) + } + + @Test + fun `forbidden when authorized for other catalog`() { + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services", + port, + null, + JwtToken(Access.WRONG_ORG_READ).toString(), + HttpMethod.GET) + Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response["status"]) + } } - @Test - fun `able to get public service by id`() { - val response = apiAuthorizedRequest( - "/catalogs/910244132/public-services/1", - port, - null, - JwtToken(Access.ORG_READ).toString(), - "GET") - Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) - val result: PublicService = mapper.readValue(response["body"] as String) - Assertions.assertEquals(PUBLIC_SERVICE_1, result) + @Nested + internal inner class GetPublicService { + @Test + fun `able to get public service by id`() { + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/1", + port, + null, + JwtToken(Access.ORG_READ).toString(), + HttpMethod.GET) + Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) + val result: PublicService = mapper.readValue(response["body"] as String) + Assertions.assertEquals(PUBLIC_SERVICE_1, result) + } + + @Test + fun `receive not found when public service is not found`() { + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/1000", + port, + null, + JwtToken(Access.ORG_READ).toString(), + HttpMethod.GET) + Assertions.assertEquals(HttpStatus.NOT_FOUND.value(), response["status"]) + } } - @Test - fun `receive not found when public service is not found`() { - val response = apiAuthorizedRequest( - "/catalogs/910244132/public-services/1000", - port, - null, - JwtToken(Access.ORG_READ).toString(), - "GET") - Assertions.assertEquals(HttpStatus.NOT_FOUND.value(), response["status"]) + @Nested + internal inner class PatchPublicService { + @Test + fun `unauthorized when missing token`() { + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services", + port, + null, + null, + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.UNAUTHORIZED.value(), response["status"]) + } + + @Test + fun `forbidden when authorized for other catalog`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.REPLACE, + path = "/title/nb", + value = "oppdater tittel")) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/1", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.WRONG_ORG_READ).toString(), + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response["status"]) + } + + @Test + fun `forbidden to replace public service nb tittle when authenticated as read user`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.REPLACE, + path = "/title/nb", + value = "oppdater tittel")) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/1", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.ORG_READ).toString(), + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response["status"]) + } + + @Test + fun `able to replace public service nb tittle when authenticated as write user`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.REPLACE, + path = "/title/nb", + value = "oppdater tittel")) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/1", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.ORG_WRITE).toString(), + HttpMethod.PATCH) + + Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) + + val result: PublicService = mapper.readValue(response["body"] as String) + val expected = PUBLIC_SERVICE_1.copy(title = PUBLIC_SERVICE_1.title!!.copy(nb = "oppdater tittel")) + + Assertions.assertEquals(expected, result) + } + + @Test + fun `forbidden to remove public service title when authenticated as read user`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.REMOVE, + path = "/title/nb")) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/1", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.ORG_READ).toString(), + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response["status"]) + } + + @Test + fun `able to remove public service title when authenticated as write user`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.REMOVE, + path = "/title/nb")) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/1", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.ORG_WRITE).toString(), + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) + + val result: PublicService = mapper.readValue(response["body"] as String) + val expected = PUBLIC_SERVICE_1.copy(title = PUBLIC_SERVICE_1.title!!.copy(nb = null)) + + Assertions.assertEquals(expected, result) + } + + @Test + fun `forbidden to add public service description when authenticated as read user`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.ADD, + path = "/description", + value = LocalizedStrings("added nb description", null, null))) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/2", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.ORG_READ).toString(), + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response["status"]) + } + + @Test + fun `able to add public service description when authenticated as write user`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.ADD, + path = "/description", + value = LocalizedStrings("added nb description", null, null))) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/2", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.ORG_WRITE).toString(), + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) + + val result: PublicService = mapper.readValue(response["body"] as String) + val expected = PUBLIC_SERVICE_2 + .copy(description = LocalizedStrings(nb = "added nb description", null, null)) + + Assertions.assertEquals(expected, result) + } + + @Test + fun `forbidden to move public service nb title to nn when authenticated as read user`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.MOVE, + path = "/title/nn", + from = "/title/nb")) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/0", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.ORG_READ).toString(), + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response["status"]) + } + + @Test + fun `able to move public service nb title to nn when authenticated as write user`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.MOVE, + path = "/title/nn", + from = "/title/nb")) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/0", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.ORG_WRITE).toString(), + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) + + val result: PublicService = mapper.readValue(response["body"] as String) + val expected = PUBLIC_SERVICE_0 + .copy(title = PUBLIC_SERVICE_0.title!!.copy(null, nn = "NB Tittel 0")) + + Assertions.assertEquals(expected, result) + } + + @Test + fun `forbidden to copy public service nb description to nn when authenticated as read user`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.COPY, + path = "/title/nn", + from = "/title/nb")) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/1", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.ORG_READ).toString(), + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.FORBIDDEN.value(), response["status"]) + } + + @Test + fun `able to copy public service nb description to nn when authenticated as write user`() { + val operations = listOf(JsonPatchOperation( + op = OpEnum.COPY, + path = "/title/nn", + from = "/title/nb")) + val response = apiAuthorizedRequest( + "/catalogs/910244132/public-services/1", + port, + mapper.writeValueAsString(operations), + JwtToken(Access.ORG_WRITE).toString(), + HttpMethod.PATCH) + Assertions.assertEquals(HttpStatus.OK.value(), response["status"]) + + val result: PublicService = mapper.readValue(response["body"] as String) + val expected = PUBLIC_SERVICE_1 + .copy(title = PUBLIC_SERVICE_1.title!!.copy(nb = "NB Tittel 1", nn = "NB Tittel 1")) + + Assertions.assertEquals(expected, result) + } } } diff --git a/src/test/kotlin/no/digdir/service_catalog/utils/TestData.kt b/src/test/kotlin/no/digdir/service_catalog/utils/TestData.kt index f0446e8..165aa86 100644 --- a/src/test/kotlin/no/digdir/service_catalog/utils/TestData.kt +++ b/src/test/kotlin/no/digdir/service_catalog/utils/TestData.kt @@ -17,16 +17,20 @@ val MONGO_ENV_VALUES: Map = ImmutableMap.of( val SERVICE_0 = Service("0", "title 0") val SERVICE_1 = Service("1", "title 1") +val SERVICE_2 = Service("2", "title 2") -val SERVICES = listOf(SERVICE_0, SERVICE_1) +val SERVICES = listOf(SERVICE_0, SERVICE_1, SERVICE_2) val PUBLIC_SERVICE_0 = PublicService("0", "910244132", - title = LocalizedStrings("Tittel 0", "Tittel 0", "Tittel 0"), + title = LocalizedStrings("NB Tittel 0", "NN Tittel 0", "EN Tittel 0"), description = LocalizedStrings("Beskrivelse 0", "Beskriving 0", "Description 0")) val PUBLIC_SERVICE_1 = PublicService("1", "910244132", - title = LocalizedStrings("Tittel 1", "Tittel 1", "Tittel 1"), + title = LocalizedStrings("NB Tittel 1", "NN Tittel 1", "EN Tittel 1"), description = LocalizedStrings("Beskrivelse 1", "Beskriving 1", "Description 1")) +val PUBLIC_SERVICE_2 = + PublicService("2", "910244132", + title = LocalizedStrings("NB Tittel 2", "NN Tittel 2", "EN Tittel 2"), null) -val PUBLIC_SERVICES = listOf(PUBLIC_SERVICE_0, PUBLIC_SERVICE_1) +val PUBLIC_SERVICES = listOf(PUBLIC_SERVICE_0, PUBLIC_SERVICE_1, PUBLIC_SERVICE_2) diff --git a/src/test/kotlin/no/digdir/service_catalog/utils/TestUtils.kt b/src/test/kotlin/no/digdir/service_catalog/utils/TestUtils.kt index a26bc03..8808b47 100644 --- a/src/test/kotlin/no/digdir/service_catalog/utils/TestUtils.kt +++ b/src/test/kotlin/no/digdir/service_catalog/utils/TestUtils.kt @@ -1,6 +1,13 @@ package no.digdir.service_catalog.utils +import org.springframework.http.HttpEntity +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory +import org.springframework.web.client.HttpClientErrorException +import org.springframework.web.client.RestTemplate import java.io.BufferedReader import java.io.OutputStreamWriter import java.net.HttpURLConnection @@ -16,21 +23,21 @@ fun apiGet(port: Int, endpoint: String, acceptHeader: String?): Map if(isOK(connection.responseCode)) { val responseBody = connection.inputStream.bufferedReader().use(BufferedReader::readText) mapOf( - "body" to responseBody, - "header" to connection.headerFields.toString(), - "status" to connection.responseCode) + "body" to responseBody, + "header" to connection.headerFields.toString(), + "status" to connection.responseCode) } else { mapOf( - "status" to connection.responseCode, - "header" to " ", - "body" to " " + "status" to connection.responseCode, + "header" to " ", + "body" to " " ) } } catch (e: Exception) { mapOf( - "status" to e.toString(), - "header" to " ", - "body" to " " + "status" to e.toString(), + "header" to " ", + "body" to " " ) }} @@ -38,44 +45,40 @@ private fun isOK(response: Int?): Boolean = if(response == null) false else HttpStatus.resolve(response)?.is2xxSuccessful == true -fun apiAuthorizedRequest(path: String, port: Int, body: String?, token: String?, method: String): Map { - val connection = URL("http://localhost:$port$path").openConnection() as HttpURLConnection - connection.requestMethod = method - connection.setRequestProperty("Content-type", "application/json") - connection.setRequestProperty("Accept", "application/json") +fun apiAuthorizedRequest( + path: String, port: Int, body: String?, token: String?, httpMethod: HttpMethod, + accept: MediaType = MediaType.APPLICATION_JSON +): Map { - if(!token.isNullOrEmpty()) { - connection.setRequestProperty("Authorization", "Bearer $token") - } - return try { - connection.doOutput = true - connection.connect() + val request = RestTemplate() + request.requestFactory = HttpComponentsClientHttpRequestFactory() + val url = "http://localhost:$port$path" + val headers = HttpHeaders() + headers.accept = listOf(accept) + token?.let { headers.setBearerAuth(it) } + headers.contentType = MediaType.APPLICATION_JSON + val entity: HttpEntity = HttpEntity(body, headers) - if(body != null) { - val writer = OutputStreamWriter(connection.outputStream) - writer.write(body) - writer.close() - } + return try { + val response = request.exchange(url, httpMethod, entity, String::class.java) + mapOf( + "body" to response.body, + "header" to response.headers, + "status" to response.statusCode.value() + ) - if(isOK(connection.responseCode)){ - mapOf( - "body" to connection.inputStream.bufferedReader().use(BufferedReader :: readText), - "header" to connection.headerFields.toString(), - "status" to connection.responseCode - ) - } else { - mapOf( - "status" to connection.responseCode, - "header" to " ", - "body" to " " - ) - } + } catch (e: HttpClientErrorException) { + mapOf( + "status" to e.statusCode.value(), + "header" to " ", + "body" to e.toString() + ) } catch (e: Exception) { mapOf( - "status" to e.toString(), - "header" to " ", - "body" to " " + "status" to e.toString(), + "header" to " ", + "body" to " " ) } }