Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CCMSPUI-376: Migrate notification summary from SOA-API to EBS-API #119

Merged
41 changes: 40 additions & 1 deletion data-api/open-api-specification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,36 @@ paths:
description: 'Not found'
'500':
description: 'Internal server error'
/users/{login-id}/notifications/summary:
get:
tags:
- notifications
summary: 'Get User Notification Summary'
operationId: 'getUserNotificationSummary'
parameters:
- name: 'login-id'
in: 'path'
required: true
schema:
type: 'string'
example: 'abc123'
responses:
'200':
description: 'Successful operation'
content:
application/json:
schema:
$ref: "#/components/schemas/notificationSummary"
'400':
description: 'Bad request'
'401':
description: 'Unauthorized'
'403':
description: 'Forbidden'
'404':
description: 'Not found'
'500':
description: 'Internal server error'
/providers/{provider-id}:
get:
tags:
Expand Down Expand Up @@ -795,7 +825,7 @@ paths:
- name: 'type'
in: 'query'
schema:
type: 'String'
type: 'string'
responses:
'200':
description: 'Successful operation'
Expand Down Expand Up @@ -1044,6 +1074,15 @@ components:
type: 'array'
items:
type: 'string'
notificationSummary:
type: 'object'
properties:
notifications:
type: 'integer'
standard_actions:
type: 'integer'
overdue_actions:
type: 'integer'
contactDetail:
type: 'object'
properties:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package uk.gov.laa.ccms.data.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import uk.gov.laa.ccms.data.api.NotificationsApi;
import uk.gov.laa.ccms.data.model.NotificationSummary;
import uk.gov.laa.ccms.data.service.NotificationService;

