Work on docs

This commit is contained in:
James Agnew 2019-09-22 13:25:50 -04:00
parent d43c1760ef
commit 0a5eca0821
71 changed files with 6318 additions and 244 deletions

View File

@ -92,10 +92,11 @@ public interface IRestfulClient {
/**
* Register a new interceptor for this client. An interceptor can be used to add additional
* logging, or add security headers, or pre-process responses, etc.
*
* @deprecated Use {@link #getInterceptorService()} to access the list of inteerceptors, register them, and unregister them
* <p>
* This is a convenience method for performing the following call:
* <code>getInterceptorService().registerInterceptor(theInterceptor)</code>
* </p>
*/
@Deprecated
void registerInterceptor(IClientInterceptor theInterceptor);
/**
@ -114,11 +115,12 @@ public interface IRestfulClient {
void setSummary(SummaryEnum theSummary);
/**
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
*
* @deprecated Use {@link #getInterceptorService()} to access the list of inteerceptors, register them, and unregister them
* Remove an interceptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}.
* <p>
* This is a convenience method for performing the following call:
* <code>getInterceptorService().unregisterInterceptor(theInterceptor)</code>
* </p>
*/
@Deprecated
void unregisterInterceptor(IClientInterceptor theInterceptor);
/**

View File

@ -1,5 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
@ -15,6 +15,85 @@
<name>HAPI FHIR - Docs</name>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client-okhttp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.ejb</groupId>
<artifactId>ejb-api</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>4.1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-subscription</artifactId>
<version>4.1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,213 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.interceptor.auth.*;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Patient;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@SuppressWarnings("unused")
public class AuthorizationInterceptors {
public class PatientResourceProvider implements IResourceProvider
{
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
public MethodOutcome create(@ResourceParam Patient thePatient, RequestDetails theRequestDetails) {
return new MethodOutcome(); // populate this
}
}
//START SNIPPET: patientAndAdmin
@SuppressWarnings("ConstantConditions")
public class PatientAndAdminAuthorizationInterceptor extends AuthorizationInterceptor {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
// Process authorization header - The following is a fake
// implementation. Obviously we'd want something more real
// for a production scenario.
//
// In this basic example we have two hardcoded bearer tokens,
// one which is for a user that has access to one patient, and
// another that has full access.
IdType userIdPatientId = null;
boolean userIsAdmin = false;
String authHeader = theRequestDetails.getHeader("Authorization");
if ("Bearer dfw98h38r".equals(authHeader)) {
// This user has access only to Patient/1 resources
userIdPatientId = new IdType("Patient", 1L);
} else if ("Bearer 39ff939jgg".equals(authHeader)) {
// This user has access to everything
userIsAdmin = true;
} else {
// Throw an HTTP 401
throw new AuthenticationException("Missing or invalid Authorization header value");
}
// If the user is a specific patient, we create the following rule chain:
// Allow the user to read anything in their own patient compartment
// Allow the user to write anything in their own patient compartment
// If a client request doesn't pass either of the above, deny it
if (userIdPatientId != null) {
return new RuleBuilder()
.allow().read().allResources().inCompartment("Patient", userIdPatientId).andThen()
.allow().write().allResources().inCompartment("Patient", userIdPatientId).andThen()
.denyAll()
.build();
}
// If the user is an admin, allow everything
if (userIsAdmin) {
return new RuleBuilder()
.allowAll()
.build();
}
// By default, deny everything. This should never get hit, but it's
// good to be defensive
return new RuleBuilder()
.denyAll()
.build();
}
}
//END SNIPPET: patientAndAdmin
//START SNIPPET: conditionalUpdate
@Update()
public MethodOutcome update(
@IdParam IdType theId,
@ResourceParam Patient theResource,
@ConditionalUrlParam String theConditionalUrl,
ServletRequestDetails theRequestDetails,
IInterceptorBroadcaster theInterceptorBroadcaster) {
// If we're processing a conditional URL...
if (isNotBlank(theConditionalUrl)) {
// Pretend we've done the conditional processing. Now let's
// notify the interceptors that an update has been performed
// and supply the actual ID that's being updated
IdType actual = new IdType("Patient", "1123");
}
// In a real server, perhaps we would process the conditional
// request differently and follow a separate path. Either way,
// let's pretend there is some storage code here.
theResource.setId(theId.withVersion("2"));
// Notify the interceptor framework when we're about to perform an update. This is
// useful as the authorization interceptor will pick this event up and use it
// to factor into a decision about whether the operation should be allowed to proceed.
IBaseResource previousContents = theResource;
IBaseResource newContents = theResource;
HookParams params = new HookParams()
.add(IBaseResource.class, previousContents)
.add(IBaseResource.class, newContents)
.add(RequestDetails.class, theRequestDetails)
.add(ServletRequestDetails.class, theRequestDetails);
theInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED, params);
MethodOutcome retVal = new MethodOutcome();
retVal.setCreated(true);
retVal.setResource(theResource);
return retVal;
}
//END SNIPPET: conditionalUpdate
public void authorizeTenantAction() {
//START SNIPPET: authorizeTenantAction
new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().read().resourcesOfType(Patient.class).withAnyId().forTenantIds("TENANTA").andThen()
.build();
}
};
//END SNIPPET: authorizeTenantAction
//START SNIPPET: patchAll
new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
// Authorize patch requests
.allow().patch().allRequests().andThen()
// Authorize actual writes that patch may perform
.allow().write().allResources().inCompartment("Patient", new IdType("Patient/123")).andThen()
.build();
}
};
//END SNIPPET: patchAll
}
//START SNIPPET: narrowing
public class MyPatientSearchNarrowingInterceptor extends SearchNarrowingInterceptor {
/**
* This method must be overridden to provide the list of compartments
* and/or resources that the current user should have access to
*/
@Override
protected AuthorizedList buildAuthorizedList(RequestDetails theRequestDetails) {
// Process authorization header - The following is a fake
// implementation. Obviously we'd want something more real
// for a production scenario.
//
// In this basic example we have two hardcoded bearer tokens,
// one which is for a user that has access to one patient, and
// another that has full access.
String authHeader = theRequestDetails.getHeader("Authorization");
if ("Bearer dfw98h38r".equals(authHeader)) {
// This user will have access to two compartments
return new AuthorizedList()
.addCompartment("Patient/123")
.addCompartment("Patient/456");
} else if ("Bearer 39ff939jgg".equals(authHeader)) {
// This user has access to everything
return new AuthorizedList();
} else {
throw new AuthenticationException("Unknown bearer token");
}
}
}
//END SNIPPET: narrowing
}

View File

@ -0,0 +1,23 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor;
import ca.uhn.fhir.rest.server.util.ITestingUiClientFactory;
import javax.servlet.http.HttpServletRequest;
public class AuthorizingTesterUiClientFactory implements ITestingUiClientFactory {
@Override
public IGenericClient newClient(FhirContext theFhirContext, HttpServletRequest theRequest, String theServerBaseUrl) {
// Create a client
IGenericClient client = theFhirContext.newRestfulGenericClient(theServerBaseUrl);
// Register an interceptor which adds credentials
client.registerInterceptor(new BasicAuthInterceptor("someusername", "somepassword"));
return client;
}
}

View File

@ -0,0 +1,66 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.RelatedPerson;
import java.util.HashSet;
import java.util.Set;
/**
* @author Bill de Beaubien on 1/13/2016.
*/
public class BundleFetcher {
public static void fetchRestOfBundle(IGenericClient theClient, Bundle theBundle) {
// we need to keep track of which resources are already in the bundle so that if other resources (e.g. Practitioner) are _included,
// we don't end up with multiple copies
Set<String> resourcesAlreadyAdded = new HashSet<String>();
addInitialUrlsToSet(theBundle, resourcesAlreadyAdded);
Bundle partialBundle = theBundle;
for (;;) {
if (partialBundle.getLink(IBaseBundle.LINK_NEXT) != null) {
partialBundle = theClient.loadPage().next(partialBundle).execute();
addAnyResourcesNotAlreadyPresentToBundle(theBundle, partialBundle, resourcesAlreadyAdded);
} else {
break;
}
}
// the self and next links for the aggregated bundle aren't really valid anymore, so remove them
theBundle.getLink().clear();
}
private static void addInitialUrlsToSet(Bundle theBundle, Set<String> theResourcesAlreadyAdded) {
for (Bundle.BundleEntryComponent entry : theBundle.getEntry()) {
theResourcesAlreadyAdded.add(entry.getFullUrl());
}
}
private static void addAnyResourcesNotAlreadyPresentToBundle(Bundle theAggregatedBundle, Bundle thePartialBundle, Set<String> theResourcesAlreadyAdded) {
for (Bundle.BundleEntryComponent entry : thePartialBundle.getEntry()) {
if (!theResourcesAlreadyAdded.contains(entry.getFullUrl())) {
theResourcesAlreadyAdded.add(entry.getFullUrl());
theAggregatedBundle.getEntry().add(entry);
}
}
}
public static void main(String[] args) {
FhirContext ctx = FhirContext.forR4();
String serverBase = "http://fhirtest.uhn.ca/baseR4";
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// use RelatedPerson because there aren't that many on the server
Bundle bundle = client.search().forResource(RelatedPerson.class).returnBundle(Bundle.class).execute();
BundleFetcher.fetchRestOfBundle(client, bundle);
if (bundle.getTotal() != bundle.getEntry().size()) {
System.out.println("Counts didn't match! Expected " + bundle.getTotal() + " but bundle only had " + bundle.getEntry().size() + " entries!");
}
// IParser parser = ctx.newXmlParser().setPrettyPrint(true);
// System.out.println(parser.encodeResourceToString(bundle));
}
}

View File

@ -0,0 +1,229 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.client.apache.GZipContentInterceptor;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor;
import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor;
import ca.uhn.fhir.rest.client.interceptor.CookieInterceptor;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Patient;
public class ClientExamples {
public interface IPatientClient extends IBasicClient {
// nothing yet
}
@SuppressWarnings("unused")
public void createProxy() {
// START SNIPPET: proxy
FhirContext ctx = FhirContext.forDstu2();
// Set connections to access the network via the HTTP proxy at
// example.com : 8888
ctx.getRestfulClientFactory().setProxy("example.com", 8888);
// If the proxy requires authentication, use the following as well
ctx.getRestfulClientFactory().setProxyCredentials("theUsername", "thePassword");
// Create the client
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
// END SNIPPET: proxy
}
@SuppressWarnings("unused")
public void processMessage() {
// START SNIPPET: processMessage
FhirContext ctx = FhirContext.forDstu3();
// Create the client
IGenericClient client = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
Bundle bundle = new Bundle();
// ..populate the bundle..
Bundle response = client
.operation()
.processMessage() // New operation for sending messages
.setMessageBundle(bundle)
.asynchronous(Bundle.class)
.execute();
// END SNIPPET: processMessage
}
@SuppressWarnings("unused")
public void cacheControl() {
FhirContext ctx = FhirContext.forDstu3();
// Create the client
IGenericClient client = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
Bundle bundle = new Bundle();
// ..populate the bundle..
// START SNIPPET: cacheControl
Bundle response = client
.search()
.forResource(Patient.class)
.returnBundle(Bundle.class)
.cacheControl(new CacheControlDirective().setNoCache(true)) // <-- add a directive
.execute();
// END SNIPPET: cacheControl
}
@SuppressWarnings("unused")
public void createOkHttp() {
// START SNIPPET: okhttp
FhirContext ctx = FhirContext.forDstu3();
// Use OkHttp
ctx.setRestfulClientFactory(new OkHttpRestfulClientFactory(ctx));
// Create the client
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
// END SNIPPET: okhttp
}
@SuppressWarnings("unused")
public void createTimeouts() {
// START SNIPPET: timeouts
FhirContext ctx = FhirContext.forDstu2();
// Set how long to try and establish the initial TCP connection (in ms)
ctx.getRestfulClientFactory().setConnectTimeout(20 * 1000);
// Set how long to block for individual read/write operations (in ms)
ctx.getRestfulClientFactory().setSocketTimeout(20 * 1000);
// Create the client
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
// END SNIPPET: timeouts
}
@SuppressWarnings("unused")
public void createSecurity() {
// START SNIPPET: security
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Create an HTTP basic auth interceptor
String username = "foobar";
String password = "boobear";
IClientInterceptor authInterceptor = new BasicAuthInterceptor(username, password);
// If you're usinf an annotation client, use this style to
// register it
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(authInterceptor);
// If you're using a generic client, use this instead
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
genericClient.registerInterceptor(authInterceptor);
// END SNIPPET: security
}
@SuppressWarnings("unused")
public void createCookie() {
// START SNIPPET: cookie
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Create a cookie interceptor. This cookie will have the name "mycookie" and
// the value "Chips Ahoy"
CookieInterceptor interceptor = new CookieInterceptor("mycookie=Chips Ahoy");
// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(interceptor);
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
annotationClient.registerInterceptor(interceptor);
// END SNIPPET: cookie
}
@SuppressWarnings("unused")
public void gzip() {
// START SNIPPET: gzip
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(new GZipContentInterceptor());
// END SNIPPET: gzip
}
@SuppressWarnings("unused")
public void createSecurityBearer() {
// START SNIPPET: securityBearer
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// In reality the token would have come from an authorization server
String token = "3w03fj.r3r3t";
BearerTokenAuthInterceptor authInterceptor = new BearerTokenAuthInterceptor(token);
// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(authInterceptor);
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
annotationClient.registerInterceptor(authInterceptor);
// END SNIPPET: securityBearer
}
@SuppressWarnings("unused")
public void createLogging() {
{
// START SNIPPET: logging
// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forDstu2();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
// Create a logging interceptor
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
// Optionally you may configure the interceptor (by default only
// summary info is logged)
loggingInterceptor.setLogRequestSummary(true);
loggingInterceptor.setLogRequestBody(true);
// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(loggingInterceptor);
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
genericClient.registerInterceptor(loggingInterceptor);
// END SNIPPET: logging
}
/******************************/
{
// START SNIPPET: clientConfig
// Create a client
FhirContext ctx = FhirContext.forDstu2();
IPatientClient client = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/");
// Request JSON encoding from the server (_format=json)
client.setEncoding(EncodingEnum.JSON);
// Request pretty printing from the server (_pretty=true)
client.setPrettyPrint(true);
// END SNIPPET: clientConfig
}
}
}

View File

@ -0,0 +1,90 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import org.hl7.fhir.r4.model.*;
public class ClientTransactionExamples {
public static void main(String[] args) {
conditionalCreate();
}
private static void conditionalCreate() {
//START SNIPPET: conditional
// Create a patient object
Patient patient = new Patient();
patient.addIdentifier()
.setSystem("http://acme.org/mrns")
.setValue("12345");
patient.addName()
.setFamily("Jameson")
.addGiven("J")
.addGiven("Jonah");
patient.setGender(Enumerations.AdministrativeGender.MALE);
// Give the patient a temporary UUID so that other resources in
// the transaction can refer to it
patient.setId(IdType.newRandomUuid());
// Create an observation object
Observation observation = new Observation();
observation.setStatus(Observation.ObservationStatus.FINAL);
observation
.getCode()
.addCoding()
.setSystem("http://loinc.org")
.setCode("789-8")
.setDisplay("Erythrocytes [#/volume] in Blood by Automated count");
observation.setValue(
new Quantity()
.setValue(4.12)
.setUnit("10 trillion/L")
.setSystem("http://unitsofmeasure.org")
.setCode("10*12/L"));
// The observation refers to the patient using the ID, which is already
// set to a temporary UUID
observation.setSubject(new Reference(patient.getIdElement().getValue()));
// Create a bundle that will be used as a transaction
Bundle bundle = new Bundle();
bundle.setType(Bundle.BundleType.TRANSACTION);
// Add the patient as an entry. This entry is a POST with an
// If-None-Exist header (conditional create) meaning that it
// will only be created if there isn't already a Patient with
// the identifier 12345
bundle.addEntry()
.setFullUrl(patient.getIdElement().getValue())
.setResource(patient)
.getRequest()
.setUrl("Patient")
.setIfNoneExist("identifier=http://acme.org/mrns|12345")
.setMethod(Bundle.HTTPVerb.POST);
// Add the observation. This entry is a POST with no header
// (normal create) meaning that it will be created even if
// a similar resource already exists.
bundle.addEntry()
.setResource(observation)
.getRequest()
.setUrl("Observation")
.setMethod(Bundle.HTTPVerb.POST);
// Log the request
FhirContext ctx = FhirContext.forR4();
System.out.println(ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(bundle));
// Create a client and post the transaction to the server
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseR4");
Bundle resp = client.transaction().withBundle(bundle).execute();
// Log the response
System.out.println(ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
//END SNIPPET: conditional
}
}

View File

@ -0,0 +1,67 @@
package ca.uhn.hapi.fhir.docs;
//START SNIPPET: client
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
import java.io.IOException;
import java.util.List;
public class CompleteExampleClient {
/**
* This is a simple client interface. It can have many methods for various
* searches but in this case it has only 1.
*/
public interface ClientInterface extends IRestfulClient {
/**
* This is translated into a URL similar to the following:
* http://fhir.healthintersections.com.au/open/Patient?identifier=urn:oid:1.2.36.146.595.217.0.1%7C12345
*/
@Search
List<Patient> findPatientsForMrn(@RequiredParam(name = Patient.SP_IDENTIFIER) Identifier theIdentifier);
}
/**
* The main method here will directly call an open FHIR server and retrieve a
* list of resources matching a given criteria, then load a linked resource.
*/
public static void main(String[] args) throws IOException {
// Create a client factory
FhirContext ctx = FhirContext.forDstu2();
// Create the client
String serverBase = "http://fhir.healthintersections.com.au/open";
ClientInterface client = ctx.newRestfulClient(ClientInterface.class, serverBase);
// Invoke the client to search for patient
Identifier identifier = new Identifier().setSystem("urn:oid:1.2.36.146.595.217.0.1").setValue("12345");
List<Patient> patients = client.findPatientsForMrn(identifier);
System.out.println("Found " + patients.size() + " patients");
// Print a value from the loaded resource
Patient patient = patients.get(0);
System.out.println("Patient Last Name: " + patient.getName().get(0).getFamily());
// Load a referenced resource
Reference managingRef = patient.getManagingOrganization();
Organization org = client.getOrganizationById(managingRef.getReferenceElement());
// Print organization name
System.out.println(org.getName());
}
}
// END SNIPPET: client

View File

@ -0,0 +1,75 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.interceptor.consent.ConsentOutcome;
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices;
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentService;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Observation;
@SuppressWarnings("unused")
public class ConsentInterceptors {
//START SNIPPET: service
public class MyConsentService implements IConsentService {
/**
* Invoked once at the start of every request
*/
@Override
public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) {
// This means that all requests should flow through the consent service
// This has performance implications - If you know that some requests
// don't need consent checking it is a good idea to return
// ConsentOutcome.AUTHORIZED instead for those requests.
return ConsentOutcome.PROCEED;
}
/**
* Can a given resource be returned to the user?
*/
@Override
public ConsentOutcome canSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) {
// In this basic example, we will filter out lab results so that they
// are never disclosed to the user. A real interceptor might do something
// more nuanced.
if (theResource instanceof Observation) {
Observation obs = (Observation)theResource;
if (obs.getCategoryFirstRep().hasCoding("http://hl7.org/fhir/codesystem-observation-category.html", "laboratory")) {
return ConsentOutcome.REJECT;
}
}
// Otherwise, allow the
return ConsentOutcome.PROCEED;
}
/**
* Modify resources that are being shown to the user
*/
@Override
public ConsentOutcome willSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) {
// Don't return the subject for Observation resources
if (theResource instanceof Observation) {
Observation obs = (Observation)theResource;
obs.setSubject(null);
}
return ConsentOutcome.AUTHORIZED;
}
@Override
public void completeOperationSuccess(RequestDetails theRequestDetails, IConsentContextServices theContextServices) {
// We could write an audit trail entry in here
}
@Override
public void completeOperationFailure(RequestDetails theRequestDetails, BaseServerResponseException theException, IConsentContextServices theContextServices) {
// We could write an audit trail entry in here
}
}
//END SNIPPET: service
}

