Skip to content

Commit

Permalink
Review fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
StaNov committed Jan 22, 2025
1 parent bc0d326 commit c8e0566
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import io.tolgee.security.authentication.AuthenticationFacade
import io.tolgee.service.notification.NotificationService
import org.springdoc.core.annotations.ParameterObject
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.data.web.PagedResourcesAssembler
import org.springframework.data.web.SortDefault
import org.springframework.hateoas.PagedModel
import org.springframework.web.bind.annotation.CrossOrigin
import org.springframework.web.bind.annotation.GetMapping
Expand All @@ -37,8 +39,11 @@ class NotificationController(
@Operation(summary = "Gets notifications of the currently logged in user, newest is first.")
@AllowApiAccess
fun getNotifications(
@ParameterObject pageable: Pageable,
@ParameterObject filters: NotificationFilters = NotificationFilters(),
@ParameterObject
@SortDefault(sort = ["id"], direction = Sort.Direction.DESC)
pageable: Pageable,
@ParameterObject
filters: NotificationFilters = NotificationFilters(),
): PagedModel<NotificationModel> {
val notifications =
notificationService.getNotifications(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package io.tolgee.api.v2.controllers.notification

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import io.tolgee.dtos.request.notification.NotificationsMarkSeenRequest
import io.tolgee.security.authentication.AllowApiAccess
import io.tolgee.security.authentication.AuthenticationFacade
import io.tolgee.service.notification.NotificationService
import jakarta.validation.Valid
import org.springframework.web.bind.annotation.*

@RestController
Expand All @@ -14,7 +16,7 @@ import org.springframework.web.bind.annotation.*
"/v2/",
],
)
@Tag(name = "Notification Operations", description = "Operations with notifications")
@Tag(name = "Notifications", description = "Manipulates notifications")
class NotificationOperationsController(
private val notificationService: NotificationService,
private val authenticationFacade: AuthenticationFacade,
Expand All @@ -23,8 +25,8 @@ class NotificationOperationsController(
@Operation(summary = "Marks notifications of the currently logged in user with given IDs as seen.")
@AllowApiAccess
fun markNotificationsAsSeen(
@RequestBody notificationIds: List<Long>,
@RequestBody @Valid request: NotificationsMarkSeenRequest,
) {
notificationService.markNotificationsAsSeen(notificationIds, authenticationFacade.authenticatedUser.id)
notificationService.markNotificationsAsSeen(request.notificationIds, authenticationFacade.authenticatedUser.id)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.tolgee.api.v2.controllers

import io.tolgee.development.testDataBuilder.data.NotificationsTestData
import io.tolgee.dtos.request.notification.NotificationsMarkSeenRequest
import io.tolgee.fixtures.andAssertThatJson
import io.tolgee.fixtures.andIsOk
import io.tolgee.repository.NotificationRepository
Expand All @@ -25,14 +26,14 @@ class NotificationControllerTest : AuthorizedControllerTest() {
testDataService.saveTestData(testData.root)
loginAsUser(testData.user.username)

performAuthGet("/v2/notifications?sort=id,DESC&filterSeen=false").andAssertThatJson {
performAuthGet("/v2/notifications?filterSeen=false").andAssertThatJson {
node("_embedded.notificationModelList").isArray.hasSize(4)
node("_embedded.notificationModelList[0].linkedTask.name").isEqualTo("Notification task 103")
node("_embedded.notificationModelList[1].linkedTask.name").isEqualTo("Notification task 102")
node("_embedded.notificationModelList[2].linkedTask.name").isEqualTo("Notification task 101")
}

performAuthGet("/v2/notifications?sort=id,DESC").andAssertThatJson {
performAuthGet("/v2/notifications").andAssertThatJson {
node("_embedded.notificationModelList").isArray.hasSize(5)
node("_embedded.notificationModelList[0].linkedTask.name").isEqualTo("Notification task 104")
}
Expand All @@ -53,7 +54,14 @@ class NotificationControllerTest : AuthorizedControllerTest() {

performAuthPut(
"/v2/notifications-mark-seen",
listOf(currentUserNotification1.id, currentUserNotification2.id, differentUserNotification.id),
NotificationsMarkSeenRequest().apply {
notificationIds =
listOf(
currentUserNotification1.id,
currentUserNotification2.id,
differentUserNotification.id,
)
},
).andIsOk

val notifications = notificationRepository.findAll()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
package io.tolgee.dtos.request.notification

import io.swagger.v3.oas.annotations.Parameter

class NotificationFilters(
@field:Parameter(
description = """Filter by the `seen` parameter.

no value = request everything

true = only seen

false = only unseen""",
)
val filterSeen: Boolean? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.tolgee.dtos.request.notification

import io.swagger.v3.oas.annotations.media.Schema

class NotificationsMarkSeenRequest {
@Schema(example = "[1,2,3]", description = "Notification IDs to be marked as seen")
var notificationIds: List<Long> = listOf()
}
8 changes: 2 additions & 6 deletions e2e/cypress/e2e/tasks/tasksNotifications.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ describe('tasks notifications', () => {

assertMessage('1 task created');

cy.gcy('notifications-count')
.find('.MuiBadge-badge')
.should('have.text', '1');
cy.gcy('notifications-count').should('have.text', '1');

getAssignedEmailNotification().then(({ taskLink, toAddress }) => {
assert(toAddress === '[email protected]', 'correct recipient');
Expand All @@ -57,9 +55,7 @@ describe('tasks notifications', () => {
dismissMenu();
cy.gcy('notifications-button').click();

cy.gcy('notifications-count')
.find('.MuiBadge-badge')
.should('not.be.visible');
cy.gcy('notifications-count').should('not.be.visible');
cy.gcy('notifications-list').contains('New review task').click();

cy.url().should('include', '/translations?task=');
Expand Down
19 changes: 13 additions & 6 deletions webapp/src/component/layout/TopBar/Notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const Notifications: FunctionComponent<{ className?: string }> = () => {
const notificationsLoadable = useApiQuery({
url: '/v2/notifications',
method: 'get',
query: { size: 10000, sort: ['id,DESC'] },
query: { size: 10000 },
});

const notifications = notificationsLoadable.data;
Expand All @@ -71,10 +71,12 @@ export const Notifications: FunctionComponent<{ className?: string }> = () => {
setAnchorEl(event.currentTarget);
markSeenMutation.mutate({
content: {
'application/json':
notificationsData != undefined
? notificationsData.map((it) => it.id)
: [],
'application/json': {
notificationIds:
notificationsData != undefined
? notificationsData.map((it) => it.id)
: [],
},
},
});
};
Expand Down Expand Up @@ -112,7 +114,12 @@ export const Notifications: FunctionComponent<{ className?: string }> = () => {
<Badge
badgeContent={unseenCount}
color="secondary"
data-cy="notifications-count"
slotProps={{
badge: {
//@ts-ignore
'data-cy': 'notifications-count',
},
}}
>
<Bell01 />
</Badge>
Expand Down
18 changes: 17 additions & 1 deletion webapp/src/service/apiSchema.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2921,6 +2921,13 @@ export interface components {
linkedTask?: components["schemas"]["TaskModel"];
project?: components["schemas"]["SimpleProjectModel"];
};
NotificationsMarkSeenRequest: {
/**
* @description Notification IDs to be marked as seen
* @example 1,2,3
*/
notificationIds: number[];
};
OAuthPublicConfigDTO: {
clientId?: string;
enabled: boolean;
Expand Down Expand Up @@ -6518,6 +6525,15 @@ export interface operations {
size?: number;
/** Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. */
sort?: string[];
/**
* Filter by the `seen` parameter.
*
* no value = request everything
*
* true = only seen
*
* false = only unseen
*/
filterSeen?: boolean;
};
};
Expand Down Expand Up @@ -6601,7 +6617,7 @@ export interface operations {
};
requestBody: {
content: {
"application/json": number[];
"application/json": components["schemas"]["NotificationsMarkSeenRequest"];
};
};
};
Expand Down

0 comments on commit c8e0566

Please sign in to comment.