From 50824e7287282009b3b138dc7eced85521662139 Mon Sep 17 00:00:00 2001 From: KevinMayfield Date: Fri, 8 Jun 2018 09:48:11 +0100 Subject: [PATCH] Improved error handling for Bundles --- .../ri/daointerface/DocumentReferenceDao.java | 6 +- .../processor/CompositionDocumentBundle.java | 158 ++++++++++-------- .../provider/BundleResourceProvider.java | 39 ++--- .../ri/gateway/https/CamelRoute.java | 21 --- .../ri/lib/OperationOutcomeFactory.java | 13 ++ 5 files changed, 120 insertions(+), 117 deletions(-) diff --git a/ccri-dao-stu3/src/main/java/uk/nhs/careconnect/ri/daointerface/DocumentReferenceDao.java b/ccri-dao-stu3/src/main/java/uk/nhs/careconnect/ri/daointerface/DocumentReferenceDao.java index 1f6448e4..99cca176 100644 --- a/ccri-dao-stu3/src/main/java/uk/nhs/careconnect/ri/daointerface/DocumentReferenceDao.java +++ b/ccri-dao-stu3/src/main/java/uk/nhs/careconnect/ri/daointerface/DocumentReferenceDao.java @@ -183,10 +183,10 @@ public DocumentReference create(FhirContext ctx, DocumentReference documentRefer if (code != null) { documentReferenceEntity.setContextPracticeSetting(code); } else { - log.info("PracticeSetting: Missing System/Code = " + documentReference.getContext().getPracticeSetting().getCoding().get(0).getSystem() + " code = " + documentReference.getClass_().getCoding().get(0).getCode()); + String logMsg ="PracticeSetting: Missing System/Code = " + documentReference.getContext().getPracticeSetting().getCoding().get(0).getSystem() + " code = " + documentReference.getContext().getPracticeSetting().getCoding().get(0).getCode(); + log.info(logMsg); - throw new IllegalArgumentException("PracticeSetting: Missing System/Code = " + documentReference.getContext().getPracticeSetting().getCoding().get(0).getSystem() - + " code = " + documentReference.getClass_().getCoding().get(0).getCode()); + throw new IllegalArgumentException(logMsg); } } if (documentReference.getContext().hasEncounter()) { diff --git a/ccri-fhirgateway-common/src/main/java/uk/nhs/careconnect/ri/gatewaylib/camel/processor/CompositionDocumentBundle.java b/ccri-fhirgateway-common/src/main/java/uk/nhs/careconnect/ri/gatewaylib/camel/processor/CompositionDocumentBundle.java index 9c05b125..49f78e00 100644 --- a/ccri-fhirgateway-common/src/main/java/uk/nhs/careconnect/ri/gatewaylib/camel/processor/CompositionDocumentBundle.java +++ b/ccri-fhirgateway-common/src/main/java/uk/nhs/careconnect/ri/gatewaylib/camel/processor/CompositionDocumentBundle.java @@ -8,6 +8,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import uk.nhs.careconnect.ri.lib.OperationOutcomeFactory; import java.util.Map; @@ -45,92 +46,109 @@ public Exchange aggregate(Exchange originalExchange, Exchange edmsExchange) { this.context = originalExchange.getContext(); - if (edmsExchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE) != null && (edmsExchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE).toString().equals("201"))) { - IBaseResource resource = ctx.newXmlParser().parseResource((String) originalExchange.getIn().getBody()); + if (edmsExchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE) != null) { - if (resource instanceof Bundle) { - bundle = (Bundle) resource; - for (Bundle.BundleEntryComponent entry : bundle.getEntry()) { + if (edmsExchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE).toString().equals("201")) { + IBaseResource resource = ctx.newXmlParser().parseResource((String) originalExchange.getIn().getBody()); - if (entry.getResource() instanceof Patient) { - patient = (Patient) entry.getResource(); - } - if (entry.getResource() instanceof Composition) { - composition = (Composition) entry.getResource(); - } + if (resource instanceof Bundle) { + bundle = (Bundle) resource; + + for (Bundle.BundleEntryComponent entry : bundle.getEntry()) { + + if (entry.getResource() instanceof Patient) { + patient = (Patient) entry.getResource(); + } + if (entry.getResource() instanceof Composition) { + composition = (Composition) entry.getResource(); + } - } - BundleCore bundleCore = new BundleCore(ctx, context, bundle); - if (patient != null) { - if (patient.getId() != null) { - bundleCore.searchAddResource(patient.getId()); - } else { - patient.setId(java.util.UUID.randomUUID().toString()); - bundleCore.searchAddResource(patient.getId()); } + try { + BundleCore bundleCore = new BundleCore(ctx, context, bundle); + if (patient != null) { + if (patient.getId() != null) { + bundleCore.searchAddResource(patient.getId()); + } else { + patient.setId(java.util.UUID.randomUUID().toString()); + bundleCore.searchAddResource(patient.getId()); + } /* Need to build DocumentReference add it to bundle and then process it. Use location from edms post */ - if (composition != null) { - DocumentReference documentReference = new DocumentReference(); - documentReference.setId(java.util.UUID.randomUUID().toString()); - if (bundle.getIdentifier() != null) { - documentReference.addIdentifier().setSystem(bundle.getIdentifier().getSystem()) - .setValue(bundle.getIdentifier().getValue()); - } - // This should be resolved - documentReference.setSubject(new Reference(patient.getId())); - if (composition.hasType()) { - documentReference.setType(composition.getType()); - } - if (composition.hasClass_()) { - documentReference.setClass_(composition.getClass_()); - } - if (composition.hasStatus()) { - // TODO convert from Composition status - documentReference.setStatus(Enumerations.DocumentReferenceStatus.CURRENT); - } - if (composition.hasAuthor()) { - for (Reference reference : composition.getAuthor()) { - documentReference.addAuthor(reference); + if (composition != null) { + DocumentReference documentReference = new DocumentReference(); + documentReference.setId(java.util.UUID.randomUUID().toString()); + if (bundle.getIdentifier() != null) { + documentReference.addIdentifier().setSystem(bundle.getIdentifier().getSystem()) + .setValue(bundle.getIdentifier().getValue()); + } + // This should be resolved + documentReference.setSubject(new Reference(patient.getId())); + if (composition.hasType()) { + documentReference.setType(composition.getType()); + } + if (composition.hasClass_()) { + documentReference.setClass_(composition.getClass_()); + } + if (composition.hasStatus()) { + // TODO convert from Composition status + documentReference.setStatus(Enumerations.DocumentReferenceStatus.CURRENT); + } + if (composition.hasAuthor()) { + for (Reference reference : composition.getAuthor()) { + documentReference.addAuthor(reference); + } + } + if (composition.hasCustodian()) { + documentReference.setCustodian(composition.getCustodian()); + } + for (Extension extension : composition.getExtension()) { + if (extension.getUrl().equals("https://fhir.nhs.uk/STU3/StructureDefinition/Extension-ITK-CareSettingType-1")) { + documentReference.getContext().setPracticeSetting((CodeableConcept) extension.getValue()); + } + } + if (composition.hasEncounter()) { + documentReference.getContext().setEncounter(composition.getEncounter()); + } + if (composition.hasDate()) { + documentReference.setCreated(composition.getDate()); + } + DocumentReference.DocumentReferenceContentComponent content = documentReference.addContent(); + if (edmsExchange.getIn().getHeader("Location") != null) { + + String[] path = edmsExchange.getIn().getHeader("Location").toString().split("/"); + String resourceId = path[path.length - 1]; + log.info("Binary resource Id = " + resourceId); + content.getAttachment().setContentType("application/fhir+xml").setUrl(hapiBase + "/Binary/" + resourceId); + } else { + + } + + log.info(ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(documentReference)); + // Add the new DocumentReference to the bundle + bundleCore.getBundle().addEntry().setResource(documentReference); + bundleCore.searchAddResource(documentReference.getId()); } - } - if (composition.hasCustodian()) { - documentReference.setCustodian(composition.getCustodian()); - } - for (Extension extension : composition.getExtension()) { - if (extension.getUrl().equals("https://fhir.nhs.uk/STU3/StructureDefinition/Extension-ITK-CareSettingType-1")) { - documentReference.getContext().setPracticeSetting((CodeableConcept) extension.getValue()); - } - } - if (composition.hasEncounter()) { - documentReference.getContext().setEncounter(composition.getEncounter()); - } - if (composition.hasDate()) { - documentReference.setCreated(composition.getDate()); - } - DocumentReference.DocumentReferenceContentComponent content = documentReference.addContent(); - if (edmsExchange.getIn().getHeader("Location") != null) { - - String[] path = edmsExchange.getIn().getHeader("Location").toString().split("/"); - String resourceId = path[path.length - 1]; - log.info("Binary resource Id = " + resourceId); - content.getAttachment().setContentType("application/fhir+xml").setUrl(hapiBase + "/Binary/" + resourceId); - } else { } - - log.info(ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(documentReference)); - // Add the new DocumentReference to the bundle - bundleCore.getBundle().addEntry().setResource(documentReference); - bundleCore.searchAddResource(documentReference.getId()); + } catch (Exception ex) { + // A number of the HAPI related function will return exceptions. + // Convert to operational outcomes + OperationOutcome operationOutcome = new OperationOutcome(); + OperationOutcome.IssueType issueType = OperationOutcomeFactory.getIssueType(ex); + + operationOutcome.addIssue() + .setSeverity(OperationOutcome.IssueSeverity.ERROR) + .setCode(issueType) + .setDiagnostics(ex.getMessage()); + edmsExchange.getIn().setBody(ctx.newXmlParser().encodeResourceToString(operationOutcome)); } } - } } diff --git a/ccri-fhirgateway-common/src/main/java/uk/nhs/careconnect/ri/gatewaylib/provider/BundleResourceProvider.java b/ccri-fhirgateway-common/src/main/java/uk/nhs/careconnect/ri/gatewaylib/provider/BundleResourceProvider.java index b86f9b1b..22b14e13 100644 --- a/ccri-fhirgateway-common/src/main/java/uk/nhs/careconnect/ri/gatewaylib/provider/BundleResourceProvider.java +++ b/ccri-fhirgateway-common/src/main/java/uk/nhs/careconnect/ri/gatewaylib/provider/BundleResourceProvider.java @@ -47,6 +47,16 @@ public MethodOutcome testResource(@ResourceParam Bundle bundle, return resourceTestProvider.testResource(bundle,theMode,theProfile); } + private Exchange buildBundlePost(Exchange exchange, String newXmlResource, String query, String method) { + exchange.getIn().setBody(newXmlResource); + exchange.getIn().setHeader(Exchange.HTTP_QUERY, query); + exchange.getIn().setHeader(Exchange.HTTP_METHOD, method); + exchange.getIn().setHeader(Exchange.HTTP_PATH, "Bundle"); + // exchange.getIn().setHeader("Prefer", "return=representation"); + exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/fhir+xml"); + return exchange; + } + @Create public MethodOutcome create(HttpServletRequest httpRequest, @ResourceParam Bundle bundle) { @@ -83,12 +93,8 @@ public MethodOutcome create(HttpServletRequest httpRequest, @ResourceParam Bundl Exchange exchangeBundle = template.send("direct:FHIRBundleCollection", ExchangePattern.InOut, new Processor() { public void process(Exchange exchange) throws Exception { - exchange.getIn().setBody(newXmlResource); - exchange.getIn().setHeader(Exchange.HTTP_QUERY, null); - exchange.getIn().setHeader(Exchange.HTTP_METHOD, "POST"); - exchange.getIn().setHeader(Exchange.HTTP_PATH, "Bundle"); - // exchange.getIn().setHeader("Prefer", "return=representation"); - exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/fhir+xml"); + exchange = buildBundlePost(exchange,newXmlResource,null,"POST"); + } }); // TODO need proper responses from the camel processor. KGM 18/Apr/2018 @@ -98,12 +104,8 @@ public void process(Exchange exchange) throws Exception { case DOCUMENT: Exchange exchangeDocument = template.send("direct:FHIRBundleDocument", ExchangePattern.InOut, new Processor() { public void process(Exchange exchange) throws Exception { - exchange.getIn().setBody(newXmlResource); - exchange.getIn().setHeader(Exchange.HTTP_QUERY, null); - exchange.getIn().setHeader(Exchange.HTTP_METHOD, "POST"); - exchange.getIn().setHeader(Exchange.HTTP_PATH, "Bundle"); - // exchange.getIn().setHeader("Prefer", "return=representation"); - exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/fhir+xml"); + exchange = buildBundlePost(exchange,newXmlResource,null,"POST"); + } }); // TODO need proper responses from the camel processor. KGM 18/Apr/2018 @@ -172,12 +174,8 @@ public MethodOutcome updateBundle(HttpServletRequest theRequest, @ResourceParam Exchange exchangeBundle = template.send("direct:FHIRBundleCollection", ExchangePattern.InOut, new Processor() { public void process(Exchange exchange) throws Exception { - exchange.getIn().setBody(newXmlResource); - exchange.getIn().setHeader(Exchange.HTTP_QUERY, conditional); - exchange.getIn().setHeader(Exchange.HTTP_METHOD, "PUT"); - exchange.getIn().setHeader(Exchange.HTTP_PATH, "Bundle"); + exchange = buildBundlePost(exchange,newXmlResource,conditional,"PUT"); - exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/fhir+xml"); } }); // TODO need proper responses from the camel processor. KGM 18/Apr/2018 @@ -187,12 +185,7 @@ public void process(Exchange exchange) throws Exception { case DOCUMENT: Exchange exchangeDocument = template.send("direct:FHIRBundleDocument", ExchangePattern.InOut, new Processor() { public void process(Exchange exchange) throws Exception { - exchange.getIn().setBody(newXmlResource); - exchange.getIn().setHeader(Exchange.HTTP_QUERY, conditional); - exchange.getIn().setHeader(Exchange.HTTP_METHOD, "PUT"); - exchange.getIn().setHeader(Exchange.HTTP_PATH, "Bundle"); - - exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/fhir+xml"); + exchange = buildBundlePost(exchange,newXmlResource,conditional,"PUT"); } }); // TODO need proper responses from the camel processor. KGM 18/Apr/2018 diff --git a/ccri-fhirgatewayhttps/src/main/java/uk/nhs/careconnect/ri/gateway/https/CamelRoute.java b/ccri-fhirgatewayhttps/src/main/java/uk/nhs/careconnect/ri/gateway/https/CamelRoute.java index e87101ed..56ba883f 100644 --- a/ccri-fhirgatewayhttps/src/main/java/uk/nhs/careconnect/ri/gateway/https/CamelRoute.java +++ b/ccri-fhirgatewayhttps/src/main/java/uk/nhs/careconnect/ri/gateway/https/CamelRoute.java @@ -51,28 +51,7 @@ public void configure() CompositionDocumentBundle compositionDocumentBundle = new CompositionDocumentBundle(ctx, hapiBase); BinaryResource binaryResource = new BinaryResource(ctx, hapiBase); -/* - // OAuth endpoint - restConfiguration() - .component("servlet") - .contextPath("oauth2") - .dataFormatProperty("prettyPrint", "true") - .enableCORS(false); - - - rest("/").description("OAuth") - .get("/{action}").to("direct:oauth2") - .post("/{action}").to("direct:oauth2"); - - from("direct:oauth2") - .routeId("auth Server") - .setHeader(Exchange.HTTP_PATH,simple("${header.action}")) - .to("log:uk.nhs.careconnect.smartOnFhir.PRE?level=INFO&showHeaders=true&showExchangeId=true") - .to(oauthBase) - .process(camelPostProcessor) - .to("log:uk.nhs.careconnect.smartOnFhir.POST?level=INFO&showHeaders=true&showExchangeId=true"); -*/ // Validation Service from("direct:FHIRValidate") diff --git a/ccri-fhirserver-common/src/main/java/uk/nhs/careconnect/ri/lib/OperationOutcomeFactory.java b/ccri-fhirserver-common/src/main/java/uk/nhs/careconnect/ri/lib/OperationOutcomeFactory.java index 80bdfd37..fd033c73 100644 --- a/ccri-fhirserver-common/src/main/java/uk/nhs/careconnect/ri/lib/OperationOutcomeFactory.java +++ b/ccri-fhirserver-common/src/main/java/uk/nhs/careconnect/ri/lib/OperationOutcomeFactory.java @@ -27,6 +27,19 @@ public static BaseServerResponseException buildOperationOutcomeException(BaseSer exception.setOperationOutcome(operationOutcome); return exception; } + public static OperationOutcome.IssueType getIssueType(Exception ex) { + OperationOutcome.IssueType issueType = OperationOutcome.IssueType.PROCESSING; + if (ex instanceof ResourceNotFoundException) issueType=OperationOutcome.IssueType.NOTFOUND; + if (ex instanceof AuthenticationException) issueType=OperationOutcome.IssueType.SECURITY; + if (ex instanceof InvalidRequestException) issueType=OperationOutcome.IssueType.INVALID; + if (ex instanceof InternalErrorException) issueType=OperationOutcome.IssueType.EXCEPTION; + if (ex instanceof ForbiddenOperationException) issueType=OperationOutcome.IssueType.FORBIDDEN; + if (ex instanceof ResourceVersionConflictException) issueType=OperationOutcome.IssueType. CONFLICT; + if (ex instanceof NotImplementedOperationException) issueType=OperationOutcome.IssueType. NOTSUPPORTED; + if (ex instanceof PreconditionFailedException) issueType=OperationOutcome.IssueType. DUPLICATE; + if (ex instanceof MethodNotAllowedException) issueType=OperationOutcome.IssueType.BUSINESSRULE; + return issueType; + } public static void convertToException (OperationOutcome outcome ) throws BaseServerResponseException { for (OperationOutcome.OperationOutcomeIssueComponent issue : outcome.getIssue()) {