View File

@ -0,0 +1,40 @@
package ca.uhn.hapi.fhir.docs;
import org.hl7.fhir.convertors.NullVersionConverterAdvisor30;
import org.hl7.fhir.convertors.VersionConvertor_10_30;
import org.hl7.fhir.convertors.VersionConvertor_14_30;
import org.hl7.fhir.exceptions.FHIRException;
public class ConverterExamples {
@SuppressWarnings("unused")
public void c1020() throws FHIRException {
//START SNIPPET: 1020
// Create a converter
NullVersionConverterAdvisor30 advisor = new NullVersionConverterAdvisor30();
VersionConvertor_10_30 converter = new VersionConvertor_10_30(advisor);
// Create an input resource to convert
org.hl7.fhir.dstu2.model.Observation input = new org.hl7.fhir.dstu2.model.Observation();
input.setEncounter(new org.hl7.fhir.dstu2.model.Reference("Encounter/123"));
// Convert the resource
org.hl7.fhir.dstu3.model.Observation output = converter.convertObservation(input);
String context = output.getContext().getReference();
//END SNIPPET: 1020
}
@SuppressWarnings("unused")
public void c1420() throws FHIRException {
//START SNIPPET: 1420
// Create a resource to convert
org.hl7.fhir.dstu2016may.model.Questionnaire input = new org.hl7.fhir.dstu2016may.model.Questionnaire();
input.setTitle("My title");
// Convert the resource
org.hl7.fhir.dstu3.model.Questionnaire output = VersionConvertor_14_30.convertQuestionnaire(input);
String context = output.getTitle();
//END SNIPPET: 1420
}
}

View File

@ -0,0 +1,121 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.util.ResourceReferenceInfo;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@SuppressWarnings("unused")
public class Copier {
private static final Logger ourLog = LoggerFactory.getLogger(Copier.class);
public static void main(String[] args) {
FhirContext ctx = FhirContext.forDstu3();
IGenericClient source = ctx.newRestfulGenericClient("http://localhost:8080/baseDstu3");
IGenericClient target = ctx.newRestfulGenericClient("https://try.smilecdr.com:8000");
List<String> resType = Arrays.asList(
"Patient", "Organization", "Encounter", "Procedure",
"Observation", "ResearchSubject", "Specimen",
"ResearchStudy", "Location", "Practitioner"
);
List<IBaseResource> queued = new ArrayList<>();
Set<String> sent = new HashSet<>();
for (String next : resType) {
copy(ctx, source, target, next, queued, sent);
}
while (queued.size() > 0) {
ourLog.info("Have {} queued resources to deliver", queued.size());
for (IBaseResource nextQueued : new ArrayList<>(queued)) {
String missingRef = null;
for (ResourceReferenceInfo nextRefInfo : ctx.newTerser().getAllResourceReferences(nextQueued)) {
String nextRef = nextRefInfo.getResourceReference().getReferenceElement().getValue();
if (isNotBlank(nextRef) && !sent.contains(nextRef)) {
missingRef = nextRef;
}
}
if (missingRef != null) {
ourLog.info("Can't send {} because of missing ref {}", nextQueued.getIdElement().getIdPart(), missingRef);
continue;
}
IIdType newId = target
.update()
.resource(nextQueued)
.execute()
.getId();
ourLog.info("Copied resource {} and got ID {}", nextQueued.getIdElement().getValue(), newId);
sent.add(nextQueued.getIdElement().toUnqualifiedVersionless().getValue());
queued.remove(nextQueued);
}
}
}
private static void copy(FhirContext theCtx, IGenericClient theSource, IGenericClient theTarget, String theResType, List<IBaseResource> theQueued, Set<String> theSent) {
Bundle received = theSource
.search()
.forResource(theResType)
.returnBundle(Bundle.class)
.execute();
copy(theCtx, theTarget, theResType, theQueued, theSent, received);
while (received.getLink("next") != null) {
ourLog.info("Fetching next page...");
received = theSource.loadPage().next(received).execute();
copy(theCtx, theTarget, theResType, theQueued, theSent, received);
}
}
private static void copy(FhirContext theCtx, IGenericClient theTarget, String theResType, List<IBaseResource> theQueued, Set<String> theSent, Bundle theReceived) {
for (Bundle.BundleEntryComponent nextEntry : theReceived.getEntry()) {
Resource nextResource = nextEntry.getResource();
nextResource.setId(theResType + "/" + "CR-" + nextResource.getIdElement().getIdPart());
boolean haveUnsentReference = false;
for (ResourceReferenceInfo nextRefInfo : theCtx.newTerser().getAllResourceReferences(nextResource)) {
IIdType nextRef = nextRefInfo.getResourceReference().getReferenceElement();
if (nextRef.hasIdPart()) {
String newRef = nextRef.getResourceType() + "/" + "CR-" + nextRef.getIdPart();
ourLog.info("Changing reference {} to {}", nextRef.getValue(), newRef);
nextRefInfo.getResourceReference().setReference(newRef);
if (!theSent.contains(newRef)) {
haveUnsentReference = true;
}
}
}
if (haveUnsentReference) {
ourLog.info("Queueing {} for delivery after", nextResource.getId());
theQueued.add(nextResource);
continue;
}
IIdType newId = theTarget
.update()
.resource(nextResource)
.execute()
.getId();
ourLog.info("Copied resource {} and got ID {}", nextResource.getId(), newId);
theSent.add(nextResource.getIdElement().toUnqualifiedVersionless().getValue());
}
}
}

View File

@ -0,0 +1,7 @@
package ca.uhn.hapi.fhir.docs;
import org.hl7.fhir.dstu3.model.Observation;
public class CustomObservation extends Observation {
}

View File

@ -0,0 +1,66 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import javax.servlet.ServletException;
import java.util.Collection;
@SuppressWarnings("serial")
public class Dstu2Examples {
private Collection<IResourceProvider> resourceProviderList;
public static void main(String[] args) {
new Dstu2Examples().getResourceTags();
}
@SuppressWarnings("unused")
public void getResourceTags() {
// START SNIPPET: context
// Create a DSTU2 context, which will use DSTU2 semantics
FhirContext ctx = FhirContext.forDstu2();
// This parser supports DSTU2
IParser parser = ctx.newJsonParser();
// This client supports DSTU2
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
// END SNIPPET: context
}
// START SNIPPET: server
public class MyServer extends RestfulServer
{
@Override
protected void initialize() throws ServletException {
// In your initialize method, assign a DSTU2 FhirContext. This
// is all that is required in order to put the server
// into DSTU2 mode
setFhirContext(FhirContext.forDstu2());
// Then set resource providers as normal, and do any other
// configuration you need to do.
setResourceProviders(resourceProviderList);
}
}
// END SNIPPET: server
public void upgrade() {
// START SNIPPET: client
FhirContext ctxDstu2 = FhirContext.forDstu2();
IGenericClient clientDstu2 = ctxDstu2.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
// END SNIPPET: client
}
}

View File

@ -0,0 +1,81 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.IResourceProvider;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Patient;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings(value= {"serial"})
public class ExampleProviders {
//START SNIPPET: plainProvider
public class PlainProvider {
/**
* This method is a Patient search, but HAPI can not automatically
* determine the resource type so it must be explicitly stated.
*/
@Search(type=Patient.class)
public Bundle searchForPatients(@RequiredParam(name=Patient.SP_NAME) StringDt theName) {
Bundle retVal = new Bundle();
// perform search
return retVal;
}
}
//END SNIPPET: plainProvider
//START SNIPPET: plainProviderServer
public class ExampleServlet extends ca.uhn.fhir.rest.server.RestfulServer {
/**
* Constructor
*/
public ExampleServlet() {
/*
* Plain providers are passed to the server in the same way
* as resource providers. You may pass both resource providers
* and and plain providers to the same server if you like.
*/
List<Object> plainProviders=new ArrayList<Object>();
plainProviders.add(new PlainProvider());
registerProviders(plainProviders);
List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
// ...add some resource providers...
registerProviders(resourceProviders);
}
}
//END SNIPPET: plainProviderServer
//START SNIPPET: addressStrategy
public class MyServlet extends ca.uhn.fhir.rest.server.RestfulServer {
/**
* Constructor
*/
public MyServlet() {
String serverBaseUrl = "http://foo.com/fhir";
setServerAddressStrategy(new HardcodedServerAddressStrategy(serverBaseUrl));
// ...add some resource providers, etc...
List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
setResourceProviders(resourceProviders);
}
}
//END SNIPPET: addressStrategy
}

View File

@ -0,0 +1,25 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.r4.model.*;
import java.util.List;
@SuppressWarnings("unused")
public class ExampleRestfulClient {
//START SNIPPET: client
public static void main(String[] args) {
FhirContext ctx = FhirContext.forDstu2();
String serverBase = "http://foo.com/fhirServerBase";
// Create the client
IRestfulClient client = ctx.newRestfulClient(IRestfulClient.class, serverBase);
// Try the client out! This method will invoke the server
List<Patient> patients = client.getPatient(new StringType("SMITH"));
}
//END SNIPPET: client
}

View File

@ -0,0 +1,45 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import java.util.ArrayList;
import java.util.List;
//START SNIPPET: servlet
/**
* In this example, we are using Servlet 3.0 annotations to define
* the URL pattern for this servlet, but we could also
* define this in a web.xml file.
*/
@WebServlet(urlPatterns= {"/fhir/*"}, displayName="FHIR Server")
public class ExampleRestfulServlet extends RestfulServer {
private static final long serialVersionUID = 1L;
/**
* The initialize method is automatically called when the servlet is starting up, so it can
* be used to configure the servlet to define resource providers, or set up
* configuration, interceptors, etc.
*/
@Override
protected void initialize() throws ServletException {
/*
* The servlet defines any number of resource providers, and
* configures itself to use them by calling
* setResourceProviders()
*/
List<IResourceProvider> resourceProviders = new ArrayList<IResourceProvider>();
resourceProviders.add(new RestfulPatientResourceProvider());
resourceProviders.add(new RestfulObservationResourceProvider());
setResourceProviders(resourceProviders);
}
}
//END SNIPPET: servlet

View File

@ -0,0 +1,105 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
import java.io.IOException;
import java.util.List;
public class ExtensionsDstu2 {
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
{
Questionnaire q= new Questionnaire();
Questionnaire.GroupQuestion item = q.getGroup().addQuestion();
item.setText("Hello");
ExtensionDt extension = new ExtensionDt(false, "http://hl7.org/fhir/StructureDefinition/translation");
item.getTextElement().addUndeclaredExtension(extension);
extension.addUndeclaredExtension(new ExtensionDt(false, "lang", new CodeDt("es")));
extension.addUndeclaredExtension(new ExtensionDt(false, "cont", new StringDt("hola")));
System.out.println(FhirContext.forDstu2().newJsonParser().setPrettyPrint(true).encodeResourceToString(q));
}
// START SNIPPET: resourceExtension
// Create an example patient
Patient patient = new Patient();
patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:example").setValue("7000135");
// Create an extension
ExtensionDt ext = new ExtensionDt();
ext.setModifier(false);
ext.setUrl("http://example.com/extensions#someext");
ext.setValue(new DateTimeDt("2011-01-02T11:13:15"));
// Add the extension to the resource
patient.addUndeclaredExtension(ext);
//END SNIPPET: resourceExtension
//START SNIPPET: resourceStringExtension
// Continuing the example from above, we will add a name to the patient, and then
// add an extension to part of that name
HumanNameDt name = patient.addName();
name.addFamily().setValue("Shmoe");
// Add a new "given name", which is of type StringDt
StringDt given = name.addGiven();
given.setValue("Joe");
// Create an extension and add it to the StringDt
ExtensionDt givenExt = new ExtensionDt(false, "http://examples.com#moreext", new StringDt("Hello"));
given.addUndeclaredExtension(givenExt);
//END SNIPPET: resourceStringExtension
FhirContext ctx = FhirContext.forDstu2();
String output = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(output);
//START SNIPPET: parseExtension
// Get all extensions (modifier or not) for a given URL
List<ExtensionDt> resourceExts = patient.getUndeclaredExtensionsByUrl("http://fooextensions.com#exts");
// Get all non-modifier extensions regardless of URL
List<ExtensionDt> nonModExts = patient.getUndeclaredExtensions();
//Get all non-modifier extensions regardless of URL
List<ExtensionDt> modExts = patient.getUndeclaredModifierExtensions();
//END SNIPPET: parseExtension
}
public void foo() {
//START SNIPPET: subExtension
Patient patient = new Patient();
// Add an extension (initially with no contents) to the resource
ExtensionDt parent = new ExtensionDt(false, "http://example.com#parent");
patient.addUndeclaredExtension(parent);
// Add two extensions as children to the parent extension
ExtensionDt child1 = new ExtensionDt(false, "http://example.com#childOne", new StringDt("value1"));
parent.addUndeclaredExtension(child1);
ExtensionDt child2 = new ExtensionDt(false, "http://example.com#childTwo", new StringDt("value1"));
parent.addUndeclaredExtension(child2);
//END SNIPPET: subExtension
}
}

View File

