Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package pl.edu.agh.project_manager.controller.dto.notification;

import pl.edu.agh.project_manager.domain.entity.notification.Notification;
import pl.edu.agh.project_manager.domain.enums.NotificationType;

import java.time.LocalDateTime;
import java.util.UUID;

public record NotificationResponse(
UUID id,
NotificationType type,
String message,
boolean isRead,
UUID referenceId,
LocalDateTime createdAt
) {
public static NotificationResponse from(Notification notification) {
return new NotificationResponse(
notification.getId(),
notification.getType(),
notification.getMessage(),
notification.isRead(),
notification.getReferenceId(),
notification.getCreatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package pl.edu.agh.project_manager.controller.notification;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import pl.edu.agh.project_manager.controller.dto.PagedResponse;
import pl.edu.agh.project_manager.controller.dto.notification.NotificationResponse;
import pl.edu.agh.project_manager.security.UserPrincipal;
import pl.edu.agh.project_manager.service.notification.NotificationService;

import java.util.UUID;

@RestController
@RequestMapping("/api/notifications")
@RequiredArgsConstructor
public class NotificationController {

private final NotificationService notificationService;

@GetMapping
public ResponseEntity<PagedResponse<NotificationResponse>> getUserNotifications(
@RequestParam(defaultValue = "true") boolean unreadOnly,
@PageableDefault(size = 20) Pageable pageable,
@AuthenticationPrincipal UserPrincipal userPrincipal
) {
return ResponseEntity.ok(
notificationService.getNotificationsForUser(
userPrincipal.userId(),
pageable.getPageNumber(),
pageable.getPageSize(),
unreadOnly
)
);
}
Comment thread
mtrznadel24 marked this conversation as resolved.

@PatchMapping("/{notificationId}/mark-as-read")
@PreAuthorize("@notificationAccess.canMarkAsRead(#notificationId, authentication.principal)")
public ResponseEntity<Void> markAsRead(
@PathVariable UUID notificationId,
@AuthenticationPrincipal UserPrincipal userPrincipal
) {
notificationService.markAsRead(notificationId, userPrincipal.userId());
return ResponseEntity.noContent().build();
}

@PatchMapping("/mark-all-as-read")
public ResponseEntity<Void> markAllAsRead(
@AuthenticationPrincipal UserPrincipal userPrincipal
) {
notificationService.markAllAsRead(userPrincipal.userId());
return ResponseEntity.noContent().build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package pl.edu.agh.project_manager.domain.entity.notification;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import pl.edu.agh.project_manager.domain.entity.user.User;
import pl.edu.agh.project_manager.domain.enums.NotificationType;

import java.time.LocalDateTime;
import java.util.UUID;

@Entity
@Table(name = "notifications", indexes = {
@Index(name = "idx_notification_recipient_created", columnList = "recipient_id, created_at")
})
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Notification {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "id", nullable = false)
private UUID id;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "recipient_id", nullable = false)
private User recipient;

@Enumerated(EnumType.STRING)
@Column(name = "type", length = 50, nullable = false)
private NotificationType type;

@Column(name = "message", length = 255, nullable = false)
private String message;

@Column(name = "is_read", nullable = false)
@Builder.Default
private boolean isRead = false;

@Column(name = "reference_id")
Comment thread
mtrznadel24 marked this conversation as resolved.
private UUID referenceId;

@CreationTimestamp
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.persistence.*;
import lombok.*;
import pl.edu.agh.project_manager.domain.entity.notification.Notification;
import pl.edu.agh.project_manager.domain.entity.project.Project;
import pl.edu.agh.project_manager.domain.entity.projectgroup.ProjectGroup;
import pl.edu.agh.project_manager.domain.enums.UserRole;
Expand Down Expand Up @@ -67,8 +68,16 @@ public class User {
@Builder.Default
private List<Qualification> qualifications = new ArrayList<>();

@OneToMany(mappedBy = "recipient", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<Notification> notifications = new ArrayList<>();

public void addQualification(Qualification qualification) {
qualifications.add(qualification);
qualification.setUser(this);
}

public String getFullName() {
return String.format("%s %s", this.name, this.surname);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package pl.edu.agh.project_manager.domain.enums;

public enum NotificationType {

ASSIGNMENT_REQUESTED,
ASSIGNMENT_ACCEPTED,
ASSIGNMENT_REJECTED,

QUALIFICATION_REQUESTED,
QUALIFICATION_ACCEPTED,
QUALIFICATION_REJECTED,

SYSTEM_NEW_EMPLOYEE

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package pl.edu.agh.project_manager.domain.event;

import pl.edu.agh.project_manager.domain.entity.user.User;
import pl.edu.agh.project_manager.domain.enums.NotificationType;

import java.util.UUID;

public record AssignmentAcceptedEvent(
UUID assignmentId,
User recipient, String
message
) implements NotificationEvent {

@Override
public UUID referenceId() {
return assignmentId;
}

@Override
public NotificationType type() {
return NotificationType.ASSIGNMENT_ACCEPTED;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package pl.edu.agh.project_manager.domain.event;

import pl.edu.agh.project_manager.domain.entity.user.User;
import pl.edu.agh.project_manager.domain.enums.NotificationType;

import java.util.UUID;

public record AssignmentRejectedEvent(
UUID assignmentId,
User recipient,
String message
) implements NotificationEvent {

@Override
public UUID referenceId() {
return assignmentId;
}

@Override
public NotificationType type() {
return NotificationType.ASSIGNMENT_REJECTED;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package pl.edu.agh.project_manager.domain.event;

import pl.edu.agh.project_manager.domain.entity.user.User;
import pl.edu.agh.project_manager.domain.enums.NotificationType;

import java.util.UUID;

public record AssignmentRequestedEvent(
UUID assignmentId,
User recipient,
String message
) implements NotificationEvent {

@Override
public UUID referenceId() {
return assignmentId;
}

@Override
public NotificationType type() {
return NotificationType.ASSIGNMENT_REQUESTED;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package pl.edu.agh.project_manager.domain.event;

import pl.edu.agh.project_manager.domain.entity.user.User;
import pl.edu.agh.project_manager.domain.enums.NotificationType;

import java.util.UUID;

public interface NotificationEvent {
User recipient();
String message();
UUID referenceId();
NotificationType type();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package pl.edu.agh.project_manager.domain.event;

import pl.edu.agh.project_manager.domain.entity.user.User;
import pl.edu.agh.project_manager.domain.enums.NotificationType;

import java.util.UUID;

public record QualificationAcceptedEvent(
UUID qualificationId,
User recipient,
String message
) implements NotificationEvent {

@Override
public UUID referenceId() {
return qualificationId;
}

@Override
public NotificationType type() {
return NotificationType.QUALIFICATION_ACCEPTED;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package pl.edu.agh.project_manager.domain.event;

import pl.edu.agh.project_manager.domain.entity.user.User;
import pl.edu.agh.project_manager.domain.enums.NotificationType;

import java.util.UUID;

public record QualificationRejectedEvent(
UUID qualificationId,
User recipient,
String message
) implements NotificationEvent {

@Override
public UUID referenceId() {
return qualificationId;
}

@Override
public NotificationType type() {
return NotificationType.QUALIFICATION_REJECTED;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package pl.edu.agh.project_manager.domain.event;

import pl.edu.agh.project_manager.domain.entity.user.User;
import pl.edu.agh.project_manager.domain.enums.NotificationType;

import java.util.UUID;

public record QualificationRequestedEvent(
UUID qualificationId,
User recipient,
String message
) implements NotificationEvent {

@Override
public UUID referenceId() {
return qualificationId;
}

@Override
public NotificationType type() {
return NotificationType.QUALIFICATION_REQUESTED;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package pl.edu.agh.project_manager.domain.event;

import pl.edu.agh.project_manager.domain.entity.user.User;
import pl.edu.agh.project_manager.domain.enums.NotificationType;

import java.util.UUID;

public record SystemNewEmployeeEvent(
UUID employeeId,
User recipient,
String message
) implements NotificationEvent {

@Override
public UUID referenceId() {
return employeeId;
}

@Override
public NotificationType type() {
return NotificationType.SYSTEM_NEW_EMPLOYEE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public enum ApiErrorCode {

QUALIFICATION_NOT_FOUND("QUAL_001", HttpStatus.NOT_FOUND, "Qualification not found"),

NOTIFICATION_NOT_FOUND("NOTIF_001", HttpStatus.NOT_FOUND, "Notification not found"),

ROLE_NOT_IN_PROJECT("REQ_001", HttpStatus.BAD_REQUEST, "Role must exist within the provided project"),
USER_ALREADY_IN_PROJECT("REQ_002", HttpStatus.CONFLICT, "User is already a member of this project"),
USER_HAS_ONGOING_REQUEST("REQ_003", HttpStatus.CONFLICT, "User already has a pending request for this project"),
Expand Down
Loading
Loading