Skip to content

Commit

Permalink
Use radar-jersey 0.11.1 and fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pvannierop committed Oct 14, 2024
1 parent 4aa1d7d commit 905ea2d
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,8 @@

package org.radarbase.datadashboard.api.domain

import jakarta.inject.Provider
import jakarta.persistence.EntityManager
import jakarta.ws.rs.core.Context
import org.radarbase.datadashboard.api.domain.model.Observation
import org.radarbase.jersey.hibernate.HibernateRepository
import org.slf4j.LoggerFactory

class ObservationRepository(
@Context em: Provider<EntityManager>,
) : HibernateRepository(em) {

fun getObservations(projectId: String, subjectId: String, topicId: String): List<Observation> {
logger.debug("Get observations in topic {} of subject {} in project {}", topicId, subjectId, projectId)

return transact {
createQuery(
"SELECT o FROM Observation o WHERE o.project = :projectId AND o.subject = :subjectId AND o.topic = :topicId ORDER BY o.observationTime DESC",
Observation::class.java,
).apply {
setParameter("projectId", projectId)
setParameter("subjectId", subjectId)
setParameter("topicId", topicId)
}.resultList
}
}

companion object {
private val logger = LoggerFactory.getLogger(ObservationRepository::class.java)
}
interface ObservationRepository {
suspend fun getObservations(projectId: String, subjectId: String, topicId: String): List<Observation>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
*
* * Copyright 2024 The Hyve
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/

package org.radarbase.datadashboard.api.domain

import jakarta.inject.Provider
import jakarta.persistence.EntityManager
import jakarta.ws.rs.core.Context
import org.radarbase.datadashboard.api.domain.model.Observation
import org.radarbase.jersey.hibernate.HibernateRepository
import org.radarbase.jersey.service.AsyncCoroutineService
import org.slf4j.LoggerFactory

class ObservationRepositoryImpl(
@Context em: Provider<EntityManager>,
@Context asyncService: AsyncCoroutineService,
) : HibernateRepository(em, asyncService), ObservationRepository {

override suspend fun getObservations(projectId: String, subjectId: String, topicId: String): List<Observation> {
logger.debug("Get observations in topic {} of subject {} in project {}", topicId, subjectId, projectId)

return transact {
createQuery(
"SELECT o FROM Observation o WHERE o.project = :projectId AND o.subject = :subjectId AND o.topic = :topicId ORDER BY o.observationTime DESC",
Observation::class.java,
).apply {
setParameter("projectId", projectId)
setParameter("subjectId", subjectId)
setParameter("topicId", topicId)
}.resultList
}
}

companion object {
private val logger = LoggerFactory.getLogger(ObservationRepositoryImpl::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import jakarta.inject.Singleton
import org.glassfish.jersey.internal.inject.AbstractBinder
import org.radarbase.datadashboard.api.config.DashboardApiConfig
import org.radarbase.datadashboard.api.domain.ObservationRepository
import org.radarbase.datadashboard.api.domain.ObservationRepositoryImpl
import org.radarbase.datadashboard.api.service.ObservationService
import org.radarbase.datadashboard.api.service.ObservationServiceImpl
import org.radarbase.jersey.enhancer.JerseyResourceEnhancer
import org.radarbase.jersey.filter.Filters

Expand All @@ -44,11 +46,11 @@ class DashboardApiEnhancer(
bind(config)
.to(DashboardApiConfig::class.java)

bind(ObservationService::class.java)
bind(ObservationServiceImpl::class.java)
.to(ObservationService::class.java)
.`in`(Singleton::class.java)

bind(ObservationRepository::class.java)
bind(ObservationRepositoryImpl::class.java)
.to(ObservationRepository::class.java)
.`in`(Singleton::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,31 @@
package org.radarbase.datadashboard.api.resource

import jakarta.annotation.Resource
import jakarta.inject.Singleton
import jakarta.ws.rs.Consumes
import jakarta.ws.rs.GET
import jakarta.ws.rs.Path
import jakarta.ws.rs.PathParam
import jakarta.ws.rs.Produces
import jakarta.ws.rs.container.AsyncResponse
import jakarta.ws.rs.container.Suspended
import jakarta.ws.rs.core.Context
import org.radarbase.auth.authorization.Permission
import org.radarbase.datadashboard.api.api.ObservationListDto
import org.radarbase.datadashboard.api.service.ObservationService
import org.radarbase.jersey.auth.Authenticated
import org.radarbase.jersey.auth.NeedsPermission
import org.radarbase.jersey.service.AsyncCoroutineService
import org.slf4j.LoggerFactory

@Path("project/{projectId}/subject/{subjectId}/topic/{topicId}")
@Resource
@Produces("application/json")
@Consumes("application/json")
@Authenticated
@Singleton
class ObservationResource(
@Context private val observationService: ObservationService,
@Context private val asyncService: AsyncCoroutineService,
) {
@GET
@Path("observations")
Expand All @@ -47,8 +52,9 @@ class ObservationResource(
@PathParam("projectId") projectId: String,
@PathParam("subjectId") subjectId: String,
@PathParam("topicId") topicId: String,
): ObservationListDto {
return observationService.getObservations(projectId = projectId, subjectId = subjectId, topicId = topicId)
@Suspended asyncResponse: AsyncResponse,
) = asyncService.runAsCoroutine(asyncResponse) {
observationService.getObservations(projectId = projectId, subjectId = subjectId, topicId = topicId)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,8 @@

package org.radarbase.datadashboard.api.service

import jakarta.ws.rs.core.Context
import org.radarbase.datadashboard.api.api.ObservationListDto
import org.radarbase.datadashboard.api.domain.ObservationRepository
import org.radarbase.datadashboard.api.domain.mapper.toDto

class ObservationService(
@Context private val observationRepository: ObservationRepository,
) {
fun getObservations(projectId: String, subjectId: String, topicId: String): ObservationListDto {
val result = this.observationRepository.getObservations(projectId = projectId, topicId = topicId, subjectId = subjectId)
return ObservationListDto(
result.map { it.toDto() },
)
}
interface ObservationService {
suspend fun getObservations(projectId: String, subjectId: String, topicId: String): ObservationListDto
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
*
* * Copyright 2024 The Hyve
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/

package org.radarbase.datadashboard.api.service

import jakarta.ws.rs.core.Context
import org.radarbase.datadashboard.api.api.ObservationListDto
import org.radarbase.datadashboard.api.domain.ObservationRepository
import org.radarbase.datadashboard.api.domain.mapper.toDto

class ObservationServiceImpl(
@Context private val observationRepository: ObservationRepository,
) : ObservationService {
override suspend fun getObservations(projectId: String, subjectId: String, topicId: String): ObservationListDto {
val result = this.observationRepository.getObservations(projectId = projectId, topicId = topicId, subjectId = subjectId)
return ObservationListDto(
result.map { it.toDto() },
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,29 @@ import org.glassfish.jersey.test.spi.TestContainerFactory
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.radarbase.auth.authorization.AuthorizationOracle
import org.radarbase.datadashboard.api.config.DashboardApiConfig
import org.radarbase.datadashboard.api.resource.ObservationResource
import org.radarbase.datadashboard.api.enhancer.DashBoardApiEnhancerFactory
import org.radarbase.jersey.auth.AuthValidator
import org.radarbase.jersey.auth.disabled.DisabledAuthValidator
import org.radarbase.jersey.auth.disabled.DisabledAuthorizationOracle
import org.radarbase.jersey.config.ConfigLoader

// These tests are ok, but the injection of the DisabledAuthValidator is not correct.
// I do not know how to fix it.
// These tests are not yet working because mocking/stubbing token validation is not yet working.
class DashboardIntegrationTest : JerseyTest() {

lateinit var disabledAuthValidator: DisabledAuthValidator

override fun configure(): ResourceConfig {
val config: DashboardApiConfig = ConfigLoader.loadConfig("src/test/resources/dashboard_test.yml", emptyArray())
val resourceConfig = ConfigLoader.loadResources(config.service.resourceConfig, config)
disabledAuthValidator = DisabledAuthValidator(config.auth)
resourceConfig.register(DashBoardApiEnhancerFactory::class)
val disabledAuthorizationOracle = DisabledAuthorizationOracle()
val disabledAuthValidator = DisabledAuthValidator(config.auth)
resourceConfig.register(object : AbstractBinder() {
override fun configure() {
bind(disabledAuthorizationOracle).to(AuthorizationOracle::class.java).ranked(1)
bind(disabledAuthValidator).to(AuthValidator::class.java).ranked(1)
}
})
resourceConfig.register(ObservationResource::class.java)
return resourceConfig
}

Expand All @@ -74,18 +75,24 @@ class DashboardIntegrationTest : JerseyTest() {
@Disabled("Token integration tests do not work yet.")
@Test
fun testGetObservationsNoToken() {
val response = target("project/project-1/subject/sub-1/topic/phone_battery_level/observations").request().get()
Assertions.assertEquals(401, response.status)
target("project/project-1/subject/sub-1/topic/phone_battery_level/observations")
.request()
.get()
.use { response ->
Assertions.assertEquals(401, response.status)
}
}

@Disabled("Token integration tests do not work yet.")
@Test
fun testGetObservationsWithToken() {
val response = target("project/project-1/subject/sub-1/topic/phone_battery_level/observations")
target("project/project-1/subject/sub-1/topic/phone_battery_level/observations")
.request()
.header(HttpHeaders.AUTHORIZATION, "Bearer " + "... encoded token ...")
.get()
Assertions.assertEquals(200, response.status)
.use { response ->
Assertions.assertEquals(200, response.status)
}
}

// TODO add more tests that include the token validation.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.radarbase.upload.mock

import jakarta.ws.rs.container.AsyncResponse
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import org.radarbase.jersey.service.AsyncCoroutineService
import kotlin.time.Duration

class MockAsyncCoroutineService : AsyncCoroutineService {
override fun <T> runAsCoroutine(
asyncResponse: AsyncResponse,
timeout: Duration,
block: suspend () -> T,
) {
runBlocking {
val result = block()
asyncResponse.resume(result)
}
}

override fun <T> runBlocking(timeout: Duration, block: suspend () -> T): T =
kotlinx.coroutines.runBlocking {
block()
}

override suspend fun <T> runInRequestScope(block: () -> T): T = block()

override suspend fun <T> suspendInRequestScope(block: (CancellableContinuation<T>) -> Unit): T = suspendCancellableCoroutine(block)
override suspend fun <T> withContext(name: String, block: suspend () -> T): T {
TODO("Not yet implemented")
}
}
Loading

0 comments on commit 905ea2d

Please sign in to comment.