diff --git a/src/main/java/eu/europeana/metis/sandbox/controller/PatternAnalysisController.java b/src/main/java/eu/europeana/metis/sandbox/controller/PatternAnalysisController.java index 4b29ca47..572c5456 100644 --- a/src/main/java/eu/europeana/metis/sandbox/controller/PatternAnalysisController.java +++ b/src/main/java/eu/europeana/metis/sandbox/controller/PatternAnalysisController.java @@ -99,21 +99,32 @@ public PatternAnalysisController(PatternAnalysisService pa @GetMapping(value = "{id}/get-dataset-pattern-analysis", produces = APPLICATION_JSON_VALUE) public ResponseEntity> getDatasetPatternAnalysis( @Parameter(description = "id of the dataset", required = true) @PathVariable("id") String datasetId) { - final Optional datasetExecutionPoint = executionPointService - .getExecutionPoint(datasetId, Step.VALIDATE_INTERNAL.toString()); - final Optional> analysisView = datasetExecutionPoint - .flatMap(executionPoint -> { - var status = finalizeDatasetPatternAnalysis(datasetId, executionPoint); - return patternAnalysisService.getDatasetPatternAnalysis(datasetId, - Step.VALIDATE_INTERNAL, executionPoint.getExecutionTimestamp()) - .map(analysis -> new DatasetProblemPatternAnalysisView<>(analysis, status)); + // Get the execution point. If it does not exist, we are done. + final ExecutionPoint executionPoint = executionPointService + .getExecutionPoint(datasetId, Step.VALIDATE_INTERNAL.toString()).orElse(null); + if (executionPoint == null) { + return new ResponseEntity<>(DatasetProblemPatternAnalysisView.getEmptyAnalysis(datasetId, + ProblemPatternAnalysisStatus.PENDING), HttpStatus.NOT_FOUND); + } + + // Finalize the problem pattern analysis if we can (i.e. if the dataset finished processing). + final ProblemPatternAnalysisStatus status = finalizeDatasetPatternAnalysis(datasetId, executionPoint); + + // Now get the pattern analysis. + final DatasetProblemPatternAnalysisView analysisView = patternAnalysisService.getDatasetPatternAnalysis( + datasetId, Step.VALIDATE_INTERNAL, executionPoint.getExecutionTimestamp()) + .map(analysis -> new DatasetProblemPatternAnalysisView<>(analysis, status)) + .orElseGet(() -> { + LOGGER.error("Result not expected to be empty when there is a non-null execution point."); + return DatasetProblemPatternAnalysisView.getEmptyAnalysis(datasetId, + ProblemPatternAnalysisStatus.ERROR); }); - return analysisView.map(analysis -> new ResponseEntity<>(analysis, HttpStatus.OK)) - .orElseGet(() -> new ResponseEntity<>( - DatasetProblemPatternAnalysisView.getViewForPendingProblemPatternAnalysis(), - HttpStatus.NOT_FOUND)); + // Wrap and return. + final HttpStatus httpStatus = analysisView.analysisStatus == ProblemPatternAnalysisStatus.ERROR + ? HttpStatus.INTERNAL_SERVER_ERROR : HttpStatus.OK; + return new ResponseEntity<>(analysisView, httpStatus); } private ProblemPatternAnalysisStatus finalizeDatasetPatternAnalysis(String datasetId, @@ -200,7 +211,7 @@ private void cleanCache() { } } - private enum ProblemPatternAnalysisStatus {PENDING, IN_PROGRESS, FINALIZED, ERROR} + enum ProblemPatternAnalysisStatus {PENDING, IN_PROGRESS, FINALIZED, ERROR} private static final class DatasetProblemPatternAnalysisView { @@ -225,9 +236,10 @@ private DatasetProblemPatternAnalysisView(DatasetProblemPatternAnalysis datas this.analysisStatus = status; } - private static DatasetProblemPatternAnalysisView getViewForPendingProblemPatternAnalysis() { - return new DatasetProblemPatternAnalysisView<>(new DatasetProblemPatternAnalysis<>("0", - null, null, new ArrayList<>()), ProblemPatternAnalysisStatus.PENDING); + private static DatasetProblemPatternAnalysisView getEmptyAnalysis(String datasetId, + ProblemPatternAnalysisStatus status) { + return new DatasetProblemPatternAnalysisView<>(new DatasetProblemPatternAnalysis<>( + datasetId, null, null, new ArrayList<>()), status); } @NotNull diff --git a/src/test/java/eu/europeana/metis/sandbox/controller/PatternAnalysisControllerTest.java b/src/test/java/eu/europeana/metis/sandbox/controller/PatternAnalysisControllerTest.java index f47c8d5e..548e41f1 100644 --- a/src/test/java/eu/europeana/metis/sandbox/controller/PatternAnalysisControllerTest.java +++ b/src/test/java/eu/europeana/metis/sandbox/controller/PatternAnalysisControllerTest.java @@ -2,19 +2,26 @@ import static java.util.Collections.emptyList; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import eu.europeana.metis.sandbox.common.Step; +import eu.europeana.metis.sandbox.controller.PatternAnalysisController.ProblemPatternAnalysisStatus; import eu.europeana.metis.sandbox.controller.ratelimit.RateLimitInterceptor; import eu.europeana.metis.sandbox.dto.report.ProgressInfoDto; +import eu.europeana.metis.sandbox.dto.report.ProgressInfoDto.Status; import eu.europeana.metis.sandbox.entity.RecordLogEntity; import eu.europeana.metis.sandbox.entity.problempatterns.ExecutionPoint; import eu.europeana.metis.sandbox.service.dataset.DatasetReportService; @@ -39,6 +46,7 @@ import java.util.Optional; import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -75,8 +83,16 @@ class PatternAnalysisControllerTest { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"); + @BeforeEach + void resetMocks() { + reset(mockPatternAnalysisService, mockExecutionPointService, mockRecordLogService, + mockDatasetReportService, lockRegistry); + } + @Test void getDatasetAnalysis_expectSuccess() throws Exception { + + // Set up the tests LocalDateTime executionTimestamp = LocalDateTime.now(); ExecutionPoint executionPoint = new ExecutionPoint(); executionPoint.setExecutionTimestamp(executionTimestamp); @@ -91,16 +107,35 @@ void getDatasetAnalysis_expectSuccess() throws Exception { .thenReturn(Optional.of(datasetProblemPatternAnalysis)); when(lockRegistry.obtain(anyString())).thenReturn(new ReentrantLock()); - when(mockDatasetReportService.getReport("datasetId")).thenReturn( - new ProgressInfoDto("", 1L, 1L, Collections.emptyList(), false, "", emptyList(), null)); - doNothing().when(mockPatternAnalysisService).finalizeDatasetPatternAnalysis(executionPoint); + // First check with dataset that is still processing + final ProgressInfoDto inProgressInfo = new ProgressInfoDto("", 1L, 0L, + Collections.emptyList(), false, "", emptyList(), null); + when(mockDatasetReportService.getReport("datasetId")).thenReturn(inProgressInfo); + assertNotEquals(Status.COMPLETED, inProgressInfo.getStatus()); mvc.perform(get("/pattern-analysis/{id}/get-dataset-pattern-analysis", "datasetId")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.datasetId", is("datasetId"))) - .andExpect(jsonPath("$.executionStep", is(Step.VALIDATE_INTERNAL.name()))) - .andExpect(jsonPath("$.executionTimestamp", is(executionTimestamp.toString()))) - .andExpect(jsonPath("$.problemPatternList", is(Collections.EMPTY_LIST))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$.datasetId", is("datasetId"))) + .andExpect(jsonPath("$.executionStep", is(Step.VALIDATE_INTERNAL.name()))) + .andExpect(jsonPath("$.executionTimestamp", is(executionTimestamp.toString()))) + .andExpect(jsonPath("$.problemPatternList", is(Collections.EMPTY_LIST))) + .andExpect(jsonPath("$.analysisStatus", is(ProblemPatternAnalysisStatus.IN_PROGRESS.name()))); + verify(mockPatternAnalysisService, never()).finalizeDatasetPatternAnalysis(any()); + + // Now check with finalized dataset. + final ProgressInfoDto completedInfo = new ProgressInfoDto("", 1L, 1L, + Collections.emptyList(), false, "", emptyList(), null); + when(mockDatasetReportService.getReport("datasetId")).thenReturn(completedInfo); + assertEquals(Status.COMPLETED, completedInfo.getStatus()); + + mvc.perform(get("/pattern-analysis/{id}/get-dataset-pattern-analysis", "datasetId")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.datasetId", is("datasetId"))) + .andExpect(jsonPath("$.executionStep", is(Step.VALIDATE_INTERNAL.name()))) + .andExpect(jsonPath("$.executionTimestamp", is(executionTimestamp.toString()))) + .andExpect(jsonPath("$.problemPatternList", is(Collections.EMPTY_LIST))) + .andExpect(jsonPath("$.analysisStatus", is(ProblemPatternAnalysisStatus.FINALIZED.name()))); + verify(mockPatternAnalysisService, times(1)).finalizeDatasetPatternAnalysis(executionPoint); } @Test @@ -130,13 +165,13 @@ void getDatasetAnalysis_withProblemPatternList_checkSorting_expectSuccess() thro when(mockDatasetReportService.getReport("datasetId")).thenReturn( new ProgressInfoDto("", 1L, 1L, Collections.emptyList(), false, "", emptyList(), null)); - doNothing().when(mockPatternAnalysisService).finalizeDatasetPatternAnalysis(executionPoint); mvc.perform(get("/pattern-analysis/{id}/get-dataset-pattern-analysis", "datasetId")) .andExpect(status().isOk()) .andExpect(jsonPath("$.datasetId", is("datasetId"))) .andExpect(jsonPath("$.executionStep", is(Step.VALIDATE_INTERNAL.name()))) .andExpect(jsonPath("$.executionTimestamp", is(executionTimestamp.toString()))) + .andExpect(jsonPath("$.analysisStatus", is(ProblemPatternAnalysisStatus.FINALIZED.name()))) .andExpect(jsonPath("$.problemPatternList[0].problemPatternDescription.problemPatternId", is(ProblemPatternDescription.ProblemPatternId.P2.toString()))) .andExpect(jsonPath("$.problemPatternList[0].recordAnalysisList[0].recordId", is("recordId1"))) @@ -146,6 +181,7 @@ void getDatasetAnalysis_withProblemPatternList_checkSorting_expectSuccess() thro .andExpect(jsonPath("$.problemPatternList[1].recordAnalysisList[0].recordId", is("recordId1"))) .andExpect(jsonPath("$.problemPatternList[1].recordAnalysisList[1].recordId", is("recordId2"))) .andExpect(jsonPath("$.problemPatternList[1].recordAnalysisList[2].recordId", is("recordId3"))); + verify(mockPatternAnalysisService, times(1)).finalizeDatasetPatternAnalysis(executionPoint); } @Test @@ -169,11 +205,13 @@ void getDatasetAnalysis_finalize_fail_expectSuccess() throws Exception { doThrow(PatternAnalysisException.class).when(mockPatternAnalysisService).finalizeDatasetPatternAnalysis(executionPoint); mvc.perform(get("/pattern-analysis/{id}/get-dataset-pattern-analysis", "datasetId")) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.datasetId", is("datasetId"))) - .andExpect(jsonPath("$.executionStep", is(Step.VALIDATE_INTERNAL.name()))) - .andExpect(jsonPath("$.executionTimestamp", is(executionTimestamp.toString()))) - .andExpect(jsonPath("$.problemPatternList", is(Collections.EMPTY_LIST))); + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.datasetId", is("datasetId"))) + .andExpect(jsonPath("$.executionStep", is(Step.VALIDATE_INTERNAL.name()))) + .andExpect(jsonPath("$.executionTimestamp", is(executionTimestamp.toString()))) + .andExpect(jsonPath("$.problemPatternList", is(Collections.EMPTY_LIST))) + .andExpect(jsonPath("$.analysisStatus", is(ProblemPatternAnalysisStatus.ERROR.name()))); + verify(mockPatternAnalysisService, times(1)).finalizeDatasetPatternAnalysis(executionPoint); } @Test @@ -185,8 +223,10 @@ void getDatasetAnalysis_getEmptyResult_expectSuccess() throws Exception { .thenReturn(Optional.empty()); mvc.perform(get("/pattern-analysis/{id}/get-dataset-pattern-analysis", "datasetId")) - .andExpect(status().isNotFound()) - .andExpect(jsonPath("$.datasetId", is("0"))); + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.datasetId", is("datasetId"))) + .andExpect(jsonPath("$.analysisStatus", is(ProblemPatternAnalysisStatus.PENDING.name()))); + verify(mockPatternAnalysisService, never()).finalizeDatasetPatternAnalysis(any()); } @Test @@ -204,11 +244,11 @@ void getDatasetAnalysis_executionPoint_getEmptyResult_expectSuccess() throws Exc when(mockDatasetReportService.getReport("datasetId")).thenReturn( new ProgressInfoDto("", 1L, 1L, Collections.emptyList(), false, "", emptyList(), null)); - doNothing().when(mockPatternAnalysisService).finalizeDatasetPatternAnalysis(executionPoint); mvc.perform(get("/pattern-analysis/{id}/get-dataset-pattern-analysis", "datasetId")) - .andExpect(status().isNotFound()) - .andExpect(jsonPath("$.datasetId", is("0"))); + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.datasetId", is("datasetId"))) + .andExpect(jsonPath("$.analysisStatus", is(ProblemPatternAnalysisStatus.ERROR.name()))); } @Test @@ -284,13 +324,13 @@ void getDatasetAnalysis_withCleanMessageReportP7AndSorted_expectSuccess() throws when(mockDatasetReportService.getReport("datasetId")).thenReturn( new ProgressInfoDto("", 1L, 1L, Collections.emptyList(), false, "", emptyList(), null)); - doNothing().when(mockPatternAnalysisService).finalizeDatasetPatternAnalysis(executionPoint); mvc.perform(get("/pattern-analysis/{id}/get-dataset-pattern-analysis", "datasetId")) .andExpect(status().isOk()) .andExpect(jsonPath("$.datasetId", is("datasetId"))) .andExpect(jsonPath("$.executionStep", is(Step.VALIDATE_INTERNAL.name()))) .andExpect(jsonPath("$.executionTimestamp", is(executionTimestamp.toString()))) + .andExpect(jsonPath("$.analysisStatus", is(ProblemPatternAnalysisStatus.FINALIZED.name()))) .andExpect(jsonPath("$.problemPatternList[0].problemPatternDescription.problemPatternId", is(ProblemPatternDescription.ProblemPatternId.P7.toString()))) .andExpect(jsonPath("$.problemPatternList[0].recordAnalysisList[0].recordId", is("recordId1"))) @@ -299,6 +339,7 @@ void getDatasetAnalysis_withCleanMessageReportP7AndSorted_expectSuccess() throws .andExpect(jsonPath("$.problemPatternList[0].recordAnalysisList[1].problemOccurrenceList[0].messageReport", is(""))) .andExpect(jsonPath("$.problemPatternList[0].recordAnalysisList[2].recordId", is("recordId3"))) .andExpect(jsonPath("$.problemPatternList[0].recordAnalysisList[2].problemOccurrenceList[0].messageReport", is(""))); + verify(mockPatternAnalysisService, times(1)).finalizeDatasetPatternAnalysis(executionPoint); } @Test