/**
* Controller class responsible for handling notification-related requests.
*
* <p>This controller serves as an interface to return requested user notification
* information. It delegates the business logic to the {@link NotificationService}.
*
* <p>This class implements the {@link NotificationsApi} interface and provides
* endpoints for retrieving notification summaries for users.
*/
@RestController
@RequiredArgsConstructor
public class NotificationsController implements NotificationsApi {

private final NotificationService notificationService;

@Override
public ResponseEntity<NotificationSummary> getUserNotificationSummary(String loginId) {
return notificationService.getUserNotificationSummary(loginId).map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package uk.gov.laa.ccms.data.entity;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import jakarta.persistence.Column;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Immutable;

/**
* Represents an immutable notification count entity corresponding to the
* "XXCCMS_NOTIFICATION_COUNT_V" database view.
*
* <p>The entity tracks the total number of notifications for a specific user login
* and notification type.
*/
@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "XXCCMS_NOTIFICATION_COUNT_V")
@Immutable
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class NotificationCount {

public static final String NOTIFICATION_TYPE_NOTIFICATIONS = "Notifications";
public static final String NOTIFICATION_TYPE_ACTIONS = "Actions";
public static final String NOTIFICATION_TYPE_OVERDUE = "Overdue";

/**
* The composite key for the NotificationCount entity.
* This key is used to uniquely identify a notification count entry
* based on the user login ID and notification type.
*/
@EmbeddedId
private NotificationCountId id;

/**
* Represents the total number of notifications associated with a specific
* user login and notification type. This field is stored in the
* "NOTIFICATION_COUNT" column of the corresponding database view.
*/
@Column(name = "NOTIFICATION_COUNT")
private Integer notificationCount;


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package uk.gov.laa.ccms.data.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* Represents a composite primary key for the NotificationCount entity in the form of a
* NotificationCountId class. This key uniquely identifies a notification count record based on a
* combination of user login ID and notification type.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NotificationCountId implements Serializable {

/**
* A unique identifier representing a user's login session. This field is mapped to the
* "USER_LOGIN_ID" column in the database.
*/
@Column(name = "USER_LOGIN_ID")
private String userLoginId;

/**
* Represents the type of notification associated with a user login session. This field is mapped
* to the "NOTIFICATION_TYPE" column in the database and uses the NotificationTypeConverter to
* translate between the NotificationType enumeration and its corresponding String representation
* for database storage.
*/
@Column(name = "NOTIFICATION_TYPE")
@Convert(converter = NotificationTypeConverter.class)
private NotificationType notificationType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package uk.gov.laa.ccms.data.entity;

import lombok.Getter;

/**
* Defines the type of notification. This enumeration is used to categorize
* different types of notifications within the system.
*
* <p>Each type has a corresponding database value which is a string representation
* of the notification type stored in the database.
*
* <p>The available types are:
* <ul>
* <li>NOTIFICATIONS: Represents general notifications. In
* the DB referred to as 'Notification'.</li>
* <li>ACTION: Represents action-required notifications. In the DB referred to as 'Action'.</li>
* <li>OVERDUE: Represents overdue notifications. In the DB referred to as 'Overdue'.</li>
* </ul>
* The enum provides a mechanism to easily handle notification types
* programmatically by using the associated string value,
* allowing seamless conversion between the programmatic representation and
* the database representation.
*/
@Getter
public enum NotificationType {
NOTIFICATIONS("Notification"),
ACTION("Action"),
OVERDUE("Overdue");

private final String dbValue;

NotificationType(String dbValue) {
this.dbValue = dbValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package uk.gov.laa.ccms.data.entity;

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

/**
* Converter implementation for mapping the NotificationType enumeration to a String
* representation in the database and vice versa.
*
* <p>This converter is automatically applied to all entity attributes of type NotificationType
* across the JPA context due to the use of the @Converter annotation with autoApply set to true.
*
* <p>The convertToDatabaseColumn method translates a NotificationType enumeration value to its
* corresponding String representation for storage in the database. If the provided
* NotificationType is null, the database column is set to null.
*
* <p>The convertToEntityAttribute method takes a String from the database and converts it back
* to its corresponding NotificationType value. If the database value is null, it returns null
* as the entity attribute. If the database String does not match any known NotificationType,
* an IllegalArgumentException is thrown.
*/
@Converter(autoApply = true)
public class NotificationTypeConverter implements AttributeConverter<NotificationType, String> {

/**
* Converts a NotificationType enumeration value to its corresponding String representation
* for storage in the database.
*
* @param notificationType the NotificationType to be converted to a database column value;
* may be null, in which case the method returns null.
* @return the String representation of the given NotificationType for database storage,
* or null if the NotificationType is null.
*/
@Override
public String convertToDatabaseColumn(NotificationType notificationType) {
return notificationType != null ? notificationType.getDbValue() : null;
}

/**
* Converts a database column value (String) to the corresponding
* NotificationType enumeration value.
*
* @param dbData the database column value that represents a
* NotificationType; may be null.
* @return the NotificationType corresponding to the given database value,
* or null if the input is null.
* @throws IllegalArgumentException if dbData does not match any known NotificationType values.
*/
@Override
public NotificationType convertToEntityAttribute(String dbData) {
if (dbData == null) {
return null;
}

return switch (dbData) {
case "Notification" -> NotificationType.NOTIFICATIONS;
case "Action" -> NotificationType.ACTION;
case "Overdue" -> NotificationType.OVERDUE;
default -> throw new IllegalArgumentException("Unknown database value: " + dbData);
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package uk.gov.laa.ccms.data.mapper;

import static java.util.Objects.isNull;

import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.function.Predicate;
import org.mapstruct.Mapper;
import uk.gov.laa.ccms.data.entity.NotificationCount;
import uk.gov.laa.ccms.data.entity.NotificationType;
import uk.gov.laa.ccms.data.model.NotificationSummary;

/**
* NotificationSummaryMapper is an interface for mapping notification count data
* to a notification summary. Utilizes Java streams to process lists of NotificationCount and
* derive a summary. This interface uses the MapStruct framework for object mapping and
* is defined as a Spring component.
*
* <p>Note: It computes the sum of notifications, actions, and overdue actions from a
* list of NotificationCount.
*
* @see NotificationSummary
* @see NotificationCount
* @see NotificationType
*/
@Mapper(componentModel = "spring")
public interface NotificationSummaryMapper {

Predicate<NotificationCount> IS_NOTIFICATION_TYPE =
x -> NotificationType.NOTIFICATIONS.equals(x.getId().getNotificationType());
Predicate<NotificationCount> IS_ACTION_TYPE =
x -> NotificationType.ACTION.equals(x.getId().getNotificationType());
Predicate<NotificationCount> IS_OVERDUE_TYPE =
x -> NotificationType.OVERDUE.equals(x.getId().getNotificationType());

/**
* Converts a list of NotificationCount objects into a NotificationSummary object
* by calculating the sum of different types of notifications.
*
* @param notificationCount a list containing instances of NotificationCount, each with a
* type and count of notifications
* @return a NotificationSummary object with the total count of notifications, standard actions,
* and overdue actions derived from the provided list
*/
default NotificationSummary toNotificationSummary(
@NotNull List<NotificationCount> notificationCount) {

int notificationSum = 0;
int actionSum = 0;
int overdueSum = 0;
if (!isNull(notificationCount)) {
// There shouldn't really be an instance where there are multiple of the same notification
// type, however if there are due to inconsistencies in the DB, use Java streams to get
// the sum.
notificationSum = notificationCount.stream()
.filter(IS_NOTIFICATION_TYPE)
.mapToInt(NotificationCount::getNotificationCount).sum();
actionSum = notificationCount.stream()
.filter(IS_ACTION_TYPE)
.mapToInt(NotificationCount::getNotificationCount).sum();
overdueSum = notificationCount.stream()
.filter(IS_OVERDUE_TYPE)
.mapToInt(NotificationCount::getNotificationCount).sum();
}
return new NotificationSummary().notifications(notificationSum).standardActions(actionSum)
.overdueActions(overdueSum);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package uk.gov.laa.ccms.data.repository;

import java.util.List;
import org.springframework.stereotype.Repository;
import uk.gov.laa.ccms.data.entity.NotificationCount;
import uk.gov.laa.ccms.data.entity.NotificationCountId;

/**
* Repository interface for accessing {@link NotificationCount} entities.
*
* <p>This repository extends the {@link ReadOnlyRepository} interface, it supports read-only
* operations for the {@code NotificationCount} entity, with the primary key of type
* {@code String}.</p>
*
* @author Jamie Briggs
* @see NotificationCount
* @see ReadOnlyRepository
*/
@Repository
public interface NotificationCountRepository
extends ReadOnlyRepository<NotificationCount, NotificationCountId> {

List<NotificationCount> findAllByIdUserLoginId(String userLoginId);
}
Loading
Loading