-
Notifications
You must be signed in to change notification settings - Fork 6
Create endpoint for resending an EhrExtract #990
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
24da3bb
b8e0173
49dd7e8
8b42391
8a1a8f5
7a4ab0e
830622d
9cd5301
1346972
b417275
28600ed
5838168
af7598e
02bfa50
c86cf77
d07b5c7
be42315
06bdba3
eb762db
e851152
3fa64f7
09bdec6
ba1f846
7cb3adc
0de4853
fbc1f17
27457f5
73c5eac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| package uk.nhs.adaptors.gp2gp.ehr; | ||
|
|
||
| import lombok.AllArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.hl7.fhir.dstu3.model.CodeableConcept; | ||
| import org.hl7.fhir.dstu3.model.Coding; | ||
| import org.hl7.fhir.dstu3.model.Meta; | ||
| import org.hl7.fhir.dstu3.model.OperationOutcome; | ||
| import org.hl7.fhir.dstu3.model.UriType; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
| import uk.nhs.adaptors.gp2gp.common.service.FhirParseService; | ||
| import uk.nhs.adaptors.gp2gp.common.service.RandomIdGeneratorService; | ||
| import uk.nhs.adaptors.gp2gp.common.service.TimestampService; | ||
| import uk.nhs.adaptors.gp2gp.common.task.TaskDispatcher; | ||
| import uk.nhs.adaptors.gp2gp.ehr.model.EhrExtractStatus; | ||
| import uk.nhs.adaptors.gp2gp.gpc.GetGpcStructuredTaskDefinition; | ||
|
|
||
| import java.util.Collections; | ||
|
|
||
| @Slf4j | ||
| @RestController | ||
| @AllArgsConstructor(onConstructor = @__(@Autowired)) | ||
| @RequestMapping(path = "/ehr-resend") | ||
| public class EhrResendController { | ||
|
|
||
| private static final String OPERATION_OUTCOME_URL = "https://fhir.nhs.uk/STU3/StructureDefinition/GPConnect-OperationOutcome-1"; | ||
| private static final String PRECONDITION_FAILED = "PRECONDITION_FAILED"; | ||
| private static final String INVALID_IDENTIFIER_VALUE = "INVALID_IDENTIFIER_VALUE"; | ||
|
|
||
| private EhrExtractStatusRepository ehrExtractStatusRepository; | ||
| private TaskDispatcher taskDispatcher; | ||
| private RandomIdGeneratorService randomIdGeneratorService; | ||
| private final TimestampService timestampService; | ||
| private final FhirParseService fhirParseService; | ||
|
|
||
| @PostMapping("/{conversationId}") | ||
| public ResponseEntity<String> scheduleEhrExtractResend(@PathVariable String conversationId) { | ||
| EhrExtractStatus ehrExtractStatus = ehrExtractStatusRepository.findByConversationId(conversationId).orElseGet(() -> null); | ||
|
|
||
| if (ehrExtractStatus == null) { | ||
| var details = getCodeableConcept(INVALID_IDENTIFIER_VALUE); | ||
| var diagnostics = "Provide a conversationId that exists and retry the operation"; | ||
|
|
||
| var operationOutcome = createOperationOutcome(OperationOutcome.IssueType.VALUE, | ||
| OperationOutcome.IssueSeverity.ERROR, | ||
| details, | ||
| diagnostics); | ||
| var errorBody = fhirParseService.encodeToJson(operationOutcome); | ||
|
|
||
| return new ResponseEntity<>(errorBody, HttpStatus.NOT_FOUND); | ||
| } | ||
|
|
||
| if (hasNoErrorsInEhrReceivedAcknowledgement(ehrExtractStatus) && ehrExtractStatus.getError() == null) { | ||
|
|
||
| var details = getCodeableConcept(PRECONDITION_FAILED); | ||
| var diagnostics = "The current resend operation is still in progress. Please wait for it to complete before retrying"; | ||
| var operationOutcome = createOperationOutcome(OperationOutcome.IssueType.BUSINESSRULE, | ||
| OperationOutcome.IssueSeverity.ERROR, | ||
| details, | ||
| diagnostics); | ||
| var errorBody = fhirParseService.encodeToJson(operationOutcome); | ||
| return new ResponseEntity<>(errorBody, HttpStatus.CONFLICT); | ||
| } | ||
|
|
||
| var updatedEhrExtractStatus = prepareEhrExtractStatusForNewResend(ehrExtractStatus); | ||
| ehrExtractStatusRepository.save(updatedEhrExtractStatus); | ||
| createGetGpcStructuredTask(updatedEhrExtractStatus); | ||
| LOGGER.info("Scheduled GetGpcStructuredTask for resend of ConversationId: {}", conversationId); | ||
|
|
||
| return new ResponseEntity<>(HttpStatus.ACCEPTED); | ||
| } | ||
|
|
||
| private static CodeableConcept getCodeableConcept(String codeableConceptCode) { | ||
| return new CodeableConcept().addCoding( | ||
| new Coding("https://fhir.nhs.uk/STU3/ValueSet/Spine-ErrorOrWarningCode-1", codeableConceptCode, null)); | ||
| } | ||
|
|
||
| private static boolean hasNoErrorsInEhrReceivedAcknowledgement(EhrExtractStatus ehrExtractStatus) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor comment, but this doesn't really feel like it belongs in the Controller. Can refactor this in a later PR though. |
||
| var ehrReceivedAcknowledgement = ehrExtractStatus.getEhrReceivedAcknowledgement(); | ||
| if (ehrReceivedAcknowledgement == null) { | ||
| return true; | ||
| } | ||
|
|
||
| var errors = ehrReceivedAcknowledgement.getErrors(); | ||
| if (errors == null || errors.isEmpty()) { | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| private EhrExtractStatus prepareEhrExtractStatusForNewResend(EhrExtractStatus ehrExtractStatus) { | ||
|
|
||
| var now = timestampService.now(); | ||
| ehrExtractStatus.setUpdatedAt(now); | ||
| ehrExtractStatus.setMessageTimestamp(now); | ||
| ehrExtractStatus.setEhrExtractCorePending(null); | ||
| ehrExtractStatus.setGpcAccessDocument(null); | ||
| ehrExtractStatus.setEhrContinue(null); | ||
| ehrExtractStatus.setEhrReceivedAcknowledgement(null); | ||
|
|
||
| return ehrExtractStatus; | ||
| } | ||
|
|
||
| private void createGetGpcStructuredTask(EhrExtractStatus ehrExtractStatus) { | ||
| var getGpcStructuredTaskDefinition = GetGpcStructuredTaskDefinition.getGetGpcStructuredTaskDefinition(randomIdGeneratorService, | ||
| ehrExtractStatus); | ||
| taskDispatcher.createTask(getGpcStructuredTaskDefinition); | ||
| } | ||
|
|
||
| public static OperationOutcome createOperationOutcome( | ||
| OperationOutcome.IssueType type, OperationOutcome.IssueSeverity severity, CodeableConcept details, String diagnostics) { | ||
| var operationOutcome = new OperationOutcome(); | ||
| Meta meta = new Meta(); | ||
| meta.setProfile(Collections.singletonList(new UriType(OPERATION_OUTCOME_URL))); | ||
| operationOutcome.setMeta(meta); | ||
| operationOutcome.addIssue() | ||
| .setCode(type) | ||
| .setSeverity(severity) | ||
| .setDetails(details) | ||
| .setDiagnostics(diagnostics); | ||
| return operationOutcome; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package uk.nhs.adaptors.gp2gp.common.service; | ||
|
|
||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import org.hl7.fhir.dstu3.model.CodeableConcept; | ||
| import org.hl7.fhir.dstu3.model.Coding; | ||
| import org.hl7.fhir.dstu3.model.OperationOutcome; | ||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.api.extension.ExtendWith; | ||
| import org.mockito.junit.jupiter.MockitoExtension; | ||
| import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; | ||
| import uk.nhs.adaptors.gp2gp.common.configuration.ObjectMapperBean; | ||
| import uk.nhs.adaptors.gp2gp.ehr.EhrResendController; | ||
| import java.util.List; | ||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
|
||
|
|
||
| @ExtendWith(MockitoExtension.class) | ||
| class FhirParseServiceTest { | ||
|
|
||
| public static final String INVALID_IDENTIFIER_VALUE = "INVALID_IDENTIFIER_VALUE"; | ||
| private static final String OPERATION_OUTCOME_URL = "https://fhir.nhs.uk/STU3/StructureDefinition/GPConnect-OperationOutcome-1"; | ||
| private OperationOutcome operationOutcome; | ||
| private ObjectMapper objectMapper; | ||
|
|
||
| @BeforeEach | ||
| void setUp() { | ||
| ObjectMapperBean objectMapperBean = new ObjectMapperBean(); | ||
| objectMapper = objectMapperBean.objectMapper(new Jackson2ObjectMapperBuilder()); | ||
|
|
||
| var details = getCodeableConcept(); | ||
| var diagnostics = "Provide a conversationId that exists and retry the operation"; | ||
| operationOutcome = EhrResendController.createOperationOutcome(OperationOutcome.IssueType.VALUE, | ||
| OperationOutcome.IssueSeverity.ERROR, | ||
| details, | ||
| diagnostics); | ||
| } | ||
|
|
||
| @Test | ||
| void ableToEncodeOperationOutcomeToJson() throws JsonProcessingException { | ||
| FhirParseService fhirParseService = new FhirParseService(); | ||
|
|
||
| String convertedToJsonOperationOutcome = fhirParseService.encodeToJson(operationOutcome); | ||
|
|
||
| JsonNode rootNode = objectMapper.readTree(convertedToJsonOperationOutcome); | ||
| String code = | ||
| rootNode.path("issue").get(0).path("details").path("coding").get(0).path("code").asText(); | ||
| String operationOutcomeUrl = rootNode.path("meta").path("profile").get(0).asText(); | ||
|
|
||
| assertEquals(INVALID_IDENTIFIER_VALUE, code); | ||
| assertEquals(OPERATION_OUTCOME_URL, operationOutcomeUrl); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. really we should be using assertJ here for readability (assertThat(code).isEqualTo(INVALID_IDENTIFIER_VALUE))
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For me it is the other way around, I find "assertEquals" to be short, clean and readable. |
||
| } | ||
|
|
||
| private static CodeableConcept getCodeableConcept() { | ||
| var details = new CodeableConcept(); | ||
| var codeableConceptCoding = new Coding(); | ||
| codeableConceptCoding.setSystem("http://fhir.nhs.net/ValueSet/gpconnect-error-or-warning-code-1"); | ||
| codeableConceptCoding.setCode(FhirParseServiceTest.INVALID_IDENTIFIER_VALUE); | ||
| details.setCoding(List.of(codeableConceptCoding)); | ||
| return details; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.