Skip to content

Commit

Permalink
feat: add service count (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
hegeaal authored Dec 20, 2023
1 parent baa79c7 commit 224b152
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package no.digdir.servicecatalog.controller

import no.digdir.servicecatalog.model.*
import no.digdir.servicecatalog.security.EndpointPermissions
import no.digdir.servicecatalog.service.CountService
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.CrossOrigin
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping

@Controller
@CrossOrigin
@RequestMapping(value = ["/internal/catalogs/count"])
class CatalogCountController(
private val endpointPermissions: EndpointPermissions,
private val countService: CountService
) {

@GetMapping(produces = [MediaType.APPLICATION_JSON_VALUE])
fun getServiceCountsForPermittedCatalogs(
@AuthenticationPrincipal jwt: Jwt
): ResponseEntity<List<ServiceCount>> {
return when {
endpointPermissions.hasSysAdminPermission(jwt) -> {
ResponseEntity(
countService.getServiceCountForAllCatalogs(),
HttpStatus.OK
)
}
else -> ResponseEntity(
countService.getServiceCountForListOfCatalogs(
endpointPermissions.getOrgsWithMinimumReadPermission(jwt)
), HttpStatus.OK
)
}
}
}
12 changes: 12 additions & 0 deletions src/main/kotlin/no/digdir/servicecatalog/model/ServiceCount.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package no.digdir.servicecatalog.model

import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonInclude

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
data class ServiceCount (
val catalogId: String,
val serviceCount: Int,
val publicServiceCount: Int
)
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,21 @@ class EndpointPermissions {
else -> false
}
}
}
fun hasSysAdminPermission(jwt: Jwt): Boolean {
val authorities: String? = jwt.claims["authorities"] as? String

return authorities?.contains(ROLE_ROOT_ADMIN) ?: false
}

fun getOrgsWithMinimumReadPermission(jwt: Jwt): Set<String> {
val authorities: String? = jwt.claims["authorities"] as? String
val regex = Regex("""[0-9]{9}""")

return authorities
?.let { regex.findAll(it) }
?.map { matchResult -> matchResult.value }
?.toSet()
?: emptySet()
}

}
36 changes: 36 additions & 0 deletions src/main/kotlin/no/digdir/servicecatalog/service/CountService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package no.digdir.servicecatalog.service

import no.digdir.servicecatalog.model.ServiceCount
import no.digdir.servicecatalog.mongodb.PublicServiceRepository
import no.digdir.servicecatalog.mongodb.ServiceRepository
import org.springframework.stereotype.Service

