All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hl7.fhir.r4.utils.client.FHIRToolingClient Maven / Gradle / Ivy

The newest version!
package org.hl7.fhir.r4.utils.client;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

import lombok.Getter;
import lombok.Setter;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r4.model.PrimitiveType;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.TerminologyCapabilities;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.utils.client.network.ByteUtils;
import org.hl7.fhir.r4.utils.client.network.Client;
import org.hl7.fhir.r4.utils.client.network.ResourceRequest;
import org.hl7.fhir.utilities.FHIRBaseToolingClient;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;

import org.hl7.fhir.utilities.http.HTTPHeader;

/**
 * Very Simple RESTful client. This is purely for use in the standalone tools
 * jar packages. It doesn't support many features, only what the tools need.
 * 

* To use, initialize class and set base service URI as follows: * *

 * 
 * FHIRSimpleClient fhirClient = new FHIRSimpleClient();
 * fhirClient.initialize("http://my.fhir.domain/myServiceRoot");
 * 
 * 
*

* Default Accept and Content-Type headers are application/fhir+xml and * application/fhir+json. *

* These can be changed by invoking the following setter functions: * *

 * 
 * setPreferredResourceFormat()
 * setPreferredFeedFormat()
 * 
 * 
*

* TODO Review all sad paths. * * @author Claude Nanjo */ public class FHIRToolingClient extends FHIRBaseToolingClient { public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssK"; public static final String DATE_FORMAT = "yyyy-MM-dd"; public static final String hostKey = "http.proxyHost"; public static final String portKey = "http.proxyPort"; private String base; private ResourceAddress resourceAddress; @Setter private ResourceFormat preferredResourceFormat; private int maxResultSetSize = -1;// _count @Getter @Setter private Client client = new Client(); private List headers = new ArrayList<>(); @Getter @Setter private String userAgent; @Setter private String acceptLanguage; @Setter private String contentLanguage; private int useCount; // Pass endpoint for client - URI public FHIRToolingClient(String baseServiceUrl, String userAgent) throws URISyntaxException { preferredResourceFormat = ResourceFormat.RESOURCE_JSON; this.userAgent = userAgent; initialize(baseServiceUrl); } public void initialize(String baseServiceUrl) throws URISyntaxException { base = baseServiceUrl; client.setBase(base); resourceAddress = new ResourceAddress(baseServiceUrl); this.maxResultSetSize = -1; } public String getPreferredResourceFormat() { return preferredResourceFormat.getHeader(); } public int getMaximumRecordCount() { return maxResultSetSize; } public void setMaximumRecordCount(int maxResultSetSize) { this.maxResultSetSize = maxResultSetSize; } public TerminologyCapabilities getTerminologyCapabilities() { TerminologyCapabilities capabilities = null; try { capabilities = (TerminologyCapabilities) client.issueGetResourceRequest(resourceAddress.resolveMetadataTxCaps(), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "TerminologyCapabilities", timeoutNormal).getReference(); } catch (Exception e) { throw new FHIRException("Error fetching the server's terminology capabilities", e); } return capabilities; } public CapabilityStatement getCapabilitiesStatementQuick() { CapabilityStatement conformance = null; try { conformance = (CapabilityStatement) client.issueGetResourceRequest(resourceAddress.resolveMetadataUri(true), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "CapabilitiesStatement", timeoutNormal).getReference(); } catch (Exception e) { throw new FHIRException("Error fetching the server's conformance statement", e); } return conformance; } public CapabilityStatement getCapabilitiesStatement() throws EFhirClientException { try { return (CapabilityStatement) client.issueGetResourceRequest(resourceAddress.resolveMetadataUri(false), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "CapabilitiesStatement-Quick", timeoutNormal) .getReference(); } catch (Exception e) { throw new FHIRException("Error fetching the server's capability statement: " + e.getMessage(), e); } } public Resource read(String resourceClass, String id) {// TODO Change this to AddressableResource recordUse(); ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "Read " + resourceClass + "/" + id, timeoutNormal); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } } catch (Exception e) { throw new FHIRException(e); } return result.getPayload(); } public T read(Class resourceClass, String id) {// TODO Change this to AddressableResource recordUse(); ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "Read " + resourceClass.getName() + "/" + id, timeoutNormal); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } } catch (Exception e) { throw new FHIRException(e); } return result.getPayload(); } public T vread(Class resourceClass, String id, String version) { recordUse(); ResourceRequest result = null; try { result = client.issueGetResourceRequest( resourceAddress.resolveGetUriFromResourceClassAndIdAndVersion(resourceClass, id, version), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "VRead " + resourceClass.getName() + "/" + id + "/?_history/" + version, timeoutNormal); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } } catch (Exception e) { throw new FHIRException("Error trying to read this version of the resource", e); } return result.getPayload(); } public T getCanonical(Class resourceClass, String canonicalURL) { recordUse(); ResourceRequest result = null; try { result = client.issueGetResourceRequest( resourceAddress.resolveGetUriFromResourceClassAndCanonical(resourceClass, canonicalURL), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "Read " + resourceClass.getName() + "?url=" + canonicalURL, timeoutNormal); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } } catch (Exception e) { handleException(0, "An error has occurred while trying to read this version of the resource", e); } Bundle bnd = (Bundle) result.getPayload(); if (bnd.getEntry().size() == 0) throw new EFhirClientException("No matching resource found for canonical URL '" + canonicalURL + "'"); if (bnd.getEntry().size() > 1) throw new EFhirClientException("Multiple matching resources found for canonical URL '" + canonicalURL + "'"); return (T) bnd.getEntry().get(0).getResource(); } public Resource update(Resource resource) { recordUse(); org.hl7.fhir.r4.utils.client.network.ResourceRequest result = null; try { result = client.issuePutRequest( resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()), ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "Update " + resource.fhirType() + "/" + resource.getId(), timeoutOperation); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } } catch (Exception e) { throw new EFhirClientException(0, "An error has occurred while trying to update this resource", e); } // TODO oe 26.1.2015 could be made nicer if only OperationOutcome locationheader // is returned with an operationOutcome would be returned (and not the resource // also) we make another read try { OperationOutcome operationOutcome = (OperationOutcome) result.getPayload(); ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = ResourceAddress .parseCreateLocation(result.getLocation()); return this.vread(resource.getClass(), resVersionedIdentifier.getId(), resVersionedIdentifier.getVersionId()); } catch (ClassCastException e) { // if we fall throught we have the correct type already in the create } return result.getPayload(); } public T update(Class resourceClass, T resource, String id) { recordUse(); ResourceRequest result = null; try { result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id), ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "Update " + resource.fhirType() + "/" + id, timeoutOperation); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } } catch (Exception e) { throw new EFhirClientException(0, "An error has occurred while trying to update this resource", e); } // TODO oe 26.1.2015 could be made nicer if only OperationOutcome locationheader // is returned with an operationOutcome would be returned (and not the resource // also) we make another read try { OperationOutcome operationOutcome = (OperationOutcome) result.getPayload(); ResourceAddress.ResourceVersionedIdentifier resVersionedIdentifier = ResourceAddress .parseCreateLocation(result.getLocation()); return this.vread(resourceClass, resVersionedIdentifier.getId(), resVersionedIdentifier.getVersionId()); } catch (ClassCastException e) { // if we fall through we have the correct type already in the create } return result.getPayload(); } public Parameters operateType(Class resourceClass, String name, Parameters params) throws IOException { recordUse(); boolean complex = false; for (ParametersParameterComponent p : params.getParameter()) complex = complex || !(p.getValue() instanceof PrimitiveType); String ps = ""; if (!complex) for (ParametersParameterComponent p : params.getParameter()) if (p.getValue() instanceof PrimitiveType) ps += p.getName() + "=" + Utilities.encodeUriParam(((PrimitiveType) p.getValue()).asStringValue()) + "&"; ResourceRequest result; URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps); if (complex) { byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true); result = client.issuePostRequest(url, body, withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "POST " + resourceClass.getName() + "/$" + name, timeoutLong); } else { result = client.issueGetResourceRequest(url, withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "GET " + resourceClass.getName() + "/$" + name, timeoutLong); } if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } if (result.getPayload() instanceof Parameters) { return (Parameters) result.getPayload(); } else { Parameters p_out = new Parameters(); p_out.addParameter().setName("return").setResource(result.getPayload()); return p_out; } } public Bundle transaction(Bundle batch) { recordUse(); Bundle transactionResult = null; try { transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(), ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat()), false), withVer(getPreferredResourceFormat(), "4.0"), "transaction", timeoutOperation + (timeoutEntry * batch.getEntry().size())); } catch (Exception e) { handleException(0, "An error occurred trying to process this transaction request", e); } return transactionResult; } @SuppressWarnings("unchecked") public OperationOutcome validate(Class resourceClass, T resource, String id) { recordUse(); ResourceRequest result = null; try { result = client.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id), ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "POST " + resourceClass.getName() + (id != null ? "/" + id : "") + "/$validate", timeoutLong); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } } catch (Exception e) { handleException(0, "An error has occurred while trying to validate this resource", e); } return (OperationOutcome) result.getPayload(); } /** * Helper method to prevent nesting of previously thrown EFhirClientExceptions. If the e param is an instance of * EFhirClientException, it will be rethrown. Otherwise, a new EFhirClientException will be thrown with e as the * cause. * * @param code The EFhirClientException code. * @param message The EFhirClientException message. * @param e The exception. * @throws EFhirClientException representing the exception. */ protected void handleException(int code, String message, Exception e) throws EFhirClientException { if (e instanceof EFhirClientException) { throw (EFhirClientException) e; } else { throw new EFhirClientException(code, message, e); } } /** * Helper method to determine whether desired resource representation * is Json or XML. * * @param format The format * @return true if the format is JSON, false otherwise */ protected boolean isJson(String format) { boolean isJson = false; if (format.toLowerCase().contains("json")) { isJson = true; } return isJson; } public Bundle fetchFeed(String url) { recordUse(); Bundle feed = null; try { feed = client.issueGetFeedRequest(new URI(url), withVer(getPreferredResourceFormat(), "4.0"), timeoutLong); } catch (Exception e) { handleException(0, "An error has occurred while trying to read a bundle", e); } return feed; } public Parameters lookupCode(Map params) { recordUse(); org.hl7.fhir.r4.utils.client.network.ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup", params), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(false), "CodeSystem/$lookup", timeoutNormal); } catch (IOException e) { throw new FHIRException(e); } if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } return (Parameters) result.getPayload(); } public Parameters lookupCode(Parameters p) { recordUse(); org.hl7.fhir.r4.utils.client.network.ResourceRequest result = null; try { result = client.issuePostRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup"), ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "CodeSystem/$lookup", timeoutNormal); } catch (IOException e) { throw new FHIRException(e); } if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } return (Parameters) result.getPayload(); } public Parameters translate(Parameters p) { recordUse(); org.hl7.fhir.r4.utils.client.network.ResourceRequest result = null; try { result = client.issuePostRequest(resourceAddress.resolveOperationUri(ConceptMap.class, "translate"), ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "ConceptMap/$translate", timeoutNormal); } catch (IOException e) { throw new FHIRException(e); } if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } return (Parameters) result.getPayload(); } public ValueSet expandValueset(ValueSet source, Parameters expParams) { recordUse(); Parameters p = expParams == null ? new Parameters() : expParams.copy(); if (source != null) { p.addParameter().setName("valueSet").setResource(source); } org.hl7.fhir.r4.utils.client.network.ResourceRequest result = null; try { result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"), ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), source == null ? "ValueSet/$expand" : "ValueSet/$expand?url=" + source.getUrl(), timeoutExpand); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } } catch (EFhirClientException e) { if (e.getServerErrors().size() > 0) { throw new EFhirClientException(e.getCode(), e.getMessage(), e.getServerErrors().get(0)); } else { throw new EFhirClientException(e.getCode(), e.getMessage(), e); } } catch (Exception e) { throw new EFhirClientException(0, e.getMessage(), e); } return result == null ? null : (ValueSet) result.getPayload(); } public String getAddress() { return base; } public ConceptMap initializeClosure(String name) { recordUse(); Parameters params = new Parameters(); params.addParameter().setName("name").setValue(new StringType(name)); ResourceRequest result = null; try { result = client.issuePostRequest( resourceAddress.resolveOperationUri(null, "closure", new HashMap()), ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "Closure?name=" + name, timeoutNormal); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } } catch (IOException e) { throw new FHIRException(e); } return result == null ? null : (ConceptMap) result.getPayload(); } public ConceptMap updateClosure(String name, Coding coding) { recordUse(); Parameters params = new Parameters(); params.addParameter().setName("name").setValue(new StringType(name)); params.addParameter().setName("concept").setValue(coding); org.hl7.fhir.r4.utils.client.network.ResourceRequest result = null; try { result = client.issuePostRequest( resourceAddress.resolveOperationUri(null, "closure", new HashMap()), ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), "UpdateClosure?name=" + name, timeoutOperation); if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } } catch (IOException e) { throw new FHIRException(e); } return result == null ? null : (ConceptMap) result.getPayload(); } public ToolingClientLogger getLogger() { return client.getLogger(); } public void setLogger(ToolingClientLogger logger) { client.setLogger(logger); } public int getRetryCount() { return client.getRetryCount(); } public void setRetryCount(int retryCount) { client.setRetryCount(retryCount); } public void setClientHeaders(Iterable headers) { this.headers = new ArrayList<>(); headers.forEach(this.headers::add); } private Iterable generateHeaders(boolean hasBody) { // Add any other headers List headers = new ArrayList<>(this.headers); if (!Utilities.noString(userAgent)) { headers.add(new HTTPHeader("User-Agent",userAgent)); } if (!Utilities.noString(acceptLanguage)) { headers.add(new HTTPHeader("Accept-Language", acceptLanguage)); } if (hasBody && !Utilities.noString(contentLanguage)) { headers.add(new HTTPHeader("Content-Language",contentLanguage)); } return headers; } public String getServerVersion() { return getCapabilitiesStatementQuick().getSoftware().getVersion(); } public Bundle search(String type, String criteria) { recordUse(); return fetchFeed(Utilities.pathURL(base, type+criteria)); } public T fetchResource(Class resourceClass, String id) { recordUse(); org.hl7.fhir.r4.utils.client.network.ResourceRequest result = null; try { result = client.issueGetResourceRequest(resourceAddress.resolveGetResource(resourceClass, id), withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(true), resourceClass.getName()+"/"+id, timeoutNormal); } catch (IOException e) { throw new FHIRException(e); } if (result.isUnsuccessfulRequest()) { throw new EFhirClientException(result.getHttpStatus(), "Server returned error code " + result.getHttpStatus(), (OperationOutcome) result.getPayload()); } return (T) result.getPayload(); } public int getUseCount() { return useCount; } private void recordUse() { useCount++; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy