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

PO-756: Add draft account DELETE endpoint #519

Merged
merged 1 commit into from
Sep 9, 2024
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
Expand Up @@ -35,6 +35,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
Expand Down Expand Up @@ -187,4 +188,21 @@ void shouldReturn503WhenDownstreamServiceIsUnavailable() throws Exception {
"message": "Opal Fines Database is currently unavailable"
}"""));
}

@Test
void testDeleteDraftAccountById() throws Exception {
DraftAccountEntity draftAccountEntity = createDraftAccountEntity();

when(draftAccountService.getDraftAccount(1L)).thenReturn(draftAccountEntity);

MvcResult result = mockMvc.perform(delete("/api/draft-accounts/1")
.header("authorization", "Bearer some_value"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.message").value("Draft Account '1' deleted"))
.andReturn();

String body = result.getResponse().getContentAsString();
logger.info(":testGetDraftAccountById: Response body:\n" + ToJsonString.toPrettyJson(body));
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package uk.gov.hmcts.opal.controllers;

import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import uk.gov.hmcts.opal.authorisation.model.UserState;
import uk.gov.hmcts.opal.dto.AddDraftAccountRequestDto;
Expand All @@ -37,6 +41,8 @@
public class DraftAccountController {

public static final String ADD_DRAFT_ACCOUNT_REQUEST_JSON = "addDraftAccountRequest.json";
public static final String ACCOUNT_DELETED_MESSAGE_FORMAT = """
{ "message": "Draft Account '%s' deleted"}""";

private final DraftAccountService draftAccountService;

Expand Down Expand Up @@ -94,6 +100,24 @@ public ResponseEntity<DraftAccountResponseDto> postDraftAccount(@RequestBody Add
return buildCreatedResponse(toGetResponseDto(response));
}

@Hidden
@DeleteMapping(value = "/{draftAccountId}", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Deletes the Draft Account for the given draftAccountId.")
@ConditionalOnProperty(prefix = "opal.testing-support-endpoints", name = "enabled", havingValue = "true")
public ResponseEntity<String> deleteDraftAccountById(
@PathVariable Long draftAccountId,
@RequestHeader(value = "Authorization", required = false) String authHeaderValue,
@RequestParam Optional<Boolean> ignoreMissing) {

log.info(":DELETE:deleteDraftAccountById: draftAccountId: {}", draftAccountId);

userStateService.checkForAuthorisedUser(authHeaderValue);

draftAccountService.deleteDraftAccount(draftAccountId, ignoreMissing);

return buildResponse(String.format(ACCOUNT_DELETED_MESSAGE_FORMAT, draftAccountId));
}

DraftAccountResponseDto toGetResponseDto(DraftAccountEntity entity) {
return DraftAccountResponseDto.builder()
.draftAccountId(entity.getDraftAccountId())
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,42 @@
import uk.gov.hmcts.opal.repository.BusinessUnitRepository;
import uk.gov.hmcts.opal.repository.DraftAccountRepository;
import uk.gov.hmcts.opal.repository.jpa.DraftAccountSpecs;
import uk.gov.hmcts.opal.service.DraftAccountServiceInterface;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Optional;

@Service
@Slf4j(topic = "DraftAccountService")
@RequiredArgsConstructor
@Qualifier("draftAccountService")
public class DraftAccountService implements DraftAccountServiceInterface {
public class DraftAccountService {

private final DraftAccountRepository draftAccountRepository;

private final BusinessUnitRepository businessUnitRepository;

private final DraftAccountSpecs specs = new DraftAccountSpecs();

@Override
public DraftAccountEntity getDraftAccount(long draftAccountId) {
return draftAccountRepository.getReferenceById(draftAccountId);
}

@Override
public void deleteDraftAccount(long draftAccountId, Optional<Boolean> ignoreMissing) {
DraftAccountEntity entity = getDraftAccount(draftAccountId);
// If the DB doesn't hold the target entity to be deleted, then no exception is thrown when a deletion is
// attempted. So we need to retrieve the entity first and try to access any property.
// This will throw an exception if the entity doesn't exist.
boolean checkExists = !(ignoreMissing.orElse(false));
if (checkExists && entity.getDraftAccountId() == null) {
// Will not get here, as JPA should throw an exception. But for testing, throw an Exception.
throw new RuntimeException("Draft Account entity '" + draftAccountId + "' does not exist in the DB.");
} else {
draftAccountRepository.delete(entity);
}
}

public List<DraftAccountEntity> searchDraftAccounts(DraftAccountSearchDto criteria) {
Page<DraftAccountEntity> page = draftAccountRepository
.findBy(specs.findBySearchCriteria(criteria),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,19 @@ void testSaveDraftAccounts_Success() {
verify(draftAccountService, times(1)).submitDraftAccount(any(), any());
}

@Test
void testDeleteDraftAccount_Success() {
// Act
ResponseEntity<String> response = draftAccountController
.deleteDraftAccountById(7L, BEARER_TOKEN, Optional.empty());

// Assert
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals("""
{ "message": "Draft Account '7' deleted"}""", response.getBody());
verify(draftAccountService, times(1)).deleteDraftAccount(any(Long.class), any());
}

DraftAccountResponseDto toGetDto(DraftAccountEntity entity) {
return DraftAccountResponseDto.builder()
.draftAccountId(entity.getDraftAccountId())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.gov.hmcts.opal.service.opal;

import jakarta.persistence.EntityNotFoundException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
Expand All @@ -24,7 +25,9 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
Expand All @@ -42,7 +45,6 @@ class DraftAccountServiceTest {
@Test
void testGetDraftAccount() {
// Arrange

DraftAccountEntity draftAccountEntity = DraftAccountEntity.builder().build();
when(draftAccountRepository.getReferenceById(any())).thenReturn(draftAccountEntity);

Expand All @@ -51,7 +53,6 @@ void testGetDraftAccount() {

// Assert
assertNotNull(result);

}

@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -98,6 +99,47 @@ void testSubmitDraftAccounts() {
assertEquals(draftAccountEntity, result);
}

@Test
void testDeleteDraftAccount_success() {
// Arrange
DraftAccountEntity draftAccountEntity = DraftAccountEntity.builder().draftAccountId(1L).build();
when(draftAccountRepository.getReferenceById(any())).thenReturn(draftAccountEntity);

// Act
draftAccountService.deleteDraftAccount(1, Optional.empty());
}

@Test
void testDeleteDraftAccount_fail1() {
// Arrange
DraftAccountEntity draftAccountEntity = mock(DraftAccountEntity.class);
when(draftAccountEntity.getDraftAccountId()).thenThrow(new EntityNotFoundException("No Entity in DB"));
when(draftAccountRepository.getReferenceById(any())).thenReturn(draftAccountEntity);

// Act
EntityNotFoundException enfe = assertThrows(
EntityNotFoundException.class, () -> draftAccountService.deleteDraftAccount(1, Optional.empty())
);

// Assert
assertEquals("No Entity in DB", enfe.getMessage());
}

@Test
void testDeleteDraftAccount_fail2() {
// Arrange
DraftAccountEntity draftAccountEntity = DraftAccountEntity.builder().build();
when(draftAccountRepository.getReferenceById(any())).thenReturn(draftAccountEntity);

// Act
RuntimeException re = assertThrows(
RuntimeException.class, () -> draftAccountService.deleteDraftAccount(8, Optional.empty())
);

// Assert
assertEquals("Draft Account entity '8' does not exist in the DB.", re.getMessage());
}

private String createAccountString() {
return """
{
Expand Down