diff --git a/src/main/java/cms/gov/madie/measure/resources/AdminController.java b/src/main/java/cms/gov/madie/measure/resources/AdminController.java index 3b4e2ccfc..16a1d504e 100644 --- a/src/main/java/cms/gov/madie/measure/resources/AdminController.java +++ b/src/main/java/cms/gov/madie/measure/resources/AdminController.java @@ -4,9 +4,14 @@ import cms.gov.madie.measure.dto.MeasureTestCaseValidationReport; import cms.gov.madie.measure.dto.MeasureTestCaseValidationReportSummary; import cms.gov.madie.measure.dto.TestCaseValidationReport; +import cms.gov.madie.measure.exceptions.ResourceNotFoundException; +import cms.gov.madie.measure.repositories.MeasureRepository; +import cms.gov.madie.measure.services.ActionLogService; import cms.gov.madie.measure.services.MeasureService; import cms.gov.madie.measure.services.MeasureSetService; import cms.gov.madie.measure.services.TestCaseService; +import gov.cms.madie.models.common.ActionType; +import gov.cms.madie.models.measure.Measure; import gov.cms.madie.models.measure.MeasureSet; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; @@ -15,11 +20,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.StopWatch; -import org.springframework.web.bind.annotation.PutMapping; -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 org.springframework.web.bind.annotation.*; import java.security.Principal; import java.util.ArrayList; @@ -34,6 +35,9 @@ public class AdminController { private final MeasureService measureService; private final TestCaseService testCaseService; private final MeasureSetService measureSetService; + private final ActionLogService actionLogService; + + private final MeasureRepository measureRepository; @Value("${madie.admin.concurrency-limit}") private int concurrencyLimit; @@ -104,6 +108,24 @@ public ResponseEntity validateAllMeasure .build()); } + @DeleteMapping("/measures/{id}") + @PreAuthorize("#request.getHeader('api-key') == #apiKey") + public ResponseEntity permDeleteMeasure( + HttpServletRequest request, + @Value("${admin-api-key}") String apiKey, + Principal principal, + @RequestHeader("Authorization") String accessToken, + @PathVariable String id) { + + Measure measureToDelete = measureService.findMeasureById(id); + if (measureToDelete != null) { + measureRepository.delete(measureToDelete); + actionLogService.logAction(id, Measure.class, ActionType.DELETED, principal.getName()); + return ResponseEntity.ok(measureToDelete); + } + throw new ResourceNotFoundException(id); + } + private Callable buildCallableForMeasureId( final String measureId, final String accessToken) { return () -> testCaseService.updateTestCaseValidResourcesWithReport(measureId, accessToken); diff --git a/src/test/java/cms/gov/madie/measure/resources/AdminControllerMvcTest.java b/src/test/java/cms/gov/madie/measure/resources/AdminControllerMvcTest.java index 2f92ba500..4304a8d4b 100644 --- a/src/test/java/cms/gov/madie/measure/resources/AdminControllerMvcTest.java +++ b/src/test/java/cms/gov/madie/measure/resources/AdminControllerMvcTest.java @@ -4,9 +4,12 @@ import cms.gov.madie.measure.dto.JobStatus; import cms.gov.madie.measure.dto.MeasureTestCaseValidationReport; import cms.gov.madie.measure.dto.TestCaseValidationReport; +import cms.gov.madie.measure.repositories.MeasureRepository; +import cms.gov.madie.measure.services.ActionLogService; import cms.gov.madie.measure.services.MeasureService; import cms.gov.madie.measure.services.MeasureSetService; import cms.gov.madie.measure.services.TestCaseService; +import gov.cms.madie.models.measure.Measure; import gov.cms.madie.models.measure.MeasureSet; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -20,8 +23,11 @@ import java.util.List; import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -42,6 +48,9 @@ public class AdminControllerMvcTest { @MockBean private MeasureService measureService; @MockBean private MeasureSetService measureSetService; @MockBean private TestCaseService testCaseService; + @MockBean private ActionLogService actionLogService; + + @MockBean private MeasureRepository measureRepository; @Autowired private MockMvc mockMvc; @@ -290,4 +299,30 @@ public void testValidateAllMeasureTestCasesOneImpactedMeasureDefaultDraftOnly() verify(testCaseService, times(1)).updateTestCaseValidResourcesWithReport(eq("M1"), anyString()); verify(testCaseService, times(1)).updateTestCaseValidResourcesWithReport(eq("M2"), anyString()); } + + @Test + public void testAdminMeasurePermaDelete() throws Exception { + Measure testMsr = Measure.builder().id("12345").build(); + when(measureService.findMeasureById(anyString())).thenReturn(testMsr); + doNothing().when(measureRepository).delete(any(Measure.class)); + + mockMvc.perform( + MockMvcRequestBuilders.delete("/admin/measures/{id}", "12345") + .with(csrf()) + .with(user(TEST_USER_ID)) + .header(ADMIN_TEST_API_KEY_HEADER, ADMIN_TEST_API_KEY_HEADER_VALUE) + .header("Authorization", "test-okta")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", equalTo("12345"))); + } + + @Test + public void testBlocksNonAuthorizedDeleteRequests() throws Exception { + mockMvc.perform( + MockMvcRequestBuilders.delete("/admin/measures/{id}", "12345") + .with(csrf()) + .with(user(TEST_USER_ID)) + .header("Authorization", "test-okta")) + .andExpect(status().isForbidden()); + } }