@ -0,0 +1,158 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import org.hl7.fhir.r4.model.*;
import java.io.IOException;
import java.util.List;
public class ExtensionsDstu3 {
public void customType() {
IGenericClient client = FhirContext.forDstu3().newRestfulGenericClient("http://foo");
//START SNIPPET: customTypeClientSimple
// Create an example patient
MyPatient custPatient = new MyPatient();
custPatient.addName().setFamily("Smith").addGiven("John");
custPatient.setPetName(new StringType("Rover")); // populate the extension
// Create the resource like normal
client.create().resource(custPatient).execute();
// You can also read the resource back like normal
custPatient = client.read().resource(MyPatient.class).withId("123").execute();
//END SNIPPET: customTypeClientSimple
//START SNIPPET: customTypeClientSearch
// Perform the search using the custom type
Bundle bundle = client
.search()
.forResource(MyPatient.class)
.returnBundle(Bundle.class)
.execute();
// Entries in the return bundle will use the given type
MyPatient pat0 = (MyPatient) bundle.getEntry().get(0).getResource();
//END SNIPPET: customTypeClientSearch
//START SNIPPET: customTypeClientSearch2
//Perform the search using the custom type
bundle = client
.history()
.onInstance(new IdType("Patient/123"))
.andReturnBundle(Bundle.class)
.preferResponseType(MyPatient.class)
.execute();
//Entries in the return bundle will use the given type
MyPatient historyPatient0 = (MyPatient) bundle.getEntry().get(0).getResource();
//END SNIPPET: customTypeClientSearch2
}
public void customTypeDeclared() {
//START SNIPPET: customTypeClientDeclared
FhirContext ctx = FhirContext.forDstu3();
// Instruct the context that if it receives a resource which
// claims to conform to the given profile (by URL), it should
// use the MyPatient type to parse this resource
ctx.setDefaultTypeForProfile("http://example.com/StructureDefinition/mypatient", MyPatient.class);
// You can declare as many default types as you like
ctx.setDefaultTypeForProfile("http://foo.com/anotherProfile", CustomObservation.class);
// Create a client
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu3");
// You can also read the resource back like normal
Patient patient = client.read().resource(Patient.class).withId("123").execute();
if (patient instanceof MyPatient) {
// If the server supplied a resource which declared to conform
// to the given profile, MyPatient will have been returned so
// process it differently..
}
//END SNIPPET: customTypeClientDeclared
}
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
// START SNIPPET: resourceExtension
// Create an example patient
Patient patient = new Patient();
patient.addIdentifier().setUse(Identifier.IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
// Create an extension
Extension ext = new Extension();
ext.setUrl("http://example.com/extensions#someext");
ext.setValue(new DateTimeType("2011-01-02T11:13:15"));
// Add the extension to the resource
patient.addExtension(ext);
//END SNIPPET: resourceExtension
//START SNIPPET: resourceStringExtension
// Continuing the example from above, we will add a name to the patient, and then
// add an extension to part of that name
HumanName name = patient.addName();
name.setFamily("Shmoe");
// Add a new "given name", which is of type String
StringType given = name.addGivenElement();
given.setValue("Joe");
// Create an extension and add it to the String
Extension givenExt = new Extension("http://examples.com#moreext", new StringType("Hello"));
given.addExtension(givenExt);
//END SNIPPET: resourceStringExtension
FhirContext ctx = FhirContext.forDstu3();
String output = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(output);
//START SNIPPET: parseExtension
// Get all extensions (modifier or not) for a given URL
List<Extension> resourceExts = patient.getExtensionsByUrl("http://fooextensions.com#exts");
// Get all non-modifier extensions regardless of URL
List<Extension> nonModExts = patient.getExtension();
//Get all non-modifier extensions regardless of URL
List<Extension> modExts = patient.getModifierExtension();
//END SNIPPET: parseExtension
}
public void foo() {
//START SNIPPET: subExtension
Patient patient = new Patient();
// Add an extension (initially with no contents) to the resource
Extension parent = new Extension("http://example.com#parent");
patient.addExtension(parent);
// Add two extensions as children to the parent extension
Extension child1 = new Extension("http://example.com#childOne", new StringType("value1"));
parent.addExtension(child1);
Extension child2 = new Extension("http://example.com#chilwo", new StringType("value1"));
parent.addExtension(child2);
//END SNIPPET: subExtension
}
}

View File

@ -0,0 +1,125 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r4.model.*;
public class FhirContextIntro {
@SuppressWarnings("unused")
public static void creatingContext() {
// START SNIPPET: creatingContext
// Create a context for DSTU2
FhirContext ctxDstu2 = FhirContext.forDstu2();
// Alternately, create a context for R4
FhirContext ctxR4 = FhirContext.forR4();
// END SNIPPET: creatingContext
}
@SuppressWarnings("unused")
public static void creatingContextHl7org() {
// START SNIPPET: creatingContextHl7org
// Create a context for DSTU3
FhirContext ctx = FhirContext.forDstu3();
// Working with RI structures is similar to how it works with the HAPI structures
org.hl7.fhir.dstu3.model.Patient patient = new org.hl7.fhir.dstu3.model.Patient();
patient.addName().addGiven("John").setFamily("Smith");
patient.getBirthDateElement().setValueAsString("1998-02-22");
// Parsing and encoding works the same way too
String encoded = ctx.newJsonParser().encodeResourceToString(patient);
// END SNIPPET: creatingContextHl7org
}
public void encodeMsg() throws DataFormatException {
FhirContext ctx = new FhirContext(Patient.class, Observation.class);
//START SNIPPET: encodeMsg
/**
* FHIR model types in HAPI are simple POJOs. To create a new
* one, invoke the default constructor and then
* start populating values.
*/
Patient patient = new Patient();
// Add an MRN (a patient identifier)
Identifier id = patient.addIdentifier();
id.setSystem("http://example.com/fictitious-mrns");
id.setValue("MRN001");
// Add a name
HumanName name = patient.addName();
name.setUse(HumanName.NameUse.OFFICIAL);
name.setFamily("Tester");
name.addGiven("John");
name.addGiven("Q");
// We can now use a parser to encode this resource into a string.
String encoded = ctx.newXmlParser().encodeResourceToString(patient);
System.out.println(encoded);
//END SNIPPET: encodeMsg
//START SNIPPET: encodeMsgJson
IParser jsonParser = ctx.newJsonParser();
jsonParser.setPrettyPrint(true);
encoded = jsonParser.encodeResourceToString(patient);
System.out.println(encoded);
//END SNIPPET: encodeMsgJson
}
public void fluent() throws DataFormatException {
FhirContext ctx = new FhirContext(Patient.class, Observation.class);
String encoded;
//START SNIPPET: encodeMsgFluent
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://example.com/fictitious-mrns").setValue("MRN001");
patient.addName().setUse(HumanName.NameUse.OFFICIAL).setFamily("Tester").addGiven("John").addGiven("Q");
encoded = ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(encoded);
//END SNIPPET: encodeMsgFluent
}
public static void parseMsg() {
FhirContext ctx = FhirContext.forR4();
//START SNIPPET: parseMsg
// The following is an example Patient resource
String msgString = "<Patient xmlns=\"http://hl7.org/fhir\">"
+ "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal</div></text>"
+ "<identifier><system value=\"http://orionhealth.com/mrn\" /><value value=\"PRP1660\" /></identifier>"
+ "<name><use value=\"official\" /><family value=\"Cardinal\" /><given value=\"John\" /></name>"
+ "<gender><coding><system value=\"http://hl7.org/fhir/v3/AdministrativeGender\" /><code value=\"M\" /></coding></gender>"
+ "<address><use value=\"home\" /><line value=\"2222 Home Street\" /></address><active value=\"true\" />"
+ "</Patient>";
// The hapi context object is used to create a new XML parser
// instance. The parser can then be used to parse (or unmarshall) the
// string message into a Patient object
IParser parser = ctx.newXmlParser();
Patient patient = parser.parseResource(Patient.class, msgString);
// The patient object has accessor methods to retrieve all of the
// data which has been parsed into the instance.
String patientId = patient.getIdentifier().get(0).getValue();
String familyName = patient.getName().get(0).getFamily();
Enumerations.AdministrativeGender gender = patient.getGender();
System.out.println(patientId); // PRP1660
System.out.println(familyName); // Cardinal
System.out.println(gender.toCode()); // male
//END SNIPPET: parseMsg
}
}

View File

@ -0,0 +1,185 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import java.util.Date;
import java.util.List;
import org.hl7.fhir.r4.model.*;
public class FhirDataModel {
public static void datatypes() {
// START SNIPPET: datatypes
Observation obs = new Observation();
// These are all equivalent
obs.setIssuedElement(new InstantType(new Date()));
obs.setIssuedElement(new InstantType(new Date(), TemporalPrecisionEnum.MILLI));
obs.setIssued(new Date());
// The InstantType also lets you work with the instant as a Java Date
// object or as a FHIR String.
Date date = obs.getIssuedElement().getValue(); // A date object
String dateString = obs.getIssuedElement().getValueAsString(); // "2014-03-08T12:59:58.068-05:00"
// END SNIPPET: datatypes
System.out.println(date);
System.out.println(dateString);
}
@SuppressWarnings("unused")
public void nonNull() {
// START SNIPPET: nonNull
Observation observation = new Observation();
// None of these calls will not return null, but instead create their
// respective
// child elements.
List<Identifier> identifierList = observation.getIdentifier();
CodeableConcept code = observation.getCode();
StringType textElement = observation.getCode().getTextElement();
// DateTimeDt is a FHIR primitive however, so the following will return
// null
// unless a value has been placed there.
Date active = observation.addIdentifier().getPeriod().getStartElement().getValue();
// END SNIPPET: nonNull
}
@SuppressWarnings("unused")
public static void codes() {
// START SNIPPET: codes
Patient patient = new Patient();
// You can set this code using a String if you want. Note that
// for "closed" valuesets (such as the one used for Patient.gender)
// you must use one of the strings defined by the FHIR specification.
// You must not define your own.
patient.getGenderElement().setValueAsString("male");
// HAPI also provides Java enumerated types which make it easier to
// deal with coded values. This code achieves the exact same result
// as the code above.
patient.setGender(Enumerations.AdministrativeGender.MALE);
// You can also retrieve coded values the same way
String genderString = patient.getGenderElement().getValueAsString();
Enumerations.AdministrativeGender genderEnum = patient.getGenderElement().getValue();
// END SNIPPET: codes
}
@SuppressWarnings("unused")
public static void codeableConcepts() {
// START SNIPPET: codeableConcepts
Patient patient = new Patient();
// Coded types can naturally be set using plain strings
Coding statusCoding = patient.getMaritalStatus().addCoding();
statusCoding.setSystem("http://hl7.org/fhir/v3/MaritalStatus");
statusCoding.setCode("M");
statusCoding.setDisplay("Married");
// You could add a second coding to the field if needed too. This
// can be useful if you want to convey the concept using different
// codesystems.
Coding secondStatus = patient.getMaritalStatus().addCoding();
secondStatus.setCode("H");
secondStatus.setSystem("http://example.com#maritalStatus");
secondStatus.setDisplay("Happily Married");
// CodeableConcept also has a text field meant to convey
// a user readable version of the concepts it conveys.
patient.getMaritalStatus().setText("Happily Married");
// There are also accessors for retrieving values
String firstCode = patient.getMaritalStatus().getCoding().get(0).getCode();
String secondCode = patient.getMaritalStatus().getCoding().get(1).getCode();
// END SNIPPET: codeableConcepts
}
public static void main(String[] args) {
tmp();
datatypes();
// START SNIPPET: observation
// Create an Observation instance
Observation observation = new Observation();
// Give the observation a status
observation.setStatus(Observation.ObservationStatus.FINAL);
// Give the observation a code (what kind of observation is this)
Coding coding = observation.getCode().addCoding();
coding.setCode("29463-7").setSystem("http://loinc.org").setDisplay("Body Weight");
// Create a quantity datatype
Quantity value = new Quantity();
value.setValue(83.9).setSystem("http://unitsofmeasure.org").setCode("kg");
observation.setValue(value);
// Set the reference range
SimpleQuantity low = new SimpleQuantity();
low.setValue(45).setSystem("http://unitsofmeasure.org").setCode("kg");
observation.getReferenceRangeFirstRep().setLow(low);
SimpleQuantity high = new SimpleQuantity();
low.setValue(90).setSystem("http://unitsofmeasure.org").setCode("kg");
observation.getReferenceRangeFirstRep().setHigh(high);
// END SNIPPET: observation
}
private static void tmp() {
// Create a FHIR Context
FhirContext ctx = FhirContext.forR4();
// Create a client
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseR4");
// Read a patient with the given ID
Patient patient = client
.read()
.resource(Patient.class)
.withId("952975")
.execute();
// Print the patient's name
String string = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(string);
}
public void namesHard() {
// START SNIPPET: namesHard
Patient patient = new Patient();
HumanName name = patient.addName();
name.setFamily("Smith");
StringType firstName = name.addGivenElement();
firstName.setValue("Rob");
StringType secondName = name.addGivenElement();
secondName.setValue("Bruce");
// END SNIPPET: namesHard
}
public void namesEasy() {
// START SNIPPET: namesEasy
Patient patient = new Patient();
patient.addName().setFamily("Smith").addGiven("Rob").addGiven("Bruce");
// END SNIPPET: namesEasy
}
}

View File

