diff --git a/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/HankeServiceITests.kt b/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/HankeServiceITests.kt index 9d3058576..58c80bef0 100644 --- a/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/HankeServiceITests.kt +++ b/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/HankeServiceITests.kt @@ -1,10 +1,14 @@ package fi.hel.haitaton.hanke +import assertk.all +import assertk.assertFailure import assertk.assertions.each +import assertk.assertions.hasClass import assertk.assertions.hasSize import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isNull +import assertk.assertions.messageContains import com.ninjasquad.springmockk.MockkBean import fi.hel.haitaton.hanke.allu.ApplicationStatus import fi.hel.haitaton.hanke.allu.CableReportService @@ -66,6 +70,7 @@ import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.skyscreamer.jsonassert.JSONAssert @@ -101,6 +106,7 @@ class HankeServiceITests : DatabaseTest() { @Autowired private lateinit var hankeKayttajaRepository: HankeKayttajaRepository @Autowired private lateinit var kayttajaTunnisteRepository: KayttajaTunnisteRepository @Autowired private lateinit var jdbcTemplate: JdbcTemplate + @Autowired private lateinit var hankeFactory: HankeFactory @BeforeEach fun clearMocks() { @@ -113,6 +119,50 @@ class HankeServiceITests : DatabaseTest() { confirmVerified(cableReportService) } + @Nested + inner class GetHankeId { + private val hankeTunnus = "HAI23-41" + + @Test + fun `Returns null if hanke not found`() { + val response = hankeService.getHankeId(hankeTunnus) + + assertThat(response).isNull() + } + + @Test + fun `Returns hanke id if hanke found`() { + val hanke = hankeFactory.save() + + val response = hankeService.getHankeId(hanke.hankeTunnus!!) + + assertThat(response).isEqualTo(hanke.id!!) + } + } + + @Nested + inner class GetHankeIdOrThrow { + private val hankeTunnus = "HAI23-41" + + @Test + fun `Throws exception if hanke not found`() { + assertFailure { hankeService.getHankeIdOrThrow(hankeTunnus) } + .all { + hasClass(HankeNotFoundException::class) + messageContains(hankeTunnus) + } + } + + @Test + fun `Returns hanke id if hanke found`() { + val hanke = hankeFactory.save() + + val response = hankeService.getHankeIdOrThrow(hanke.hankeTunnus!!) + + assertThat(response).isEqualTo(hanke.id!!) + } + } + @Test fun `create Hanke with full data set succeeds and returns a new domain object with the correct values`() { val hanke: Hanke = getATestHanke().withYhteystiedot { it.id = null }.withHankealue() @@ -302,12 +352,13 @@ class HankeServiceITests : DatabaseTest() { @Test fun `getHankeHakemuksetPair hanke does not exist throws not found`() { - val exception = - assertThrows { - hankeService.getHankeWithApplications("HAI-1234") - } + val hankeTunnus = "HAI-1234" - assertThat(exception).hasMessage("Hanke HAI-1234 not found") + assertFailure { hankeService.getHankeWithApplications(hankeTunnus) } + .all { + hasClass(HankeNotFoundException::class) + messageContains(hankeTunnus) + } } @Test diff --git a/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/permissions/HankeKayttajaControllerITest.kt b/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/permissions/HankeKayttajaControllerITest.kt index 5adaa70cc..5d829c3c3 100644 --- a/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/permissions/HankeKayttajaControllerITest.kt +++ b/services/hanke-service/src/integrationTest/kotlin/fi/hel/haitaton/hanke/permissions/HankeKayttajaControllerITest.kt @@ -68,6 +68,58 @@ class HankeKayttajaControllerITest(@Autowired override val mockMvc: MockMvc) : C confirmVerified(hankeKayttajaService, hankeService, permissionService) } + @Nested + inner class Whoami { + private val url = "/hankkeet/$HANKE_TUNNUS/whoami" + private val hankeId = 14 + private val kayttooikeustaso = Kayttooikeustaso.KAIKKIEN_MUOKKAUS + + private val permissionEntity = + PermissionEntity( + hankeId = hankeId, + userId = USERNAME, + kayttooikeustaso = KayttooikeustasoEntity(0, kayttooikeustaso, 0) + ) + + @Test + fun `Returns 404 if hanke not found`() { + every { hankeService.getHankeIdOrThrow(HANKE_TUNNUS) } throws + HankeNotFoundException(HANKE_TUNNUS) + + get(url).andExpect(status().isNotFound).andExpect(hankeError(HankeError.HAI1001)) + + verify { hankeService.getHankeIdOrThrow(HANKE_TUNNUS) } + } + + @Test + fun `Returns 404 if permission not found`() { + every { hankeService.getHankeIdOrThrow(HANKE_TUNNUS) } returns hankeId + every { permissionService.findPermission(hankeId, USERNAME) } returns null + + get(url).andExpect(status().isNotFound).andExpect(hankeError(HankeError.HAI1001)) + + verifySequence { + hankeService.getHankeIdOrThrow(HANKE_TUNNUS) + permissionService.findPermission(hankeId, USERNAME) + } + } + + @Test + fun `Returns kayttooikeustaso`() { + every { hankeService.getHankeIdOrThrow(HANKE_TUNNUS) } returns hankeId + every { permissionService.findPermission(hankeId, USERNAME) } returns permissionEntity + + val response: WhoamiResponse = get(url).andExpect(status().isOk).andReturnBody() + + val expectedResponse = WhoamiResponse(USERNAME, kayttooikeustaso) + assertThat(response).isEqualTo(expectedResponse) + verifySequence { + hankeService.getHankeIdOrThrow(HANKE_TUNNUS) + permissionService.findPermission(hankeId, USERNAME) + } + } + } + @Nested inner class GetHankeKayttajat { diff --git a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeError.kt b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeError.kt index 486af75c0..b68dc60b9 100644 --- a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeError.kt +++ b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeError.kt @@ -71,7 +71,7 @@ data class HankeErrorDetail( ) class HankeNotFoundException(val hankeTunnus: String?) : - RuntimeException("Hanke $hankeTunnus not found") + RuntimeException("Hanke not found with hankeTunnus $hankeTunnus") class HankeArgumentException(message: String) : RuntimeException(message) diff --git a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeService.kt b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeService.kt index 5f3dcee2d..222fe32e1 100644 --- a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeService.kt +++ b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeService.kt @@ -14,6 +14,8 @@ interface HankeService { fun getHankeId(hankeTunnus: String): Int? + fun getHankeIdOrThrow(hankeTunnus: String): Int + fun getHankeWithApplications(hankeTunnus: String): HankeWithApplications @Transactional fun createHanke(hanke: Hanke): Hanke diff --git a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeServiceImpl.kt b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeServiceImpl.kt index a31473e71..ee94099f2 100644 --- a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeServiceImpl.kt +++ b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/HankeServiceImpl.kt @@ -78,6 +78,9 @@ open class HankeServiceImpl( override fun getHankeId(hankeTunnus: String): Int? = hankeRepository.findByHankeTunnus(hankeTunnus)?.id + override fun getHankeIdOrThrow(hankeTunnus: String): Int = + getHankeId(hankeTunnus) ?: throw HankeNotFoundException(hankeTunnus) + /** * Hanke does not contain hakemukset. This function wraps Hanke and its hakemukset to a pair. */ diff --git a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/permissions/HankeKayttajaController.kt b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/permissions/HankeKayttajaController.kt index 751a397d2..16e5c69c2 100644 --- a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/permissions/HankeKayttajaController.kt +++ b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/permissions/HankeKayttajaController.kt @@ -1,6 +1,7 @@ package fi.hel.haitaton.hanke.permissions import fi.hel.haitaton.hanke.HankeError +import fi.hel.haitaton.hanke.HankeNotFoundException import fi.hel.haitaton.hanke.HankeService import fi.hel.haitaton.hanke.configuration.Feature import fi.hel.haitaton.hanke.configuration.FeatureFlags @@ -36,6 +37,30 @@ class HankeKayttajaController( private val disclosureLogService: DisclosureLogService, private val featureFlags: FeatureFlags, ) { + @GetMapping("/hankkeet/{hankeTunnus}/whoami") + @Operation(summary = "Get your own permission for a hanke") + @ApiResponses( + value = + [ + ApiResponse( + description = "Your permissions", + responseCode = "200", + content = [Content(schema = Schema(implementation = WhoamiResponse::class))] + ), + ApiResponse( + description = "Hanke not found", + responseCode = "404", + content = [Content(schema = Schema(implementation = HankeError::class))] + ), + ] + ) + fun whoami(@PathVariable hankeTunnus: String): WhoamiResponse { + val userId = currentUserId() + val hankeId = hankeService.getHankeIdOrThrow(hankeTunnus) + + return permissionService.findPermission(hankeId, userId)?.let { WhoamiResponse(it) } + ?: throw HankeNotFoundException(hankeTunnus) + } @GetMapping("/hankkeet/{hankeTunnus}/kayttajat") @Operation( diff --git a/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/permissions/WhoamiResponse.kt b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/permissions/WhoamiResponse.kt new file mode 100644 index 000000000..3f2b6ad70 --- /dev/null +++ b/services/hanke-service/src/main/kotlin/fi/hel/haitaton/hanke/permissions/WhoamiResponse.kt @@ -0,0 +1,7 @@ +package fi.hel.haitaton.hanke.permissions + +data class WhoamiResponse(val userId: String, val kayttooikeustaso: Kayttooikeustaso) { + constructor( + permissionEntity: PermissionEntity + ) : this(permissionEntity.userId, permissionEntity.kayttooikeustaso.kayttooikeustaso) +} diff --git a/services/hanke-service/src/test/kotlin/fi/hel/haitaton/hanke/HankeServiceTest.kt b/services/hanke-service/src/test/kotlin/fi/hel/haitaton/hanke/HankeServiceTest.kt new file mode 100644 index 000000000..06be17f61 --- /dev/null +++ b/services/hanke-service/src/test/kotlin/fi/hel/haitaton/hanke/HankeServiceTest.kt @@ -0,0 +1,99 @@ +package fi.hel.haitaton.hanke + +import assertk.all +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.hasClass +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull +import assertk.assertions.isNull +import assertk.assertions.messageContains +import fi.hel.haitaton.hanke.application.ApplicationService +import fi.hel.haitaton.hanke.geometria.GeometriatService +import fi.hel.haitaton.hanke.logging.AuditLogService +import fi.hel.haitaton.hanke.logging.HankeLoggingService +import fi.hel.haitaton.hanke.permissions.HankeKayttajaService +import fi.hel.haitaton.hanke.permissions.PermissionService +import fi.hel.haitaton.hanke.tormaystarkastelu.TormaystarkasteluLaskentaService +import io.mockk.every +import io.mockk.mockk +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class HankeServiceTest { + + private val hankeRepository: HankeRepository = mockk() + private val tormaystarkasteluService: TormaystarkasteluLaskentaService = mockk() + private val hanketunnusService: HanketunnusService = mockk() + private val geometriatService: GeometriatService = mockk() + private val auditLogService: AuditLogService = mockk() + private val hankeLoggingService: HankeLoggingService = mockk() + private val applicationService: ApplicationService = mockk() + private val permissionService: PermissionService = mockk() + private val hankeKayttajaService: HankeKayttajaService = mockk() + + private val hankeService = + HankeServiceImpl( + hankeRepository, + tormaystarkasteluService, + hanketunnusService, + geometriatService, + auditLogService, + hankeLoggingService, + applicationService, + permissionService, + hankeKayttajaService, + ) + + @Nested + inner class GetHankeId { + val hankeTunnus = "HAI23-1" + val hankeId = 9984 + + @Test + fun `Returns hanke id if hanke found`() { + every { hankeRepository.findByHankeTunnus(hankeTunnus) } returns + HankeEntity(id = hankeId) + + val response = hankeService.getHankeId(hankeTunnus) + + assertThat(response).isNotNull().isEqualTo(hankeId) + } + + @Test + fun `Returns null if hanke not found`() { + every { hankeRepository.findByHankeTunnus(hankeTunnus) } returns null + + val response = hankeService.getHankeId(hankeTunnus) + + assertThat(response).isNull() + } + } + + @Nested + inner class GetHankeIdOrThrow { + val hankeTunnus = "HAI23-1" + val hankeId = 9984 + + @Test + fun `Returns hanke id if hanke found`() { + every { hankeRepository.findByHankeTunnus(hankeTunnus) } returns + HankeEntity(id = hankeId) + + val response = hankeService.getHankeIdOrThrow(hankeTunnus) + + assertThat(response).isNotNull().isEqualTo(hankeId) + } + + @Test + fun `Returns null if hanke not found`() { + every { hankeRepository.findByHankeTunnus(hankeTunnus) } returns null + + assertFailure { hankeService.getHankeIdOrThrow(hankeTunnus) } + .all { + hasClass(HankeNotFoundException::class) + messageContains(hankeTunnus) + } + } + } +}