@Service
class CountService(
private val serviceRepository: ServiceRepository,
private val publicServiceRepository: PublicServiceRepository,
) {
private fun getAllDistinctCatalogIds(): List<String> {
val serviceCatalogIds = serviceRepository.findAll().map { it.catalogId }
val publicServiceCatalogIds = publicServiceRepository.findAll().map { it.catalogId }

return (serviceCatalogIds + publicServiceCatalogIds).distinct()
}

fun getServiceCountForListOfCatalogs(catalogIds: Set<String>): List<ServiceCount> =
catalogIds.map { getServiceCountForCatalog(it) }

fun getServiceCountForAllCatalogs(): List<ServiceCount> =
getAllDistinctCatalogIds().map { getServiceCountForCatalog(it) }

private fun getServiceCountForCatalog(catalogId: String): ServiceCount =
ServiceCount(
catalogId = catalogId,
serviceCount = serviceRepository.getByCatalogId(catalogId)
.distinctBy { it.id }
.size,
publicServiceCount = publicServiceRepository.getByCatalogId(catalogId)
.distinctBy { it.id }
.size,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package no.digdir.servicecatalog.integration

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import no.digdir.servicecatalog.model.ServiceCount
import no.digdir.servicecatalog.utils.ApiTestContext
import no.digdir.servicecatalog.utils.LIST_OF_SERVICE_COUNTS_ROOT
import no.digdir.servicecatalog.utils.SERVICE_COUNT_1
import no.digdir.servicecatalog.utils.apiAuthorizedRequest
import no.digdir.servicecatalog.utils.jwt.Access
import no.digdir.servicecatalog.utils.jwt.JwtToken
import org.junit.jupiter.api.Assertions
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


@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = ["spring.profiles.active=test"]
)
@ContextConfiguration(initializers = [ApiTestContext.Initializer::class])
@Tag("integration")
class ServiceCountTests : ApiTestContext() {
private val mapper = jacksonObjectMapper()
val path = "/internal/catalogs/count"

@Test
fun `get service counts when sysAdmin`() {

val response = apiAuthorizedRequest(
path,
port,
null,
JwtToken(Access.ROOT).toString(),
HttpMethod.GET
)
Assertions.assertEquals(HttpStatus.OK.value(), response["status"])

val result: List<ServiceCount> = mapper.readValue(response["body"] as String)
Assertions.assertEquals(LIST_OF_SERVICE_COUNTS_ROOT, result)
}

@Test
fun `get service counts with read permission`() {
val response = apiAuthorizedRequest(
path,
port,
null,
JwtToken(Access.ORG_READ).toString(),
HttpMethod.GET
)
Assertions.assertEquals(HttpStatus.OK.value(), response["status"])

val result: List<ServiceCount> = mapper.readValue(response["body"] as String)
Assertions.assertEquals(listOf(SERVICE_COUNT_1), result)
}

@Test
fun `get service counts with write permission`() {
val response = apiAuthorizedRequest(
path,
port,
null,
JwtToken(Access.ORG_WRITE).toString(),
HttpMethod.GET
)
Assertions.assertEquals(HttpStatus.OK.value(), response["status"])

val result: List<ServiceCount> = mapper.readValue(response["body"] as String)
Assertions.assertEquals(listOf(SERVICE_COUNT_1), result)
}

@Test
fun `unauthorized when missing token`() {
val response = apiAuthorizedRequest(
path,
port,
null,
null,
HttpMethod.GET)
Assertions.assertEquals(HttpStatus.UNAUTHORIZED.value(), response["status"])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ abstract class ApiTestContext {

publicServiceRepository.deleteAll()
publicServiceRepository.saveAll(PUBLIC_SERVICES)
publicServiceRepository.save(PUBLIC_SERVICE_DIFFERENT_CATALOG)
}

internal class Initializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
Expand Down
19 changes: 12 additions & 7 deletions src/test/kotlin/no/digdir/servicecatalog/utils/TestData.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
package no.digdir.servicecatalog.utils

import no.digdir.servicecatalog.model.ContactPoint
import no.digdir.servicecatalog.model.LocalizedStrings
import no.digdir.servicecatalog.model.Output
import no.digdir.servicecatalog.model.PublicService
import no.digdir.servicecatalog.model.PublicServiceToBeCreated
import no.digdir.servicecatalog.model.Service
import no.digdir.servicecatalog.model.ServiceToBeCreated
import no.digdir.servicecatalog.model.*
import org.testcontainers.shaded.com.google.common.collect.ImmutableMap

const val MONGO_USER = "testuser"
Expand Down Expand Up @@ -79,7 +73,18 @@ val PUBLIC_SERVICE_2 =
title = LocalizedStrings("NB Tittel 2", "NN Tittel 2", "EN Tittel 2"),
description = null,
published = true, produces = null, contactPoints = null, homepage = null, status = null)
val PUBLIC_SERVICE_DIFFERENT_CATALOG =
PublicService("123", "123456789",
title = LocalizedStrings("NB Tittel 0", "NN Tittel 0", "EN Tittel 0"),
description = null,
published = true, produces = null, contactPoints = null, homepage = null, status = null)

val PUBLIC_SERVICE_TO_BE_CREATED = PublicServiceToBeCreated(title = LocalizedStrings("NB Tittel 2", "NN Tittel 2", "EN Tittel 2"), null, null, null, null, null)

val PUBLIC_SERVICES = listOf(PUBLIC_SERVICE_0, PUBLIC_SERVICE_1, PUBLIC_SERVICE_2)

val SERVICE_COUNT_1 = ServiceCount(catalogId = "910244132", serviceCount = 3, publicServiceCount = 3)

val SERVICE_COUNT_2 = ServiceCount(catalogId = "123456789", serviceCount = 0, publicServiceCount = 1 )

val LIST_OF_SERVICE_COUNTS_ROOT = listOf(SERVICE_COUNT_1, SERVICE_COUNT_2)

0 comments on commit 224b152

Please sign in to comment.