@ -0,0 +1,534 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.PerformanceOptionsEnum;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SearchStyleEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*;
import java.util.ArrayList;
import java.util.List;
public class GenericClientExample {
public static void deferModelScanning() {
// START SNIPPET: deferModelScanning
// Create a context and configure it for deferred child scanning
FhirContext ctx = FhirContext.forDstu2();
ctx.setPerformanceOptions(PerformanceOptionsEnum.DEFERRED_MODEL_SCANNING);
// Now create a client and use it
String serverBase = "http://fhirtest.uhn.ca/baseDstu2";
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// END SNIPPET: deferModelScanning
}
public static void performance() {
// START SNIPPET: dontValidate
// Create a context
FhirContext ctx = FhirContext.forDstu2();
// Disable server validation (don't pull the server's metadata first)
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
// Now create a client and use it
String serverBase = "http://fhirtest.uhn.ca/baseDstu2";
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// END SNIPPET: dontValidate
}
public static void simpleExample() {
// START SNIPPET: simple
// We're connecting to a DSTU1 compliant server in this example
FhirContext ctx = FhirContext.forDstu2();
String serverBase = "http://fhirtest.uhn.ca/baseDstu2";
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// Perform a search
Bundle results = client
.search()
.forResource(Patient.class)
.where(Patient.FAMILY.matches().value("duck"))
.returnBundle(Bundle.class)
.execute();
System.out.println("Found " + results.getEntry().size() + " patients named 'duck'");
// END SNIPPET: simple
}
@SuppressWarnings("unused")
public static void fluentSearch() {
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir.healthintersections.com.au/open");
{
// START SNIPPET: create
Patient patient = new Patient();
// ..populate the patient object..
patient.addIdentifier().setSystem("urn:system").setValue("12345");
patient.addName().setFamily("Smith").addGiven("John");
// Invoke the server create method (and send pretty-printed JSON
// encoding to the server
// instead of the default which is non-pretty printed XML)
MethodOutcome outcome = client.create()
.resource(patient)
.prettyPrint()
.encodedJson()
.execute();
// The MethodOutcome object will contain information about the
// response from the server, including the ID of the created
// resource, the OperationOutcome response, etc. (assuming that
// any of these things were provided by the server! They may not
// always be)
IIdType id = outcome.getId();
System.out.println("Got ID: " + id.getValue());
// END SNIPPET: create
}
{
Patient patient = new Patient();
// START SNIPPET: createConditional
// One form
MethodOutcome outcome = client.create()
.resource(patient)
.conditionalByUrl("Patient?identifier=system%7C00001")
.execute();
// Another form
MethodOutcome outcome2 = client.create()
.resource(patient)
.conditional()
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
.execute();
// This will return Boolean.TRUE if the server responded with an HTTP 201 created,
// otherwise it will return null.
Boolean created = outcome.getCreated();
// The ID of the created, or the pre-existing resource
IIdType id = outcome.getId();
// END SNIPPET: createConditional
}
{
// START SNIPPET: validate
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://hospital.com").setValue("123445");
patient.addName().setFamily("Smith").addGiven("John");
// Validate the resource
MethodOutcome outcome = client.validate()
.resource(patient)
.execute();
// The returned object will contain an operation outcome resource
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
// If the OperationOutcome has any issues with a severity of ERROR or SEVERE,
// the validation failed.
for (OperationOutcome.OperationOutcomeIssueComponent nextIssue : oo.getIssue()) {
if (nextIssue.getSeverity().ordinal() >= OperationOutcome.IssueSeverity.ERROR.ordinal()) {
System.out.println("We failed validation!");
}
}
// END SNIPPET: validate
}
{
// START SNIPPET: update
Patient patient = new Patient();
// ..populate the patient object..
patient.addIdentifier().setSystem("urn:system").setValue("12345");
patient.addName().setFamily("Smith").addGiven("John");
// To update a resource, it should have an ID set (if the resource
// object
// comes from the results of a previous read or search, it will already
// have one though)
patient.setId("Patient/123");
// Invoke the server update method
MethodOutcome outcome = client.update()
.resource(patient)
.execute();
// The MethodOutcome object will contain information about the
// response from the server, including the ID of the created
// resource, the OperationOutcome response, etc. (assuming that
// any of these things were provided by the server! They may not
// always be)
IdType id = (IdType) outcome.getId();
System.out.println("Got ID: " + id.getValue());
// END SNIPPET: update
}
{
Patient patient = new Patient();
// START SNIPPET: updateConditional
client.update()
.resource(patient)
.conditionalByUrl("Patient?identifier=system%7C00001")
.execute();
client.update()
.resource(patient)
.conditional()
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
.execute();
// END SNIPPET: updateConditional
}
{
// START SNIPPET: etagupdate
// First, let's retrive the latest version of a resource
// from the server
Patient patient = client.read().resource(Patient.class).withId("123").execute();
// If the server is a version aware server, we should now know the latest version
// of the resource
System.out.println("Version ID: " + patient.getIdElement().getVersionIdPart());
// Now let's make a change to the resource
patient.setGender(Enumerations.AdministrativeGender.FEMALE);
// Invoke the server update method - Because the resource has
// a version, it will be included in the request sent to
// the server
try {
MethodOutcome outcome = client
.update()
.resource(patient)
.execute();
} catch (PreconditionFailedException e) {
// If we get here, the latest version has changed
// on the server so our update failed.
}
// END SNIPPET: etagupdate
}
{
// START SNIPPET: conformance
// Retrieve the server's conformance statement and print its
// description
CapabilityStatement conf = client.capabilities().ofType(CapabilityStatement.class).execute();
System.out.println(conf.getDescriptionElement().getValue());
// END SNIPPET: conformance
}
{
// START SNIPPET: delete
IBaseOperationOutcome resp = client.delete().resourceById(new IdType("Patient", "1234")).execute();
// outcome may be null if the server didn't return one
if (resp != null) {
OperationOutcome outcome = (OperationOutcome) resp;
System.out.println(outcome.getIssueFirstRep().getDetails().getCodingFirstRep().getCode());
}
// END SNIPPET: delete
}
{
// START SNIPPET: deleteConditional
client.delete()
.resourceConditionalByUrl("Patient?identifier=system%7C00001")
.execute();
client.delete()
.resourceConditionalByType("Patient")
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001"))
.execute();
// END SNIPPET: deleteConditional
}
{
// START SNIPPET: search
Bundle response = client.search()
.forResource(Patient.class)
.where(Patient.BIRTHDATE.beforeOrEquals().day("2011-01-01"))
.and(Patient.GENERAL_PRACTITIONER.hasChainedProperty(Organization.NAME.matches().value("Smith")))
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: search
// START SNIPPET: searchOr
response = client.search()
.forResource(Patient.class)
.where(Patient.FAMILY.matches().values("Smith", "Smyth"))
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchOr
// START SNIPPET: searchAnd
response = client.search()
.forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto"))
.and(Patient.ADDRESS.matches().values("Ontario"))
.and(Patient.ADDRESS.matches().values("Canada"))
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchAnd
// START SNIPPET: searchCompartment
response = client.search()
.forResource(Patient.class)
.withIdAndCompartment("123", "condition")
.where(Patient.ADDRESS.matches().values("Toronto"))
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchCompartment
// START SNIPPET: searchUrl
String searchUrl = "http://example.com/base/Patient?identifier=foo";
// Search URL can also be a relative URL in which case the client's base
// URL will be added to it
searchUrl = "Patient?identifier=foo";
response = client.search()
.byUrl(searchUrl)
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchUrl
// START SNIPPET: searchSubsetSummary
response = client.search()
.forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto"))
.returnBundle(Bundle.class)
.summaryMode(SummaryEnum.TRUE)
.execute();
// END SNIPPET: searchSubsetSummary
// START SNIPPET: searchSubsetElements
response = client.search()
.forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto"))
.returnBundle(Bundle.class)
.elementsSubset("identifier", "name") // only include the identifier and name
.execute();
// END SNIPPET: searchSubsetElements
// START SNIPPET: searchAdv
response = client.search()
.forResource(Patient.class)
.encodedJson()
.where(Patient.BIRTHDATE.beforeOrEquals().day("2012-01-22"))
.and(Patient.BIRTHDATE.after().day("2011-01-01"))
.withTag("http://acme.org/codes", "needs-review")
.include(Patient.INCLUDE_ORGANIZATION.asRecursive())
.include(Patient.INCLUDE_GENERAL_PRACTITIONER.asNonRecursive())
.revInclude(Provenance.INCLUDE_TARGET)
.lastUpdated(new DateRangeParam("2011-01-01", null))
.sort().ascending(Patient.BIRTHDATE)
.sort().descending(Patient.NAME).limitTo(123)
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchAdv
// START SNIPPET: searchPost
response = client.search()
.forResource("Patient")
.where(Patient.NAME.matches().value("Tester"))
.usingStyle(SearchStyleEnum.POST)
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchPost
// START SNIPPET: searchComposite
response = client.search()
.forResource("Observation")
.where(Observation.CODE_VALUE_DATE
.withLeft(Observation.CODE.exactly().code("FOO$BAR"))
.withRight(Observation.VALUE_DATE.exactly().day("2001-01-01")))
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: searchComposite
}
{
// START SNIPPET: transaction
List<IResource> resources = new ArrayList<IResource>();
// .. populate this list - note that you can also pass in a populated
// Bundle if you want to create one manually ..
List<IBaseResource> response = client.transaction().withResources(resources).execute();
// END SNIPPET: transaction
}
{
// START SNIPPET: read
// search for patient 123
Patient patient = client.read()
.resource(Patient.class)
.withId("123")
.execute();
// END SNIPPET: read
}
{
// START SNIPPET: vread
// search for patient 123 (specific version 888)
Patient patient = client.read()
.resource(Patient.class)
.withIdAndVersion("123", "888")
.execute();
// END SNIPPET: vread
}
{
// START SNIPPET: readabsolute
// search for patient 123 on example.com
String url = "http://example.com/fhir/Patient/123";
Patient patient = client.read()
.resource(Patient.class)
.withUrl(url)
.execute();
// END SNIPPET: readabsolute
}
{
// START SNIPPET: etagread
// search for patient 123
Patient patient = client.read()
.resource(Patient.class)
.withId("123")
.ifVersionMatches("001").returnNull()
.execute();
if (patient == null) {
// resource has not changed
}
// END SNIPPET: etagread
}
}
@SuppressWarnings("unused")
public static void history() {
IGenericClient client = FhirContext.forDstu2().newRestfulGenericClient("");
{
Bundle response;
// START SNIPPET: historyDstu2
response = client
.history()
.onServer()
.returnBundle(Bundle.class)
.execute();
// END SNIPPET: historyDstu2
}
{
Bundle response;
// START SNIPPET: historyFeatures
response = client
.history()
.onServer()
.returnBundle(Bundle.class)
.since(new InstantType("2012-01-01T12:22:32.038Z"))
.count(100)
.execute();
// END SNIPPET: historyFeatures
}
}
public static void main(String[] args) {
paging();
}
private static void paging() {
{
// START SNIPPET: searchPaging
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
// Perform a search
Bundle resultBundle = client.search()
.forResource(Patient.class)
.where(Patient.NAME.matches().value("Smith"))
.returnBundle(Bundle.class)
.execute();
if (resultBundle.getLink(Bundle.LINK_NEXT) != null) {
// load next page
Bundle nextPage = client.loadPage().next(resultBundle).execute();
}
// END SNIPPET: searchPaging
}
}
@SuppressWarnings("unused")
private static void operationHttpGet() {
// START SNIPPET: operationHttpGet
// Create a client to talk to the HeathIntersections server
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir-dev.healthintersections.com.au/open");
client.registerInterceptor(new LoggingInterceptor(true));
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateType("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateType("2015-03-01"));
// Invoke $everything on "Patient/1"
Parameters outParams = client
.operation()
.onInstance(new IdType("Patient", "1"))
.named("$everything")
.withParameters(inParams)
.useHttpGet() // Use HTTP GET instead of POST
.execute();
// END SNIPPET: operationHttpGet
}
@SuppressWarnings("unused")
private static void operation() {
// START SNIPPET: operation
// Create a client to talk to the HeathIntersections server
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir-dev.healthintersections.com.au/open");
client.registerInterceptor(new LoggingInterceptor(true));
// Create the input parameters to pass to the server
Parameters inParams = new Parameters();
inParams.addParameter().setName("start").setValue(new DateType("2001-01-01"));
inParams.addParameter().setName("end").setValue(new DateType("2015-03-01"));
// Invoke $everything on "Patient/1"
Parameters outParams = client
.operation()
.onInstance(new IdType("Patient", "1"))
.named("$everything")
.withParameters(inParams)
.execute();
/*
* Note that the $everything operation returns a Bundle instead
* of a Parameters resource. The client operation methods return a
* Parameters instance however, so HAPI creates a Parameters object
* with a single parameter containing the value.
*/
Bundle responseBundle = (Bundle) outParams.getParameter().get(0).getResource();
// Print the response bundle
System.out.println(ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(responseBundle));
// END SNIPPET: operation
}
@SuppressWarnings("unused")
private static void operationNoIn() {
// START SNIPPET: operationNoIn
// Create a client to talk to the HeathIntersections server
FhirContext ctx = FhirContext.forDstu2();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir-dev.healthintersections.com.au/open");
client.registerInterceptor(new LoggingInterceptor(true));
// Invoke $everything on "Patient/1"
Parameters outParams = client
.operation()
.onInstance(new IdType("Patient", "1"))
.named("$everything")
.withNoParameters(Parameters.class) // No input parameters
.execute();
// END SNIPPET: operationNoIn
}
}

View File

@ -0,0 +1,61 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.SearchParameter;
public class GenomicsUploader {
public static void main(String[] theArgs) {
FhirContext ctx = FhirContext.forR4();
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseR4");
client.registerInterceptor(new LoggingInterceptor(false));
SearchParameter dnaSequenceVariantName = new SearchParameter();
dnaSequenceVariantName.setId("SearchParameter/dnaSequenceVariantName");
dnaSequenceVariantName.setStatus(Enumerations.PublicationStatus.ACTIVE);
dnaSequenceVariantName.addBase("Observation");
dnaSequenceVariantName.setCode("dnaSequenceVariantName");
dnaSequenceVariantName.setType(Enumerations.SearchParamType.TOKEN);
dnaSequenceVariantName.setTitle("DNASequenceVariantName");
dnaSequenceVariantName.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsDNASequenceVariantName')");
dnaSequenceVariantName.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(dnaSequenceVariantName).execute();
SearchParameter dNAVariantId = new SearchParameter();
dNAVariantId.setId("SearchParameter/dNAVariantId");
dNAVariantId.setStatus(Enumerations.PublicationStatus.ACTIVE);
dNAVariantId.addBase("Observation");
dNAVariantId.setCode("dnaVariantId");
dNAVariantId.setType(Enumerations.SearchParamType.TOKEN);
dNAVariantId.setTitle("DNAVariantId");
dNAVariantId.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsDNAVariantId')");
dNAVariantId.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(dNAVariantId).execute();
SearchParameter gene = new SearchParameter();
gene.setId("SearchParameter/gene");
gene.setStatus(Enumerations.PublicationStatus.ACTIVE);
gene.addBase("Observation");
gene.setCode("gene");
gene.setType(Enumerations.SearchParamType.TOKEN);
gene.setTitle("Gene");
gene.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsGene')");
gene.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(gene).execute();
SearchParameter alleleName = new SearchParameter();
alleleName.setId("SearchParameter/alleleName");
alleleName.setStatus(Enumerations.PublicationStatus.ACTIVE);
alleleName.addBase("Observation");
alleleName.setCode("alleleName");
alleleName.setType(Enumerations.SearchParamType.TOKEN);
alleleName.setTitle("AlleleName");
alleleName.setExpression("Observation.extension('http://hl7.org/fhir/StructureDefinition/observation-geneticsAlleleName')");
alleleName.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
client.update().resource(alleleName).execute();
}
}

View File

@ -0,0 +1,50 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.ProxyAuthenticationStrategy;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Patient;
public class HttpProxy {
public static void main(String[] args) {
/*
* This is out ot date - Just keeping
* it in case it's helpful...
*/
final String authUser = "username";
final String authPassword = "password";
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope("10.10.10.10", 8080),
new UsernamePasswordCredentials(authUser, authPassword));
HttpHost myProxy = new HttpHost("10.10.10.10", 8080);
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
clientBuilder
.setProxy(myProxy)
.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy())
.setDefaultCredentialsProvider(credsProvider)
.disableCookieManagement();
CloseableHttpClient httpClient = clientBuilder.build();
FhirContext ctx = FhirContext.forDstu2();
String serverBase = "http://spark.furore.com/fhir/";
ctx.getRestfulClientFactory().setHttpClient(httpClient);
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
IdType id = new IdType("Patient", "123");
Patient patient = client.read().resource(Patient.class).withId(id).execute();
}
}

View File

@ -0,0 +1,71 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import java.util.List;
//START SNIPPET: provider
/**
* All RESTful clients must be an interface which extends IBasicClient
*/
public interface IRestfulClient extends IBasicClient {
/**
* The "@Read" annotation indicates that this method supports the
* read operation. Read operations should return a single resource
* instance.
*
* @param theId
* The read operation takes one parameter, which must be of type
* IdType and must be annotated with the "@Read.IdParam" annotation.
* @return
* Returns a resource matching this identifier, or null if none exists.
*/
@Read()
Patient getResourceById(@IdParam IIdType theId);
/**
* The "@Read" annotation indicates that this method supports the
* read operation. Read operations should return a single resource
* instance.
*
* @param theId
* The read operation takes one parameter, which must be of type
* IdType and must be annotated with the "@Read.IdParam" annotation.
* @return
* Returns a resource matching this identifier, or null if none exists.
*/
@Read()
Organization getOrganizationById(@IdParam IIdType theId);
/**
* The "@Search" annotation indicates that this method supports the
* search operation. You may have many different methods annotated with
* this annotation, to support many different search criteria. This
* example searches by family name.
*
* @param theFamilyName
* This operation takes one parameter which is the search criteria. It is
* annotated with the "@Required" annotation. This annotation takes one argument,
* a string containing the name of the search criteria. The datatype here
* is StringDt, but there are other possible parameter types depending on the
* specific search criteria.
* @return
* This method returns a list of Patients. This list may contain multiple
* matching resources, or it may also be empty.
*/
@Search()
List<Patient> getPatient(@RequiredParam(name = Patient.SP_FAMILY) StringType theFamilyName);
}
//END SNIPPET: provider

View File

@ -0,0 +1,56 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.Search;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.hapi.rest.server.R4BundleFactory;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import java.util.ArrayList;
import java.util.List;
public class IncludesExamples {
public static void main(String[] args) {
testSearchForPatients();
}
private static void testSearchForPatients() {
List<IBaseResource> resources = new IncludesExamples().searchForPatients();
// Create a bundle with both
FhirContext ctx = FhirContext.forDstu2();
R4BundleFactory bf = new R4BundleFactory(ctx);
bf.initializeBundleFromResourceList(null, resources, "http://example.com/base", "http://example.com/base/Patient", 1, BundleTypeEnum.SEARCHSET);
IBaseResource b = bf.getResourceBundle();
// Encode the bundle
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b);
System.out.println(encoded);
}
// START SNIPPET: addIncludes
@Search
private List<IBaseResource> searchForPatients() {
// Create an organization
Organization org = new Organization();
org.setId("Organization/65546");
org.setName("Test Organization");
// Create a patient
Patient patient = new Patient();
patient.setId("Patient/1333");
patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
patient.getManagingOrganization().setResource(org);
// Here we return only the patient object, which has links to other resources
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
retVal.add(patient);
return retVal;
}
// END SNIPPET: addIncludes
}

View File

@ -0,0 +1,28 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.client.JaxRsRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IGenericClient;
@SuppressWarnings(value= {"serial"})
public class JaxRsClient {
public static void main(String[] args) {
//START SNIPPET: createClient
// Create a client
FhirContext ctx = FhirContext.forDstu2();
// Create an instance of the JAX RS client factory and
// set it on the context
JaxRsRestfulClientFactory clientFactory = new JaxRsRestfulClientFactory(ctx);
ctx.setRestfulClientFactory(clientFactory);
// This client uses JAX-RS!
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
//END SNIPPET: createClient
}
}

View File

@ -0,0 +1,40 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.concurrent.ConcurrentHashMap;
/**
* Conformance Rest Service
*
* @author Peter Van Houte
*/
// START SNIPPET: jax-rs-conformance
@Path("")
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class JaxRsConformanceProvider extends AbstractJaxRsConformanceProvider {
@EJB
private JaxRsPatientRestProvider provider;
public JaxRsConformanceProvider() {
super("My Server Description", "My Server Name", "My Server Version");
}
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> map = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
map.put(JaxRsConformanceProvider.class, this);
map.put(JaxRsPatientRestProvider.class, provider);
return map;
}
}
// END SNIPPET: jax-rs-conformance

View File

@ -0,0 +1,72 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
* A demo JaxRs Patient Rest Provider
*/
@Local
@Stateless
// START SNIPPET: jax-rs-provider-construction
@Path("/Patient")
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Patient> {
public JaxRsPatientRestProvider() {
super(JaxRsPatientRestProvider.class);
}
// END SNIPPET: jax-rs-provider-construction
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Create
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional) {
// create the patient ...
return new MethodOutcome(new IdType(1L)).setCreated(true);
}
// START SNIPPET: jax-rs-provider-operation
@GET
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@Operation(name = "someCustomOperation", idempotent = true, returnParameters = {
@OperationParam(name = "return", type = StringDt.class) })
public Parameters someCustomOperation(@IdParam IdType myId, @OperationParam(name = "dummy") StringDt dummyInput) {
Parameters parameters = new Parameters();
parameters.addParameter().setName("return").setValue(new StringType("My Dummy Result"));
return parameters;
}
// END SNIPPET: jax-rs-provider-operation
@POST
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
}

View File

@ -0,0 +1,50 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Patient;
public class Multitenancy {
//START SNIPPET: enableUrlBaseTenantIdentificationStrategy
public class MyServer extends RestfulServer {
@Override
protected void initialize() {
setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
// ... do other initialization ...
}
}
//END SNIPPET: enableUrlBaseTenantIdentificationStrategy
//START SNIPPET: resourceProvider
public class MyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Read
public Patient read(RequestDetails theRequestDetails, @IdParam IdType theId) {
String tenantId = theRequestDetails.getTenantId();
String resourceId = theId.getIdPart();
// Use these two values to fetch the patient
return new Patient();
}
}
//END SNIPPET: resourceProvider
}

