Skip to content

Commit

Permalink
fix: migrate notifications to Spring Boot 3
Browse files Browse the repository at this point in the history
  • Loading branch information
cyyynthia committed Jan 26, 2024
1 parent 8823857 commit 1d54b2e
Show file tree
Hide file tree
Showing 46 changed files with 720 additions and 543 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,20 @@ class NotificationPreferencesController(
fun updateGlobalPreferences(
@RequestBody @Validated preferencesDto: NotificationPreferencesDto,
): NotificationPreferencesDto {
val updated = notificationPreferencesService.setPreferencesOfUser(
authenticationFacade.authenticatedUser.id,
preferencesDto,
)
val updated =
notificationPreferencesService.setPreferencesOfUser(
authenticationFacade.authenticatedUser.id,
preferencesDto,
)

return NotificationPreferencesDto.fromEntity(updated)
}

@GetMapping("/project/{id}")
@Operation(summary = "Fetch the notification preferences of the current user for a specific project")
fun getPerProjectPreferences(@PathVariable("id") id: Long): NotificationPreferencesDto {
fun getPerProjectPreferences(
@PathVariable("id") id: Long,
): NotificationPreferencesDto {
return notificationPreferencesService.getProjectPreferences(
authenticationFacade.authenticatedUser.id,
id,
Expand All @@ -82,19 +85,22 @@ class NotificationPreferencesController(
@PathVariable("id") id: Long,
@RequestBody @Validated preferencesDto: NotificationPreferencesDto,
): NotificationPreferencesDto {
val updated = notificationPreferencesService.setProjectPreferencesOfUser(
authenticationFacade.authenticatedUser.id,
id,
preferencesDto,
)
val updated =
notificationPreferencesService.setProjectPreferencesOfUser(
authenticationFacade.authenticatedUser.id,
id,
preferencesDto,
)

return NotificationPreferencesDto.fromEntity(updated)
}

@DeleteMapping("/project/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Delete the notification preferences of the current user for a specific project")
fun deletePerProjectPreferences(@PathVariable("id") id: Long) {
fun deletePerProjectPreferences(
@PathVariable("id") id: Long,
) {
notificationPreferencesService.deleteProjectPreferencesOfUser(
authenticationFacade.authenticatedUser.id,
id,
Expand All @@ -103,15 +109,17 @@ class NotificationPreferencesController(

@PostMapping("/project/{id}/subscribe")
@Operation(summary = "Subscribe to notifications for a given project")
fun subscribeToProject(@PathVariable("id") id: Long): ResponseEntity<String> {
fun subscribeToProject(
@PathVariable("id") id: Long,
): ResponseEntity<String> {
return ResponseEntity(
"Coming soon! Please see https://github.com/tolgee/tolgee-platform/issues/1360 for progress on this. :D",
HttpHeaders().also {
@Suppress("UastIncorrectHttpHeaderInspection")
it.add(
"x-hey-curious-reader",
"oh hey there, didn't expect you here... " +
"if you're here, might as well join us! https://tolgee.io/career"
"if you're here, might as well join us! https://tolgee.io/career",
)
},
HttpStatus.NOT_IMPLEMENTED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import io.tolgee.hateoas.notifications.UserNotificationModelAssembler
import io.tolgee.notifications.NotificationStatus
import io.tolgee.notifications.UserNotificationService
import io.tolgee.security.authentication.AuthenticationFacade
import org.springdoc.api.annotations.ParameterObject
import org.springdoc.core.annotations.ParameterObject
import org.springframework.data.domain.Pageable
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.GetMapping
Expand All @@ -48,19 +48,22 @@ class NotificationsController(
@RequestParam("status", defaultValue = "UNREAD,READ") status: Set<NotificationStatus>,
@ParameterObject pageable: Pageable,
): List<UserNotificationModel> {
val notifications = userNotificationService.findNotificationsOfUserFilteredPaged(
authenticationFacade.authenticatedUser.id,
status,
pageable,
)
val notifications =
userNotificationService.findNotificationsOfUserFilteredPaged(
authenticationFacade.authenticatedUser.id,
status,
pageable,
)

return notifications.map { userNotificationModelAssembler.toModel(it) }
}

@PostMapping("/mark-as-read")
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Marks a given set of notifications as read.")
fun markNotificationsAsRead(@RequestBody notifications: List<Long>) {
fun markNotificationsAsRead(
@RequestBody notifications: List<Long>,
) {
userNotificationService.markAsRead(
authenticationFacade.authenticatedUser.id,
notifications,
Expand All @@ -77,7 +80,9 @@ class NotificationsController(
@PostMapping("/mark-as-unread")
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Marks a given set of notifications as unread.")
fun markNotificationsAsUnread(@RequestBody notifications: List<Long>) {
fun markNotificationsAsUnread(
@RequestBody notifications: List<Long>,
) {
userNotificationService.markAsUnread(
authenticationFacade.authenticatedUser.id,
notifications,
Expand All @@ -87,7 +92,9 @@ class NotificationsController(
@PostMapping("/mark-as-done")
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Marks a given set of notifications as done.")
fun markNotificationsAsDone(@RequestBody notifications: List<Long>) {
fun markNotificationsAsDone(
@RequestBody notifications: List<Long>,
) {
userNotificationService.markAsDone(
authenticationFacade.authenticatedUser.id,
notifications,
Expand All @@ -104,7 +111,9 @@ class NotificationsController(
@PostMapping("/unmark-as-done")
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Un-marks a given set of notifications as done.")
fun unmarkNotificationsAsDone(@RequestBody notifications: Collection<Long>) {
fun unmarkNotificationsAsDone(
@RequestBody notifications: Collection<Long>,
) {
userNotificationService.unmarkAsDone(
authenticationFacade.authenticatedUser.id,
notifications,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@ class UserNotificationModelAssembler(
private val batchJobModelAssembler: BatchJobModelAssembler,
private val applicationContext: ApplicationContext,
) : RepresentationModelAssemblerSupport<UserNotification, UserNotificationModel>(
NotificationsController::class.java, UserNotificationModel::class.java
) {
NotificationsController::class.java,
UserNotificationModel::class.java,
) {
override fun toModel(entity: UserNotification): UserNotificationModel {
val project = entity.project?.let { simpleProjectModelAssembler.toModel(it) }
val modifiedEntities = assembleEntityChanges(entity.modifiedEntities).ifEmpty { null }
val batchJob = entity.batchJob?.let {
val view = batchJobService.getView(it)
batchJobModelAssembler.toModel(view)
}
val batchJob =
entity.batchJob?.let {
val view = batchJobService.getView(it)
batchJobModelAssembler.toModel(view)
}

return UserNotificationModel(
id = entity.id,
Expand All @@ -58,10 +60,11 @@ class UserNotificationModelAssembler(
}

private fun assembleEntityChanges(modifiedEntities: List<ActivityModifiedEntity>): List<SimpleModifiedEntityView> {
val provider = ModifiedEntitiesViewProvider(
applicationContext,
modifiedEntities
)
val provider =
ModifiedEntitiesViewProvider(
applicationContext,
modifiedEntities,
)

return provider.getSimple()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class ProjectModelAssembler(
avatar = avatarService.getAvatarLinks(view.avatarHash),
organizationRole = view.organizationRole,
organizationOwner = view.organizationOwner.let { simpleOrganizationModelAssembler.toModel(it) },
baseLanguage = baseLanguage?.let { languageModelAssembler.toModel(LanguageDto.fromEntity(it, it.id)) },
baseLanguage = baseLanguage.let { languageModelAssembler.toModel(LanguageDto.fromEntity(it, it.id)) },
directPermission = view.directPermission?.let { permissionModelAssembler.toModel(it) },
computedPermission = computedPermissionModelAssembler.toModel(computedPermissions),
).add(link).also { model ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ abstract class AbstractNotificationTest : AuthorizedControllerTest() {

entityManager.refresh(it.arguments[0])

println("Dispatched!")
// Wait a bit to make sure everything's *actually* persisted
// Kind of an ugly way to synchronize everything, but it is what it is
taskScheduler.schedule(
{ semaphore.release() },
Date().addMilliseconds(100)
Date().addMilliseconds(100),
)

it.arguments[0]
Expand All @@ -76,7 +77,7 @@ abstract class AbstractNotificationTest : AuthorizedControllerTest() {
// Kind of an ugly way to synchronize everything, but it is what it is
taskScheduler.schedule(
{ semaphore.release(list.size) },
Date().addMilliseconds(100)
Date().addMilliseconds(100),
)

it.arguments[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,27 @@ class RemappedNotificationsTest : AbstractNotificationTest() {
fun `it does properly remap imports to key and translation notifications`() {
performAuthMultipart(
url = "/v2/projects/${testData.project1.id}/import",
files = listOf(
MockMultipartFile(
"files", "en.json", "application/json",
"""{"new-key1": "New string 1", "new-key2": "New string 2"}""".toByteArray()
files =
listOf(
MockMultipartFile(
"files",
"en.json",
"application/json",
"""{"new-key1": "New string 1", "new-key2": "New string 2"}""".toByteArray(),
),
MockMultipartFile(
"files",
"fr.json",
"application/json",
"""{"some-key": "Updated", "new-key1": "New FR string 1", "new-key2": "New FR string 2"}""".toByteArray(),
),
MockMultipartFile(
"files",
"cs.json",
"application/json",
"""{"new-key1": "New CZ string 1", "new-key2": "New CZ string 2"}""".toByteArray(),
),
),
MockMultipartFile(
"files", "fr.json", "application/json",
"""{"some-key": "Updated", "new-key1": "New FR string 1", "new-key2": "New FR string 2"}""".toByteArray()
),
MockMultipartFile(
"files", "cs.json", "application/json",
"""{"new-key1": "New CZ string 1", "new-key2": "New CZ string 2"}""".toByteArray()
)
)
).andIsOk

performAuthPut("/v2/projects/${testData.project1.id}/import/apply?forceMode=OVERRIDE", null).andIsOk
Expand Down Expand Up @@ -155,26 +162,31 @@ class RemappedNotificationsTest : AbstractNotificationTest() {

@Test
fun `it does remap complex key edits to relevant notification types`() {
val screenshotId = performAuthMultipart(
url = "/v2/image-upload",
files = listOf(
MockMultipartFile(
"image", "image.png", "image/png",
generateImage(100, 100).inputStream.readAllBytes()
)
)
).andIsCreated.andGetContentAsJsonMap["id"].let { (it as Int).toLong() }
val screenshotId =
performAuthMultipart(
url = "/v2/image-upload",
files =
listOf(
MockMultipartFile(
"image",
"image.png",
"image/png",
generateImage(100, 100).inputStream.readAllBytes(),
),
),
).andIsCreated.andGetContentAsJsonMap["id"].let { (it as Int).toLong() }

performAuthPut(
"/v2/projects/${testData.project1.id}/keys/${testData.keyProject1.id}/complex-update",
ComplexEditKeyDto(
name = "new-name",
namespace = "new-namespace",
translations = mapOf("en" to "New EN string", "fr" to "New FR string"),
screenshotsToAdd = listOf(
KeyScreenshotDto(uploadedImageId = screenshotId)
)
)
screenshotsToAdd =
listOf(
KeyScreenshotDto(uploadedImageId = screenshotId),
),
),
).andIsOk

waitUntilUserNotificationDispatch(25)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package io.tolgee.notifications

import io.tolgee.development.testDataBuilder.data.NotificationsTestData
import io.tolgee.dtos.request.LanguageDto
import io.tolgee.dtos.request.LanguageRequest
import io.tolgee.dtos.request.key.CreateKeyDto
import io.tolgee.dtos.request.translation.comment.TranslationCommentWithLangKeyDto
import io.tolgee.fixtures.andIsCreated
Expand All @@ -41,27 +41,28 @@ class UserNotificationDebounceTest : AbstractNotificationTest() {
fun `it debounces notifications of the same type`() {
performAuthPost(
"/v2/projects/${testData.calmProject.id}/keys/create",
CreateKeyDto(name = "test-key-1")
CreateKeyDto(name = "test-key-1"),
).andIsCreated

waitUntilUserNotificationDispatch()
userNotificationRepository.findAllByRecipient(testData.alice).assert.hasSize(1)

performAuthPost(
"/v2/projects/${testData.calmProject.id}/keys/create",
CreateKeyDto(name = "test-key-2")
CreateKeyDto(name = "test-key-2"),
).andIsCreated

waitUntilUserNotificationDispatch()
userNotificationRepository.findAllByRecipient(testData.alice).assert.hasSize(1)

performAuthPost(
url = "/v2/projects/${testData.calmProject.id}/languages",
content = LanguageDto(
name = "Meow",
originalName = "meow",
tag = "meow-en",
)
content =
LanguageRequest(
name = "Meow",
originalName = "meow",
tag = "meow-en",
),
).andIsOk

waitUntilUserNotificationDispatch()
Expand All @@ -72,15 +73,15 @@ class UserNotificationDebounceTest : AbstractNotificationTest() {
fun `it only debounces notifications within the same project`() {
performAuthPost(
"/v2/projects/${testData.calmProject.id}/keys/create",
CreateKeyDto(name = "test-key-1")
CreateKeyDto(name = "test-key-1"),
).andIsCreated

waitUntilUserNotificationDispatch()
userNotificationRepository.findAllByRecipient(testData.alice).assert.hasSize(1)

performAuthPost(
"/v2/projects/${testData.project2.id}/keys/create",
CreateKeyDto(name = "test-key-2")
CreateKeyDto(name = "test-key-2"),
).andIsCreated

waitUntilUserNotificationDispatch()
Expand All @@ -94,8 +95,8 @@ class UserNotificationDebounceTest : AbstractNotificationTest() {
TranslationCommentWithLangKeyDto(
keyId = testData.keyCalmProject.id,
languageId = testData.keyCalmEnTranslation.language.id,
text = "This is a test"
)
text = "This is a test",
),
).andIsCreated

waitUntilUserNotificationDispatch()
Expand All @@ -106,8 +107,8 @@ class UserNotificationDebounceTest : AbstractNotificationTest() {
TranslationCommentWithLangKeyDto(
keyId = testData.keyCalmProject.id,
languageId = testData.keyCalmEnTranslation.language.id,
text = "This is a test 2"
)
text = "This is a test 2",
),
).andIsCreated

waitUntilUserNotificationDispatch()
Expand All @@ -118,8 +119,8 @@ class UserNotificationDebounceTest : AbstractNotificationTest() {
TranslationCommentWithLangKeyDto(
keyId = testData.keyCalmProject.id,
languageId = testData.calmProjectFr.id,
text = "This is a test"
)
text = "This is a test",
),
).andIsCreated

waitUntilUserNotificationDispatch()
Expand Down
Loading

0 comments on commit 1d54b2e

Please sign in to comment.