Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a01cbff
add search params for users `firstName` and `lastName`
lucas-phillips28 Feb 11, 2025
cc8c1ca
add tests
lucas-phillips28 Feb 11, 2025
50e24c9
Update Swagger v2 Spec Bump APIm revision to 94
pre-devops Feb 11, 2025
397e3d2
coverage
lucas-phillips28 Feb 11, 2025
b9d1dd0
Merge branch 'master' into S28-2069/admin-search-users
lydiaralphgov Nov 14, 2025
55390a5
Bumped APIm revision to 127
pre-devops Nov 14, 2025
31075ce
Merge branch 'master' into S28-2069/admin-search-users
lydiaralphgov Nov 17, 2025
b9dfe41
Fix Admin Search users Tests
GeorgeBarnesUK Jun 4, 2026
60c832f
Update OpenAPI Spec for pre-api
pre-devops Jun 8, 2026
6010635
Merge branch 'master' into S28-2069/admin-search-users
GeorgeBarnesUK Jun 8, 2026
b4968ac
Ignore conflicts in API spec as it will be re-generated by Actions
lydiaralphgov Jun 18, 2026
99412d4
Update OpenAPI Spec for pre-api
pre-devops Jun 18, 2026
c7474a1
PMD changes. Still need to change tests to match new param list.
lydiaralphgov Jun 18, 2026
e3566de
Fixed UserService tests
lydiaralphgov Jun 19, 2026
6f3c551
Fixed controller tests
lydiaralphgov Jun 19, 2026
41d7b4d
Merge branch 'master' into S28-2069/admin-search-users
lydiaralphgov Jun 19, 2026
9549174
Updated IT
lydiaralphgov Jun 19, 2026
af73cb3
Checkstyle
lydiaralphgov Jun 19, 2026
81246e1
Use native null check
lydiaralphgov Jun 22, 2026
ea01d32
T&Cs unit test
lydiaralphgov Jun 22, 2026
b018b0e
App and portal access services + tests
lydiaralphgov Jun 22, 2026
3819181
Checkstyle
lydiaralphgov Jun 22, 2026
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
8 changes: 8 additions & 0 deletions pre-api-stg.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5186,6 +5186,14 @@ paths:
in: query
name: name
type: string
- description: The first name of the user to search by
in: query
name: firstName
type: string
- description: The last name of the user to search by
in: query
name: lastName
type: string
- description: The email of the user to search by
in: query
name: email
Expand Down
2 changes: 1 addition & 1 deletion specs/pre-api.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import uk.gov.hmcts.reform.preapi.entities.Court;
import uk.gov.hmcts.reform.preapi.entities.Recording;
import uk.gov.hmcts.reform.preapi.enums.CaseState;
import uk.gov.hmcts.reform.preapi.enums.CaseState;
import uk.gov.hmcts.reform.preapi.enums.CourtType;
import uk.gov.hmcts.reform.preapi.enums.RecordingOrigin;
import uk.gov.hmcts.reform.preapi.enums.UpsertResult;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class TermsAndConditionsServiceIT extends IntegrationTestBase {

@BeforeEach
public void setUp() {
termsAndConditionsApp = HelperFactory.createTermsAndConditions(TermsAndConditionsType.APP, "app content");
termsAndConditionsApp = HelperFactory.createTermsAndConditions(TermsAndConditionsType.APP,
"app content");
entityManager.persist(termsAndConditionsApp);

termsAndConditionsPortal = HelperFactory.createTermsAndConditions(
Expand All @@ -32,30 +33,33 @@ public void setUp() {

@Test
@Transactional
void getLatestTermsAndConditionsApp() {
var latestAppTerms1 = termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.APP);
void getLatestTermsAndConditionsByTypeApp() {
var latestAppTerms1 = termsAndConditionsService.getLatestTermsAndConditionsByType(TermsAndConditionsType.APP);

assertThat(latestAppTerms1.getId()).isEqualTo(termsAndConditionsApp.getId());

var newTerms = HelperFactory.createTermsAndConditions(TermsAndConditionsType.APP, "app content 2");
entityManager.persist(newTerms);

var latestAppTerms2 = termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.APP);
var latestAppTerms2 = termsAndConditionsService.getLatestTermsAndConditionsByType(TermsAndConditionsType.APP);
assertThat(latestAppTerms2.getId()).isEqualTo(newTerms.getId());
assertThat(latestAppTerms2.getCreatedAt()).isAfter(latestAppTerms1.getCreatedAt());
}

@Test
@Transactional
void getLatestTermsAndConditionsPortal() {
var latestPortalTerms1 = termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.PORTAL);
void getLatestTermsAndConditionsByTypePortal() {
var latestPortalTerms1 = termsAndConditionsService
.getLatestTermsAndConditionsByType(TermsAndConditionsType.PORTAL);

assertThat(latestPortalTerms1.getId()).isEqualTo(termsAndConditionsPortal.getId());

var newTerms = HelperFactory.createTermsAndConditions(TermsAndConditionsType.PORTAL, "portal content 2");
var newTerms = HelperFactory.createTermsAndConditions(TermsAndConditionsType.PORTAL,
"portal content 2");
entityManager.persist(newTerms);

var latestPortalTerms2 = termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.PORTAL);
var latestPortalTerms2 = termsAndConditionsService
.getLatestTermsAndConditionsByType(TermsAndConditionsType.PORTAL);
assertThat(latestPortalTerms2.getId()).isEqualTo(newTerms.getId());
assertThat(latestPortalTerms2.getCreatedAt()).isAfter(latestPortalTerms1.getCreatedAt());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.AccessDeniedException;
import uk.gov.hmcts.reform.preapi.controllers.params.SearchUsers;
import uk.gov.hmcts.reform.preapi.dto.base.BaseUserDTO;
import uk.gov.hmcts.reform.preapi.entities.AppAccess;
import uk.gov.hmcts.reform.preapi.entities.Court;
Expand Down Expand Up @@ -114,14 +115,18 @@ void searchUsersAsAdmin() {
entityManager.persist(userEntity);
entityManager.flush();

var users = userService.findAllBy(null, null, null, null, null, null, false, null, Pageable.unpaged()).toList();
var users = userService
.findAllBy(null, Pageable.unpaged()).toList();

Assertions.assertEquals(users.size(), 2);
Assertions.assertTrue(users.stream().anyMatch(user -> user.getId().equals(portalUserEntity.getId())));
Assertions.assertTrue(users.stream().anyMatch(user -> user.getId().equals(appUserEntity.getId())));
Assertions.assertFalse(users.stream().anyMatch(user -> user.getId().equals(userEntity.getId())));

var users2 = userService.findAllBy(null, null, null, null, null, null, true, null, Pageable.unpaged()).toList();
SearchUsers params = new SearchUsers();
params.setIncludeDeleted(true);
var users2 = userService
.findAllBy(params, Pageable.unpaged()).toList();

Assertions.assertEquals(users2.size(), 3);
Assertions.assertTrue(users2.stream().anyMatch(user -> user.getId().equals(portalUserEntity.getId())));
Expand All @@ -137,7 +142,10 @@ public void searchUsersAsNonAdmin() {
entityManager.persist(userEntity);
entityManager.flush();

var users = userService.findAllBy(null, null, null, null, null, null, false, null, Pageable.unpaged()).toList();
var users = userService.findAllBy(
null,
Pageable.unpaged()
).toList();

Assertions.assertEquals(users.size(), 2);
Assertions.assertTrue(users.stream().anyMatch(user -> user.getId().equals(portalUserEntity.getId())));
Expand All @@ -146,7 +154,10 @@ public void searchUsersAsNonAdmin() {

var message = Assertions.assertThrows(
AccessDeniedException.class,
() -> userService.findAllBy(null, null, null, null, null, null, true, null, Pageable.unpaged()).toList()
() -> userService.findAllBy(
null,
Pageable.unpaged()
).toList()
).getMessage();

Assertions.assertEquals(message, "Access Denied");
Expand All @@ -156,15 +167,10 @@ public void searchUsersAsNonAdmin() {
@Transactional
@Test
void testGetUserByAccessType() {
SearchUsers params = new SearchUsers();
params.setAccessType(AccessType.APP);
var resultApp = userService.findAllBy(
null,
null,
null,
null,
null,
AccessType.APP,
false,
null,
params,
PageRequest.of(0, 20)
);
Assertions.assertEquals(2, resultApp.getContent().size());
Expand All @@ -173,15 +179,9 @@ void testGetUserByAccessType() {
Assertions.assertEquals(appUserEntity.getId(), usersApp.get(0).getId());
Assertions.assertEquals(userEntity.getId(), usersApp.get(1).getId());

params.setAccessType(AccessType.PORTAL);
var resultPortal = userService.findAllBy(
null,
null,
null,
null,
null,
AccessType.PORTAL,
false,
null,
params,
PageRequest.of(0, 20)
);
Assertions.assertEquals(2, resultPortal.getContent().size());
Expand All @@ -190,7 +190,11 @@ void testGetUserByAccessType() {
Assertions.assertEquals(userEntity.getFirstName(), usersPortal.get(0).getFirstName());
Assertions.assertEquals(portalUserEntity.getId(), usersPortal.get(1).getId());

var resultAll = userService.findAllBy(null, null, null, null, null, null, false,null, PageRequest.of(0, 20));
params.setAccessType(null);
var resultAll = userService.findAllBy(
params,
PageRequest.of(0, 20)
);
Assertions.assertEquals(3, resultAll.getContent().size());
var usersAll = resultAll.getContent().stream()
.sorted(Comparator.comparing(BaseUserDTO::getFirstName)).toList();
Expand All @@ -199,15 +203,9 @@ void testGetUserByAccessType() {
Assertions.assertEquals(portalUserEntity.getId(), usersAll.get(2).getId());

portalAccessEntity2.setStatus(AccessStatus.INACTIVE);
params.setAccessType(AccessType.PORTAL);
var resultPortal2 = userService.findAllBy(
null,
null,
null,
null,
null,
AccessType.PORTAL,
false,
null,
params,
PageRequest.of(0, 20)
);
Assertions.assertEquals(1, resultPortal2.getContent().size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public TermsAndConditionsController(TermsAndConditionsService termsAndConditions
summary = "Get the latest terms and conditions for the app"
)
public ResponseEntity<TermsAndConditionsDTO> getLatestAppTermsAndConditions() {
return ResponseEntity.ok(termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.APP));
return ResponseEntity.ok(termsAndConditionsService
.getLatestTermsAndConditionsByType(TermsAndConditionsType.APP));
}

@GetMapping("/portal-terms-and-conditions/latest")
Expand All @@ -43,7 +44,8 @@ public ResponseEntity<TermsAndConditionsDTO> getLatestAppTermsAndConditions() {
summary = "Get the latest terms and conditions for the portal"
)
public ResponseEntity<TermsAndConditionsDTO> getLatestPortalTermsAndConditions() {
return ResponseEntity.ok(termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.PORTAL));
return ResponseEntity.ok(termsAndConditionsService
.getLatestTermsAndConditionsByType(TermsAndConditionsType.PORTAL));
}

@PostMapping("/accept-terms-and-conditions/{termsId}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ public ResponseEntity<UserDTO> getUserById(@PathVariable UUID userId) {
description = "The name of the user to search by",
schema = @Schema(implementation = String.class)
)
@Parameter(
name = "firstName",
description = "The first name of the user to search by",
schema = @Schema(implementation = String.class)
)
@Parameter(
name = "lastName",
description = "The last name of the user to search by",
schema = @Schema(implementation = String.class)
)
@Parameter(
name = "email",
description = "The email of the user to search by",
Expand Down Expand Up @@ -133,17 +143,7 @@ public ResponseEntity<PagedModel<EntityModel<UserDTO>>> getUsers(
@Parameter(hidden = true) Pageable pageable,
@Parameter(hidden = true) PagedResourcesAssembler<UserDTO> assembler
) {
Page<UserDTO> resultPage = userService.findAllBy(
params.getName(),
params.getEmail(),
params.getOrganisation(),
params.getCourtId(),
params.getRoleId(),
params.getAccessType(),
params.getIncludeDeleted() != null && params.getIncludeDeleted(),
params.getAppActive(),
pageable
);
Page<UserDTO> resultPage = userService.findAllBy(params, pageable);

if (pageable.getPageNumber() > resultPage.getTotalPages()) {
throw new RequestedPageOutOfRangeException(pageable.getPageNumber(), resultPage.getTotalPages());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
@Data
public class SearchUsers {
private String name;
private String firstName;
private String lastName;
private String email;
private String organisation;
private UUID courtId;
Expand All @@ -20,6 +22,14 @@ public String getName() {
return name != null && !name.isEmpty() ? name : null;
}

public String getFirstName() {
return firstName != null && !firstName.isEmpty() ? firstName : null;
}

public String getLastName() {
return lastName != null && !lastName.isEmpty() ? lastName : null;
}

public String getEmail() {
return email != null && !email.isEmpty() ? email : null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class PortalAppAccessValidator

private final RoleRepository roleRepository;

private static final String PORTAL_ROLE_NAME = "Level 3";
public static final String PORTAL_ROLE_NAME = "Level 3";

@Autowired
public PortalAppAccessValidator(final RoleRepository roleRepository) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package uk.gov.hmcts.reform.preapi.repositories;


import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
Expand Down Expand Up @@ -44,6 +43,8 @@ public interface UserRepository extends JpaRepository<User, UUID> {
ELSE 0
END = 1
)
AND (:firstName IS NULL OR u.firstName ILIKE %:firstName%)
AND (:lastName IS NULL OR u.lastName ILIKE %:lastName%)
AND (:organisation IS NULL OR u.organisation ILIKE %:organisation%)
AND (CAST(:courtId as uuid) IS NULL OR EXISTS (SELECT 1 FROM u.appAccess aa WHERE aa.court.id = :courtId))
AND (CAST(:roleId as uuid) IS NULL OR EXISTS (SELECT 1 FROM u.appAccess aa WHERE aa.role.id = :roleId))
Expand All @@ -58,8 +59,11 @@ OR EXISTS (
AND (:isAppUser = false OR EXISTS (SELECT 1 FROM u.appAccess aa WHERE aa.user = u AND aa.deletedAt IS NULL))
"""
)
@SuppressWarnings("PMD.ExcessiveParameterList")
Page<User> searchAllBy(
@Param("name") String name,
@Param("firstName") String firstName,
@Param("lastName") String lastName,
@Param("email") String email,
@Param("organisation") String organisation,
@Param("courtId") UUID courtId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,26 @@ public void deleteById(UUID appId) {
appAccessRepository.save(access);
});
}

@Transactional
public void deleteByUserId(UUID userId) {
appAccessRepository
.findAllByUser_IdAndDeletedAtNullAndUser_DeletedAtNull(userId)
.forEach(access -> {
access.setActive(false);
access.setDeletedAt(Timestamp.from(Instant.now()));
appAccessRepository.save(access);
});
}

@Transactional
public void undeleteByUserId(UUID userId) {
appAccessRepository
.findAllByUser_IdAndDeletedAtIsNotNull(userId)
.forEach(a -> {
a.setDeletedAt(null);
a.setActive(true);
appAccessRepository.save(a);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.transaction.annotation.Transactional;
import uk.gov.hmcts.reform.preapi.dto.CreatePortalAccessDTO;
import uk.gov.hmcts.reform.preapi.entities.PortalAccess;
import uk.gov.hmcts.reform.preapi.entities.User;
import uk.gov.hmcts.reform.preapi.enums.AccessStatus;
import uk.gov.hmcts.reform.preapi.enums.UpsertResult;
import uk.gov.hmcts.reform.preapi.exception.NotFoundException;
Expand Down Expand Up @@ -53,6 +54,51 @@ public void deleteById(UUID portalId) {
});
}

@Transactional
public void deleteByUserId(UUID userId) {
portalAccessRepository
.findByUser_IdAndDeletedAtNullAndUser_DeletedAtNull(userId)
.ifPresent(access -> {
access.setStatus(AccessStatus.INACTIVE);
access.setDeletedAt(Timestamp.from(Instant.now()));
portalAccessRepository.save(access);
});
}

@Transactional
public void undeleteByUserId(UUID userId) {
portalAccessRepository
.findAllByUser_IdAndDeletedAtIsNotNull(userId)
.forEach(p -> {
p.setDeletedAt(null);
portalAccessRepository.save(p);
});
}

public Boolean exists(UUID id) {
return portalAccessRepository.existsById(id);
}

public Boolean isNotDeletedPortalUser(UUID userId) {
return portalAccessRepository
.findByUser_IdAndDeletedAtNullAndUser_DeletedAtNull(userId)
.isPresent();
}

public void upsertPortalAccessEntity(UUID id,
User userEntity,
AccessStatus accessStatus,
Timestamp timestamp) {
PortalAccess portalAccessEntity = portalAccessRepository
.findByUser_IdAndDeletedAtNullAndUser_DeletedAtNull(id)
.orElse(new PortalAccess());

portalAccessEntity.setUser(userEntity);
portalAccessEntity.setStatus(accessStatus);
portalAccessEntity.setInvitedAt(timestamp);
portalAccessRepository.save(portalAccessEntity);
}

private AccessStatus getNewStatus(CreatePortalAccessDTO dto) {
if (dto.getStatus() == AccessStatus.ACTIVE && dto.getRegisteredAt() == null) {
return AccessStatus.INVITATION_SENT;
Expand Down
Loading