View File

@ -0,0 +1,99 @@
package ca.uhn.hapi.fhir.docs;
//START SNIPPET: patientDef
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import java.util.ArrayList;
import java.util.List;
/**
* Definition class for adding extensions to the built-in
* Patient resource type.
*
* Note the "profile" attribute below, which indicates the URL/ID of the
* profile implemented by this resource. You are not required to supply this,
* but if you do it will be automatically populated in the resource meta
* tag if the resource is returned by a server.
*/
@ResourceDef(name="Patient", profile="http://example.com/StructureDefinition/mypatient")
public class MyPatient extends Patient {
private static final long serialVersionUID = 1L;
/**
* Each extension is defined in a field. Any valid HAPI Data Type
* can be used for the field type. Note that the [name=""] attribute
* in the @Child annotation needs to match the name for the bean accessor
* and mutator methods.
*/
@Child(name="petName")
@Extension(url="http://example.com/dontuse#petname", definedLocally=false, isModifier=false)
@Description(shortDefinition="The name of the patient's favourite pet")
private StringType myPetName;
/**
* The second example extension uses a List type to provide
* repeatable values. Note that a [max=] value has been placed in
* the @Child annotation.
*
* Note also that this extension is a modifier extension
*/
@Child(name="importantDates", max=Child.MAX_UNLIMITED)
@Extension(url="http://example.com/dontuse#importantDates", definedLocally=false, isModifier=true)
@Description(shortDefinition="Some dates of note for this patient")
private List<DateTimeType> myImportantDates;
/**
* It is important to override the isEmpty() method, adding a check for any
* newly added fields.
*/
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myPetName, myImportantDates);
}
/********
* Accessors and mutators follow
*
* IMPORTANT:
* Each extension is required to have an getter/accessor and a stter/mutator.
* You are highly recommended to create getters which create instances if they
* do not already exist, since this is how the rest of the HAPI FHIR API works.
********/
/** Getter for important dates */
public List<DateTimeType> getImportantDates() {
if (myImportantDates==null) {
myImportantDates = new ArrayList<DateTimeType>();
}
return myImportantDates;
}
/** Getter for pet name */
public StringType getPetName() {
if (myPetName == null) {
myPetName = new StringType();
}
return myPetName;
}
/** Setter for important dates */
public void setImportantDates(List<DateTimeType> theImportantDates) {
myImportantDates = theImportantDates;
}
/** Setter for pet name */
public void setPetName(StringType thePetName) {
myPetName = thePetName;
}
}
//END SNIPPET: patientDef

View File

@ -0,0 +1,86 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class MyPatientUse {
@ResourceDef()
public static class MyPatient extends Patient {
@Child(name="petName")
@Extension(url="http://example.com/dontuse#petname", definedLocally=false, isModifier=false)
@Description(shortDefinition="The name of the patient's favourite pet")
private StringType myPetName;
public StringType getPetName() {
if(myPetName==null) {
myPetName = new StringType();
}
return myPetName;
}
public void setPetName(StringType thePetName) {
myPetName = thePetName;
}
public List<DateTimeType> getImportantDates() {
if (myImportantDates==null) {
myImportantDates= new ArrayList<>();
}
return myImportantDates;
}
public void setImportantDates(List<DateTimeType> theImportantDates) {
myImportantDates = theImportantDates;
}
@Child(name="importantDates", max=Child.MAX_UNLIMITED)
@Extension(url="http://example.com/dontuse#importantDates", definedLocally=false, isModifier=true)
@Description(shortDefinition="Some dates of note for the patient")
private List<DateTimeType> myImportantDates;
}
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
//START SNIPPET: patientUse
MyPatient patient = new MyPatient();
patient.setPetName(new StringType("Fido"));
patient.getImportantDates().add(new DateTimeType("2010-01-02"));
patient.getImportantDates().add(new DateTimeType("2014-01-26T11:11:11"));
patient.addName().setFamily("Smith").addGiven("John").addGiven("Quincy").addSuffix("Jr");
IParser p = FhirContext.forDstu2().newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
System.out.println(messageString);
//END SNIPPET: patientUse
//START SNIPPET: patientParse
IParser parser = FhirContext.forDstu2().newXmlParser();
MyPatient newPatient = parser.parseResource(MyPatient.class, messageString);
//END SNIPPET: patientParse
{
FhirContext ctx2 = FhirContext.forDstu2();
RuntimeResourceDefinition def = ctx2.getResourceDefinition(patient);
System.out.println(ctx2.newXmlParser().setPrettyPrint(true).encodeResourceToString(def.toProfile()));
}
}
}

View File

@ -0,0 +1,39 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.parser.DataFormatException;
import org.hl7.fhir.r4.model.Patient;
@SuppressWarnings("unused")
public class Narrative {
public static void main(String[] args) throws DataFormatException {
//START SNIPPET: example1
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:foo").setValue("7000135");
patient.addName().setFamily("Smith").addGiven("John").addGiven("Edward");
patient.addAddress().addLine("742 Evergreen Terrace").setCity("Springfield").setState("ZZ");
FhirContext ctx = FhirContext.forDstu2();
// Use the narrative generator
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
// Encode the output, including the narrative
String output = ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(output);
//END SNIPPET: example1
}
public void simple() {
//START SNIPPET: simple
Patient pat = new Patient();
pat.getText().setStatus(org.hl7.fhir.r4.model.Narrative.NarrativeStatus.GENERATED);
pat.getText().setDivAsString("<div>This is the narrative text<br/>this is line 2</div>");
//END SNIPPET: simple
}
}

View File

@ -0,0 +1,21 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.narrative.CustomThymeleafNarrativeGenerator;
@SuppressWarnings("unused")
public class NarrativeGenerator {
public void testGenerator() {
//START SNIPPET: gen
FhirContext ctx = FhirContext.forDstu2();
String propFile = "classpath:/com/foo/customnarrative.properties";
CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator(propFile);
ctx.setNarrativeGenerator(gen);
//END SNIPPET: gen
}
}

View File

@ -0,0 +1,4 @@
package ca.uhn.hapi.fhir.docs;
public class NewInterceptors {
}

View File

@ -0,0 +1,85 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.InstantType;
import org.hl7.fhir.r4.model.Patient;
import javax.annotation.Nonnull;
import java.util.List;
@SuppressWarnings("null")
// START SNIPPET: provider
public class PagingPatientProvider implements IResourceProvider {
/**
* Search for Patient resources matching a given family name
*/
@Search
public IBundleProvider search(@RequiredParam(name = Patient.SP_FAMILY) StringParam theFamily) {
final InstantType searchTime = InstantType.withCurrentTime();
/**
* First, we'll search the database for a set of database row IDs that
* match the given search criteria. That way we can keep just the row IDs
* around, and load the actual resources on demand later as the client
* pages through them.
*/
final List<Long> matchingResourceIds = null; // <-- implement this
/**
* Return a bundle provider which can page through the IDs and return the
* resources that go with them.
*/
return new IBundleProvider() {
@Override
public Integer size() {
return matchingResourceIds.size();
}
@Nonnull
@Override
public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
int end = Math.max(theToIndex, matchingResourceIds.size() - 1);
List<Long> idsToReturn = matchingResourceIds.subList(theFromIndex, end);
return loadResourcesByIds(idsToReturn);
}
@Override
public InstantType getPublished() {
return searchTime;
}
@Override
public Integer preferredPageSize() {
// Typically this method just returns null
return null;
}
@Override
public String getUuid() {
return null;
}
};
}
/**
* Load a list of patient resources given their IDs
*/
private List<IBaseResource> loadResourcesByIds(List<Long> theIdsToReturn) {
// .. implement this search against the database ..
return null;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
}
// END SNIPPET: provider

View File

@ -0,0 +1,33 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
@SuppressWarnings({ "serial" })
//START SNIPPET: provider
public class PagingServer extends RestfulServer {
public PagingServer() {
/*
* Set the resource providers as always. Here we are using the paging
* provider from the example below, but it is not strictly neccesary
* to use a paging resource provider as well. If a normal resource
* provider is used (one which returns List<?> instead of IBundleProvider)
* then the loaded resources will be stored by the IPagingProvider.
*/
setResourceProviders(new PagingPatientProvider());
/*
* Set a paging provider. Here a simple in-memory implementation
* is used, but you may create your own.
*/
FifoMemoryPagingProvider pp = new FifoMemoryPagingProvider(10);
pp.setDefaultPageSize(10);
pp.setMaximumPageSize(100);
setPagingProvider(pp);
}
}
//END SNIPPET: provider

View File

@ -0,0 +1,42 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import java.io.IOException;
public class Parser {
public static void main(String[] args) throws DataFormatException, IOException {
{
//START SNIPPET: disableStripVersions
FhirContext ctx = FhirContext.forDstu2();
IParser parser = ctx.newJsonParser();
// Disable the automatic stripping of versions from references on the parser
parser.setStripVersionsFromReferences(false);
//END SNIPPET: disableStripVersions
//START SNIPPET: disableStripVersionsCtx
ctx.getParserOptions().setStripVersionsFromReferences(false);
//END SNIPPET: disableStripVersionsCtx
}
{
//START SNIPPET: disableStripVersionsField
FhirContext ctx = FhirContext.forDstu2();
IParser parser = ctx.newJsonParser();
// Preserve versions only on these two fields (for the given parser)
parser.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference", "Patient.managingOrganization");
// You can also apply this setting to the context so that it will
// flow to all parsers
ctx.getParserOptions().setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference", "Patient.managingOrganization");
//END SNIPPET: disableStripVersionsField
}
}
}

View File

@ -0,0 +1,31 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Patch;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.api.PatchTypeEnum;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.OperationOutcome;
public class PatchExamples {
//START SNIPPET: patch
@Patch
public OperationOutcome patientPatch(@IdParam IdType theId, PatchTypeEnum thePatchType, @ResourceParam String theBody) {
if (thePatchType == PatchTypeEnum.JSON_PATCH) {
// do something
}
if (thePatchType == PatchTypeEnum.XML_PATCH) {
// do something
}
OperationOutcome retVal = new OperationOutcome();
retVal.getText().setDivAsString("<div>OK</div>");
return retVal;
}
//END SNIPPET: patch
}

View File

@ -0,0 +1,53 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import java.io.IOException;
import java.util.List;
public class QuickUsage {
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
Patient patient = new Patient();
patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:fake:mrns").setValue("7000135");
patient.addIdentifier().setUse(IdentifierUseEnum.SECONDARY).setSystem("urn:fake:otherids").setValue("3287486");
patient.addName().addFamily("Smith").addGiven("John").addGiven("Q").addSuffix("Junior");
patient.setGender(AdministrativeGenderEnum.MALE);
FhirContext ctx = FhirContext.forDstu2();
String xmlEncoded = ctx.newXmlParser().encodeResourceToString(patient);
String jsonEncoded = ctx.newJsonParser().encodeResourceToString(patient);
MyClientInterface client = ctx.newRestfulClient(MyClientInterface.class, "http://foo/fhir");
IdentifierDt searchParam = new IdentifierDt("urn:someidentifiers", "7000135");
List<Patient> clients = client.findPatientsByIdentifier(searchParam);
}
public interface MyClientInterface extends IRestfulClient
{
/** A FHIR search */
@Search
public List<Patient> findPatientsByIdentifier(@RequiredParam(name = "identifier") IdentifierDt theIdentifier);
/** A FHIR create */
@Create
public MethodOutcome createPatient(@ResourceParam Patient thePatient);
}
}

View File

@ -0,0 +1,29 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//START SNIPPET: interceptor
public class RequestCounterInterceptor extends InterceptorAdapter
{
private int myRequestCount;
public int getRequestCount() {
return myRequestCount;
}
/**
* Override the incomingRequestPreProcessed method, which is called
* for each incoming request before any processing is done
*/
@Override
public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) {
myRequestCount++;
return true;
}
}
//END SNIPPET: interceptor

View File

@ -0,0 +1,35 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//START SNIPPET: interceptor
public class RequestExceptionInterceptor extends InterceptorAdapter
{
@Override
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest,
HttpServletResponse theServletResponse) throws ServletException, IOException {
// HAPI's server exceptions know what the appropriate HTTP status code is
theServletResponse.setStatus(theException.getStatusCode());
// Provide a response ourself
theServletResponse.setContentType("text/plain");
theServletResponse.getWriter().append("Failed to process!");
theServletResponse.getWriter().close();
// Since we handled this response in the interceptor, we must return false
// to stop processing immediately
return false;
}
}
//END SNIPPET: interceptor

View File

@ -0,0 +1,36 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
public class ResourceRefs {
private static FhirContext ourCtx = FhirContext.forDstu2();
public static void main(String[] args) {
manualContained();
}
public static void manualContained() {
// START SNIPPET: manualContained
// Create an organization, and give it a local ID
Organization org = new Organization();
org.setId("#localOrganization");
org.getNameElement().setValue("Contained Test Organization");
// Create a patient
Patient patient = new Patient();
patient.setId("Patient/1333");
patient.addIdentifier().setSystem("urn:mrns").setValue("253345");
// Set the reference, and manually add the contained resource
patient.getManagingOrganization().setReference("#localOrganization");
patient.getContained().add(org);
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(encoded);
// END SNIPPET: manualContained
}
}

View File

@ -0,0 +1,87 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.server.IResourceProvider;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Patient;
import java.util.Collections;
import java.util.List;
//START SNIPPET: provider
/**
* All resource providers must implement IResourceProvider
*/
public class RestfulObservationResourceProvider implements IResourceProvider {
/**
* The getResourceType method comes from IResourceProvider, and must
* be overridden to indicate what type of resource this provider
* supplies.
*/
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
/**
* The "@Read" annotation indicates that this method supports the
* read operation. It takes one argument, the Resource type being returned.
*
* @param theId
* The read operation takes one parameter, which must be of type
* IdType and must be annotated with the "@Read.IdParam" annotation.
* @return
* Returns a resource matching this identifier, or null if none exists.
*/
@Read()
public Patient getResourceById(@IdParam IdType theId) {
Patient patient = new Patient();
patient.addIdentifier();
patient.getIdentifier().get(0).setSystem("urn:hapitest:mrns");
patient.getIdentifier().get(0).setValue("00002");
patient.addName().setFamily("Test");
patient.getName().get(0).addGiven("PatientOne");
patient.setGender(Enumerations.AdministrativeGender.FEMALE);
return patient;
}
/**
* The "@Search" annotation indicates that this method supports the
* search operation. You may have many different methods annotated with
* this annotation, to support many different search criteria. This
* example searches by family name.
*
* @param theFamilyName
* This operation takes one parameter which is the search criteria. It is
* annotated with the "@Required" annotation. This annotation takes one argument,
* a string containing the name of the search criteria. The datatype here
* is StringDt, but there are other possible parameter types depending on the
* specific search criteria.
* @return
* This method returns a list of Patients. This list may contain multiple
* matching resources, or it may also be empty.
*/
@Search()
public List<Patient> getPatient(@RequiredParam(name = Patient.SP_FAMILY) StringDt theFamilyName) {
Patient patient = new Patient();
patient.addIdentifier();
patient.getIdentifier().get(0).setUse(Identifier.IdentifierUse.OFFICIAL);
patient.getIdentifier().get(0).setSystem("urn:hapitest:mrns");
patient.getIdentifier().get(0).setValue("00001");
patient.addName();
patient.getName().get(0).setFamily("Test");
patient.getName().get(0).addGiven("PatientOne");
patient.setGender(Enumerations.AdministrativeGender.MALE);
return Collections.singletonList(patient);
}
}
//END SNIPPET: provider

View File

@ -0,0 +1,91 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import org.hl7.fhir.r4.model.IdType;
import java.util.Collections;
import java.util.List;
//START SNIPPET: provider
/**
* All resource providers must implement IResourceProvider
*/
public class RestfulPatientResourceProvider implements IResourceProvider {
/**
* The getResourceType method comes from IResourceProvider, and must
* be overridden to indicate what type of resource this provider
* supplies.
*/
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
/**
* The "@Read" annotation indicates that this method supports the
* read operation. Read operations should return a single resource
* instance.
*
* @param theId
* The read operation takes one parameter, which must be of type
* IdType and must be annotated with the "@Read.IdParam" annotation.
* @return
* Returns a resource matching this identifier, or null if none exists.
*/
@Read()
public Patient getResourceById(@IdParam IdType theId) {
Patient patient = new Patient();
patient.addIdentifier();
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00002");
patient.addName().addFamily("Test");
patient.getName().get(0).addGiven("PatientOne");
patient.setGender(AdministrativeGenderEnum.FEMALE);
return patient;
}
/**
* The "@Search" annotation indicates that this method supports the
* search operation. You may have many different methods annotated with
* this annotation, to support many different search criteria. This
* example searches by family name.
*
* @param theFamilyName
* This operation takes one parameter which is the search criteria. It is
* annotated with the "@Required" annotation. This annotation takes one argument,
* a string containing the name of the search criteria. The datatype here
* is StringParam, but there are other possible parameter types depending on the
* specific search criteria.
* @return
* This method returns a list of Patients. This list may contain multiple
* matching resources, or it may also be empty.
*/
@Search()
public List<Patient> getPatient(@RequiredParam(name = Patient.SP_FAMILY) StringParam theFamilyName) {
Patient patient = new Patient();
patient.addIdentifier();
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00001");
patient.addName();
patient.getName().get(0).addFamily(theFamilyName.getValue());
patient.getName().get(0).addGiven("PatientOne");
patient.setGender(AdministrativeGenderEnum.MALE);
return Collections.singletonList(patient);
}
}
//END SNIPPET: provider

