From 89fb37f5aa5f8a2e616c4110de0983997b7f6be1 Mon Sep 17 00:00:00 2001 From: Hege Aalvik Date: Fri, 10 Nov 2023 14:28:48 +0100 Subject: [PATCH 1/2] feat: add create method for public service --- .../controller/PublicServiceController.kt | 25 +++++- .../service_catalog/model/PublicService.kt | 6 ++ .../service/PublicServiceService.kt | 15 ++++ .../integration/PublicServices.kt | 83 +++++++++++++++++-- .../digdir/service_catalog/utils/TestData.kt | 3 + 5 files changed, 125 insertions(+), 7 deletions(-) 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 51326da..3c866c4 100644 --- a/src/main/kotlin/no/digdir/service_catalog/controller/PublicServiceController.kt +++ b/src/main/kotlin/no/digdir/service_catalog/controller/PublicServiceController.kt @@ -1,15 +1,16 @@ 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.model.* import no.digdir.service_catalog.security.EndpointPermissions import no.digdir.service_catalog.service.PublicServiceService +import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.security.oauth2.jwt.Jwt import org.springframework.stereotype.Controller +import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.CrossOrigin import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping @@ -73,6 +74,20 @@ class PublicServiceController(private val publicServiceService: PublicServiceSer ResponseEntity(HttpStatus.FORBIDDEN) } + @PostMapping(consumes = [MediaType.APPLICATION_JSON_VALUE]) + fun createPublicService( + @AuthenticationPrincipal jwt: Jwt, + @PathVariable catalogId: String, + @RequestBody publicServiceToBeCreated: PublicServiceToBeCreated + ): ResponseEntity = + if (endpointPermissions.hasOrgWritePermission(jwt, catalogId)) { + val created = publicServiceService.createPublicService(catalogId, publicServiceToBeCreated) + ResponseEntity( + locationHeaderForCreated(newId = created.id, catalogId), + HttpStatus.CREATED + ) + } else ResponseEntity(HttpStatus.FORBIDDEN) + @PostMapping(value = ["/{id}/publish"], produces = [MediaType.APPLICATION_JSON_VALUE]) fun publishPublicService( @AuthenticationPrincipal jwt: Jwt, @@ -85,3 +100,9 @@ class PublicServiceController(private val publicServiceService: PublicServiceSer ResponseEntity(HttpStatus.FORBIDDEN) } } + +private fun locationHeaderForCreated(newId: String, catalogId: String): HttpHeaders = + HttpHeaders().apply { + add(HttpHeaders.LOCATION, "/$catalogId/catalogs/{catalogId}/public-services/$newId") + add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.LOCATION) + } diff --git a/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt b/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt index 9bf2206..0ce183e 100644 --- a/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt +++ b/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt @@ -17,3 +17,9 @@ data class PublicService ( val description: LocalizedStrings?, val isPublished: Boolean = false ) + +@JsonIgnoreProperties(ignoreUnknown = true) +data class PublicServiceToBeCreated( + val title: LocalizedStrings, + val description: LocalizedStrings? +) 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 a4ba136..b07983b 100644 --- a/src/main/kotlin/no/digdir/service_catalog/service/PublicServiceService.kt +++ b/src/main/kotlin/no/digdir/service_catalog/service/PublicServiceService.kt @@ -3,12 +3,14 @@ package no.digdir.service_catalog.service import no.digdir.service_catalog.model.JsonPatchOperation import no.digdir.service_catalog.model.OpEnum import no.digdir.service_catalog.model.PublicService +import no.digdir.service_catalog.model.PublicServiceToBeCreated import no.digdir.service_catalog.mongodb.PublicServiceRepository import org.slf4j.LoggerFactory import org.springframework.data.repository.findByIdOrNull import org.springframework.http.HttpStatus import org.springframework.stereotype.Service import org.springframework.web.server.ResponseStatusException +import java.util.* @Service class PublicServiceService(private val publicServiceRepository: PublicServiceRepository) { @@ -42,6 +44,19 @@ class PublicServiceService(private val publicServiceRepository: PublicServiceRep throw ex } + fun createPublicService(catalogId: String, publicServiceToBeCreated: PublicServiceToBeCreated): PublicService = + try { + PublicService( + id = UUID.randomUUID().toString(), + catalogId = catalogId, + title = publicServiceToBeCreated.title, + description = publicServiceToBeCreated.description + ).let { publicServiceRepository.insert(it) } + } catch (ex: Exception) { + logger.error("Failed to create public service for $catalogId", ex) + throw ex + } + fun publishPublicService(id: String, catalogId: String): PublicService? = try { findPublicServiceById(id, catalogId) 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 d6fa4a6..c99fc9a 100644 --- a/src/test/kotlin/no/digdir/service_catalog/integration/PublicServices.kt +++ b/src/test/kotlin/no/digdir/service_catalog/integration/PublicServices.kt @@ -6,11 +6,7 @@ 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_1 -import no.digdir.service_catalog.utils.PUBLIC_SERVICE_2 -import no.digdir.service_catalog.utils.apiAuthorizedRequest +import no.digdir.service_catalog.utils.* import no.digdir.service_catalog.utils.jwt.Access import no.digdir.service_catalog.utils.jwt.JwtToken import org.junit.jupiter.api.Assertions @@ -22,6 +18,7 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus import org.springframework.test.context.ContextConfiguration +import kotlin.test.assertEquals @TestInstance(TestInstance.Lifecycle.PER_CLASS) @SpringBootTest( @@ -336,6 +333,82 @@ class PublicServices: ApiTestContext() { } } + @Nested + internal inner class CreatePublicService { + private val path = "/catalogs/910244132/public-services" + + @Test + fun `create PublicService as OrgWrite`() { + val before = apiAuthorizedRequest( + path, + port, + null, + JwtToken(Access.ORG_ADMIN).toString(), + HttpMethod.GET + ) + assertEquals(HttpStatus.OK.value(), before["status"]) + + val createResponse = apiAuthorizedRequest( + path, + port, + mapper.writeValueAsString(PUBLIC_SERVICE_TO_BE_CREATED), + JwtToken(Access.ORG_WRITE).toString(), + HttpMethod.POST + ) + assertEquals(HttpStatus.CREATED.value(), createResponse["status"]) + + val after = apiAuthorizedRequest( + path, + port, + null, + JwtToken(Access.ORG_ADMIN).toString(), + HttpMethod.GET + ) + assertEquals(HttpStatus.OK.value(), after["status"]) + + val allPublicServicesBefore: List = mapper.readValue(before["body"] as String) + val allPublicServicesAfter: List = mapper.readValue(after["body"] as String) + assertEquals(allPublicServicesBefore.size + 1, allPublicServicesAfter.size) + } + + @Test + fun `create PublicService as OrgAdmin`() { + val createResponse = apiAuthorizedRequest( + path, + port, + mapper.writeValueAsString(PUBLIC_SERVICE_TO_BE_CREATED), + JwtToken(Access.ORG_ADMIN).toString(), + HttpMethod.POST + ) + assertEquals(HttpStatus.CREATED.value(), createResponse["status"]) + } + + @Test + fun `create PublicService as OrgRead forbidden`() { + val createResponse = apiAuthorizedRequest( + path, + port, + mapper.writeValueAsString(PUBLIC_SERVICE_TO_BE_CREATED), + JwtToken(Access.ORG_READ).toString(), + HttpMethod.POST + ) + assertEquals(HttpStatus.FORBIDDEN.value(), createResponse["status"]) + } + + @Test + fun `create PublicService as WrongOrg forbidden`() { + val createResponse = apiAuthorizedRequest( + path, + port, + mapper.writeValueAsString(PUBLIC_SERVICE_TO_BE_CREATED), + JwtToken(Access.WRONG_ORG_WRITE).toString(), + HttpMethod.POST + ) + assertEquals(HttpStatus.FORBIDDEN.value(), createResponse["status"]) + } + } + + @Nested internal inner class PublishPublicService { val pathService1 = "/catalogs/910244132/public-services/1/publish" 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 f1789f1..f7c9747 100644 --- a/src/test/kotlin/no/digdir/service_catalog/utils/TestData.kt +++ b/src/test/kotlin/no/digdir/service_catalog/utils/TestData.kt @@ -2,6 +2,7 @@ package no.digdir.service_catalog.utils import no.digdir.service_catalog.model.LocalizedStrings import no.digdir.service_catalog.model.PublicService +import no.digdir.service_catalog.model.PublicServiceToBeCreated import no.digdir.service_catalog.model.Service import org.testcontainers.shaded.com.google.common.collect.ImmutableMap @@ -34,4 +35,6 @@ val PUBLIC_SERVICE_2 = PublicService("2", "910244132", title = LocalizedStrings("NB Tittel 2", "NN Tittel 2", "EN Tittel 2"), null) +val PUBLIC_SERVICE_TO_BE_CREATED = PublicServiceToBeCreated(title = LocalizedStrings("NB Tittel 2", "NN Tittel 2", "EN Tittel 2"), null) + val PUBLIC_SERVICES = listOf(PUBLIC_SERVICE_0, PUBLIC_SERVICE_1, PUBLIC_SERVICE_2) From da714db808993f245e20605d9f0343c3bcfad365 Mon Sep 17 00:00:00 2001 From: Hege Aalvik <48259307+hegeaal@users.noreply.github.com> Date: Mon, 13 Nov 2023 16:29:52 +0100 Subject: [PATCH 2/2] Update src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt Co-authored-by: Nils Ove Tendenes <50194012+NilsOveTen@users.noreply.github.com> --- .../kotlin/no/digdir/service_catalog/model/PublicService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt b/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt index 0ce183e..36e41d4 100644 --- a/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt +++ b/src/main/kotlin/no/digdir/service_catalog/model/PublicService.kt @@ -20,6 +20,6 @@ data class PublicService ( @JsonIgnoreProperties(ignoreUnknown = true) data class PublicServiceToBeCreated( - val title: LocalizedStrings, + val title: LocalizedStrings?, val description: LocalizedStrings? )