Skip to content

Commit

Permalink
DST-16098 Send Domain Event when username is changed (#797)
Browse files Browse the repository at this point in the history
* DST-16098 : Domain Event when username is changed

* Fix for failing tests

* DST-16098 Feedback

* DST-16098 Feedback

* Failing test fix
  • Loading branch information
robertmccormackbconline authored Aug 22, 2024
1 parent e5d33fa commit 4eca260
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package uk.co.bconline.ndelius.model.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.time.LocalDateTime;

@Getter
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "DOMAIN_EVENT")
public class DomainEventEntity
{
@Id
@Column(name = "DOMAIN_EVENT_ID")
@SequenceGenerator(name = "DOMAIN_EVENT_ID_SEQ", sequenceName = "DOMAIN_EVENT_ID_SEQ", allocationSize = 1)
@GeneratedValue(generator = "DOMAIN_EVENT_ID_SEQ")
private Long id;

@Column(name = "MESSAGE_BODY")
@Lob
private String messageBody;

@Column(name = "MESSAGE_ATTRIBUTES")
private String messageAttributes;

@ManyToOne()
@JoinColumn(name = "DOMAIN_EVENT_TYPE_ID", insertable = false, updatable = false)
private ReferenceDataEntity domainEventType;

@Column(name = "DOMAIN_EVENT_TYPE_ID")
private Long domainEventTypeId;

@Column(name = "CREATED_DATETIME")
private LocalDateTime createdDateTime;

@Column(name = "FAILED_PUBLISHING")
private Boolean failedPublishing;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package uk.co.bconline.ndelius.model.notification;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Map;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class HmppsDomainEvent
{
private String eventType;
private int version;
private String description;
private String occurredAt;
private Map<String, String> additionalInformation;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package uk.co.bconline.ndelius.model.notification;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum HmppsDomainEventType
{
UMT_USERNAME_CHANGED("probation-user.username.changed", "The username for a probation user has been changed");

private final String eventType;
private final String eventDescription;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package uk.co.bconline.ndelius.repository.db;

import org.springframework.data.jpa.repository.JpaRepository;
import uk.co.bconline.ndelius.model.entity.DomainEventEntity;

public interface DomainEventRepository extends JpaRepository<DomainEventEntity, Long>
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package uk.co.bconline.ndelius.service;

import uk.co.bconline.ndelius.model.notification.HmppsDomainEventType;

import java.util.Map;

public interface DomainEventService
{
void insertDomainEvent(HmppsDomainEventType eventType, Map<String, String> attributes);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package uk.co.bconline.ndelius.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import uk.co.bconline.ndelius.model.entity.DomainEventEntity;
import uk.co.bconline.ndelius.model.notification.HmppsDomainEvent;
import uk.co.bconline.ndelius.model.notification.HmppsDomainEventType;
import uk.co.bconline.ndelius.repository.db.DomainEventRepository;
import uk.co.bconline.ndelius.repository.db.ReferenceDataRepository;
import uk.co.bconline.ndelius.service.DomainEventService;

import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;

@Slf4j
@Service
public class DomainEventServiceImpl implements DomainEventService
{
private final ReferenceDataRepository referenceDataRepository;

private final DomainEventRepository domainEventRepository;

private final ObjectMapper mapper;

private static final String DOMAIN_EVENT_TYPE_REF_DATA_CODE_SET = "DOMAIN EVENT TYPE";

@Autowired
public DomainEventServiceImpl(
ReferenceDataRepository referenceDataRepository,
DomainEventRepository domainEventRepository,
ObjectMapper mapper) {
this.referenceDataRepository = referenceDataRepository;
this.domainEventRepository = domainEventRepository;
this.mapper = mapper;
}

@Override
@SneakyThrows
public void insertDomainEvent(HmppsDomainEventType eventType, Map<String, String> additionalInformation)
{
val type = referenceDataRepository.findByCodeAndReferenceDataMasterCodeSetName(eventType.getEventType(), DOMAIN_EVENT_TYPE_REF_DATA_CODE_SET)
.orElseThrow(() -> new IllegalStateException("Reference data for domain event type " + eventType.getEventType() + " not found"));
val message = HmppsDomainEvent.builder()
.eventType(eventType.getEventType())
.description(eventType.getEventDescription())
.occurredAt(ZonedDateTime.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME))
.additionalInformation(additionalInformation)
.version(1)
.build();
val attributes = Map.of("eventType", Map.of("Type", "String", "Value", eventType.getEventType()));
val entity = DomainEventEntity.builder()
.messageBody(mapper.writeValueAsString(message))
.messageAttributes(mapper.writeValueAsString(attributes))
.domainEventTypeId(type.getId())
.createdDateTime(LocalDateTime.now())
.build();

domainEventRepository.save(entity);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@
import uk.co.bconline.ndelius.model.entry.UserEntry;
import uk.co.bconline.ndelius.model.entry.UserPreferencesEntry;
import uk.co.bconline.ndelius.model.entry.projections.UserHomeAreaProjection;
import uk.co.bconline.ndelius.model.notification.HmppsDomainEventType;
import uk.co.bconline.ndelius.repository.ldap.UserEntryRepository;
import uk.co.bconline.ndelius.repository.ldap.UserPreferencesRepository;
import uk.co.bconline.ndelius.service.DomainEventService;
import uk.co.bconline.ndelius.service.GroupService;
import uk.co.bconline.ndelius.service.UserEntryService;
import uk.co.bconline.ndelius.service.UserRoleService;
import uk.co.bconline.ndelius.transformer.SearchResultTransformer;
import uk.co.bconline.ndelius.util.SearchUtils;

import javax.naming.Name;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -68,6 +71,7 @@ public class UserEntryServiceImpl implements UserEntryService, UserDetailsServic
private final LdapTemplate ldapTemplate;
private final LdapTemplate exportLdapTemplate;
private final SearchResultTransformer searchResultTransformer;
private final DomainEventService domainEventService;

@Autowired
public UserEntryServiceImpl(
Expand All @@ -77,14 +81,16 @@ public UserEntryServiceImpl(
GroupService groupService,
LdapTemplate ldapTemplate,
@Qualifier("exportLdapTemplate") LdapTemplate exportLdapTemplate,
SearchResultTransformer searchResultTransformer) {
SearchResultTransformer searchResultTransformer,
DomainEventService domainEventService) {
this.userRepository = userRepository;
this.preferencesRepository = preferencesRepository;
this.userRoleService = userRoleService;
this.groupService = groupService;
this.ldapTemplate = ldapTemplate;
this.exportLdapTemplate = exportLdapTemplate;
this.searchResultTransformer = searchResultTransformer;
this.domainEventService = domainEventService;
}

@Override
Expand Down Expand Up @@ -253,7 +259,8 @@ public void save(UserEntry user) {
}

@Override
public void save(String existingUsername, UserEntry user) {
public void save(String existingUsername, UserEntry user)
{
// Keep hold of the new username, if it's different we'll rename it later
val newUsername = user.getUsername();
user.setUsername(existingUsername);
Expand All @@ -267,6 +274,13 @@ public void save(String existingUsername, UserEntry user) {
val newDn = LdapNameBuilder.newInstance(getDn(newUsername)).build();
log.debug("Renaming LDAP entry from {} to {}", oldDn, newDn);
ldapTemplate.rename(oldDn, newDn);

// Send Domain event
val additionalInformation = Map.of(
"fromUsername", existingUsername,
"toUsername", newUsername
);
domainEventService.insertDomainEvent(HmppsDomainEventType.UMT_USERNAME_CHANGED, additionalInformation);
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ INSERT INTO R_REFERENCE_DATA_MASTER (REFERENCE_DATA_MASTER_ID, CODE_SET_NAME) VA
INSERT INTO R_STANDARD_REFERENCE_LIST (STANDARD_REFERENCE_LIST_ID, CODE_VALUE, CODE_DESCRIPTION, SELECTABLE, REFERENCE_DATA_MASTER_ID) VALUES (STANDARD_REFERENCE_LIST_ID_SEQ.NEXTVAL, 'GRADE1', 'Grade 1', 'Y', (SELECT REFERENCE_DATA_MASTER_ID FROM R_REFERENCE_DATA_MASTER WHERE CODE_SET_NAME = 'OFFICER GRADE'));
INSERT INTO R_STANDARD_REFERENCE_LIST (STANDARD_REFERENCE_LIST_ID, CODE_VALUE, CODE_DESCRIPTION, SELECTABLE, REFERENCE_DATA_MASTER_ID) VALUES (STANDARD_REFERENCE_LIST_ID_SEQ.NEXTVAL, 'GRADE2', 'Grade 2', 'Y', (SELECT REFERENCE_DATA_MASTER_ID FROM R_REFERENCE_DATA_MASTER WHERE CODE_SET_NAME = 'OFFICER GRADE'));
INSERT INTO R_STANDARD_REFERENCE_LIST (STANDARD_REFERENCE_LIST_ID, CODE_VALUE, CODE_DESCRIPTION, SELECTABLE, REFERENCE_DATA_MASTER_ID) VALUES (STANDARD_REFERENCE_LIST_ID_SEQ.NEXTVAL, 'GRADE3', 'Grade 3', 'N', (SELECT REFERENCE_DATA_MASTER_ID FROM R_REFERENCE_DATA_MASTER WHERE CODE_SET_NAME = 'OFFICER GRADE'));
INSERT INTO R_REFERENCE_DATA_MASTER (REFERENCE_DATA_MASTER_ID, CODE_SET_NAME) VALUES (REFERENCE_DATA_MASTER_ID_SEQ.NEXTVAL, 'DOMAIN EVENT TYPE');
INSERT INTO R_STANDARD_REFERENCE_LIST (STANDARD_REFERENCE_LIST_ID, CODE_VALUE, CODE_DESCRIPTION, SELECTABLE, REFERENCE_DATA_MASTER_ID) VALUES (STANDARD_REFERENCE_LIST_ID_SEQ.NEXTVAL, 'probation-user.username.changed', 'probation-user.username.changed', 'Y', (SELECT REFERENCE_DATA_MASTER_ID FROM R_REFERENCE_DATA_MASTER WHERE CODE_SET_NAME = 'DOMAIN EVENT TYPE'));

-- Users/Staff
INSERT INTO STAFF (STAFF_ID, ROW_VERSION, FORENAME, FORENAME2, SURNAME, OFFICER_CODE, STAFF_GRADE_ID, START_DATE, END_DATE) VALUES (STAFF_ID_SEQ.NEXTVAL, 0, 'dummy', NULL, 'staff', 'N01A000', (SELECT STANDARD_REFERENCE_LIST_ID FROM R_STANDARD_REFERENCE_LIST WHERE CODE_VALUE = 'GRADE1'), CURRENT_TIMESTAMP-10, NULL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.springframework.web.context.WebApplicationContext;
import uk.co.bconline.ndelius.model.*;
import uk.co.bconline.ndelius.model.entity.StaffEntity;
import uk.co.bconline.ndelius.repository.db.DomainEventRepository;
import uk.co.bconline.ndelius.repository.db.StaffRepository;

import java.time.LocalDate;
Expand Down Expand Up @@ -50,6 +51,9 @@ public class UserControllerUpdateTest
@Autowired
private StaffRepository staffRepository;

@Autowired
private DomainEventRepository domainEventRepository;

private MockMvc mvc;

@Before
Expand Down Expand Up @@ -156,6 +160,7 @@ public void userCanBeRenamed() throws Exception
{
String username = nextTestUsername();
String token = token(mvc);
int preDomainEventCount = domainEventRepository.findAll().size();

// Given
mvc.perform(post("/api/user")
Expand Down Expand Up @@ -183,6 +188,8 @@ public void userCanBeRenamed() throws Exception
.header("Authorization", "Bearer " + token))
.andExpect(status().isOk())
.andExpect(jsonPath("$.username", is(username + "-renamed")));

assertEquals(preDomainEventCount + 1, domainEventRepository.findAll().size());
}

@Test
Expand Down

0 comments on commit 4eca260

Please sign in to comment.