View File

@ -0,0 +1,65 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
import org.apache.commons.codec.binary.Base64;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SecurityInterceptors {
// START SNIPPET: basicAuthInterceptor
public class BasicSecurityInterceptor extends InterceptorAdapter
{
/**
* This interceptor implements HTTP Basic Auth, which specifies that
* a username and password are provided in a header called Authorization.
*/
@Override
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
String authHeader = theRequest.getHeader("Authorization");
// The format of the header must be:
// Authorization: Basic [base64 of username:password]
if (authHeader == null || authHeader.startsWith("Basic ") == false) {
throw new AuthenticationException("Missing or invalid Authorization header");
}
String base64 = authHeader.substring("Basic ".length());
String base64decoded = new String(Base64.decodeBase64(base64));
String[] parts = base64decoded.split("\\:");
String username = parts[0];
String password = parts[1];
/*
* Here we test for a hardcoded username & password. This is
* not typically how you would implement this in a production
* system of course..
*/
if (!username.equals("someuser") || !password.equals("thepassword")) {
throw new AuthenticationException("Invalid username or password");
}
// Return true to allow the request to proceed
return true;
}
}
//END SNIPPET: basicAuthInterceptor
public void basicAuthInterceptorRealm() {
//START SNIPPET: basicAuthInterceptorRealm
AuthenticationException ex = new AuthenticationException();
ex.addAuthenticateHeaderForRealm("myRealm");
throw ex;
//END SNIPPET: basicAuthInterceptorRealm
}
}

View File

@ -0,0 +1,29 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.RestfulServer;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
@SuppressWarnings("serial")
public class ServerETagExamples {
// START SNIPPET: disablingETags
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class RestfulServerWithLogging extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// ETag support is enabled by default
setETagSupport(ETagSupportEnum.ENABLED);
}
}
// END SNIPPET: disablingETags
}

View File

@ -0,0 +1,33 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Patient;
public abstract class ServerExceptionsExample implements IResourceProvider {
private boolean databaseIsDown;
//START SNIPPET: returnOO
@Read
public Patient read(@IdParam IdType theId) {
if (databaseIsDown) {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverityEnum.FATAL).setDetails("Database is down");
throw new InternalErrorException("Database is down", oo);
}
Patient patient = new Patient(); // populate this
return patient;
}
//END SNIPPET: returnOO
}

View File

@ -0,0 +1,80 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
import java.io.IOException;
import java.util.List;
public class ServerInterceptors {
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {
// START SNIPPET: resourceExtension
// Create an example patient
Patient patient = new Patient();
patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:example").setValue("7000135");
// Create an extension
ExtensionDt ext = new ExtensionDt();
ext.setModifier(false);
ext.setUrl("http://example.com/extensions#someext");
ext.setValue(new DateTimeDt("2011-01-02T11:13:15"));
// Add the extension to the resource
patient.addUndeclaredExtension(ext);
//END SNIPPET: resourceExtension
//START SNIPPET: resourceStringExtension
HumanNameDt name = patient.addName();
name.addFamily().setValue("Shmoe");
StringDt given = name.addGiven();
given.setValue("Joe");
ExtensionDt ext2 = new ExtensionDt(false, "http://examples.com#moreext", new StringDt("Hello"));
given.addUndeclaredExtension(ext2);
//END SNIPPET: resourceStringExtension
String output = FhirContext.forDstu2().newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
System.out.println(output);
//START SNIPPET: parseExtension
// Get all extensions (modifier or not) for a given URL
List<ExtensionDt> resourceExts = patient.getUndeclaredExtensionsByUrl("http://fooextensions.com#exts");
// Get all non-modifier extensions regardless of URL
List<ExtensionDt> nonModExts = patient.getUndeclaredExtensions();
//Get all non-modifier extensions regardless of URL
List<ExtensionDt> modExts = patient.getUndeclaredModifierExtensions();
//END SNIPPET: parseExtension
}
public void foo() {
//START SNIPPET: subExtension
Patient patient = new Patient();
ExtensionDt parent = new ExtensionDt(false, "http://example.com#parent");
patient.addUndeclaredExtension(parent);
ExtensionDt child1 = new ExtensionDt(false, "http://example.com#childOne", new StringDt("value1"));
parent.addUndeclaredExtension(child1);
ExtensionDt child2 = new ExtensionDt(false, "http://example.com#childTwo", new StringDt("value1"));
parent.addUndeclaredExtension(child2);
//END SNIPPET: subExtension
}
}

View File

@ -0,0 +1,43 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.rest.annotation.Search;
import org.hl7.fhir.r4.model.InstantType;
import org.hl7.fhir.r4.model.Patient;
import java.util.ArrayList;
import java.util.List;
public class ServerMetadataExamples {
// START SNIPPET: serverMethod
@Search
public List<Patient> getAllPatients() {
ArrayList<Patient> retVal = new ArrayList<Patient>();
// Create a patient to return
Patient patient = new Patient();
retVal.add(patient);
patient.setId("Patient/123");
patient.addName().setFamily("Smith").addGiven("John");
// Add tags to the resource
patient.getMeta().addTag()
.setSystem("http://example.com/tags")
.setCode("tag1")
.setDisplay("Some tag");
patient.getMeta().addTag()
.setSystem("http://example.com/tags")
.setCode("tag2")
.setDisplay("Another tag");
// Set the lastUpdate date
patient.getMeta().setLastUpdatedElement(new InstantType("2014-07-12T11:22:27Z"));
return retVal;
}
// END SNIPPET: serverMethod
}

View File

@ -0,0 +1,106 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.param.*;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.IdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
public class ServerOperations {
private static final Logger ourLog = LoggerFactory.getLogger(ServerOperations.class);
//START SNIPPET: manualInputAndOutput
@Operation(name="$manualInputAndOutput", manualResponse=true, manualRequest=true)
public void manualInputAndOutput(HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws IOException {
String contentType = theServletRequest.getContentType();
byte[] bytes = IOUtils.toByteArray(theServletRequest.getInputStream());
ourLog.info("Received call with content type {} and {} bytes", contentType, bytes.length);
theServletResponse.setContentType(contentType);
theServletResponse.getOutputStream().write(bytes);
theServletResponse.getOutputStream().close();
}
//END SNIPPET: manualInputAndOutput
//START SNIPPET: searchParamBasic
@Operation(name="$find-matches", idempotent=true)
public Parameters findMatchesBasic(
@OperationParam(name="date") DateParam theDate,
@OperationParam(name="code") TokenParam theCode) {
Parameters retVal = new Parameters();
// Populate bundle with matching resources
return retVal;
}
//END SNIPPET: searchParamBasic
//START SNIPPET: searchParamAdvanced
@Operation(name="$find-matches", idempotent=true)
public Parameters findMatchesAdvanced(
@OperationParam(name="dateRange") DateRangeParam theDate,
@OperationParam(name="name") List<StringParam> theName,
@OperationParam(name="code") TokenAndListParam theEnd) {
Parameters retVal = new Parameters();
// Populate bundle with matching resources
return retVal;
}
//END SNIPPET: searchParamAdvanced
//START SNIPPET: patientTypeOperation
@Operation(name="$everything", idempotent=true)
public Bundle patientTypeOperation(
@OperationParam(name="start") DateDt theStart,
@OperationParam(name="end") DateDt theEnd) {
Bundle retVal = new Bundle();
// Populate bundle with matching resources
return retVal;
}
//END SNIPPET: patientTypeOperation
//START SNIPPET: patientInstanceOperation
@Operation(name="$everything", idempotent=true)
public Bundle patientInstanceOperation(
@IdParam IdType thePatientId,
@OperationParam(name="start") DateDt theStart,
@OperationParam(name="end") DateDt theEnd) {
Bundle retVal = new Bundle();
// Populate bundle with matching resources
return retVal;
}
//END SNIPPET: patientInstanceOperation
//START SNIPPET: serverOperation
@Operation(name="$closure")
public ConceptMap closureOperation(
@OperationParam(name="name") StringDt theStart,
@OperationParam(name="concept") List<Coding> theEnd,
@OperationParam(name="version") IdType theVersion) {
ConceptMap retVal = new ConceptMap();
// Populate bundle with matching resources
return retVal;
}
//END SNIPPET: serverOperation
}

View File

@ -0,0 +1,153 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.interceptor.*;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
import org.springframework.web.cors.CorsConfiguration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import java.util.Arrays;
@SuppressWarnings("serial")
public class ServletExamples {
// START SNIPPET: loggingInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class RestfulServerWithLogging extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Now register the logging interceptor
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
registerInterceptor(loggingInterceptor);
// The SLF4j logger "test.accesslog" will receive the logging events
loggingInterceptor.setLoggerName("test.accesslog");
// This is the format for each line. A number of substitution variables may
// be used here. See the JavaDoc for LoggingInterceptor for information on
// what is available.
loggingInterceptor.setMessageFormat("Source[${remoteAddr}] Operation[${operationType} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}]");
}
}
// END SNIPPET: loggingInterceptor
// START SNIPPET: validatingInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class ValidatingServerWithLogging extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Create an interceptor to validate incoming requests
RequestValidatingInterceptor requestInterceptor = new RequestValidatingInterceptor();
// Register a validator module (you could also use SchemaBaseValidator and/or SchematronBaseValidator)
requestInterceptor.addValidatorModule(new FhirInstanceValidator());
requestInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
requestInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
requestInterceptor.setResponseHeaderValue("Validation on ${line}: ${message} ${severity}");
requestInterceptor.setResponseHeaderValueNoIssues("No issues detected");
// Now register the validating interceptor
registerInterceptor(requestInterceptor);
// Create an interceptor to validate responses
// This is configured in the same way as above
ResponseValidatingInterceptor responseInterceptor = new ResponseValidatingInterceptor();
responseInterceptor.addValidatorModule(new FhirInstanceValidator());
responseInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
responseInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
responseInterceptor.setResponseHeaderValue("Validation on ${line}: ${message} ${severity}");
responseInterceptor.setResponseHeaderValueNoIssues("No issues detected");
registerInterceptor(responseInterceptor);
}
}
// END SNIPPET: validatingInterceptor
// START SNIPPET: exceptionInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class RestfulServerWithExceptionHandling extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Now register the interceptor
ExceptionHandlingInterceptor interceptor = new ExceptionHandlingInterceptor();
registerInterceptor(interceptor);
// Return the stack trace to the client for the following exception types
interceptor.setReturnStackTracesForExceptionTypes(InternalErrorException.class, NullPointerException.class);
}
}
// END SNIPPET: exceptionInterceptor
// START SNIPPET: responseHighlighterInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class RestfulServerWithResponseHighlighter extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Now register the interceptor
ResponseHighlighterInterceptor interceptor = new ResponseHighlighterInterceptor();
registerInterceptor(interceptor);
}
}
// END SNIPPET: responseHighlighterInterceptor
// START SNIPPET: corsInterceptor
@WebServlet(urlPatterns = { "/fhir/*" }, displayName = "FHIR Server")
public class RestfulServerWithCors extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ... define your resource providers here ...
// Define your CORS configuration. This is an example
// showing a typical setup. You should customize this
// to your specific needs
CorsConfiguration config = new CorsConfiguration();
config.addAllowedHeader("x-fhir-starter");
config.addAllowedHeader("Origin");
config.addAllowedHeader("Accept");
config.addAllowedHeader("X-Requested-With");
config.addAllowedHeader("Content-Type");
config.addAllowedOrigin("*");
config.addExposedHeader("Location");
config.addExposedHeader("Content-Location");
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
// Create the interceptor and register it
CorsInterceptor interceptor = new CorsInterceptor(config);
registerInterceptor(interceptor);
}
}
// END SNIPPET: corsInterceptor
}

View File

@ -0,0 +1,62 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Patient;
import java.util.ArrayList;
import java.util.List;
public class TagsExamples {
public static void main(String[] args) {
new TagsExamples().getResourceTags();
}
@SuppressWarnings("unused")
public void getResourceTags() {
// START SNIPPET: getResourceTags
IGenericClient client = FhirContext.forDstu2().newRestfulGenericClient("http://fhir.healthintersections.com.au/open");
Patient p = client.read(Patient.class, "1");
// Retrieve the list of tags from the resource metadata
List<Coding> tags = p.getMeta().getTag();
// tags may be empty if no tags were read in
if (tags.isEmpty()) {
System.out.println("No tags!");
} else {
// You may iterate over all the tags
for (Coding next : tags) {
System.out.println(next.getSystem() + " - " + next.getCode());
}
// You may also get a specific tag (by system and code)
Coding tag = p.getMeta().getTag("http://hl7.org/fhir/tag", "http://foo");
}
// END SNIPPET: getResourceTags
}
// START SNIPPET: serverMethod
@Search
public List<Patient> getAllPatients() {
ArrayList<Patient> retVal = new ArrayList<Patient>();
// Create a patient to return
Patient patient = new Patient();
patient.setId("Patient/123");
patient.addName().setFamily("Smith").addGiven("John");
// Add some tags to the patient
patient.getMeta().addTag("http://example.com/tags", "tag2", "Some tag");
patient.getMeta().addTag("http://example.com/tags", "tag1", "Another tag");
return retVal;
}
// END SNIPPET: serverMethod
}

View File

@ -0,0 +1,104 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.validation.PrePopulatedValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.io.File;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class ValidateDirectory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateDirectory.class);
public static void main(String[] args) throws Exception {
// Load all profiles in this directory
File profileDirectory = new File("/tmp/directory/with/profiles");
// Validate resources in this directory
File resourceDirectory = new File("/tmp/directory/with/resources/to/validate");
FhirContext ctx = FhirContext.forDstu3();
IParser xmlParser = ctx.newXmlParser();
IParser jsonParser = ctx.newJsonParser();
Map<String, StructureDefinition> structureDefinitions = new HashMap<String, StructureDefinition>();
Map<String, CodeSystem> codeSystems = new HashMap<String, CodeSystem>();
Map<String, ValueSet> valueSets = new HashMap<String, ValueSet>();
// Load all profile files
for (File nextFile : profileDirectory.listFiles()) {
IBaseResource parsedRes = null;
if (nextFile.getAbsolutePath().toLowerCase().endsWith(".xml")) {
parsedRes = xmlParser.parseResource(new FileReader(nextFile));
} else if (nextFile.getAbsolutePath().toLowerCase().endsWith(".json")) {
parsedRes = jsonParser.parseResource(new FileReader(nextFile));
} else {
ourLog.info("Ignoring file: {}", nextFile.getName());
}
if (parsedRes instanceof StructureDefinition) {
StructureDefinition res = (StructureDefinition) parsedRes;
if (isNotBlank(res.getUrl())) {
structureDefinitions.put(res.getUrl(), res);
}
} else if (parsedRes instanceof ValueSet) {
ValueSet res = (ValueSet) parsedRes;
if (isNotBlank(res.getUrl())) {
valueSets.put(res.getUrl(), res);
}
} else if (parsedRes instanceof CodeSystem) {
CodeSystem res = (CodeSystem) parsedRes;
if (isNotBlank(res.getUrl())) {
codeSystems.put(res.getUrl(), res);
}
}
}
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
ValidationSupportChain validationSupportChain = new ValidationSupportChain();
validationSupportChain.addValidationSupport(new DefaultProfileValidationSupport());
validationSupportChain.addValidationSupport(new PrePopulatedValidationSupport(structureDefinitions, valueSets, codeSystems));
instanceValidator.setValidationSupport(validationSupportChain);
FhirValidator val = ctx.newValidator();
val.registerValidatorModule(instanceValidator);
// Loop through the files in the validation directory and validate each one
for (File nextFile : resourceDirectory.listFiles()) {
if (nextFile.getAbsolutePath().toLowerCase().endsWith(".xml")) {
ourLog.info("Going to validate: {}", nextFile.getName());
} else if (nextFile.getAbsolutePath().toLowerCase().endsWith(".json")) {
ourLog.info("Going to validate: {}", nextFile.getName());
} else {
ourLog.info("Ignoring file: {}", nextFile.getName());
continue;
}
String input = IOUtils.toString(new FileReader(nextFile));
ValidationResult result = val.validateWithResult(input);
IBaseOperationOutcome oo = result.toOperationOutcome();
ourLog.info("Result:\n{}", xmlParser.setPrettyPrint(true).encodeResourceToString(oo));
}
}
}

View File

