diff --git a/api/src/main/java/gov/cms/ab2d/api/controller/AdminAPI.java b/api/src/main/java/gov/cms/ab2d/api/controller/AdminAPI.java index 43c14b6609..c76834ae03 100644 --- a/api/src/main/java/gov/cms/ab2d/api/controller/AdminAPI.java +++ b/api/src/main/java/gov/cms/ab2d/api/controller/AdminAPI.java @@ -22,10 +22,7 @@ import javax.validation.constraints.NotBlank; import java.time.OffsetDateTime; -import static gov.cms.ab2d.api.controller.common.ApiText.APPLICATION_JSON; -import static gov.cms.ab2d.api.controller.common.ApiText.OUT_FORMAT; -import static gov.cms.ab2d.api.controller.common.ApiText.SINCE; -import static gov.cms.ab2d.api.controller.common.ApiText.TYPE_PARAM; +import static gov.cms.ab2d.api.controller.common.ApiText.*; import static gov.cms.ab2d.common.util.Constants.API_PREFIX_V1; import static gov.cms.ab2d.common.util.Constants.ADMIN_PREFIX; import static gov.cms.ab2d.fhir.BundleUtils.EOB; @@ -58,13 +55,14 @@ public ResponseEntity udpateClient(@RequestBody PdpClientDTO pdpCl @PostMapping("/job/{contractNumber}") public ResponseEntity createJobByContractOnBehalfOfClient(@PathVariable @NotBlank String contractNumber, - HttpServletRequest request, - @RequestParam(required = false, name = TYPE_PARAM, defaultValue = EOB) String resourceTypes, - @RequestParam(required = false, name = OUT_FORMAT) String outputFormat, - @RequestParam(required = false, name = SINCE) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime since) { + HttpServletRequest request, + @RequestParam(required = false, name = TYPE_PARAM, defaultValue = EOB) String resourceTypes, + @RequestParam(required = false, name = OUT_FORMAT) String outputFormat, + @RequestParam(required = false, name = SINCE) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime since, + @RequestParam(required = false, name = UNTIL) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime until) { pdpClientService.setupClientImpersonation(contractNumber, request); - return bulkDataAccessAPIV1.exportPatientsWithContract(request, contractNumber, resourceTypes, outputFormat, since); + return bulkDataAccessAPIV1.exportPatientsWithContract(request, contractNumber, resourceTypes, outputFormat, since, until); } @ResponseStatus(value = HttpStatus.OK) diff --git a/api/src/main/java/gov/cms/ab2d/api/controller/common/ApiCommon.java b/api/src/main/java/gov/cms/ab2d/api/controller/common/ApiCommon.java index 44af65dcc6..9428150397 100644 --- a/api/src/main/java/gov/cms/ab2d/api/controller/common/ApiCommon.java +++ b/api/src/main/java/gov/cms/ab2d/api/controller/common/ApiCommon.java @@ -3,7 +3,6 @@ import gov.cms.ab2d.api.controller.InMaintenanceModeException; import gov.cms.ab2d.api.controller.TooManyRequestsException; import gov.cms.ab2d.api.remote.JobClient; -import gov.cms.ab2d.contracts.model.Contract; import gov.cms.ab2d.common.model.PdpClient; import gov.cms.ab2d.common.properties.PropertiesService; import gov.cms.ab2d.common.service.ContractService; @@ -11,13 +10,11 @@ import gov.cms.ab2d.common.service.InvalidContractException; import gov.cms.ab2d.common.service.PdpClientService; import gov.cms.ab2d.common.util.PropertyConstants; +import gov.cms.ab2d.contracts.model.Contract; import gov.cms.ab2d.eventclient.clients.SQSEventClient; import gov.cms.ab2d.eventclient.events.ApiResponseEvent; import gov.cms.ab2d.fhir.FhirVersion; import gov.cms.ab2d.job.dto.StartJobDTO; -import java.time.OffsetDateTime; -import java.util.Set; -import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; import org.springframework.http.HttpHeaders; @@ -26,15 +23,12 @@ import org.springframework.stereotype.Service; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import javax.servlet.http.HttpServletRequest; +import java.time.OffsetDateTime; +import java.util.Set; -import static gov.cms.ab2d.common.util.Constants.FHIR_PREFIX; -import static gov.cms.ab2d.common.util.Constants.JOB_LOG; -import static gov.cms.ab2d.common.util.Constants.ORGANIZATION; -import static gov.cms.ab2d.common.util.Constants.SINCE_EARLIEST_DATE; -import static gov.cms.ab2d.common.util.Constants.ZIPFORMAT; -import static gov.cms.ab2d.common.util.Constants.ZIP_SUPPORT_ON; +import static gov.cms.ab2d.common.util.Constants.*; import static gov.cms.ab2d.fhir.BundleUtils.EOB; -import static java.time.format.DateTimeFormatter.ISO_DATE_TIME; import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME; import static org.springframework.http.HttpHeaders.CONTENT_LOCATION; @@ -87,14 +81,27 @@ public void checkSinceTime(OffsetDateTime date) { if (date.isAfter(OffsetDateTime.now())) { throw new InvalidClientInputException("You can not use a time after the current time for _since"); } - try { - OffsetDateTime ed = OffsetDateTime.parse(SINCE_EARLIEST_DATE, ISO_DATE_TIME); - if (date.isBefore(ed)) { - log.error("Invalid _since time received {}", date); - throw new InvalidClientInputException("_since must be after " + ed.format(ISO_OFFSET_DATE_TIME)); - } - } catch (Exception ex) { - throw new InvalidClientInputException("${api.since.date.earliest} date value '" + SINCE_EARLIEST_DATE + "' is invalid"); + if (date.isBefore(SINCE_EARLIEST_DATE_TIME)) { + log.error("Invalid _since time received {}", date); + throw new InvalidClientInputException("_since must be after " + SINCE_EARLIEST_DATE_TIME.format(ISO_OFFSET_DATE_TIME)); + } + } + + public void checkUntilTime(OffsetDateTime since, OffsetDateTime until, FhirVersion version) { + if (until == null) { + return; + } + if (version.equals(FhirVersion.STU3)) { + log.error("_until is not available for V1"); + throw new InvalidClientInputException("The _until parameter is only available with version 2 (FHIR R4) of the API"); + } + if (since != null && until.isBefore(since)) { + log.error("Invalid _until time received {}", until); + throw new InvalidClientInputException("_until must be after _since " + since.format(ISO_OFFSET_DATE_TIME)); + } + if (until.isBefore(SINCE_EARLIEST_DATE_TIME)) { + log.error("Invalid _until time received {}", until); + throw new InvalidClientInputException("_until must be after " + SINCE_EARLIEST_DATE_TIME.format(ISO_OFFSET_DATE_TIME)); } } @@ -149,15 +156,16 @@ public void logSuccessfulJobCreation(String jobGuid) { } public StartJobDTO checkValidCreateJob(HttpServletRequest request, String contractNumber, OffsetDateTime since, - String resourceTypes, String outputFormat, FhirVersion version) { + OffsetDateTime until, String resourceTypes, String outputFormat, FhirVersion version) { PdpClient pdpClient = pdpClientService.getCurrentClient(); contractNumber = checkIfContractAttested(contractService.getContractByContractId(pdpClient.getContractId()), contractNumber); checkIfInMaintenanceMode(); checkIfCurrentClientCanAddJob(); checkResourceTypesAndOutputFormat(resourceTypes, outputFormat); checkSinceTime(since); + checkUntilTime(since, until, version); return new StartJobDTO(contractNumber, pdpClient.getOrganization(), resourceTypes, - getCurrentUrl(request), outputFormat, since, version); + getCurrentUrl(request), outputFormat, since, until, version); } protected String checkIfContractAttested(Contract contract, String contractNumber) { diff --git a/api/src/main/java/gov/cms/ab2d/api/controller/common/ApiText.java b/api/src/main/java/gov/cms/ab2d/api/controller/common/ApiText.java index ac11367242..4de12c4415 100644 --- a/api/src/main/java/gov/cms/ab2d/api/controller/common/ApiText.java +++ b/api/src/main/java/gov/cms/ab2d/api/controller/common/ApiText.java @@ -13,7 +13,9 @@ public class ApiText { public static final String BULK_RESPONSE = "Absolute URL of an endpoint for subsequent status requests (polling location)"; public static final String RUNNING_JOBIDS = "URLs of currently running jobs. To cancel one of those jobs, invoke the Status DELETE call."; public static final String BULK_SINCE = "Beginning time of query. Returns all records \"since\" this time. At this time, it must be after " + SINCE_EARLIEST_DATE; + public static final String BULK_UNTIL = "The _until parameter is only available with version 2 (FHIR R4) of the API."; public static final String BULK_SINCE_DEFAULT = " If no value is provided, it will default to the last time a successful job was requested if it exists. The earliest accepted date is 2020-02-13T00:00:00.000-05:00"; + public static final String BULK_UNTIL_DEFAULT = " If no value is provided, it will default to the current date."; public static final String BULK_RESPONSE_LONG = "Absolute URL of an endpoint for subsequent status requests (polling location)"; public static final String EXPORT_STARTED = "Export request has started"; public static final String MAX_JOBS = "Too many jobs are currently running. Either wait for currently running jobs to finish or cancel some/all of those jobs."; @@ -43,6 +45,7 @@ public class ApiText { public static final String ASYNC = "respond-async"; public static final String OUT_FORMAT = "_outputFormat"; public static final String SINCE = "_since"; + public static final String UNTIL = "_until"; public static final String TYPE_PARAM = "_type"; public static final String PREFER = "Prefer"; public static final String X_PROG = "X-Progress"; diff --git a/api/src/main/java/gov/cms/ab2d/api/controller/v1/BulkDataAccessAPIV1.java b/api/src/main/java/gov/cms/ab2d/api/controller/v1/BulkDataAccessAPIV1.java index 511b5906e3..741d6530f1 100644 --- a/api/src/main/java/gov/cms/ab2d/api/controller/v1/BulkDataAccessAPIV1.java +++ b/api/src/main/java/gov/cms/ab2d/api/controller/v1/BulkDataAccessAPIV1.java @@ -29,31 +29,14 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import static gov.cms.ab2d.api.controller.common.ApiText.APPLICATION_JSON; -import static gov.cms.ab2d.api.controller.common.ApiText.ASYNC; -import static gov.cms.ab2d.api.controller.common.ApiText.BULK_RESPONSE; -import static gov.cms.ab2d.api.controller.common.ApiText.BULK_SINCE; -import static gov.cms.ab2d.api.controller.common.ApiText.CONTRACT_NO; -import static gov.cms.ab2d.api.controller.common.ApiText.EXPORT_STARTED; -import static gov.cms.ab2d.api.controller.common.ApiText.MAX_JOBS; -import static gov.cms.ab2d.api.controller.common.ApiText.OUT_FORMAT; -import static gov.cms.ab2d.api.controller.common.ApiText.PREFER; -import static gov.cms.ab2d.api.controller.common.ApiText.RUNNING_JOBIDS; -import static gov.cms.ab2d.api.controller.common.ApiText.SINCE; -import static gov.cms.ab2d.api.controller.common.ApiText.TYPE_PARAM; +import static gov.cms.ab2d.api.controller.common.ApiText.*; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_CONTRACT_EXPORT; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_EXPORT; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_EXPORT_TYPE; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_MAIN; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_OUTPUT_FORMAT; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_PREFER; -import static gov.cms.ab2d.common.util.Constants.API_PREFIX_V1; -import static gov.cms.ab2d.common.util.Constants.CONTRACT_LOG; -import static gov.cms.ab2d.common.util.Constants.FHIR_JSON_CONTENT_TYPE; -import static gov.cms.ab2d.common.util.Constants.FHIR_PREFIX; -import static gov.cms.ab2d.common.util.Constants.FHIR_NDJSON_CONTENT_TYPE; -import static gov.cms.ab2d.common.util.Constants.REQUEST_ID; -import static gov.cms.ab2d.common.util.Constants.SINCE_EARLIEST_DATE; +import static gov.cms.ab2d.common.util.Constants.*; import static gov.cms.ab2d.fhir.BundleUtils.EOB; import static gov.cms.ab2d.fhir.FhirVersion.STU3; import static org.springframework.http.HttpHeaders.CONTENT_LOCATION; @@ -84,8 +67,8 @@ public BulkDataAccessAPIV1(JobClient jobClient, ApiCommon apiCommon) { "application/fhir+ndjson", "application/ndjson", "ndjson" }, defaultValue = FHIR_NDJSON_CONTENT_TYPE) ), - @Parameter(name = SINCE, description = BULK_SINCE, schema = @Schema(type = "date-time", description = SINCE_EARLIEST_DATE)) - + @Parameter(name = SINCE, description = BULK_SINCE, schema = @Schema(type = "date-time", description = SINCE_EARLIEST_DATE)), + @Parameter(name = UNTIL, description = BULK_UNTIL, schema = @Schema(type = "date-time", description = UNTIL_EXAMPLE_DATE)) } ) @ApiResponses(value = { @@ -107,10 +90,12 @@ public ResponseEntity exportAllPatients( @RequestParam(name = OUT_FORMAT, required = false, defaultValue = FHIR_NDJSON_CONTENT_TYPE) String outputFormat, @RequestParam(required = false, name = SINCE) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - OffsetDateTime since) { + OffsetDateTime since, + @RequestParam(required = false, name = UNTIL) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + OffsetDateTime until) { log.info("Received request to export"); - StartJobDTO startJobDTO = apiCommon.checkValidCreateJob(request, null, since, resourceTypes, outputFormat, STU3); + StartJobDTO startJobDTO = apiCommon.checkValidCreateJob(request, null, since, until, resourceTypes, outputFormat, STU3); String jobGuid = jobClient.createJob(startJobDTO); apiCommon.logSuccessfulJobCreation(jobGuid); return apiCommon.returnStatusForJobCreation(jobGuid, API_PREFIX_V1, (String) request.getAttribute(REQUEST_ID), request); @@ -129,7 +114,7 @@ public ResponseEntity exportAllPatients( }, defaultValue = FHIR_NDJSON_CONTENT_TYPE) ), @Parameter(name = SINCE, description = BULK_SINCE, example = SINCE_EARLIEST_DATE, schema = @Schema(type = "date-time")) - } + } ) @ApiResponses(value = { @ApiResponse(responseCode = "202", description = EXPORT_STARTED, headers = @@ -153,12 +138,14 @@ public ResponseEntity exportPatientsWithContract( @RequestParam(required = false, name = OUT_FORMAT) String outputFormat, @RequestParam(required = false, name = SINCE) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - OffsetDateTime since + OffsetDateTime since, + @RequestParam(required = false, name = UNTIL) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + OffsetDateTime until ) { MDC.put(CONTRACT_LOG, contractNumber); log.info("Received request to export by contractNumber"); - StartJobDTO startJobDTO = apiCommon.checkValidCreateJob(request, contractNumber, since, resourceTypes, + StartJobDTO startJobDTO = apiCommon.checkValidCreateJob(request, contractNumber, since, until, resourceTypes, outputFormat, STU3); String jobGuid = jobClient.createJob(startJobDTO); apiCommon.logSuccessfulJobCreation(jobGuid); diff --git a/api/src/main/java/gov/cms/ab2d/api/controller/v2/BulkDataAccessAPIV2.java b/api/src/main/java/gov/cms/ab2d/api/controller/v2/BulkDataAccessAPIV2.java index 849b0db246..bdc604a99a 100644 --- a/api/src/main/java/gov/cms/ab2d/api/controller/v2/BulkDataAccessAPIV2.java +++ b/api/src/main/java/gov/cms/ab2d/api/controller/v2/BulkDataAccessAPIV2.java @@ -30,31 +30,13 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import static gov.cms.ab2d.api.controller.common.ApiText.APPLICATION_JSON; -import static gov.cms.ab2d.api.controller.common.ApiText.ASYNC; -import static gov.cms.ab2d.api.controller.common.ApiText.BULK_RESPONSE; -import static gov.cms.ab2d.api.controller.common.ApiText.BULK_RESPONSE_LONG; -import static gov.cms.ab2d.api.controller.common.ApiText.BULK_SINCE_DEFAULT; -import static gov.cms.ab2d.api.controller.common.ApiText.CONTRACT_NO; -import static gov.cms.ab2d.api.controller.common.ApiText.EXPORT_STARTED; -import static gov.cms.ab2d.api.controller.common.ApiText.MAX_JOBS; -import static gov.cms.ab2d.api.controller.common.ApiText.OUT_FORMAT; -import static gov.cms.ab2d.api.controller.common.ApiText.PREFER; -import static gov.cms.ab2d.api.controller.common.ApiText.RUNNING_JOBIDS; -import static gov.cms.ab2d.api.controller.common.ApiText.SINCE; -import static gov.cms.ab2d.api.controller.common.ApiText.TYPE_PARAM; +import static gov.cms.ab2d.api.controller.common.ApiText.*; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_CONTRACT_EXPORT; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_EXPORT; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_EXPORT_TYPE; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_OUTPUT_FORMAT; import static gov.cms.ab2d.api.util.SwaggerConstants.BULK_PREFER; -import static gov.cms.ab2d.common.util.Constants.API_PREFIX_V2; -import static gov.cms.ab2d.common.util.Constants.CONTRACT_LOG; -import static gov.cms.ab2d.common.util.Constants.FHIR_JSON_CONTENT_TYPE; -import static gov.cms.ab2d.common.util.Constants.FHIR_PREFIX; -import static gov.cms.ab2d.common.util.Constants.FHIR_NDJSON_CONTENT_TYPE; -import static gov.cms.ab2d.common.util.Constants.REQUEST_ID; -import static gov.cms.ab2d.common.util.Constants.SINCE_EARLIEST_DATE; +import static gov.cms.ab2d.common.util.Constants.*; import static gov.cms.ab2d.fhir.BundleUtils.EOB; import static gov.cms.ab2d.fhir.FhirVersion.R4; import static org.springframework.http.HttpHeaders.CONTENT_LOCATION; @@ -86,8 +68,8 @@ public BulkDataAccessAPIV2(JobClient jobClient, ApiCommon apiCommon) { "application/fhir+ndjson", "application/ndjson", "ndjson" }, defaultValue = FHIR_NDJSON_CONTENT_TYPE) ), - @Parameter(name = SINCE, description = BULK_SINCE_DEFAULT, schema = @Schema(type = "date-time", description = SINCE_EARLIEST_DATE)) - + @Parameter(name = SINCE, description = BULK_SINCE_DEFAULT, schema = @Schema(type = "date-time", description = SINCE_EARLIEST_DATE)), + @Parameter(name = UNTIL, description = BULK_UNTIL_DEFAULT, schema = @Schema(type = "date-time", description = UNTIL_EXAMPLE_DATE)), } ) @ApiResponses(value = { @@ -110,10 +92,12 @@ public ResponseEntity exportAllPatients( @RequestParam(name = OUT_FORMAT, required = false, defaultValue = FHIR_NDJSON_CONTENT_TYPE) String outputFormat, @RequestParam(required = false, name = SINCE) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - OffsetDateTime since) { + OffsetDateTime since, + @RequestParam(required = false, name = UNTIL) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + OffsetDateTime until) { log.info("Received request to export"); - StartJobDTO startJobDTO = apiCommon.checkValidCreateJob(request, null, since, resourceTypes, + StartJobDTO startJobDTO = apiCommon.checkValidCreateJob(request, null, since, until, resourceTypes, outputFormat, R4); String jobGuid = jobClient.createJob(startJobDTO); apiCommon.logSuccessfulJobCreation(jobGuid); @@ -132,9 +116,7 @@ public ResponseEntity exportAllPatients( "application/fhir+ndjson", "application/ndjson", "ndjson" }, defaultValue = FHIR_NDJSON_CONTENT_TYPE) ), - @Parameter(name = SINCE, description = BULK_SINCE_DEFAULT, example = SINCE_EARLIEST_DATE, schema = @Schema(type = "date-time")) - - } + @Parameter(name = SINCE, description = BULK_SINCE_DEFAULT, example = SINCE_EARLIEST_DATE, schema = @Schema(type = "date-time"))} ) @ApiResponses({ @ApiResponse(responseCode = "202", description = EXPORT_STARTED, @@ -160,12 +142,14 @@ public ResponseEntity exportPatientsWithContract( @RequestParam(required = false, name = OUT_FORMAT) String outputFormat, @RequestParam(required = false, name = SINCE) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) - OffsetDateTime since + OffsetDateTime since, + @RequestParam(required = false, name = UNTIL) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + OffsetDateTime until ) { MDC.put(CONTRACT_LOG, contractNumber); log.info("Received request to export by contractNumber"); - StartJobDTO startJobDTO = apiCommon.checkValidCreateJob(request, contractNumber, since, resourceTypes, + StartJobDTO startJobDTO = apiCommon.checkValidCreateJob(request, contractNumber, since, until, resourceTypes, outputFormat, R4); String jobGuid = jobClient.createJob(startJobDTO); apiCommon.logSuccessfulJobCreation(jobGuid); diff --git a/api/src/test/java/gov/cms/ab2d/api/controller/common/ApiCommonTest.java b/api/src/test/java/gov/cms/ab2d/api/controller/common/ApiCommonTest.java index 9a65b7d87a..cb84648c53 100644 --- a/api/src/test/java/gov/cms/ab2d/api/controller/common/ApiCommonTest.java +++ b/api/src/test/java/gov/cms/ab2d/api/controller/common/ApiCommonTest.java @@ -1,20 +1,19 @@ package gov.cms.ab2d.api.controller.common; -import gov.cms.ab2d.contracts.model.Contract; import gov.cms.ab2d.common.model.PdpClient; import gov.cms.ab2d.common.service.ContractService; import gov.cms.ab2d.common.service.InvalidClientInputException; import gov.cms.ab2d.common.service.InvalidContractException; import gov.cms.ab2d.common.service.PdpClientService; +import gov.cms.ab2d.contracts.model.Contract; import gov.cms.ab2d.fhir.FhirVersion; import java.time.OffsetDateTime; import java.time.ZoneOffset; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.time.OffsetDateTime; +import static gov.cms.ab2d.common.util.Constants.SINCE_EARLIEST_DATE_TIME; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -44,8 +43,8 @@ class ApiCommonTest { @Test void unattestedCheck() { InvalidContractException ice = assertThrows(InvalidContractException.class, () -> - apiCommon.checkValidCreateJob(null, CONTRACT_NUMBER, null, - "resource_type", "jpg", FhirVersion.STU3)); + apiCommon.checkValidCreateJob(null, CONTRACT_NUMBER, null, null, + "resource_type", "jpg", FhirVersion.STU3)); assertNotNull(ice); assertTrue(ice.getMessage().contains(CONTRACT_NUMBER + " is not attested.")); } @@ -54,13 +53,30 @@ void unattestedCheck() { void contractNumberMismatch() { final String bogusContractNumber = "BOGUS"; InvalidContractException ice = assertThrows(InvalidContractException.class, () -> - apiCommon.checkValidCreateJob(null, bogusContractNumber, null, + apiCommon.checkValidCreateJob(null, bogusContractNumber, null, null, "resource_type", "jpg", FhirVersion.STU3)); assertNotNull(ice); assertTrue(ice.getMessage().contains(bogusContractNumber + " not associated with internal id")); } @Test + void checkSinceTimeTest() { + assertDoesNotThrow(() -> apiCommon.checkSinceTime(SINCE_EARLIEST_DATE_TIME)); + assertDoesNotThrow(() -> apiCommon.checkSinceTime(null)); + assertThrows(InvalidClientInputException.class, () -> apiCommon.checkSinceTime(OffsetDateTime.now().plusMonths(1))); + assertThrows(InvalidClientInputException.class, () -> apiCommon.checkSinceTime(SINCE_EARLIEST_DATE_TIME.minusMonths(1))); + } + + @Test + void checkUntilTimeTest() { + OffsetDateTime currentDate = OffsetDateTime.now(); + assertDoesNotThrow(() -> apiCommon.checkUntilTime(SINCE_EARLIEST_DATE_TIME, currentDate, FhirVersion.R4)); + assertDoesNotThrow(() -> apiCommon.checkUntilTime(SINCE_EARLIEST_DATE_TIME, null, FhirVersion.R4)); + assertThrows(InvalidClientInputException.class, () -> apiCommon.checkUntilTime(SINCE_EARLIEST_DATE_TIME, currentDate, FhirVersion.STU3)); + assertThrows(InvalidClientInputException.class, () -> apiCommon.checkUntilTime(currentDate, SINCE_EARLIEST_DATE_TIME, FhirVersion.R4)); + assertThrows(InvalidClientInputException.class, () -> apiCommon.checkUntilTime(null, SINCE_EARLIEST_DATE_TIME.minusMonths(1), FhirVersion.R4)); + } + void testCheckSinceTime() { assertDoesNotThrow(() -> { apiCommon.checkSinceTime(null); diff --git a/api/src/test/java/gov/cms/ab2d/api/remote/JobClientMock.java b/api/src/test/java/gov/cms/ab2d/api/remote/JobClientMock.java index 2380a85245..d43757e368 100644 --- a/api/src/test/java/gov/cms/ab2d/api/remote/JobClientMock.java +++ b/api/src/test/java/gov/cms/ab2d/api/remote/JobClientMock.java @@ -89,7 +89,7 @@ public void switchAllJobsToNewOrganization(String organization) { private StartJobDTO convert(StartJobDTO orig, String organization) { return new StartJobDTO(orig.getContractNumber(), organization, orig.getResourceTypes(), - orig.getUrl(), orig.getOutputFormat(), orig.getSince(), orig.getVersion()); + orig.getUrl(), orig.getOutputFormat(), orig.getSince(), orig.getUntil(), orig.getVersion()); } public List getActiveJobIds(String organization) { diff --git a/common/src/main/java/gov/cms/ab2d/common/util/Constants.java b/common/src/main/java/gov/cms/ab2d/common/util/Constants.java index 3b8d1c65c2..6599de4bbf 100644 --- a/common/src/main/java/gov/cms/ab2d/common/util/Constants.java +++ b/common/src/main/java/gov/cms/ab2d/common/util/Constants.java @@ -1,5 +1,8 @@ package gov.cms.ab2d.common.util; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + public final class Constants { private Constants() { } @@ -47,6 +50,7 @@ private Constants() { } // wanted to include it in the swagger documentation and for the swagger annotation, the value has to be // constant at compile time so I put it here. public static final String SINCE_EARLIEST_DATE = "2020-02-13T00:00:00.000-05:00"; - + public static final OffsetDateTime SINCE_EARLIEST_DATE_TIME = OffsetDateTime.of(2020, 2, 13, 0, 0, 0, 0, ZoneOffset.ofHours(-5)); + public static final String UNTIL_EXAMPLE_DATE = "2024-01-01T00:00:00.000-05:00"; public static final String ZIPFORMAT = "application/zip"; } diff --git a/common/src/test/java/gov/cms/ab2d/common/util/DateUtilTest.java b/common/src/test/java/gov/cms/ab2d/common/util/DateUtilTest.java index c4a408be59..a449997cf8 100644 --- a/common/src/test/java/gov/cms/ab2d/common/util/DateUtilTest.java +++ b/common/src/test/java/gov/cms/ab2d/common/util/DateUtilTest.java @@ -8,13 +8,16 @@ import java.util.Date; import java.util.TimeZone; +import static gov.cms.ab2d.common.util.Constants.SINCE_EARLIEST_DATE; +import static gov.cms.ab2d.common.util.Constants.SINCE_EARLIEST_DATE_TIME; import static gov.cms.ab2d.common.util.DateUtil.AB2D_ZONE; +import static java.time.format.DateTimeFormatter.ISO_DATE_TIME; import static org.junit.jupiter.api.Assertions.assertEquals; class DateUtilTest { @Test - void testFormatDateAsDateTimeAsUTC() throws ParseException { + void testFormatDateAsDateTimeAsUTC() { LocalDateTime localDateTime = LocalDateTime.of(2019, 10, 21, 0, 0); String formattedDate = DateUtil.formatLocalDateTimeAsUTC(localDateTime); @@ -39,4 +42,10 @@ void testGetESTOffset() { String offset = DateUtil.getESTOffset(); assertEquals(expectedOffset, offset); } + + @Test + void testSinceEarliestDateTime() { + OffsetDateTime startCheck = OffsetDateTime.parse(SINCE_EARLIEST_DATE, ISO_DATE_TIME); + assertEquals(SINCE_EARLIEST_DATE_TIME, startCheck); + } } diff --git a/job/src/main/java/gov/cms/ab2d/job/dto/StartJobDTO.java b/job/src/main/java/gov/cms/ab2d/job/dto/StartJobDTO.java index 1b0e54ed76..9c638a2cba 100644 --- a/job/src/main/java/gov/cms/ab2d/job/dto/StartJobDTO.java +++ b/job/src/main/java/gov/cms/ab2d/job/dto/StartJobDTO.java @@ -26,6 +26,7 @@ public class StartJobDTO { @NotNull private final String outputFormat; private final OffsetDateTime since; + private final OffsetDateTime until; @NotNull private final FhirVersion version; } diff --git a/job/src/main/java/gov/cms/ab2d/job/model/Job.java b/job/src/main/java/gov/cms/ab2d/job/model/Job.java index df576e1e5b..0d231bf888 100644 --- a/job/src/main/java/gov/cms/ab2d/job/model/Job.java +++ b/job/src/main/java/gov/cms/ab2d/job/model/Job.java @@ -73,6 +73,9 @@ public class Job { @Column(columnDefinition = "TIMESTAMP WITH TIME ZONE") private OffsetDateTime since; + @Column(columnDefinition = "TIMESTAMP WITH TIME ZONE") + private OffsetDateTime until; + @Column(columnDefinition = "TIMESTAMP WITH TIME ZONE") private OffsetDateTime expiresAt; diff --git a/job/src/main/java/gov/cms/ab2d/job/service/JobServiceImpl.java b/job/src/main/java/gov/cms/ab2d/job/service/JobServiceImpl.java index 0e2493ba7e..060ca7ca74 100644 --- a/job/src/main/java/gov/cms/ab2d/job/service/JobServiceImpl.java +++ b/job/src/main/java/gov/cms/ab2d/job/service/JobServiceImpl.java @@ -62,6 +62,7 @@ public Job createJob(StartJobDTO startJobDTO) { job.setOutputFormat(startJobDTO.getOutputFormat()); job.setProgress(0); job.setSince(startJobDTO.getSince()); + job.setUntil(startJobDTO.getUntil()); job.setFhirVersion(startJobDTO.getVersion()); job.setOrganization(startJobDTO.getOrganization()); diff --git a/job/src/test/java/gov/cms/ab2d/job/model/JobTest.java b/job/src/test/java/gov/cms/ab2d/job/model/JobTest.java index 389d9c5fee..063d18dd87 100644 --- a/job/src/test/java/gov/cms/ab2d/job/model/JobTest.java +++ b/job/src/test/java/gov/cms/ab2d/job/model/JobTest.java @@ -28,6 +28,7 @@ void codeCoverageGetterSetter() { job.equals(job); job.addJobOutput(new JobOutput()); job.setSince(job.getSince()); + job.setUntil(job.getUntil()); job.setSinceSource(job.getSinceSource()); assertFalse(job.hasJobBeenCancelled()); job.setStatus(JobStatus.CANCELLED); diff --git a/job/src/test/java/gov/cms/ab2d/job/service/JobServiceTest.java b/job/src/test/java/gov/cms/ab2d/job/service/JobServiceTest.java index 3a2d38982f..049be19b7d 100644 --- a/job/src/test/java/gov/cms/ab2d/job/service/JobServiceTest.java +++ b/job/src/test/java/gov/cms/ab2d/job/service/JobServiceTest.java @@ -162,7 +162,7 @@ private StartJobDTO buildStartJobResourceTypes(String resourceTypes) { private StartJobDTO buildStartJob(String contractNumber, String resourceTypes, String outputFormat) { String organization = pdpClientService.getCurrentClient().getOrganization(); - return new StartJobDTO(contractNumber, organization, resourceTypes, LOCAL_HOST, outputFormat, null, STU3); + return new StartJobDTO(contractNumber, organization, resourceTypes, LOCAL_HOST, outputFormat, null, null, STU3); } @Test diff --git a/job/src/test/java/gov/cms/ab2d/job/service/MaxJobsTest.java b/job/src/test/java/gov/cms/ab2d/job/service/MaxJobsTest.java index 27dc2d4a64..f0216d3a32 100644 --- a/job/src/test/java/gov/cms/ab2d/job/service/MaxJobsTest.java +++ b/job/src/test/java/gov/cms/ab2d/job/service/MaxJobsTest.java @@ -79,7 +79,7 @@ private void createMaxJobs() { Contract contract = contractServiceStub.getAllAttestedContracts().iterator().next(); String organization = pdpClientService.getCurrentClient().getOrganization(); startJobDTO = new StartJobDTO(contract.getContractNumber(), organization, - EOB, LOCAL_HOST, FHIR_NDJSON_CONTENT_TYPE, null, STU3); + EOB, LOCAL_HOST, FHIR_NDJSON_CONTENT_TYPE, null, null, STU3); for (int idx = 0; idx < MAX_JOBS_PER_CLIENT; idx++) { Job retJob = jobService.createJob(startJobDTO); assertNotNull(retJob); diff --git a/pom.xml b/pom.xml index 9d14049608..c42e89c9b7 100644 --- a/pom.xml +++ b/pom.xml @@ -45,10 +45,10 @@ 1.12.10 - 2.2.3 - 1.2.9 + 2.3.0 + 1.2.10 1.3.4 - 1.9.4 + 1.9.7 1.2.5 1.2.4 diff --git a/worker/src/main/java/gov/cms/ab2d/worker/processor/ContractProcessorImpl.java b/worker/src/main/java/gov/cms/ab2d/worker/processor/ContractProcessorImpl.java index e85c5e0ab2..1991f070d4 100644 --- a/worker/src/main/java/gov/cms/ab2d/worker/processor/ContractProcessorImpl.java +++ b/worker/src/main/java/gov/cms/ab2d/worker/processor/ContractProcessorImpl.java @@ -242,11 +242,6 @@ private void loadEobRequests(ContractData contractData) throws InterruptedExcept return; } - //Ignore for S4802 during Centene support - List ignoredContracts = Arrays.asList("S4802", "Z1001"); - if (ignoredContracts.contains(contractData.getContract().getContractNumber())) { - return; - } // Verify that the number of benes requested matches the number expected from the database and fail // immediately if the two do not match ProgressTracker progressTracker = jobProgressService.getStatus(jobUuid); @@ -341,6 +336,7 @@ private Future queuePatientClaimsRequest(List preprocess: job FAILED because the _until parameter is only available with version 2 (FHIR R4)."); + + eventLogger.logAndAlert(job.buildJobStatusChangeEvent(FAILED, EOB_JOB_FAILURE + " Job " + jobUuid + + "failed because the _until parameter is only available with version 2 (FHIR R4)"), PUBLIC_LIST); + + job.setStatus(FAILED); + job.setStatusMessage("failed because the _until parameter is only available with version 2 (FHIR R4)."); + + jobRepository.save(job); + return job; + } + + if (job.getSince() != null && job.getUntil() != null + && job.getUntil().toInstant().isBefore(job.getSince().toInstant())) { + log.warn("JobPreProcessorImpl > preprocess: job FAILED because the _until is before _since."); + + eventLogger.logAndAlert(job.buildJobStatusChangeEvent(FAILED, EOB_JOB_FAILURE + " Job " + jobUuid + + "failed because the _until is before _since."), PUBLIC_LIST); + + job.setStatus(FAILED); + job.setStatusMessage("failed because the _until is before _since."); + + jobRepository.save(job); + return job; + } + ContractDTO contract = contractWorkerClient.getContractByContractNumber(job.getContractNumber()); if (contract == null) { throw new IllegalArgumentException("A job must always have a contract."); @@ -132,10 +160,13 @@ String getStatusString(Job job) { } String contractNum = job.getContractNumber() == null ? "(unknown)" : job.getContractNumber(); String statusString = String.format("%s for %s in progress", EOB_JOB_STARTED, contractNum); + DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME; if (job.getSince() != null) { - DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME; statusString += " (since date: " + job.getSince().format(formatter) + ")"; } + if (job.getUntil() != null) { + statusString += " (until date: " + job.getUntil().format(formatter) + ")"; + } return statusString; } diff --git a/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsCollector.java b/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsCollector.java index 7877d9c25b..ebf7a70aad 100644 --- a/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsCollector.java +++ b/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsCollector.java @@ -151,11 +151,12 @@ private boolean matchingPatient(IBaseResource benefit, CoverageSummary patient) * Create custom NewRelic event and log it * @param since since date used if in use */ - public void logBundleEvent(OffsetDateTime since) { + public void logBundleEvent(OffsetDateTime since, OffsetDateTime until) { Map event = new HashMap<>(); event.put("organization", claimsRequest.getOrganization()); event.put("contract", claimsRequest.getContractNum()); event.put("since", since); + event.put("until", until); event.put("jobid", claimsRequest.getJob()); event.put("bundles", bundles); event.put("raweobs", rawEobs); diff --git a/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsProcessorImpl.java b/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsProcessorImpl.java index 4488a03eca..c23043c0a8 100644 --- a/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsProcessorImpl.java +++ b/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsProcessorImpl.java @@ -14,6 +14,15 @@ import gov.cms.ab2d.fhir.BundleUtils; import gov.cms.ab2d.fhir.FhirVersion; import gov.cms.ab2d.worker.config.SearchConfig; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.AsyncResult; +import org.springframework.stereotype.Component; import java.io.File; import java.io.IOException; @@ -26,21 +35,9 @@ import java.util.List; import java.util.concurrent.Future; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.hl7.fhir.instance.model.api.IBaseBundle; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.AsyncResult; -import org.springframework.stereotype.Component; - - import static gov.cms.ab2d.aggregator.FileOutputType.DATA; import static gov.cms.ab2d.aggregator.FileOutputType.ERROR; -import static gov.cms.ab2d.common.util.Constants.SINCE_EARLIEST_DATE; -import static java.time.format.DateTimeFormatter.ISO_DATE_TIME; +import static gov.cms.ab2d.common.util.Constants.SINCE_EARLIEST_DATE_TIME; @Slf4j @Component @@ -57,8 +54,6 @@ public class PatientClaimsProcessorImpl implements PatientClaimsProcessor { @Value("${bfd.retry.backoffDelay:250}") private int bfdTimeout; - private static final OffsetDateTime START_CHECK = OffsetDateTime.parse(SINCE_EARLIEST_DATE, ISO_DATE_TIME); - /** * Process the retrieval of patient explanation of benefit objects and return the result * for further post-processing @@ -71,7 +66,7 @@ public Future process(PatientClaimsRequest request) { FhirVersion fhirVersion = request.getVersion(); try { String anyErrors = writeOutData(request, fhirVersion, update); - if (anyErrors != null && anyErrors.length() > 0) { + if (anyErrors != null && !anyErrors.isEmpty()) { writeOutErrors(anyErrors, request); } } catch (Exception ex) { @@ -177,8 +172,9 @@ private List getEobBundleResources(PatientClaimsRequest request, IBaseBundle eobBundle; - // Guarantee that since date provided with job doesn't violate AB2D requirements + // Guarantee that since and until dates provided with job don't violate AB2D requirements OffsetDateTime sinceTime = getSinceTime(request); + OffsetDateTime untilTime = getUntilTime(request, sinceTime); try { @@ -186,19 +182,17 @@ private List getEobBundleResources(PatientClaimsRequest request, BFDClient.BFD_BULK_JOB_ID.set(request.getJob()); // Make first request and begin looping over remaining pages - eobBundle = bfdClient.requestEOBFromServer(request.getVersion(), patient.getIdentifiers().getBeneficiaryId(), sinceTime, request.getContractNum()); + eobBundle = bfdClient.requestEOBFromServer(request.getVersion(), patient.getIdentifiers().getBeneficiaryId(), sinceTime, untilTime, request.getContractNum()); collector.filterAndAddEntries(eobBundle, patient); - // Only for S4802 contracts (Centene support) - - while (BundleUtils.getNextLink(eobBundle) != null && isContinue(eobBundle, request)) { + while (BundleUtils.getNextLink(eobBundle) != null) { eobBundle = bfdClient.requestNextBundleFromServer(request.getVersion(), eobBundle, request.getContractNum()); collector.filterAndAddEntries(eobBundle, patient); } // Log request to Kinesis and NewRelic logSuccessful(request, beneficiaryId, requestStartTime); - collector.logBundleEvent(sinceTime); + collector.logBundleEvent(sinceTime, untilTime); return collector.getEobs(); } catch (Exception ex) { @@ -218,23 +212,6 @@ private List getEobBundleResources(PatientClaimsRequest request, } } - //Centene Support - boolean isContinue(IBaseResource resource, PatientClaimsRequest request) { - OffsetDateTime sinceTime = request.getSinceTime(); - if (sinceTime == null) { - return true; - } - Date lastUpdated = resource.getMeta().getLastUpdated(); - if (lastUpdated == null) { - return false; - } - //AB2D-5892 (Sprint 3)Centene customer support to provide 2 year data - if (request.getContractNum().equals("S4802") || request.getContractNum().equals("Z1001")) { - return lastUpdated.getTime() < sinceTime.plusMonths(1).toInstant().toEpochMilli(); - } - return true; - } - /** * Determine what since date to use if any. *

@@ -249,7 +226,7 @@ private OffsetDateTime getSinceTime(PatientClaimsRequest request) { OffsetDateTime sinceTime = request.getSinceTime(); if (sinceTime == null) { - if (request.getAttTime().isAfter(START_CHECK) || request.getAttTime().isEqual(START_CHECK)) { + if (request.getAttTime().isAfter(SINCE_EARLIEST_DATE_TIME) || request.getAttTime().isEqual(SINCE_EARLIEST_DATE_TIME)) { sinceTime = request.getAttTime(); } } else { @@ -259,7 +236,7 @@ private OffsetDateTime getSinceTime(PatientClaimsRequest request) { } // Should not be possible but just in case - if (sinceTime.isBefore(START_CHECK)) { + if (sinceTime.isBefore(SINCE_EARLIEST_DATE_TIME)) { sinceTime = null; } } @@ -267,6 +244,18 @@ private OffsetDateTime getSinceTime(PatientClaimsRequest request) { return sinceTime; } + private OffsetDateTime getUntilTime(PatientClaimsRequest request, OffsetDateTime updatedSinceTime) { + OffsetDateTime untilTime = request.getUntilTime(); + if (untilTime == null || untilTime.isAfter(OffsetDateTime.now())) + return null; + + if (updatedSinceTime != null && untilTime.isBefore(updatedSinceTime)) { + return null; + } + + return untilTime; + } + private void logSuccessful(PatientClaimsRequest request, long beneficiaryId, OffsetDateTime start) { logManager.log(EventClient.LogType.KINESIS, new BeneficiarySearchEvent(request.getOrganization(), request.getJob(), request.getContractNum(), diff --git a/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsRequest.java b/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsRequest.java index 354cf15412..df8244c02e 100644 --- a/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsRequest.java +++ b/worker/src/main/java/gov/cms/ab2d/worker/processor/PatientClaimsRequest.java @@ -36,6 +36,12 @@ public class PatientClaimsRequest { @Nullable private final OffsetDateTime sinceTime; + /** + * Optional datetime that PDP wants data to. + */ + @Nullable + private final OffsetDateTime untilTime; + /** * Organization name of contract that is not case sensitive */ diff --git a/worker/src/main/java/gov/cms/ab2d/worker/processor/coverage/CoverageDriverImpl.java b/worker/src/main/java/gov/cms/ab2d/worker/processor/coverage/CoverageDriverImpl.java index 6f1c8795a4..78810ac474 100644 --- a/worker/src/main/java/gov/cms/ab2d/worker/processor/coverage/CoverageDriverImpl.java +++ b/worker/src/main/java/gov/cms/ab2d/worker/processor/coverage/CoverageDriverImpl.java @@ -519,11 +519,13 @@ public boolean isCoverageAvailable(Job job, ContractDTO contract) throws Interru @Override public int numberOfBeneficiariesToProcess(Job job, ContractDTO contract) { - ZonedDateTime time; - //Centene support - if (job.getContractNumber().equals("S4802") || job.getContractNumber().equals("Z1001")) - time = job.getSince().atZoneSameInstant(AB2D_ZONE).plusMonths(1); - else time = getEndDateTime(); + // ZonedDateTime time; +// //Centene and Humana support +// if (job.getContractNumber().equals("S4802") || job.getContractNumber().equals("Z1001")) +// time = job.getSince().atZoneSameInstant(AB2D_ZONE).plusMonths(1); +// else if (job.getContractNumber().equals("S5884")) +// time = job.getSince().atZoneSameInstant(AB2D_ZONE).plusMonths(2); + ZonedDateTime time = getEndDateTime(); if (contract == null) { throw new CoverageDriverException("cannot retrieve metadata for job missing contract"); diff --git a/worker/src/test/java/gov/cms/ab2d/worker/processor/AggregatorJobTest.java b/worker/src/test/java/gov/cms/ab2d/worker/processor/AggregatorJobTest.java index bff1988edd..e85cd4c054 100644 --- a/worker/src/test/java/gov/cms/ab2d/worker/processor/AggregatorJobTest.java +++ b/worker/src/test/java/gov/cms/ab2d/worker/processor/AggregatorJobTest.java @@ -83,23 +83,23 @@ void testWriteBunch() throws ParseException, IOException, InterruptedException { String org = "org1"; final Token token = NewRelic.getAgent().getTransaction().getToken(); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(1L), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(1))); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(2L), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(2))); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(3L), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(3))); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(4L), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(4))); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(5L), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(5))); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(6L), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(6))); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(7L), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(7))); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(8L), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(8))); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(9L), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(9))); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(10L), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(10))); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(1L), any(), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(1))); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(2L), any(), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(2))); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(3L), any(), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(3))); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(4L), any(), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(4))); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(5L), any(), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(5))); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(6L), any(), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(6))); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(7L), any(), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(7))); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(8L), any(), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(8))); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(9L), any(), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(9))); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(10L), any(), any(), any())).thenReturn(BundleUtils.createBundle(createBundleEntry(10))); ContractForCoverageDTO contract = createContract(contractNo); PatientClaimsRequest request = new PatientClaimsRequest(createCoverageSummaries(10, contract), OffsetDateTime.of(2020, 1, 1, 0, 0, 0, 9, ZoneOffset.UTC), OffsetDateTime.of(2020, 1, 1, 0, 0, 0, 9, ZoneOffset.UTC), - org, job, contract.getContractNumber(), Contract.ContractType.NORMAL, token, FhirVersion.STU3, tempDir.getAbsolutePath()); + null, org, job, contract.getContractNumber(), Contract.ContractType.NORMAL, token, FhirVersion.STU3, tempDir.getAbsolutePath()); ReflectionTestUtils.setField(processor, "earliestDataDate", "01/01/2020"); Future future = processor.process(request); diff --git a/worker/src/test/java/gov/cms/ab2d/worker/processor/ContractProcessorInvalidPatientTest.java b/worker/src/test/java/gov/cms/ab2d/worker/processor/ContractProcessorInvalidPatientTest.java index 68563d4471..9419a88433 100644 --- a/worker/src/test/java/gov/cms/ab2d/worker/processor/ContractProcessorInvalidPatientTest.java +++ b/worker/src/test/java/gov/cms/ab2d/worker/processor/ContractProcessorInvalidPatientTest.java @@ -118,9 +118,9 @@ void testInvalidBenes() throws IOException { org.hl7.fhir.dstu3.model.Bundle b1 = BundleUtils.createBundle(createBundleEntry("1")); org.hl7.fhir.dstu3.model.Bundle b2 = BundleUtils.createBundle(createBundleEntry("2")); org.hl7.fhir.dstu3.model.Bundle b4 = BundleUtils.createBundle(createBundleEntry("4")); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(1L), any(), any())).thenReturn(b1); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(2L), any(), any())).thenReturn(b2); - when(bfdClient.requestEOBFromServer(eq(STU3), eq(3L), any(), any())).thenReturn(b4); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(1L), any(), any(), any())).thenReturn(b1); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(2L), any(), any(), any())).thenReturn(b2); + when(bfdClient.requestEOBFromServer(eq(STU3), eq(3L), any(), any(), any())).thenReturn(b4); when(coverageDriver.numberOfBeneficiariesToProcess(any(Job.class), any(ContractDTO.class))).thenReturn(3); diff --git a/worker/src/test/java/gov/cms/ab2d/worker/processor/JobPreProcessorUnitTest.java b/worker/src/test/java/gov/cms/ab2d/worker/processor/JobPreProcessorUnitTest.java index cae7f97d28..46ae868c7d 100644 --- a/worker/src/test/java/gov/cms/ab2d/worker/processor/JobPreProcessorUnitTest.java +++ b/worker/src/test/java/gov/cms/ab2d/worker/processor/JobPreProcessorUnitTest.java @@ -83,6 +83,39 @@ void checkJobExistsBeforeProcessing() { assertEquals("Job missing-job-id was not found", exceptionThrown.getMessage()); } + @Test + void checkUntilAndSTU3Parameters() { + when(jobRepository.findByJobUuid(job.getJobUuid())).thenReturn(job); + job.setFhirVersion(STU3); + job.setUntil(OffsetDateTime.of(2022, 7, 11, 1, 2, 3, 0, ZoneOffset.UTC)); + job.setStatus(SUBMITTED); + cut.preprocess(job.getJobUuid()); + assertEquals(JobStatus.FAILED, job.getStatus()); + } + + @Test + void checkUntilAndR4Parameters() throws InterruptedException { + when(jobRepository.findByJobUuid(job.getJobUuid())).thenReturn(job); + job.setFhirVersion(R4); + job.setUntil(OffsetDateTime.of(2022, 7, 11, 1, 2, 3, 0, ZoneOffset.UTC)); + job.setStatus(SUBMITTED); + when(contractWorkerClient.getContractByContractNumber(job.getContractNumber())).thenReturn(contract); + when(coverageDriver.isCoverageAvailable(any(Job.class), any(ContractDTO.class))).thenReturn(true); + cut.preprocess(job.getJobUuid()); + assertEquals(JobStatus.IN_PROGRESS, job.getStatus()); + } + + @Test + void checkUntilAndSinceParameters() { + when(jobRepository.findByJobUuid(job.getJobUuid())).thenReturn(job); + job.setFhirVersion(R4); + job.setSince(OffsetDateTime.of(2024, 7, 11, 1, 2, 3, 0, ZoneOffset.UTC)); + job.setUntil(OffsetDateTime.of(2022, 7, 11, 1, 2, 3, 0, ZoneOffset.UTC)); + job.setStatus(SUBMITTED); + cut.preprocess(job.getJobUuid()); + assertEquals(JobStatus.FAILED, job.getStatus()); + } + @Test @DisplayName("Throws exception when the job for the given JobUuid is not in submitted status") void whenTheJobForTheGivenJobUuidIsNotInSubmittedStatus_ThrowsException() { @@ -188,6 +221,7 @@ void testDefaultSinceSTU3() { when(contractWorkerClient.getContractByContractNumber(job.getContractNumber())).thenReturn(contract); cut.preprocess(job.getJobUuid()); assertNull(job.getSince()); + assertNull(job.getUntil()); verify(jobRepository, never()).findByContractNumberEqualsAndStatusInAndStartedByOrderByCompletedAtDesc(anyString(), any(), any()); assertNull(job.getSinceSource()); } @@ -204,6 +238,7 @@ void testDefaultSinceR4FirstRun() { cut.preprocess(job.getJobUuid()); assertNull(job.getSince()); + assertNull(job.getUntil()); assertEquals(SinceSource.FIRST_RUN, job.getSinceSource()); } diff --git a/worker/src/test/java/gov/cms/ab2d/worker/processor/JobProcessorIntegrationTest.java b/worker/src/test/java/gov/cms/ab2d/worker/processor/JobProcessorIntegrationTest.java index bbeb1fc1b8..5ea918ccdc 100644 --- a/worker/src/test/java/gov/cms/ab2d/worker/processor/JobProcessorIntegrationTest.java +++ b/worker/src/test/java/gov/cms/ab2d/worker/processor/JobProcessorIntegrationTest.java @@ -184,7 +184,7 @@ void setUp() { copy.getPatient().setReference("Patient/" + args.getArgument(1)); return EobTestDataUtil.createBundle(copy); }); - when(mockBfdClient.requestEOBFromServer(eq(STU3), anyLong(), any(), anyString())).thenAnswer((args) -> { + when(mockBfdClient.requestEOBFromServer(eq(STU3), anyLong(), any(), any(), anyString())).thenAnswer((args) -> { ExplanationOfBenefit copy = EOB.copy(); copy.getPatient().setReference("Patient/" + args.getArgument(1)); return EobTestDataUtil.createBundle(copy); @@ -294,7 +294,7 @@ void whenJobSubmittedByParentClient_ProcessAllContractsForChildrenSponsors() { @DisplayName("When bene has no eobs then do not count bene toward statistic") void when_beneHasNoEobs_notCounted() { reset(mockBfdClient); - OngoingStubbing stubbing = when(mockBfdClient.requestEOBFromServer(eq(STU3), anyLong(), any(), anyString())); + OngoingStubbing stubbing = when(mockBfdClient.requestEOBFromServer(eq(STU3), anyLong(), any(), any(), anyString())); stubbing = andThenAnswerEobs(stubbing, 0, 95); stubbing.thenReturn(BundleUtils.createBundle()) .thenReturn(BundleUtils.createBundle()) @@ -328,7 +328,7 @@ void when_beneHasNoEobs_notCounted() { @DisplayName("When the error count is below threshold, job does not fail") void when_errorCount_is_below_threshold_do_not_fail_job() { reset(mockBfdClient); - OngoingStubbing stubbing = when(mockBfdClient.requestEOBFromServer(eq(STU3), anyLong(), any(), anyString())); + OngoingStubbing stubbing = when(mockBfdClient.requestEOBFromServer(eq(STU3), anyLong(), any(), any(), anyString())); stubbing = andThenAnswerEobs(stubbing, 0, 95); stubbing.thenThrow(fail, fail, fail, fail, fail); @@ -366,7 +366,7 @@ void when_errorCount_is_not_below_threshold_fail_job() { when(mockCoverageDriver.numberOfBeneficiariesToProcess(any(Job.class), any(ContractDTO.class))).thenReturn(40); andThenAnswerPatients(mockCoverageDriver, contractForCoverageDTO, 10, 40); - OngoingStubbing stubbing = when(mockBfdClient.requestEOBFromServer(eq(STU3), anyLong(), any(), anyString())); + OngoingStubbing stubbing = when(mockBfdClient.requestEOBFromServer(eq(STU3), anyLong(), any(), any(), anyString())); stubbing = andThenAnswerEobs(stubbing, 0, 20); stubbing = stubbing.thenThrow(fail, fail, fail, fail, fail, fail, fail, fail, fail, fail); andThenAnswerEobs(stubbing, 30, 10); diff --git a/worker/src/test/java/gov/cms/ab2d/worker/processor/PatientClaimsCollectorTest.java b/worker/src/test/java/gov/cms/ab2d/worker/processor/PatientClaimsCollectorTest.java index 056882468c..04d13f0cf5 100644 --- a/worker/src/test/java/gov/cms/ab2d/worker/processor/PatientClaimsCollectorTest.java +++ b/worker/src/test/java/gov/cms/ab2d/worker/processor/PatientClaimsCollectorTest.java @@ -90,17 +90,17 @@ void testAfterSinceDate() { CoverageSummary coverageSummary = new CoverageSummary(createIdentifierWithoutMbi(PATIENT_ID), null, List.of()); - PatientClaimsRequest request1 = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, "client", "job", + PatientClaimsRequest request1 = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, null,"client", "job", "contractNum", Contract.ContractType.CLASSIC_TEST, noOpToken, STU3, null); PatientClaimsCollector collector1 = new PatientClaimsCollector(request1, EPOCH); assertTrue(collector1.afterSinceDate(eob)); - PatientClaimsRequest request2 = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, oneMinuteEarlier, "client", "job", + PatientClaimsRequest request2 = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, oneMinuteEarlier, null,"client", "job", "contractNum", Contract.ContractType.CLASSIC_TEST, noOpToken, STU3, null); PatientClaimsCollector collector2 = new PatientClaimsCollector(request2, EPOCH); assertTrue(collector2.afterSinceDate(eob)); - PatientClaimsRequest request3 = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, oneMinuteLater, "client", "job", + PatientClaimsRequest request3 = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, oneMinuteLater, null,"client", "job", "contractNum", Contract.ContractType.CLASSIC_TEST, noOpToken, STU3, null); PatientClaimsCollector collector3 = new PatientClaimsCollector(request3, EPOCH); assertFalse(collector3.afterSinceDate(eob)); @@ -120,10 +120,10 @@ void testNewR4DD() throws IOException { IBaseBundle bundle = EobTestDataUtil.createBundle(ab2dEob); long beneId = Long.parseLong(ab2dEob.getPatient().getReference().replace("Patient/", "")); - Identifiers ids = new Identifiers(beneId, "ABC", new LinkedHashSet()); + Identifiers ids = new Identifiers(beneId, "ABC", new LinkedHashSet<>()); CoverageSummary coverageSummary = new CoverageSummary(ids, null, List.of()); - PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), EARLY_ATT_DATE, null, "client", "job", + PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), EARLY_ATT_DATE, null, null,"client", "job", "contractNum", Contract.ContractType.CLASSIC_TEST, noOpToken, R4, null); PatientClaimsCollector collector = new PatientClaimsCollector(request, EPOCH); @@ -141,7 +141,7 @@ void whenSkipBillablePeriod() { CoverageSummary coverageSummary = new CoverageSummary(createIdentifierWithoutMbi(PATIENT_ID), null, List.of()); - PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, "client", "job", + PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, null,"client", "job", "contractNum", Contract.ContractType.CLASSIC_TEST, noOpToken, R4, null); PatientClaimsCollector collector = new PatientClaimsCollector(request, EPOCH); @@ -149,14 +149,14 @@ void whenSkipBillablePeriod() { collector.filterAndAddEntries(BUNDLE, coverageSummary); assertEquals(1, collector.getEobs().size()); - request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, "client", "job", + request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, null,"client", "job", "contractNum", Contract.ContractType.SYNTHEA, noOpToken, R4, null); collector = new PatientClaimsCollector(request, EPOCH); collector.filterAndAddEntries(BUNDLE, coverageSummary); assertTrue(collector.getEobs().isEmpty()); - request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, "client", "job", + request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, null,"client", "job", "contractNum", Contract.ContractType.NORMAL, noOpToken, R4, null); collector = new PatientClaimsCollector(request, EPOCH); @@ -171,7 +171,7 @@ void patientIdMustMatchRequest() { null, List.of(TestUtil.getOpenRange())); - PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, "client", "job", + PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, null,"client", "job", "contractNum", Contract.ContractType.NORMAL, noOpToken, STU3, null); PatientClaimsCollector collector = new PatientClaimsCollector(request, EPOCH); @@ -180,7 +180,7 @@ void patientIdMustMatchRequest() { coverageSummary = new CoverageSummary(createIdentifierWithoutMbi(PATIENT_ID), null, List.of(TestUtil.getOpenRange())); - request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, "client", "job", + request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, null,"client", "job", "contractNum", Contract.ContractType.NORMAL, noOpToken, STU3, null); collector = new PatientClaimsCollector(request, EPOCH); @@ -195,7 +195,7 @@ void normalBehavior() { CoverageSummary coverageSummary = new CoverageSummary(createIdentifierWithoutMbi(PATIENT_ID), null, List.of(TestUtil.getOpenRange())); - PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), EARLY_ATT_DATE, null, "client", "job", + PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), EARLY_ATT_DATE, null, null,"client", "job", "contractNum", Contract.ContractType.NORMAL, noOpToken, STU3, null); PatientClaimsCollector collector = new PatientClaimsCollector(request, EPOCH); @@ -216,7 +216,7 @@ void oldSinceOldAttestation_thenFilterOut() { eob.getBillablePeriod().setEnd(SDF.parse("10/13/1970")); IBaseBundle oldBundle = EobTestDataUtil.createBundle(eob); - PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now().minusYears(100), null, "client", "job", + PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now().minusYears(100), null, null,"client", "job", "contractNum", Contract.ContractType.NORMAL, noOpToken, STU3, null); PatientClaimsCollector collector = new PatientClaimsCollector(request, EPOCH); collector.filterAndAddEntries(oldBundle, coverageSummary); @@ -240,7 +240,7 @@ void whenEarlyAttestation_blockEarlyEobs() { eob.getBillablePeriod().setEnd(SDF.parse("12/30/2019")); IBaseBundle oldBundle = EobTestDataUtil.createBundle(eob); - PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now().minusYears(100), null, "client", "job", + PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now().minusYears(100), null, null,"client", "job", "contractNum", Contract.ContractType.NORMAL, noOpToken, STU3, null); PatientClaimsCollector collector = new PatientClaimsCollector(request, EPOCH); collector.filterAndAddEntries(oldBundle, coverageSummary); @@ -264,7 +264,7 @@ void whenEarlyAttestation_allowEarlyEobs() { eob.getBillablePeriod().setEnd(SDF.parse("01/03/2020")); IBaseBundle oldBundle = EobTestDataUtil.createBundle(eob); - PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now().minusYears(100), null, "client", "job", + PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now().minusYears(100), null,null, "client", "job", "contractNum", Contract.ContractType.NORMAL, noOpToken, STU3, null); PatientClaimsCollector collector = new PatientClaimsCollector(request, EPOCH); collector.filterAndAddEntries(oldBundle, coverageSummary); @@ -288,7 +288,7 @@ void whenAttestationToday_blockEobs() { eob.getBillablePeriod().setEnd(SDF.parse("01/03/2020")); IBaseBundle oldBundle = EobTestDataUtil.createBundle(eob); - PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now(), null, "client", "job", + PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now(), null, null,"client", "job", "contractNum", Contract.ContractType.NORMAL, noOpToken, STU3, null); PatientClaimsCollector collector = new PatientClaimsCollector(request, EPOCH); collector.filterAndAddEntries(oldBundle, coverageSummary); @@ -306,7 +306,7 @@ void whenBundleNull_thenFailQuietly() { CoverageSummary coverageSummary = new CoverageSummary(createIdentifierWithoutMbi(PATIENT_ID), null, List.of(TestUtil.getOpenRange())); - PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now(), null, "client", "job", + PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now(), null, null,"client", "job", "contractNum", Contract.ContractType.NORMAL, noOpToken, STU3, null); PatientClaimsCollector collector = new PatientClaimsCollector(request, EPOCH); collector.filterAndAddEntries(null, coverageSummary); @@ -327,7 +327,7 @@ void whenBundleEntriesNull_thenFailQuietly() { IBaseBundle oldBundle = EobTestDataUtil.createBundle(eob); ReflectionTestUtils.setField(oldBundle, "entry", null); - PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now(), null, "client", "job", + PatientClaimsRequest request = new PatientClaimsRequest(List.of(coverageSummary), OffsetDateTime.now(), null, null,"client", "job", "contractNum", Contract.ContractType.NORMAL, noOpToken, STU3, null); PatientClaimsCollector collector = new PatientClaimsCollector(request, EPOCH); collector.filterAndAddEntries(oldBundle, coverageSummary); diff --git a/worker/src/test/java/gov/cms/ab2d/worker/processor/PatientClaimsProcessorUnitTest.java b/worker/src/test/java/gov/cms/ab2d/worker/processor/PatientClaimsProcessorUnitTest.java index 9a5d1b0286..bdcfd363d6 100644 --- a/worker/src/test/java/gov/cms/ab2d/worker/processor/PatientClaimsProcessorUnitTest.java +++ b/worker/src/test/java/gov/cms/ab2d/worker/processor/PatientClaimsProcessorUnitTest.java @@ -16,10 +16,7 @@ import java.nio.file.Paths; import java.time.OffsetDateTime; import java.time.ZoneOffset; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ExecutionException; import lombok.extern.slf4j.Slf4j; import org.hl7.fhir.dstu3.model.ExplanationOfBenefit; @@ -117,14 +114,13 @@ void setUp() throws Exception { null, List.of(TestUtil.getOpenRange())); coverageSummaries.add(fileSummary); - request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, "client", "job", + request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, null,"client", "job", CONTRACT_NUM, Contract.ContractType.NORMAL, noOpToken, STU3, tmpEfsMountDir.getAbsolutePath()); } @Test void testWriteOutErrors() throws IOException { - PatientClaimsProcessorImpl impl = (PatientClaimsProcessorImpl) cut; - impl.writeOutErrors("The errors", request); + cut.writeOutErrors("The errors", request); Path p = Path.of(tmpEfsMountDir.getAbsolutePath(), "job", searchConfig.getFinishedDir()); File[] files = p.toFile().listFiles(); assertNotNull(files); @@ -154,9 +150,9 @@ void process_whenPatientHasDataWithBadLastUpdated() throws ExecutionException, I assertEquals(3, bundle1.getEntry().size()); - PatientClaimsRequest request2 = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, LATER_ATT_DATE, "client", "job", + PatientClaimsRequest request2 = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, LATER_ATT_DATE, null,"client", "job", CONTRACT_NUM, Contract.ContractType.NORMAL, noOpToken, STU3, tmpEfsMountDir.getAbsolutePath()); - when(mockBfdClient.requestEOBFromServer(STU3, patientId, request2.getAttTime(), contractDTO.getContractNumber())).thenReturn(bundle1); + when(mockBfdClient.requestEOBFromServer(STU3, patientId, request2.getAttTime(), null, contractDTO.getContractNumber())).thenReturn(bundle1); cut.process(request2).get(); } @@ -164,7 +160,7 @@ void process_whenPatientHasDataWithBadLastUpdated() throws ExecutionException, I @Test void process_whenPatientHasSinglePageOfClaimsData() throws ExecutionException, InterruptedException { org.hl7.fhir.dstu3.model.Bundle bundle1 = EobTestDataUtil.createBundle(eob.copy()); - when(mockBfdClient.requestEOBFromServer(STU3, patientId, request.getAttTime(), CONTRACT_NUM)).thenReturn(bundle1); + when(mockBfdClient.requestEOBFromServer(STU3, patientId, request.getAttTime(), null, CONTRACT_NUM)).thenReturn(bundle1); ProgressTrackerUpdate update = cut.process(request).get(); assertNotNull(update); @@ -174,7 +170,7 @@ void process_whenPatientHasSinglePageOfClaimsData() throws ExecutionException, I assertEquals(1, update.getPatientWithEobCount()); assertEquals(0, update.getPatientFailureCount()); - verify(mockBfdClient).requestEOBFromServer(STU3, patientId, request.getAttTime(), CONTRACT_NUM); + verify(mockBfdClient).requestEOBFromServer(STU3, patientId, request.getAttTime(), null, CONTRACT_NUM); verify(mockBfdClient, never()).requestNextBundleFromServer(STU3, bundle1, CONTRACT_NUM); } @@ -185,7 +181,7 @@ void process_whenPatientHasMultiplePagesOfClaimsData() throws ExecutionException org.hl7.fhir.dstu3.model.Bundle bundle2 = EobTestDataUtil.createBundle(eob.copy()); - when(mockBfdClient.requestEOBFromServer(STU3, patientId, request.getAttTime(), CONTRACT_NUM)).thenReturn(bundle1); + when(mockBfdClient.requestEOBFromServer(STU3, patientId, request.getAttTime(), null, CONTRACT_NUM)).thenReturn(bundle1); when(mockBfdClient.requestNextBundleFromServer(STU3, bundle1, CONTRACT_NUM)).thenReturn(bundle2); ProgressTrackerUpdate update = cut.process(request).get(); @@ -196,28 +192,28 @@ void process_whenPatientHasMultiplePagesOfClaimsData() throws ExecutionException assertEquals(1, update.getPatientWithEobCount()); assertEquals(0, update.getPatientFailureCount()); - verify(mockBfdClient).requestEOBFromServer(STU3, patientId, request.getAttTime(), CONTRACT_NUM); + verify(mockBfdClient).requestEOBFromServer(STU3, patientId, request.getAttTime(), null, CONTRACT_NUM); verify(mockBfdClient).requestNextBundleFromServer(STU3, bundle1, CONTRACT_NUM); } @Test void process_whenBfdClientThrowsException() { org.hl7.fhir.dstu3.model.Bundle bundle1 = EobTestDataUtil.createBundle(eob.copy()); - when(mockBfdClient.requestEOBFromServer(STU3, patientId, request.getAttTime(), CONTRACT_NUM)).thenThrow(new RuntimeException("Test Exception")); + when(mockBfdClient.requestEOBFromServer(STU3, patientId, request.getAttTime(), null,CONTRACT_NUM)).thenThrow(new RuntimeException("Test Exception")); var exceptionThrown = assertThrows(ExecutionException.class, () -> cut.process(request).get()); assertTrue(exceptionThrown.getCause().getMessage().startsWith("Test Exception")); - verify(mockBfdClient).requestEOBFromServer(STU3, patientId, request.getAttTime(), CONTRACT_NUM); + verify(mockBfdClient).requestEOBFromServer(STU3, patientId, request.getAttTime(),null, CONTRACT_NUM); verify(mockBfdClient, never()).requestNextBundleFromServer(STU3, bundle1, CONTRACT_NUM); } @Test void process_whenPatientHasNoEOBClaimsData() throws ExecutionException, InterruptedException { org.hl7.fhir.dstu3.model.Bundle bundle1 = new org.hl7.fhir.dstu3.model.Bundle(); - when(mockBfdClient.requestEOBFromServer(STU3, patientId, request.getAttTime(), CONTRACT_NUM)).thenReturn(bundle1); + when(mockBfdClient.requestEOBFromServer(STU3, patientId, request.getAttTime(),null, CONTRACT_NUM)).thenReturn(bundle1); ProgressTrackerUpdate update = cut.process(request).get(); assertNotNull(update); @@ -227,7 +223,7 @@ void process_whenPatientHasNoEOBClaimsData() throws ExecutionException, Interrup assertEquals(0, update.getPatientWithEobCount()); assertEquals(0, update.getPatientFailureCount()); - verify(mockBfdClient).requestEOBFromServer(STU3, patientId, request.getAttTime(), CONTRACT_NUM); + verify(mockBfdClient).requestEOBFromServer(STU3, patientId, request.getAttTime(), null, CONTRACT_NUM); verify(mockBfdClient, never()).requestNextBundleFromServer(STU3, bundle1, CONTRACT_NUM); } @@ -238,15 +234,15 @@ void process_whenPatientHasSinglePageOfClaimsDataSince() throws ExecutionExcepti OffsetDateTime sinceDate = EARLY_ATT_DATE.plusDays(1); - request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, sinceDate, "client", "job", + request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, sinceDate, null,"client", "job", CONTRACT_NUM, Contract.ContractType.NORMAL, noOpToken, STU3, tmpEfsMountDir.getAbsolutePath()); org.hl7.fhir.dstu3.model.Bundle bundle1 = EobTestDataUtil.createBundle(eob.copy()); - when(mockBfdClient.requestEOBFromServer(STU3, patientId, LATER_ATT_DATE, CONTRACT_NUM)).thenReturn(bundle1); + when(mockBfdClient.requestEOBFromServer(STU3, patientId, LATER_ATT_DATE, null, CONTRACT_NUM)).thenReturn(bundle1); cut.process(request).get(); - verify(mockBfdClient).requestEOBFromServer(STU3, patientId, LATER_ATT_DATE, CONTRACT_NUM); + verify(mockBfdClient).requestEOBFromServer(STU3, patientId, LATER_ATT_DATE, null, CONTRACT_NUM); verify(mockBfdClient, never()).requestNextBundleFromServer(STU3, bundle1, CONTRACT_NUM); } @@ -255,15 +251,15 @@ void process_whenPatientHasSinglePageOfClaimsDataEarlyAttDate() throws Execution // Override default behavior of setup coverageSummary = new CoverageSummary(createIdentifierWithoutMbi(patientId), null, List.of(TestUtil.getOpenRange())); - request = new PatientClaimsRequest(List.of(coverageSummary), EARLY_ATT_DATE, null, "client", "job", + request = new PatientClaimsRequest(List.of(coverageSummary), EARLY_ATT_DATE, null, null,"client", "job", CONTRACT_NUM, Contract.ContractType.NORMAL, noOpToken, STU3, tmpEfsMountDir.getAbsolutePath()); org.hl7.fhir.dstu3.model.Bundle bundle1 = EobTestDataUtil.createBundle(eob.copy()); - when(mockBfdClient.requestEOBFromServer(STU3, patientId, null, CONTRACT_NUM)).thenReturn(bundle1); + when(mockBfdClient.requestEOBFromServer(STU3, patientId, null, null,CONTRACT_NUM)).thenReturn(bundle1); cut.process(request).get(); - verify(mockBfdClient).requestEOBFromServer(STU3, patientId, null, CONTRACT_NUM); + verify(mockBfdClient).requestEOBFromServer(STU3, patientId, null, null,CONTRACT_NUM); verify(mockBfdClient, never()).requestNextBundleFromServer(STU3, bundle1, CONTRACT_NUM); } @@ -272,15 +268,15 @@ void process_whenPatientHasSinglePageOfClaimsDataEarlySinceDate() throws Executi // Override default behavior of setup coverageSummary = new CoverageSummary(createIdentifierWithoutMbi(patientId), null, List.of(TestUtil.getOpenRange())); - request = new PatientClaimsRequest(List.of(coverageSummary), EARLY_ATT_DATE, EARLY_SINCE_DATE, "client", "job", + request = new PatientClaimsRequest(List.of(coverageSummary), EARLY_ATT_DATE, EARLY_SINCE_DATE, null,"client", "job", CONTRACT_NUM, Contract.ContractType.NORMAL, noOpToken, STU3, tmpEfsMountDir.getAbsolutePath()); org.hl7.fhir.dstu3.model.Bundle bundle1 = EobTestDataUtil.createBundle(eob.copy()); - when(mockBfdClient.requestEOBFromServer(STU3, patientId, null, CONTRACT_NUM)).thenReturn(bundle1); + when(mockBfdClient.requestEOBFromServer(STU3, patientId, null,null, CONTRACT_NUM)).thenReturn(bundle1); cut.process(request).get(); - verify(mockBfdClient).requestEOBFromServer(STU3, patientId, null, CONTRACT_NUM); + verify(mockBfdClient).requestEOBFromServer(STU3, patientId, null,null, CONTRACT_NUM); verify(mockBfdClient, never()).requestNextBundleFromServer(STU3, bundle1, CONTRACT_NUM); } @@ -307,4 +303,25 @@ void testFilterAsEob() { org.hl7.fhir.r4.model.Patient patient = new org.hl7.fhir.r4.model.Patient(); assertFalse(gov.cms.ab2d.fhir.BundleUtils.isExplanationOfBenefitResource(patient)); } + +// @Test +// void isContinueTest(){ +// var eob = createEOB(); +// // lastUpdated == null && until == null +// assertFalse(cut.isContinue(eob, request)); +// +// request = new PatientClaimsRequest(List.of(coverageSummary), LATER_ATT_DATE, null, +// OffsetDateTime.of(2024, 2, 15, 0, 0, 0, 0, ZoneOffset.UTC),"client", "job", +// CONTRACT_NUM, Contract.ContractType.NORMAL, noOpToken, STU3, tmpEfsMountDir.getAbsolutePath()); +// // lastUpdated == null && until != null +// assertFalse(cut.isContinue(eob, request)); +// +// eob.getMeta().setLastUpdated(new Date()); +// // lastUpdated == null && until != null && lastUpdated > until +// assertFalse(cut.isContinue(eob, request)); +// +// eob.getMeta().setLastUpdated( new GregorianCalendar(2024, Calendar.FEBRUARY, 1).getTime()); +// // lastUpdated == null && until != null && lastUpdated < until +// assertTrue(cut.isContinue(eob, request)); +// } }