@ -0,0 +1,331 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.validation.*;
import ca.uhn.fhir.validation.schematron.SchematronBaseValidator;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r4.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import javax.servlet.ServletException;
import java.io.File;
import java.io.FileReader;
import java.util.List;
@SuppressWarnings("serial")
public class ValidatorExamples {
public void validationIntro() {
// START SNIPPET: validationIntro
FhirContext ctx = FhirContext.forDstu3();
// Ask the context for a validator
FhirValidator validator = ctx.newValidator();
// Create some modules and register them
IValidatorModule module1 = new SchemaBaseValidator(ctx);
validator.registerValidatorModule(module1);
IValidatorModule module2 = new SchematronBaseValidator(ctx);
validator.registerValidatorModule(module2);
// Pass a resource in to be validated. The resource can
// be an IBaseResource instance, or can be a raw String
// containing a serialized resource as text.
Patient resource = new Patient();
ValidationResult result = validator.validateWithResult(resource);
String resourceText = "<Patient.....>";
ValidationResult result2 = validator.validateWithResult(resourceText);
// The result object now contains the validation results
for (SingleValidationMessage next : result.getMessages()) {
System.out.println(next.getLocationString() + " " + next.getMessage());
}
// END SNIPPET: validationIntro
}
// START SNIPPET: serverValidation
public class MyRestfulServer extends RestfulServer {
@Override
protected void initialize() throws ServletException {
// ...Configure resource providers, etc...
// Create a context, set the error handler and instruct
// the server to use it
FhirContext ctx = FhirContext.forDstu3();
ctx.setParserErrorHandler(new StrictErrorHandler());
setFhirContext(ctx);
}
}
// END SNIPPET: serverValidation
@SuppressWarnings("unused")
public void enableValidation() {
// START SNIPPET: clientValidation
FhirContext ctx = FhirContext.forDstu3();
ctx.setParserErrorHandler(new StrictErrorHandler());
// This client will have strict parser validation enabled
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu3");
// END SNIPPET: clientValidation
}
public void parserValidation() {
// START SNIPPET: parserValidation
FhirContext ctx = FhirContext.forDstu3();
// Create a parser and configure it to use the strict error handler
IParser parser = ctx.newXmlParser();
parser.setParserErrorHandler(new StrictErrorHandler());
// This example resource is invalid, as Patient.active can not repeat
String input = "<Patient><active value=\"true\"/><active value=\"false\"/></Patient>";
// The following will throw a DataFormatException because of the StrictErrorHandler
parser.parseResource(Patient.class, input);
// END SNIPPET: parserValidation
}
public void validateResource() {
// START SNIPPET: basicValidation
// As always, you need a context
FhirContext ctx = FhirContext.forDstu3();
// Create and populate a new patient object
Patient p = new Patient();
p.addName().setFamily("Smith").addGiven("John").addGiven("Q");
p.addIdentifier().setSystem("urn:foo:identifiers").setValue("12345");
p.addTelecom().setSystem(ContactPoint.ContactPointSystem.PHONE).setValue("416 123-4567");
// Request a validator and apply it
FhirValidator val = ctx.newValidator();
// Create the Schema/Schematron modules and register them. Note that
// you might want to consider keeping these modules around as long-term
// objects: they parse and then store schemas, which can be an expensive
// operation.
IValidatorModule module1 = new SchemaBaseValidator(ctx);
IValidatorModule module2 = new SchematronBaseValidator(ctx);
val.registerValidatorModule(module1);
val.registerValidatorModule(module2);
ValidationResult result = val.validateWithResult(p);
if (result.isSuccessful()) {
System.out.println("Validation passed");
} else {
// We failed validation!
System.out.println("Validation failed");
}
// The result contains a list of "messages"
List<SingleValidationMessage> messages = result.getMessages();
for (SingleValidationMessage next : messages) {
System.out.println("Message:");
System.out.println(" * Location: " + next.getLocationString());
System.out.println(" * Severity: " + next.getSeverity());
System.out.println(" * Message : " + next.getMessage());
}
// You can also convert the results into an OperationOutcome resource
OperationOutcome oo = (OperationOutcome) result.toOperationOutcome();
String results = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(oo);
System.out.println(results);
// END SNIPPET: basicValidation
}
public static void main(String[] args) throws Exception {
instanceValidator();
}
private static void instanceValidator() throws Exception {
// START SNIPPET: instanceValidator
FhirContext ctx = FhirContext.forDstu3();
// Create a FhirInstanceValidator and register it to a validator
FhirValidator validator = ctx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
validator.registerValidatorModule(instanceValidator);
/*
* If you want, you can configure settings on the validator to adjust
* its behaviour during validation
*/
instanceValidator.setAnyExtensionsAllowed(true);
/*
* Let's create a resource to validate. This Observation has some fields
* populated, but it is missing Observation.status, which is mandatory.
*/
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://loinc.org").setCode("12345-6");
obs.setValue(new StringType("This is a value"));
// Validate
ValidationResult result = validator.validateWithResult(obs);
/*
* Note: You can also explicitly declare a profile to validate against
* using the block below.
*/
// ValidationResult result = validator.validateWithResult(obs, new ValidationOptions().addProfile("http://myprofile.com"));
// Do we have any errors or fatal errors?
System.out.println(result.isSuccessful()); // false
// Show the issues
for (SingleValidationMessage next : result.getMessages()) {
System.out.println(" Next issue " + next.getSeverity() + " - " + next.getLocationString() + " - " + next.getMessage());
}
// Prints:
// Next issue ERROR - /f:Observation - Element '/f:Observation.status': minimum required = 1, but only found 0
// Next issue WARNING - /f:Observation/f:code - Unable to validate code "12345-6" in code system "http://loinc.org"
// You can also convert the result into an operation outcome if you
// need to return one from a server
OperationOutcome oo = (OperationOutcome) result.toOperationOutcome();
// END SNIPPET: instanceValidator
}
private static void instanceValidatorCustom() throws Exception {
// START SNIPPET: instanceValidatorCustom
FhirContext ctx = FhirContext.forDstu3();
// Create a FhirInstanceValidator and register it to a validator
FhirValidator validator = ctx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
validator.registerValidatorModule(instanceValidator);
IValidationSupport valSupport = new IValidationSupport() {
@Override
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ValueSet.ConceptSetComponent theInclude) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
@Override
public ValueSet fetchValueSet(FhirContext theContext, String theSystem) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
// TODO: implement (or return null if your implementation does not support this function)
return false;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theProfileName) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
// TODO: implement (or return null if your implementation does not support this function)
return null;
}
};
/*
* ValidationSupportChain strings multiple instances of IValidationSupport together. The
* code below is useful because it means that when the validator wants to load a
* StructureDefinition or a ValueSet, it will first use DefaultProfileValidationSupport,
* which loads the default HL7 versions. Any StructureDefinitions which are not found in
* the built-in set are delegated to your custom implementation.
*/
ValidationSupportChain support = new ValidationSupportChain(new DefaultProfileValidationSupport(), valSupport);
instanceValidator.setValidationSupport(support);
// END SNIPPET: instanceValidatorCustom
}
@SuppressWarnings("unused")
private static void validateFiles() throws Exception {
// START SNIPPET: validateFiles
FhirContext ctx = FhirContext.forR4();
// Create a validator and configure it
FhirValidator validator = ctx.newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
// Get a list of files in a given directory
String[] fileList = new File("/home/some/dir").list(new WildcardFileFilter("*.txt"));
for (String nextFile : fileList) {
// For each file, load the contents into a string
String nextFileContents = IOUtils.toString(new FileReader(nextFile));
// Parse that string (this example assumes JSON encoding)
IBaseResource resource = ctx.newJsonParser().parseResource(nextFileContents);
// Apply the validation. This will throw an exception on the first
// validation failure
ValidationResult result = validator.validateWithResult(resource);
if (result.isSuccessful() == false) {
throw new Exception("We failed!");
}
}
// END SNIPPET: validateFiles
}
}

View File

@ -0,0 +1,48 @@
package ca.uhn.hapi.fhir.docs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
public class ValidatorExamplesDstu3 {
public void validateProfileDstu3() {
// START SNIPPET: validateFiles
FhirContext ctx = FhirContext.forDstu3();
FhirValidator validator = ctx.newValidator();
// Typically if you are doing profile validation, you want to disable
// the schema/schematron validation since the profile will specify
// all the same rules (and more)
validator.setValidateAgainstStandardSchema(false);
validator.setValidateAgainstStandardSchematron(false);
// FhirInstanceValidator is the validation module that handles
// profile validation. So, create an InstanceValidator module
// and register it to the validator.
FhirInstanceValidator instanceVal = new FhirInstanceValidator();
validator.registerValidatorModule(instanceVal);
// FhirInstanceValidator requires an instance of "IValidationSupport" in
// order to function. This module is used by the validator to actually obtain
// all of the resources it needs in order to perform validation. Specifically,
// the validator uses it to fetch StructureDefinitions, ValueSets, CodeSystems,
// etc, as well as to perform terminology validation.
//
// The implementation used here (ValidationSupportChain) is allows for
// multiple implementations to be used in a chain, where if a specific resource
// is needed the whole chain is tried and the first module which is actually
// able to answer is used. The first entry in the chain that we register is
// the DefaultProfileValidationSupport, which supplies the "built-in" FHIR
// StructureDefinitions and ValueSets
ValidationSupportChain validationSupportChain = new ValidationSupportChain();
validationSupportChain.addValidationSupport(new DefaultProfileValidationSupport());
instanceVal.setValidationSupport(validationSupportChain);
// END SNIPPET: validateFiles
}
}

View File

@ -0,0 +1,83 @@
package ca.uhn.hapi.fhir.docs.customtype;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;
import org.hl7.fhir.dstu3.model.BackboneElement;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
//START SNIPPET: resource
@ResourceDef(name = "Patient")
public class CustomCompositeExtension extends Patient {
private static final long serialVersionUID = 1L;
/**
* A custom extension
*/
@Child(name = "foo")
@Extension(url="http://acme.org/fooParent", definedLocally = false, isModifier = false)
protected FooParentExtension fooParentExtension;
public FooParentExtension getFooParentExtension() {
return fooParentExtension;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(fooParentExtension);
}
public void setFooParentExtension(FooParentExtension theFooParentExtension) {
fooParentExtension = theFooParentExtension;
}
@Block
public static class FooParentExtension extends BackboneElement {
private static final long serialVersionUID = 4522090347756045145L;
@Child(name = "childA")
@Extension(url = "http://acme.org/fooChildA", definedLocally = false, isModifier = false)
private StringType myChildA;
@Child(name = "childB")
@Extension(url = "http://acme.org/fooChildB", definedLocally = false, isModifier = false)
private StringType myChildB;
@Override
public FooParentExtension copy() {
FooParentExtension copy = new FooParentExtension();
copy.myChildA = myChildA;
copy.myChildB = myChildB;
return copy;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myChildA, myChildB);
}
public StringType getChildA() {
return myChildA;
}
public StringType getChildB() {
return myChildB;
}
public void setChildA(StringType theChildA) {
myChildA = theChildA;
}
public void setChildB(StringType theChildB) {
myChildB = theChildB;
}
}
}
//END SNIPPET: resource

View File

@ -0,0 +1,63 @@
package ca.uhn.hapi.fhir.docs.customtype;
//START SNIPPET: datatype
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.util.ElementUtil;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.Type;
import org.hl7.fhir.instance.model.api.ICompositeType;
/**
* This is an example of a custom datatype.
*
* This is an STU3 example so it extends Type and implements ICompositeType. For
* DSTU2 it would extend BaseIdentifiableElement and implement ICompositeDatatype.
*/
@DatatypeDef(name="CustomDatatype")
public class CustomDatatype extends Type implements ICompositeType {
private static final long serialVersionUID = 1L;
@Child(name = "date", order = 0, min = 1, max = 1)
private DateTimeType myDate;
@Child(name = "kittens", order = 1, min = 1, max = 1)
private StringType myKittens;
public DateTimeType getDate() {
if (myDate == null)
myDate = new DateTimeType();
return myDate;
}
public StringType getKittens() {
return myKittens;
}
@Override
public boolean isEmpty() {
return ElementUtil.isEmpty(myDate, myKittens);
}
public CustomDatatype setDate(DateTimeType theValue) {
myDate = theValue;
return this;
}
public CustomDatatype setKittens(StringType theKittens) {
myKittens = theKittens;
return this;
}
@Override
protected CustomDatatype typedCopy() {
CustomDatatype retVal = new CustomDatatype();
super.copyValues(retVal);
retVal.myDate = myDate;
return retVal;
}
}
//END SNIPPET: datatype

View File

@ -0,0 +1,86 @@
package ca.uhn.hapi.fhir.docs.customtype;
// START SNIPPET: resource
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;
import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.ResourceType;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.Type;
import java.util.ArrayList;
import java.util.List;
/**
* This is an example of a custom resource that also uses a custom
* datatype.
*
* Note that we are extensing DomainResource for an STU3
* resource. For DSTU2 it would be BaseResource.
*/
@ResourceDef(name = "CustomResource", profile = "http://hl7.org/fhir/profiles/custom-resource")
public class CustomResource extends DomainResource {
private static final long serialVersionUID = 1L;
/**
* We give the resource a field with name "television". This field has no
* specific type, so it's a choice[x] field for any type.
*/
@Child(name="television", min=1, max=Child.MAX_UNLIMITED, order=0)
private List<Type> myTelevision;
/**
* We'll give it one more field called "dogs"
*/
@Child(name = "dogs", min=0, max=1, order=1)
private StringType myDogs;
@Override
public CustomResource copy() {
CustomResource retVal = new CustomResource();
super.copyValues(retVal);
retVal.myTelevision = myTelevision;
retVal.myDogs = myDogs;
return retVal;
}
public List<Type> getTelevision() {
if (myTelevision == null) {
myTelevision = new ArrayList<Type>();
}
return myTelevision;
}
public StringType getDogs() {
return myDogs;
}
@Override
public ResourceType getResourceType() {
return null;
}
@Override
public FhirVersionEnum getStructureFhirVersionEnum() {
return FhirVersionEnum.DSTU3;
}
@Override
public boolean isEmpty() {
return ElementUtil.isEmpty(myTelevision, myDogs);
}
public void setTelevision(List<Type> theValue) {
this.myTelevision = theValue;
}
public void setDogs(StringType theDogs) {
myDogs = theDogs;
}
}
// END SNIPPET: resource

View File

@ -0,0 +1,43 @@
package ca.uhn.hapi.fhir.docs.customtype;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.StringType;
import java.util.Date;
public class CustomUsage {
public static void main(String[] args) {
// START SNIPPET: usage
// Create a context. Note that we declare the custom types we'll be using
// on the context before actually using them
FhirContext ctx = FhirContext.forDstu3();
ctx.registerCustomType(CustomResource.class);
ctx.registerCustomType(CustomDatatype.class);
// Now let's create an instance of our custom resource type
// and populate it with some data
CustomResource res = new CustomResource();
// Add some values, including our custom datatype
DateType value0 = new DateType("2015-01-01");
res.getTelevision().add(value0);
CustomDatatype value1 = new CustomDatatype();
value1.setDate(new DateTimeType(new Date()));
value1.setKittens(new StringType("FOO"));
res.getTelevision().add(value1);
res.setDogs(new StringType("Some Dogs"));
// Now let's serialize our instance
String output = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
System.out.println(output);
// END SNIPPET: usage
}
}

View File

@ -0,0 +1,25 @@
package ca.uhn.hapi.fhir.docs.interceptor;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.subscription.module.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.module.subscriber.ResourceDeliveryMessage;
/**
* Interceptor class
*/
@Interceptor
public class MyTestInterceptor {
@Hook(Pointcut.SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY)
public boolean beforeRestHookDelivery(ResourceDeliveryMessage theDeliveryMessage, CanonicalSubscription theSubscription) {
String header = "Authorization: Bearer 1234567";
theSubscription.addHeader(header);
return true;
}
}

View File

@ -1,4 +1,7 @@
section.introduction.title=Welcome to HAPI FHIR
page.introduction.table_of_contents=Table of Contents
page.introduction.changelog=Changelog
page.introduction.introduction=Introduction
section.appendix.title=Appendix
page.appendix.changelog=Changelog

View File

@ -0,0 +1,113 @@
# Introduction to HAPI FHIR
The HAPI FHIR library is an implementation of the [HL7 FHIR specification](http://hl7.org/fhir/) for Java. Explaining what FHIR is would be beyond the scope of this documentation, so if you have not previously worked with FHIR, the specification is a good place to start. This is often not actually the case when discussing messaging protocols, but in this case it is so: The FHIR specification is designed to be readable and implementable, and is filled with good information.
Part of the key to why FHIR is a good specification is the fact that its design is based on the design of other successful APIs (in particular, the FHIR designers often reference the Highrise API as a key influence in the design of the spec.)
HAPI FHIR is based on the same principle, but applied to the Java implementation: We have based the design of this API on the JAXB and JAX-WS APIs, which we consider to be very well thought-out, and very usable APIs. This does <b>not</b> mean that HAPI-FHIR actually uses these two APIs however, or that HAPI-FHIR is in any way compliant with JAXB ([JSR222](https://jcp.org/en/jsr/detail?id=222)) or JAX-WS ([JSR224](https://jcp.org/en/jsr/detail?id=222)), only that we have tried to emulate the easy-to-use, but flexible design of these specifications.
# Getting Started
To get started with HAPI FHIR, first download a copy and add it to your project. See the [Download Page](./download.html) for instructions.
## A Note on FHIR Versions
Before discussing HAPI itself, a quick word about FHIR versions. FHIR is not yet a finalized "1.0" standard. It is currently in the DSTU phase, which means that it is changing in subtle and non-subtle ways between releases. Before trying to use FHIR, you will need to determine which version of FHIR you want to support in your application. Typically this would be the latest version, but if you are looking to interact with an application which already exists, you will probably want to implement the same version implemented by that application.
See the [note on DSTU2 support](doc_dstu2.html) for more information on supporting multiple versions of FHIR.
## Introducing the FHIR Context
HAPI defines model classes for every resource type and datatype defined by the FHIR specification. For example, here is the [Patient](../apidocs/hapi-fhir-structures-r4/ca/uhn/fhir/model/r4/resource/Patient.html) resource specification. If you browse the JavaDoc you will see getters and setters for the various properties that make up a Patient resource.
We will come back to how to interact with these objects in a moment, but first we need to create a [FhirContext](../apidocs/hapi-fhir-base/ca/uhn/fhir/context/FhirContext.html). FhirContext is the starting point to using HAPI, and acts as a factory for most other parts of the API as well as a runtime cache of information that HAPI needs to operate. Users of the JAXB API may find this class to be similar in purpose to the [JAXBContext](http://docs.oracle.com/javaee/5/api/javax/xml/bind/JAXBContext.html) class from that API.
Creating a FhirContext is as simple as instantiating one. A FhirContext instance is specific to a given version of the FHIR specification, so it is recommended that you use one of the factory methods indicating the FHIR version you wish to support in your application, as shown in the following snippet:
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/FhirContextIntro.java|creatingContext}}
```
## Parsing a resource from a String
This [Parser instance](../apidocs/hapi-fhir-base/ca/uhn/fhir/parser/IParser.html) can then be used to parse messages. Note that you may use the context to create as many parsers are you want.
**Performance tip:** The FhirContext is an expensive object to create, so you should try to create it once and keep it around during the life of your application. Parsers, on the other hand, are very lightweight and do not need to be reused.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/FhirContextIntro.java|parseMsg}}
```
## Encoding a Resource to a String
The parser can also be used to encode a resource (which you can populate with your own values) just as easily.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/FhirContextIntro.java|encodeMsg}}
```
<!--/* ****** The section below on fluent references the snippet above ***** */-->
<!--/* ****** so be careful about any reordering! ***** */-->
This code gives the following output:
```xml
<Patient xmlns="http://hl7.org/fhir">
<identifier>
<system value="http://example.com/fictitious-mrns"/>
<value value="MRN001"/>
</identifier>
<name>
<use value="official"/>
<family value="Tester"/>
<given value="John"/>
<given value="Q"/>
</name>
</Patient>
```
## Fluent Programming
Much of the HAPI FHIR API is designed using a fluent style, where method calls can be chained in a natural way. This leads to tighter and easier-to-read code.
The following snippet is functionally identical to the example above:
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/FhirContextIntro.java|encodeMsgFluent}}
```
# JSON Encoding
JSON parsing/encoding is also supported.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/FhirContextIntro.java|encodeMsgJson}}
```
This code gives the following output:
```json
{
"resourceType":"Patient",
"identifier":[
{
"system":"http://example.com/fictitious-mrns",
"value":"MRN001"
}
],
"name":[
{
"use":"official",
"family":[
"Tester"
],
"given":[
"John",
"Q"
]
}
]
}
```

View File

@ -50,26 +50,31 @@
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2.1</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>

View File

@ -33,7 +33,21 @@
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>

View File

@ -41,6 +41,11 @@
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -104,12 +104,13 @@
</dependency>
<!-- karaf test -->
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-karaf</artifactId>
<version>${pax.exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-container-karaf</artifactId>
<version>${pax.exam.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit4</artifactId>

View File

@ -57,7 +57,7 @@ public class JsonParserDstu2_1Test {
private FhirContext ourCtx = FhirContext.forDstu2_1();
@Configuration
public Option[] config() throws IOException {
public Option[] config() {
return options(
KARAF.option(),
WRAP.option(),
@ -431,7 +431,7 @@ public class JsonParserDstu2_1Test {
*/
@Test
public void testEncodeEmptyTag() {
ArrayList<org.hl7.fhir.dstu2016may.model.Coding> tagList = new ArrayList<org.hl7.fhir.dstu2016may.model.Coding>();
ArrayList<org.hl7.fhir.dstu2016may.model.Coding> tagList = new ArrayList<>();
tagList.add(new org.hl7.fhir.dstu2016may.model.Coding());
tagList.add(new org.hl7.fhir.dstu2016may.model.Coding().setDisplay("Label"));
@ -447,7 +447,7 @@ public class JsonParserDstu2_1Test {
*/
@Test
public void testEncodeEmptyTag2() {
ArrayList<org.hl7.fhir.dstu2016may.model.Coding> tagList = new ArrayList<org.hl7.fhir.dstu2016may.model.Coding>();
ArrayList<org.hl7.fhir.dstu2016may.model.Coding> tagList = new ArrayList<>();
tagList.add(new org.hl7.fhir.dstu2016may.model.Coding().setSystem("scheme").setCode("code"));
tagList.add(new org.hl7.fhir.dstu2016may.model.Coding().setDisplay("Label"));

View File

@ -177,6 +177,12 @@
syntax. Both are now supported on all servers in order to avoid breaking backwards
compatibility, with the new syntax now being emitted in R4+ clients.
</action>
<action type="change">
The hapi-fhir-jaxrs-server module now lists dependencies on structures JARs as optional
dependencies, in order to avoid automatically importing all versions. This means that implementers
of JAX-RS servers may now need to add an explicit dependency on one or more structures JARs to
their own project.
</action>
</release>
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
<action type="fix">

View File

@ -1,225 +1,227 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
<properties>
<title>Introduction</title>
<author email="jamesagnew@users.sourceforge.net">James Agnew</author>
</properties>
<body>
<!-- The body of the document contains a number of sections -->
<section name="Introduction to HAPI FHIR">
<p>
The HAPI FHIR library is an implementation of the
<a href="http://hl7.org/implement/standards/fhir/">HL7 FHIR specification</a>
for Java. Explaining what FHIR is would be beyond the scope of this documentation,
so if you have not previously worked with FHIR, the specification is a good place to
start. This is often not actually the case when discussing messaging protocols, but
in this case it is so: The FHIR specification is designed to be readable and
implementable, and is filled with good information.
</p>
<p>
Part of the key to why FHIR is a good specification is the fact that its design
is based on the design of other successful APIs (in particular, the FHIR designers
often reference the Highrise API as a key influence in the design of the spec.)
</p>
<p>
HAPI FHIR is based on the same principle, but applied to the Java implementation: We
have based the design of this API on the JAXB and JAX-WS APIs, which we consider to be
very well thought-out, and very usable APIs. This does <b>not</b> mean that HAPI-FHIR
actually uses these two APIs however, or that HAPI-FHIR is in any way compliant with
JAXB (<a href="https://jcp.org/en/jsr/detail?id=222">JSR222</a>) or JAX-WS
(<a href="https://jcp.org/en/jsr/detail?id=222">JSR224</a>), only that we have tried
to emluate the easy-to-use, but flexible design of these specifications.
</p>
</section>
<section name="Getting Started">
<p>
To get started with HAPI FHIR, first download a copy and add it
to your project. See the <a href="./download.html">Download Page</a>
for instructions.
</p>
<subsection name="A Note on FHIR Versions">
<p>
Before discussing HAPI itself, a quick word about FHIR versions. FHIR
is not yet a finalized "1.0" standard. It is currently in the DSTU phase,
which means that it is changing in subtle and non-subtle ways between releases.
Before trying to use FHIR, you will need to determine which version of FHIR
you want to support in your application. Typically this would be the
latest version, but if you are looking to interact with an application which
already exists, you will probably want to implement the same version implemented
by that application.
</p>
<p>
See the
<a href="doc_dstu2.html">note on DSTU2 support</a>
for more information on supporting multiple versions of FHIR.
</p>
</subsection>
<subsection name="Introducing the FHIR Context">
<p>
HAPI defines model classes for every resource type and datatype defined by the FHIR specification.
For example, here is the <a href="./apidocs/ca/uhn/fhir/model/dstu/resource/Patient.html">Patient</a>
resource specification. If you browse the JavaDoc you will see getters and setters for the
various properties that make up a Patient resource.
</p>
<p>
We will come back to how to interact with these objects in a moment, but first
we need to create a
<a href="./apidocs/ca/uhn/fhir/context/FhirContext.html">FhirContext</a>.
FhirContext is the starting point to using HAPI, and acts as a factory for most
other parts of the API as well as a runtime cache of information that HAPI needs
to operate. Users of the JAXB API may find this class to be similar in purpose to
the
<a href="http://docs.oracle.com/javaee/5/api/javax/xml/bind/JAXBContext.html">JAXBContext</a>
class from that API.
</p>
<p>
Creating a FhirContext is as simple as instantiating one. A FhirContext instance is
specific to a given version of the FHIR specification, so it is recommended that you
use one of the factory methods indicating the FHIR version you wish to support in your
application, as shown in the following snippet:
</p>
<macro name="snippet">
<param name="id" value="creatingContext" />
<param name="file" value="examples/src/main/java/example/FhirContextIntro.java" />
</macro>
</subsection>
<subsection name="Parsing a resource from a String">
<p>
This
<a href="./apidocs/ca/uhn/fhir/parser/IParser.html">Parser instance</a>
can then be used to parse messages. Note that you may use the context to
create as many parsers are you want.
</p>
<p>
<b>Performance tip: </b>
The FhirContext is an expensive object to create, so you should try to create
it once and keep it around during the life of your application. Parsers, on
the other hand, are very lightweight and do not need to be reused.
</p>
<macro name="snippet">
<param name="id" value="parseMsg" />
<param name="file" value="examples/src/main/java/example/FhirContextIntro.java" />
</macro>
</subsection>
<subsection name="Encoding a Resource to a String">
<p>
The parser can also be used to encode a resource (which you can populate
with your own values) just as easily.
</p>
<macro name="snippet">
<param name="id" value="encodeMsg" />
<param name="file" value="examples/src/main/java/example/FhirContextIntro.java" />
</macro>
<!-- ****** The section below on fluent references the snippet above
****** so be careful about any reordering! -->
<p>
This code gives the following output:
</p>
<source><![CDATA[<Patient xmlns="http://hl7.org/fhir">
<identifier>
<system value="http://example.com/fictitious-mrns"/>
<value value="MRN001"/>
</identifier>
<name>
<use value="official"/>
<family value="Tester"/>
<given value="John"/>
<given value="Q"/>
</name>
</Patient>]]></source>
</subsection>
<subsection name="Fluent Programming">
<p>
Much of the HAPI FHIR API is designed using a fluent style,
where method calls can be chained in a natural way. This
leads to tighter and easier-to-read code.
</p>
<p>
The following snippet is functionally identical to the
example above:
</p>
<macro name="snippet">
<param name="id" value="encodeMsgFluent" />
<param name="file" value="examples/src/main/java/example/FhirContextIntro.java" />
</macro>
</subsection>
<subsection name="JSON Encoding">
<p>
JSON parsing/encoding is also supported.
</p>
<macro name="snippet">
<param name="id" value="encodeMsgJson" />
<param name="file" value="examples/src/main/java/example/FhirContextIntro.java" />
</macro>
<p>
This code gives the following output:
</p>
<source><![CDATA[{
"resourceType":"Patient",
"identifier":[
{
"system":"http://example.com/fictitious-mrns",
"value":"MRN001"
}
],
"name":[
{
"use":"official",
"family":[
"Tester"
],
"given":[
"John",
"Q"
]
}
]
}]]></source>
</subsection>
</section>
</body>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
<properties>
<title>Introduction</title>
<author email="jamesagnew@users.sourceforge.net">James Agnew</author>
</properties>
<body>
<!-- CONTENTS HAVE BEEN MIGRATED -->
<!-- The body of the document contains a number of sections -->
<section name="Introduction to HAPI FHIR">
<p>
The HAPI FHIR library is an implementation of the
<a href="http://hl7.org/implement/standards/fhir/">HL7 FHIR specification</a>
for Java. Explaining what FHIR is would be beyond the scope of this documentation,
so if you have not previously worked with FHIR, the specification is a good place to
start. This is often not actually the case when discussing messaging protocols, but
in this case it is so: The FHIR specification is designed to be readable and
implementable, and is filled with good information.
</p>
<p>
Part of the key to why FHIR is a good specification is the fact that its design
is based on the design of other successful APIs (in particular, the FHIR designers
often reference the Highrise API as a key influence in the design of the spec.)
</p>
<p>
HAPI FHIR is based on the same principle, but applied to the Java implementation: We
have based the design of this API on the JAXB and JAX-WS APIs, which we consider to be
very well thought-out, and very usable APIs. This does <b>not</b> mean that HAPI-FHIR
actually uses these two APIs however, or that HAPI-FHIR is in any way compliant with
JAXB (<a href="https://jcp.org/en/jsr/detail?id=222">JSR222</a>) or JAX-WS
(<a href="https://jcp.org/en/jsr/detail?id=222">JSR224</a>), only that we have tried
to emluate the easy-to-use, but flexible design of these specifications.
</p>
</section>
<section name="Getting Started">
<p>
To get started with HAPI FHIR, first download a copy and add it
to your project. See the <a href="./download.html">Download Page</a>
for instructions.
</p>
<subsection name="A Note on FHIR Versions">
<p>
Before discussing HAPI itself, a quick word about FHIR versions. FHIR
is not yet a finalized "1.0" standard. It is currently in the DSTU phase,
which means that it is changing in subtle and non-subtle ways between releases.
Before trying to use FHIR, you will need to determine which version of FHIR
you want to support in your application. Typically this would be the
latest version, but if you are looking to interact with an application which
already exists, you will probably want to implement the same version implemented
by that application.
</p>
<p>
See the
<a href="doc_dstu2.html">note on DSTU2 support</a>
for more information on supporting multiple versions of FHIR.
</p>
</subsection>
<subsection name="Introducing the FHIR Context">
<p>
HAPI defines model classes for every resource type and datatype defined by the FHIR specification.
For example, here is the <a href="./apidocs/ca/uhn/fhir/model/dstu/resource/Patient.html">Patient</a>
resource specification. If you browse the JavaDoc you will see getters and setters for the
various properties that make up a Patient resource.
</p>
<p>
We will come back to how to interact with these objects in a moment, but first
we need to create a
<a href="./apidocs/ca/uhn/fhir/context/FhirContext.html">FhirContext</a>.
FhirContext is the starting point to using HAPI, and acts as a factory for most
other parts of the API as well as a runtime cache of information that HAPI needs
to operate. Users of the JAXB API may find this class to be similar in purpose to
the
<a href="http://docs.oracle.com/javaee/5/api/javax/xml/bind/JAXBContext.html">JAXBContext</a>
class from that API.
</p>
<p>
Creating a FhirContext is as simple as instantiating one. A FhirContext instance is
specific to a given version of the FHIR specification, so it is recommended that you
use one of the factory methods indicating the FHIR version you wish to support in your
application, as shown in the following snippet:
</p>
<macro name="snippet">
<param name="id" value="creatingContext" />
<param name="file" value="examples/src/main/java/example/FhirContextIntro.java" />
</macro>
</subsection>
<subsection name="Parsing a resource from a String">
<p>
This
<a href="./apidocs/ca/uhn/fhir/parser/IParser.html">Parser instance</a>
can then be used to parse messages. Note that you may use the context to
create as many parsers are you want.
</p>
<p>
<b>Performance tip: </b>
The FhirContext is an expensive object to create, so you should try to create
it once and keep it around during the life of your application. Parsers, on
the other hand, are very lightweight and do not need to be reused.
</p>
<macro name="snippet">
<param name="id" value="parseMsg" />
<param name="file" value="examples/src/main/java/example/FhirContextIntro.java" />
</macro>
</subsection>
<subsection name="Encoding a Resource to a String">
<p>
The parser can also be used to encode a resource (which you can populate
with your own values) just as easily.
</p>
<macro name="snippet">
<param name="id" value="encodeMsg" />
<param name="file" value="examples/src/main/java/example/FhirContextIntro.java" />
</macro>
<!-- ****** The section below on fluent references the snippet above
****** so be careful about any reordering! -->
<p>
This code gives the following output:
</p>
<source><![CDATA[<Patient xmlns="http://hl7.org/fhir">
<identifier>
<system value="http://example.com/fictitious-mrns"/>
<value value="MRN001"/>
</identifier>
<name>
<use value="official"/>
<family value="Tester"/>
<given value="John"/>
<given value="Q"/>
</name>
</Patient>]]></source>
</subsection>
<subsection name="Fluent Programming">
<p>
Much of the HAPI FHIR API is designed using a fluent style,
where method calls can be chained in a natural way. This
leads to tighter and easier-to-read code.
</p>
<p>
The following snippet is functionally identical to the
example above:
</p>
<macro name="snippet">
<param name="id" value="encodeMsgFluent" />
<param name="file" value="examples/src/main/java/example/FhirContextIntro.java" />
</macro>
</subsection>
<subsection name="JSON Encoding">
<p>
JSON parsing/encoding is also supported.
</p>
<macro name="snippet">
<param name="id" value="encodeMsgJson" />
<param name="file" value="examples/src/main/java/example/FhirContextIntro.java" />
</macro>
<p>
This code gives the following output:
</p>
<source><![CDATA[{
"resourceType":"Patient",
"identifier":[
{
"system":"http://example.com/fictitious-mrns",
"value":"MRN001"
}
],
"name":[
{
"use":"official",
"family":[
"Tester"
],
"given":[
"John",
"Q"
]
}
]
}]]></source>
</subsection>
</section>
</body>
</document>