Merge branch 'master' into ja_20200206_multitenancy
This commit is contained in:
commit
ec6fe70acb
|
@ -0,0 +1,17 @@
|
|||
# Two years until issues go stale
|
||||
daysUntilStale: 730
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: wontfix
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
|
@ -1,125 +0,0 @@
|
|||
/target/
|
||||
|
||||
# Created by https://www.gitignore.io
|
||||
|
||||
### Java ###
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
|
||||
|
||||
### Vim ###
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
*.un~
|
||||
Session.vim
|
||||
.netrwhist
|
||||
*~
|
||||
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
|
||||
|
||||
*.iml
|
||||
|
||||
## Directory-based project format:
|
||||
.idea/
|
||||
# if you remove the above rule, at least ignore the following:
|
||||
|
||||
# User-specific stuff:
|
||||
# .idea/workspace.xml
|
||||
# .idea/tasks.xml
|
||||
# .idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
# .idea/dataSources.ids
|
||||
# .idea/dataSources.xml
|
||||
# .idea/sqlDataSources.xml
|
||||
# .idea/dynamic.xml
|
||||
# .idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
# .idea/gradle.xml
|
||||
# .idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
# .idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
|
||||
|
||||
|
||||
### Eclipse ###
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
133
examples/pom.xml
133
examples/pom.xml
|
@ -1,133 +0,0 @@
|
|||
<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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>4.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-base-examples</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HAPI FHIR - Examples (for site)</name>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-bom</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>import</scope>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-base</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu2.1</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-r4</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-converter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-client-okhttp</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- jax rs server -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>javax.ws.rs-api</artifactId>
|
||||
<version>2.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.ejb</groupId>
|
||||
<artifactId>ejb-api</artifactId>
|
||||
<version>3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-base</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j_version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -1,216 +0,0 @@
|
|||
package example;
|
||||
|
||||
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.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
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.RestOperationTypeEnum;
|
||||
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.IServerInterceptor;
|
||||
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 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.
|
||||
IdDt 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 IdDt("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 IdDt 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
|
||||
IdDt actual = new IdDt("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
|
||||
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package example;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.RelatedPerson;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
|
||||
/**
|
||||
* @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.Entry entry : theBundle.getEntry()) {
|
||||
theResourcesAlreadyAdded.add(entry.getFullUrl());
|
||||
}
|
||||
}
|
||||
|
||||
private static void addAnyResourcesNotAlreadyPresentToBundle(Bundle theAggregatedBundle, Bundle thePartialBundle, Set<String> theResourcesAlreadyAdded) {
|
||||
for (Bundle.Entry entry : thePartialBundle.getEntry()) {
|
||||
if (!theResourcesAlreadyAdded.contains(entry.getFullUrl())) {
|
||||
theResourcesAlreadyAdded.add(entry.getFullUrl());
|
||||
theAggregatedBundle.getEntry().add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
String serverBase = "http://fhirtest.uhn.ca/baseDstu2";
|
||||
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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
package example;
|
||||
|
||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.client.apache.GZipContentInterceptor;
|
||||
import ca.uhn.fhir.rest.client.api.*;
|
||||
import ca.uhn.fhir.rest.client.interceptor.*;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package example;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.*;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.*;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
|
||||
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()
|
||||
.addFamily("Jameson")
|
||||
.addGiven("J")
|
||||
.addGiven("Jonah");
|
||||
patient.setGender(AdministrativeGenderEnum.MALE);
|
||||
|
||||
// Give the patient a temporary UUID so that other resources in
|
||||
// the transaction can refer to it
|
||||
patient.setId(IdDt.newRandomUuid());
|
||||
|
||||
// Create an observation object
|
||||
Observation observation = new Observation();
|
||||
observation.setStatus(ObservationStatusEnum.FINAL);
|
||||
observation
|
||||
.getCode()
|
||||
.addCoding()
|
||||
.setSystem("http://loinc.org")
|
||||
.setCode("789-8")
|
||||
.setDisplay("Erythrocytes [#/volume] in Blood by Automated count");
|
||||
observation.setValue(
|
||||
new QuantityDt()
|
||||
.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 ResourceReferenceDt(patient.getId().getValue()));
|
||||
|
||||
// Create a bundle that will be used as a transaction
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.setType(BundleTypeEnum.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.getId().getValue())
|
||||
.setResource(patient)
|
||||
.getRequest()
|
||||
.setUrl("Patient")
|
||||
.setIfNoneExist("identifier=http://acme.org/mrns|12345")
|
||||
.setMethod(HTTPVerbEnum.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(HTTPVerbEnum.POST);
|
||||
|
||||
// Log the request
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
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/baseDstu2");
|
||||
Bundle resp = client.transaction().withBundle(bundle).execute();
|
||||
|
||||
// Log the response
|
||||
System.out.println(ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||
//END SNIPPET: conditional
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package example;
|
||||
|
||||
//START SNIPPET: client
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
|
||||
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 static 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) IdentifierDt 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
|
||||
List<Patient> patients = client.findPatientsForMrn(new IdentifierDt("urn:oid:1.2.36.146.595.217.0.1", "12345"));
|
||||
|
||||
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().get(0).getValue());
|
||||
|
||||
// Load a referenced resource
|
||||
ResourceReferenceDt managingRef = patient.getManagingOrganization();
|
||||
Organization org = (Organization) managingRef.loadResource(client);
|
||||
|
||||
// Print organization name
|
||||
System.out.println(org.getName());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// END SNIPPET: client
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
package example;
|
||||
|
||||
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
|
||||
|
||||
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package example;
|
||||
|
||||
import org.hl7.fhir.convertors.conv10_30.Observation10_30;
|
||||
import org.hl7.fhir.convertors.conv14_30.Questionnaire14_30;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
|
||||
public class ConverterExamples {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void c1020() throws FHIRException {
|
||||
//START SNIPPET: 1020
|
||||
// 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 = Observation10_30.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 = Questionnaire14_30.convertQuestionnaire(input);
|
||||
String context = output.getTitle();
|
||||
//END SNIPPET: 1420
|
||||
}
|
||||
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
package example;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package example;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
|
||||
public class CustomObservation extends Observation {
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
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;
|
||||
|
||||
@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
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
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;
|
||||
|
||||
@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
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
|
||||
@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 StringDt("SMITH"));
|
||||
|
||||
}
|
||||
//END SNIPPET: client
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
||||
//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
|
||||
|
||||
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
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.resource.Questionnaire.GroupQuestion;
|
||||
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;
|
||||
|
||||
public class ExtensionsDstu2 {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void main(String[] args) throws DataFormatException, IOException {
|
||||
|
||||
{
|
||||
Questionnaire q= new 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
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Identifier.IdentifierUse;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
|
||||
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(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
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
package example;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.NameUseEnum;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
|
||||
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 static void main(String[] args) throws DataFormatException {
|
||||
|
||||
new FhirContextIntro().encodeMsg();
|
||||
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
IdentifierDt id = patient.addIdentifier();
|
||||
id.setSystem("http://example.com/fictitious-mrns");
|
||||
id.setValue("MRN001");
|
||||
|
||||
// Add a name
|
||||
HumanNameDt name = patient.addName();
|
||||
name.setUse(NameUseEnum.OFFICIAL);
|
||||
name.addFamily("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(NameUseEnum.OFFICIAL).addFamily("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().get(0).getValue();
|
||||
String gender = patient.getGender();
|
||||
|
||||
System.out.println(patientId); // PRP1660
|
||||
System.out.println(familyName); // Cardinal
|
||||
System.out.println(gender); // M
|
||||
//END SNIPPET: parseMsg
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.dstu2.composite.*;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.*;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
|
||||
public class FhirDataModel {
|
||||
|
||||
public static void datatypes() {
|
||||
// START SNIPPET: datatypes
|
||||
Observation obs = new Observation();
|
||||
|
||||
// These are all equivalent
|
||||
obs.setIssued(new InstantDt(new Date()));
|
||||
obs.setIssued(new Date(), TemporalPrecisionEnum.MILLI);
|
||||
obs.setIssuedWithMillisPrecision(new Date());
|
||||
|
||||
// The InstantDt 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<IdentifierDt> identifierList = observation.getIdentifier();
|
||||
CodeableConceptDt code = observation.getCode();
|
||||
StringDt 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().setValue("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(AdministrativeGenderEnum.MALE);
|
||||
|
||||
// You can also retrieve coded values the same way
|
||||
String genderString = patient.getGenderElement().getValueAsString();
|
||||
AdministrativeGenderEnum genderEnum = patient.getGenderElement().getValueAsEnum();
|
||||
|
||||
// The following is a shortcut to create
|
||||
patient.setMaritalStatus(MaritalStatusCodesEnum.M);
|
||||
// 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
|
||||
CodingDt 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.
|
||||
CodingDt 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
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void codeableConceptEnums() {
|
||||
// START SNIPPET: codeableConceptEnums
|
||||
Patient patient = new Patient();
|
||||
|
||||
// Set the CodeableConcept's first coding to use the code
|
||||
// and codesystem associated with the M value.
|
||||
patient.setMaritalStatus(MaritalStatusCodesEnum.M);
|
||||
|
||||
// If you need to set other fields (such as the display name) after
|
||||
// using the Enum type, you may still do so.
|
||||
patient.getMaritalStatus().getCodingFirstRep().setDisplay("Married");
|
||||
patient.getMaritalStatus().getCodingFirstRep().setVersion("1.0");
|
||||
patient.getMaritalStatus().getCodingFirstRep().setUserSelected(true);
|
||||
|
||||
// You can use accessors to retrieve values from CodeableConcept fields
|
||||
|
||||
// Returns "M"
|
||||
String code = patient.getMaritalStatus().getCodingFirstRep().getCode();
|
||||
|
||||
// Returns "http://hl7.org/fhir/v3/MaritalStatus". This value was also
|
||||
// populated via the enum above.
|
||||
String codeSystem = patient.getMaritalStatus().getCodingFirstRep().getCode();
|
||||
|
||||
// In many cases, Enum types can be used to retrieve values as well. Note that
|
||||
// the setter takes a single type, but the getter returns a Set, because the
|
||||
// field can technicaly contain more than one code and codesystem. BE CAREFUL
|
||||
// when using this method however, as no Enum will be returned in the case
|
||||
// that the field contains only a code other than the ones defined by the Enum.
|
||||
Set<MaritalStatusCodesEnum> status = patient.getMaritalStatus().getValueAsEnum();
|
||||
// END SNIPPET: codeableConceptEnums
|
||||
|
||||
}
|
||||
|
||||
|
||||
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(ObservationStatusEnum.FINAL);
|
||||
|
||||
// Give the observation a code (what kind of observation is this)
|
||||
CodingDt coding = observation.getCode().addCoding();
|
||||
coding.setCode("29463-7").setSystem("http://loinc.org").setDisplay("Body Weight");
|
||||
|
||||
// Create a quantity datatype
|
||||
QuantityDt value = new QuantityDt();
|
||||
value.setValue(83.9).setSystem("http://unitsofmeasure.org").setCode("kg");
|
||||
observation.setValue(value);
|
||||
|
||||
// Set the reference range
|
||||
SimpleQuantityDt low = new SimpleQuantityDt();
|
||||
low.setValue(45).setSystem("http://unitsofmeasure.org").setCode("kg");
|
||||
observation.getReferenceRangeFirstRep().setLow(low);
|
||||
SimpleQuantityDt high = new SimpleQuantityDt();
|
||||
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.forDstu2();
|
||||
|
||||
// Create a client
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
|
||||
|
||||
// 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();
|
||||
HumanNameDt name = patient.addName();
|
||||
StringDt family = name.addFamily();
|
||||
family.setValue("Smith");
|
||||
StringDt firstName = name.addGiven();
|
||||
firstName.setValue("Rob");
|
||||
StringDt secondName = name.addGiven();
|
||||
secondName.setValue("Bruce");
|
||||
// END SNIPPET: namesHard
|
||||
}
|
||||
|
||||
public void namesEasy() {
|
||||
// START SNIPPET: namesEasy
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addFamily("Smith").addGiven("Rob").addGiven("Bruce");
|
||||
// END SNIPPET: namesEasy
|
||||
}
|
||||
|
||||
}
|
|
@ -1,536 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.PerformanceOptionsEnum;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu2.resource.*;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome.Issue;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
|
||||
import ca.uhn.fhir.model.primitive.*;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
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;
|
||||
|
||||
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(ca.uhn.fhir.model.dstu2.resource.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().addFamily("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)
|
||||
IdDt id = (IdDt) 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
|
||||
IdDt id = (IdDt) outcome.getId();
|
||||
// END SNIPPET: createConditional
|
||||
}
|
||||
{
|
||||
// START SNIPPET: validate
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("http://hospital.com").setValue("123445");
|
||||
patient.addName().addFamily("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 (Issue nextIssue : oo.getIssue()) {
|
||||
if (nextIssue.getSeverityElement().getValueAsEnum().ordinal() >= IssueSeverityEnum.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().addFamily("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)
|
||||
IdDt id = (IdDt) 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.getId().getVersionIdPart());
|
||||
|
||||
// Now let's make a change to the resource
|
||||
patient.setGender(AdministrativeGenderEnum.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
|
||||
Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
|
||||
System.out.println(conf.getDescriptionElement().getValue());
|
||||
// END SNIPPET: conformance
|
||||
}
|
||||
{
|
||||
// START SNIPPET: delete
|
||||
IBaseOperationOutcome resp = client.delete().resourceById(new IdDt("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().getDetailsElement().getValue());
|
||||
}
|
||||
// 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
|
||||
ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.BIRTHDATE.beforeOrEquals().day("2011-01-01"))
|
||||
.and(Patient.CAREPROVIDER.hasChainedProperty(Organization.NAME.matches().value("Health")))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.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(ca.uhn.fhir.model.dstu2.resource.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(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
// END SNIPPET: searchUrl
|
||||
|
||||
// START SNIPPET: searchSubsetSummary
|
||||
response = client.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.ADDRESS.matches().values("Toronto"))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.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(ca.uhn.fhir.model.dstu2.resource.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_CAREPROVIDER.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("");
|
||||
{
|
||||
ca.uhn.fhir.model.dstu2.resource.Bundle response;
|
||||
// START SNIPPET: historyDstu2
|
||||
response = client
|
||||
.history()
|
||||
.onServer()
|
||||
.andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
// END SNIPPET: historyDstu2
|
||||
}
|
||||
{
|
||||
ca.uhn.fhir.model.dstu2.resource.Bundle response;
|
||||
// START SNIPPET: historyFeatures
|
||||
response = client
|
||||
.history()
|
||||
.onServer()
|
||||
.andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.since(new InstantDt("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 DateDt("2001-01-01"));
|
||||
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
|
||||
|
||||
// Invoke $everything on "Patient/1"
|
||||
Parameters outParams = client
|
||||
.operation()
|
||||
.onInstance(new IdDt("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 DateDt("2001-01-01"));
|
||||
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
|
||||
|
||||
// Invoke $everything on "Patient/1"
|
||||
Parameters outParams = client
|
||||
.operation()
|
||||
.onInstance(new IdDt("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 IdDt("Patient", "1"))
|
||||
.named("$everything")
|
||||
.withNoParameters(Parameters.class) // No input parameters
|
||||
.execute();
|
||||
// END SNIPPET: operationNoIn
|
||||
}
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package example;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package example;
|
||||
|
||||
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.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
|
||||
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);
|
||||
|
||||
IdDt id = new IdDt("Patient", "123");
|
||||
client.read(Patient.class, id);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
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.client.api.IBasicClient;
|
||||
|
||||
//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
|
||||
* IdDt 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 IdDt 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 theIdentifier
|
||||
* 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);
|
||||
|
||||
}
|
||||
//END SNIPPET: provider
|
||||
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.server.provider.dstu2.Dstu2BundleFactory;
|
||||
|
||||
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();
|
||||
|
||||
Dstu2BundleFactory bf = new Dstu2BundleFactory(ctx);
|
||||
bf.addRootPropertiesToBundle(null, null, null, null, null, resources.size(), BundleTypeEnum.SEARCHSET, null);
|
||||
bf.addResourcesToBundle(new ArrayList<>(resources), BundleTypeEnum.SEARCHSET, null, null, null);
|
||||
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
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package example;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
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 ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
||||
/**
|
||||
* 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
|
|
@ -1,68 +0,0 @@
|
|||
package example;
|
||||
|
||||
import javax.ejb.Local;
|
||||
import javax.ejb.Stateless;
|
||||
import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
|
||||
/**
|
||||
* 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 IdDt(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 IdDt myId, @OperationParam(name = "dummy") StringDt dummyInput) {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter().setName("return").setValue(new StringDt("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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package example;
|
||||
|
||||
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
|
||||
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
package example;
|
||||
|
||||
//START SNIPPET: patientDef
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.DateTimeType;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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
|
|
@ -1,86 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
|
||||
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 StringDt myPetName;
|
||||
|
||||
public StringDt getPetName() {
|
||||
if(myPetName==null) {
|
||||
myPetName = new StringDt();
|
||||
}
|
||||
return myPetName;
|
||||
}
|
||||
|
||||
public void setPetName(StringDt thePetName) {
|
||||
myPetName = thePetName;
|
||||
}
|
||||
|
||||
public List<DateTimeDt> getImportantDates() {
|
||||
if (myImportantDates==null) {
|
||||
myImportantDates=new ArrayList<DateTimeDt>();
|
||||
}
|
||||
return myImportantDates;
|
||||
}
|
||||
|
||||
public void setImportantDates(List<DateTimeDt> 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<DateTimeDt> myImportantDates;
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void main(String[] args) throws DataFormatException, IOException {
|
||||
//START SNIPPET: patientUse
|
||||
MyPatient patient = new MyPatient();
|
||||
patient.setPetName(new StringDt("Fido"));
|
||||
patient.getImportantDates().add(new DateTimeDt("2010-01-02"));
|
||||
patient.getImportantDates().add(new DateTimeDt("2014-01-26T11:11:11"));
|
||||
|
||||
patient.addName().addFamily("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()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package example;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.NarrativeStatusEnum;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
@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().addFamily("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(NarrativeStatusEnum.GENERATED);
|
||||
pat.getText().setDiv("<div>This is the narrative text<br/>this is line 2</div>");
|
||||
//END SNIPPET: simple
|
||||
}
|
||||
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package example;
|
||||
|
||||
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
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package example;
|
||||
|
||||
public class NewInterceptors {
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
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 javax.annotation.Nonnull;
|
||||
|
||||
@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 InstantDt searchTime = InstantDt.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 InstantDt 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 IResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
}
|
||||
// END SNIPPET: provider
|
|
@ -1,33 +0,0 @@
|
|||
package example;
|
||||
|
||||
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
|
|
@ -1,42 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package example;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
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 ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package example;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
||||
|
||||
//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
|
|
@ -1,36 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
||||
|
||||
//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
|
|
@ -1,36 +0,0 @@
|
|||
package example;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu2.resource.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().getContainedResources().add(org);
|
||||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
System.out.println(encoded);
|
||||
// END SNIPPET: manualContained
|
||||
}
|
||||
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
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.server.IResourceProvider;
|
||||
|
||||
//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
|
||||
* IdDt 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 IdDt 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 theIdentifier
|
||||
* 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(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("Test");
|
||||
patient.getName().get(0).addGiven("PatientOne");
|
||||
patient.setGender(AdministrativeGenderEnum.MALE);
|
||||
return Collections.singletonList(patient);
|
||||
}
|
||||
|
||||
}
|
||||
//END SNIPPET: provider
|
|
@ -1,90 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
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.IdDt;
|
||||
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;
|
||||
|
||||
//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
|
||||
* IdDt 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 IdDt 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
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,66 +0,0 @@
|
|||
package example;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package example;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
|
||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
||||
@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
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package example;
|
||||
|
||||
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
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;
|
||||
|
||||
public abstract class ServerExceptionsExample implements IResourceProvider {
|
||||
|
||||
private boolean databaseIsDown;
|
||||
|
||||
//START SNIPPET: returnOO
|
||||
@Read
|
||||
public Patient read(@IdParam IdDt 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
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum;
|
||||
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package example;
|
||||
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
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
|
||||
patient.getMeta().addTag()
|
||||
.setSystem(Tag.HL7_ORG_FHIR_TAG)
|
||||
.setCode("some_tag")
|
||||
.setDisplay("Some tag");
|
||||
patient.getMeta().addTag()
|
||||
.setSystem(Tag.HL7_ORG_FHIR_TAG)
|
||||
.setCode("another_tag")
|
||||
.setDisplay("Another tag");
|
||||
|
||||
// Set the last updated date
|
||||
patient.getMeta().setLastUpdatedElement(new InstantType("2011-02-22T11:22:00.0122Z"));
|
||||
|
||||
return retVal;
|
||||
}
|
||||
// END SNIPPET: serverMethod
|
||||
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
|
||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.ConceptMap;
|
||||
import ca.uhn.fhir.model.primitive.DateDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
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.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
||||
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);
|
||||
|
||||
// In a real example we might do something more interesting with the received bytes,
|
||||
// here we'll just replace them with hardcoded ones
|
||||
bytes = new byte[] { 0, 1, 2, 3 };
|
||||
|
||||
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 IdDt 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<CodingDt> theEnd,
|
||||
@OperationParam(name="version") IdDt theVersion) {
|
||||
|
||||
ConceptMap retVal = new ConceptMap();
|
||||
// Populate bundle with matching resources
|
||||
return retVal;
|
||||
}
|
||||
//END SNIPPET: serverOperation
|
||||
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
|
||||
import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
|
||||
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;
|
||||
|
||||
@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
|
||||
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.*;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
|
||||
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
|
||||
TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(p);
|
||||
|
||||
// tags may be null if no tags were read in
|
||||
if (tags == null) {
|
||||
System.out.println("No tags!");
|
||||
} else {
|
||||
|
||||
// You may iterate over all the tags
|
||||
for (Tag next : tags) {
|
||||
System.out.println(next.getScheme() + " - " + next.getTerm());
|
||||
}
|
||||
|
||||
// You may also get a list of tags matching a given scheme
|
||||
List<Tag> someTags = tags.getTagsWithScheme("http://hl7.org/fhir/tag");
|
||||
|
||||
// Or a specific tag (by scheme and term)
|
||||
Tag specificTag = tags.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().addFamily("Smith").addGiven("John");
|
||||
|
||||
// Create a tag list and add it to the resource
|
||||
TagList tags = new TagList();
|
||||
ResourceMetadataKeyEnum.TAG_LIST.put(patient, tags);
|
||||
|
||||
// Add some tags to the list
|
||||
tags.addTag(Tag.HL7_ORG_FHIR_TAG, "http://foo/tag1.html", "Some tag");
|
||||
tags.addTag(Tag.HL7_ORG_FHIR_TAG, "http://foo/tag2.html", "Another tag");
|
||||
|
||||
return retVal;
|
||||
}
|
||||
// END SNIPPET: serverMethod
|
||||
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
package example;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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 ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package example;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
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.ValidationSupportChain;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
|
||||
import java.io.FileReader;
|
||||
|
||||
public class ValidateSimple {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateSimple.class);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
FhirContext ctx = FhirContext.forR4();
|
||||
|
||||
// Create a validator module
|
||||
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
|
||||
|
||||
// We'll create a validation chain with only the DefaultProfileValidationupport registered
|
||||
ValidationSupportChain validationSupportChain = new ValidationSupportChain();
|
||||
validationSupportChain.addValidationSupport(new DefaultProfileValidationSupport());
|
||||
instanceValidator.setValidationSupport(validationSupportChain);
|
||||
|
||||
// Create a validator and register the InstanceValidator module
|
||||
FhirValidator val = ctx.newValidator();
|
||||
val.registerValidatorModule(instanceValidator);
|
||||
|
||||
// Read in the file and validate it
|
||||
String nextFile = args[0];
|
||||
try (FileReader fileReader = new FileReader(nextFile)) {
|
||||
String input = IOUtils.toString(fileReader);
|
||||
ValidationResult result = val.validateWithResult(input);
|
||||
IBaseOperationOutcome oo = result.toOperationOutcome();
|
||||
ourLog.info("Result:\n{}", ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(oo));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,331 +0,0 @@
|
|||
package example;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.*;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
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;
|
||||
|
||||
@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(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 org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent expandValueSet(FhirContext theContext, org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent theInclude) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchValueSet(FhirContext theContext, String theSystem) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
// TODO: implement
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSet) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
|
||||
// TODO: implement
|
||||
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.forDstu3();
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package example;
|
||||
|
||||
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package example.customtype;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.BackboneElement;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
|
||||
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;
|
||||
|
||||
//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
|
|
@ -1,63 +0,0 @@
|
|||
package example.customtype;
|
||||
|
||||
//START SNIPPET: datatype
|
||||
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;
|
||||
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.util.ElementUtil;
|
||||
|
||||
/**
|
||||
* 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
|
|
@ -1,86 +0,0 @@
|
|||
package example.customtype;
|
||||
|
||||
// START SNIPPET: resource
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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 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;
|
||||
|
||||
/**
|
||||
* 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
|
|
@ -1,44 +0,0 @@
|
|||
package example.customtype;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.DateTimeType;
|
||||
import org.hl7.fhir.dstu3.model.DateType;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package example.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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<configuration scan="true" scanPeriod="30 seconds">
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>4.3.0-SNAPSHOT</version>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -181,7 +181,16 @@
|
|||
</reports>
|
||||
<configuration>
|
||||
<links>
|
||||
<link>http://jamesagnew.github.io/hapi-fhir/apidocs/</link>
|
||||
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-base</link>
|
||||
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-dstu2</link>
|
||||
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-dstu3</link>
|
||||
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-r4</link>
|
||||
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-r5</link>
|
||||
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-client</link>
|
||||
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-server</link>
|
||||
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-jpaserver-base</link>
|
||||
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-converter</link>
|
||||
<link>https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-structures-validation</link>
|
||||
<link>https://docs.oracle.com/javaee/7/api/</link>
|
||||
</links>
|
||||
<additionalparam>-Xdoclint:none</additionalparam>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.3.0-SNAPSHOT</version>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.3.0-SNAPSHOT</version>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -20,8 +20,8 @@
|
|||
|
||||
<!-- JSON -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- XML -->
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
package ca.uhn.fhir.context;
|
||||
|
||||
import ca.uhn.fhir.context.api.AddProfileTagEnum;
|
||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||
import ca.uhn.fhir.fluentpath.IFluentPath;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.fhirpath.IFhirPath;
|
||||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||
import ca.uhn.fhir.model.api.IElement;
|
||||
import ca.uhn.fhir.model.api.IFhirVersion;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.view.ViewGenerator;
|
||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||
import ca.uhn.fhir.parser.*;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.IParserErrorHandler;
|
||||
import ca.uhn.fhir.parser.JsonParser;
|
||||
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||
import ca.uhn.fhir.parser.RDFParser;
|
||||
import ca.uhn.fhir.parser.XmlParser;
|
||||
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
|
@ -20,7 +27,6 @@ import ca.uhn.fhir.util.ReflectionUtil;
|
|||
import ca.uhn.fhir.util.VersionUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.jena.riot.Lang;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
|
@ -97,7 +103,7 @@ public class FhirContext {
|
|||
private Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
|
||||
private volatile IRestfulClientFactory myRestfulClientFactory;
|
||||
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
|
||||
private IContextValidationSupport<?, ?, ?, ?, ?, ?> myValidationSupport;
|
||||
private IValidationSupport myValidationSupport;
|
||||
private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap();
|
||||
|
||||
/**
|
||||
|
@ -371,6 +377,19 @@ public class FhirContext {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configured performance options
|
||||
*
|
||||
* @see PerformanceOptionsEnum for a list of available options
|
||||
*/
|
||||
public void setPerformanceOptions(final PerformanceOptionsEnum... thePerformanceOptions) {
|
||||
Collection<PerformanceOptionsEnum> asList = null;
|
||||
if (thePerformanceOptions != null) {
|
||||
asList = Arrays.asList(thePerformanceOptions);
|
||||
}
|
||||
setPerformanceOptions(asList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
|
||||
* for extending the core library.
|
||||
|
@ -543,16 +562,37 @@ public class FhirContext {
|
|||
|
||||
/**
|
||||
* Returns the validation support module configured for this context, creating a default
|
||||
* implementation if no module has been passed in via the {@link #setValidationSupport(IContextValidationSupport)}
|
||||
* implementation if no module has been passed in via the {@link #setValidationSupport(IValidationSupport)}
|
||||
* method
|
||||
*
|
||||
* @see #setValidationSupport(IContextValidationSupport)
|
||||
* @see #setValidationSupport(IValidationSupport)
|
||||
*/
|
||||
public IContextValidationSupport<?, ?, ?, ?, ?, ?> getValidationSupport() {
|
||||
if (myValidationSupport == null) {
|
||||
myValidationSupport = myVersion.createValidationSupport();
|
||||
public IValidationSupport getValidationSupport() {
|
||||
IValidationSupport retVal = myValidationSupport;
|
||||
if (retVal == null) {
|
||||
retVal = new DefaultProfileValidationSupport(this);
|
||||
|
||||
/*
|
||||
* If hapi-fhir-validation is on the classpath, we can create a much more robust
|
||||
* validation chain using the classes found in that package
|
||||
*/
|
||||
String inMemoryTermSvcType = "org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport";
|
||||
String commonCodeSystemsSupportType = "org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService";
|
||||
if (ReflectionUtil.typeExists(inMemoryTermSvcType)) {
|
||||
IValidationSupport inMemoryTermSvc = ReflectionUtil.newInstanceOrReturnNull(inMemoryTermSvcType, IValidationSupport.class, new Class<?>[]{FhirContext.class}, new Object[]{this});
|
||||
IValidationSupport commonCodeSystemsSupport = ReflectionUtil.newInstanceOrReturnNull(commonCodeSystemsSupportType, IValidationSupport.class, new Class<?>[]{FhirContext.class}, new Object[]{this});
|
||||
retVal = ReflectionUtil.newInstanceOrReturnNull("org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain", IValidationSupport.class, new Class<?>[]{IValidationSupport[].class}, new Object[]{new IValidationSupport[]{
|
||||
retVal,
|
||||
inMemoryTermSvc,
|
||||
commonCodeSystemsSupport
|
||||
}});
|
||||
assert retVal != null : "Failed to instantiate " + "org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain";
|
||||
}
|
||||
return myValidationSupport;
|
||||
|
||||
|
||||
myValidationSupport = retVal;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -560,7 +600,7 @@ public class FhirContext {
|
|||
* is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc)
|
||||
* as well as to provide terminology services to modules such as the validator and FluentPath executor
|
||||
*/
|
||||
public void setValidationSupport(IContextValidationSupport<?, ?, ?, ?, ?, ?> theValidationSupport) {
|
||||
public void setValidationSupport(IValidationSupport theValidationSupport) {
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
|
@ -585,12 +625,21 @@ public class FhirContext {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new FluentPath engine which can be used to exvaluate
|
||||
* @since 2.2
|
||||
* @deprecated Deprecated in HAPI FHIR 5.0.0. Use {@link #newFhirPath()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public IFhirPath newFluentPath() {
|
||||
return newFhirPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new FhirPath engine which can be used to evaluate
|
||||
* path expressions over FHIR resources. Note that this engine will use the
|
||||
* {@link IContextValidationSupport context validation support} module which is
|
||||
* {@link IValidationSupport context validation support} module which is
|
||||
* configured on the context at the time this method is called.
|
||||
* <p>
|
||||
* In other words, call {@link #setValidationSupport(IContextValidationSupport)} before
|
||||
* In other words, you may wish to call {@link #setValidationSupport(IValidationSupport)} before
|
||||
* calling {@link #newFluentPath()}
|
||||
* </p>
|
||||
* <p>
|
||||
|
@ -600,10 +649,10 @@ public class FhirContext {
|
|||
* {@link UnsupportedOperationException}
|
||||
* </p>
|
||||
*
|
||||
* @since 2.2
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public IFluentPath newFluentPath() {
|
||||
return myVersion.createFluentPathExecutor(this);
|
||||
public IFhirPath newFhirPath() {
|
||||
return myVersion.createFhirPathExecutor(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -641,7 +690,6 @@ public class FhirContext {
|
|||
return new RDFParser(this, myParserErrorHandler, Lang.TURTLE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a new client instance. This method requires an interface which is defined specifically for your use
|
||||
* cases to contain methods for each of the RESTful operations you wish to implement (e.g. "read ImagingStudy",
|
||||
|
@ -861,19 +909,6 @@ public class FhirContext {
|
|||
myParserErrorHandler = theParserErrorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configured performance options
|
||||
*
|
||||
* @see PerformanceOptionsEnum for a list of available options
|
||||
*/
|
||||
public void setPerformanceOptions(final PerformanceOptionsEnum... thePerformanceOptions) {
|
||||
Collection<PerformanceOptionsEnum> asList = null;
|
||||
if (thePerformanceOptions != null) {
|
||||
asList = Arrays.asList(thePerformanceOptions);
|
||||
}
|
||||
setPerformanceOptions(asList);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"cast"})
|
||||
private List<Class<? extends IElement>> toElementList(final Collection<Class<? extends IBaseResource>> theResourceTypes) {
|
||||
if (theResourceTypes == null) {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.dao.r5;
|
||||
package ca.uhn.fhir.context.support;
|
||||
|
||||
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
|
||||
|
||||
/*
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
|
@ -22,6 +20,17 @@ import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
public interface IJpaValidationSupportR5 extends IValidationSupport {
|
||||
// nothing yet
|
||||
public class ConceptValidationOptions {
|
||||
|
||||
public boolean isInferSystem() {
|
||||
return myInferSystem;
|
||||
}
|
||||
|
||||
public ConceptValidationOptions setInferSystem(boolean theInferSystem) {
|
||||
myInferSystem = theInferSystem;
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean myInferSystem;
|
||||
|
||||
}
|
|
@ -0,0 +1,354 @@
|
|||
package ca.uhn.fhir.context.support;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class DefaultProfileValidationSupport implements IValidationSupport {
|
||||
|
||||
private static final String URL_PREFIX_STRUCTURE_DEFINITION = "http://hl7.org/fhir/StructureDefinition/";
|
||||
private static final String URL_PREFIX_STRUCTURE_DEFINITION_BASE = "http://hl7.org/fhir/";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class);
|
||||
private final FhirContext myCtx;
|
||||
|
||||
private Map<String, IBaseResource> myCodeSystems;
|
||||
private Map<String, IBaseResource> myStructureDefinitions;
|
||||
private Map<String, IBaseResource> myValueSets;
|
||||
private List<String> myTerminologyResources;
|
||||
private List<String> myStructureDefinitionResources;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theFhirContext The context to use
|
||||
*/
|
||||
public DefaultProfileValidationSupport(FhirContext theFhirContext) {
|
||||
myCtx = theFhirContext;
|
||||
}
|
||||
|
||||
|
||||
private void initializeResourceLists() {
|
||||
|
||||
if (myTerminologyResources != null && myStructureDefinitionResources != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> terminologyResources = new ArrayList<>();
|
||||
List<String> structureDefinitionResources = new ArrayList<>();
|
||||
switch (getFhirContext().getVersion().getVersion()) {
|
||||
case DSTU2:
|
||||
case DSTU2_HL7ORG:
|
||||
terminologyResources.add("/org/hl7/fhir/instance/model/valueset/valuesets.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/instance/model/valueset/v2-tables.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/instance/model/valueset/v3-codesystems.xml");
|
||||
Properties profileNameProperties = new Properties();
|
||||
try {
|
||||
profileNameProperties.load(DefaultProfileValidationSupport.class.getResourceAsStream("/org/hl7/fhir/instance/model/profile/profiles.properties"));
|
||||
for (Object nextKey : profileNameProperties.keySet()) {
|
||||
structureDefinitionResources.add("/org/hl7/fhir/instance/model/profile/" + nextKey);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
break;
|
||||
case DSTU2_1:
|
||||
terminologyResources.add("/org/hl7/fhir/dstu2016may/model/valueset/valuesets.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/dstu2016may/model/valueset/v2-tables.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/dstu2016may/model/valueset/v3-codesystems.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/dstu2016may/model/profile/profiles-resources.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/dstu2016may/model/profile/profiles-types.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/dstu2016may/model/profile/profiles-others.xml");
|
||||
break;
|
||||
case DSTU3:
|
||||
terminologyResources.add("/org/hl7/fhir/dstu3/model/valueset/valuesets.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/dstu3/model/valueset/v2-tables.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/dstu3/model/valueset/v3-codesystems.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/dstu3/model/profile/profiles-resources.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/dstu3/model/profile/profiles-types.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/dstu3/model/profile/profiles-others.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/dstu3/model/extension/extension-definitions.xml");
|
||||
break;
|
||||
case R4:
|
||||
terminologyResources.add("/org/hl7/fhir/r4/model/valueset/valuesets.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/r4/model/valueset/v2-tables.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/r4/model/valueset/v3-codesystems.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/r4/model/profile/profiles-resources.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/r4/model/profile/profiles-types.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/r4/model/profile/profiles-others.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/r4/model/extension/extension-definitions.xml");
|
||||
break;
|
||||
case R5:
|
||||
structureDefinitionResources.add("/org/hl7/fhir/r5/model/profile/profiles-resources.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/r5/model/profile/profiles-types.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/r5/model/profile/profiles-others.xml");
|
||||
structureDefinitionResources.add("/org/hl7/fhir/r5/model/extension/extension-definitions.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/r5/model/valueset/valuesets.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/r5/model/valueset/v2-tables.xml");
|
||||
terminologyResources.add("/org/hl7/fhir/r5/model/valueset/v3-codesystems.xml");
|
||||
break;
|
||||
}
|
||||
|
||||
myTerminologyResources = terminologyResources;
|
||||
myStructureDefinitionResources = structureDefinitionResources;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> fetchAllConformanceResources() {
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<>();
|
||||
retVal.addAll(myCodeSystems.values());
|
||||
retVal.addAll(myStructureDefinitions.values());
|
||||
retVal.addAll(myValueSets.values());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
|
||||
return toList(provideStructureDefinitionMap());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchCodeSystem(String theSystem) {
|
||||
return fetchCodeSystemOrValueSet(theSystem, true);
|
||||
}
|
||||
|
||||
private IBaseResource fetchCodeSystemOrValueSet(String theSystem, boolean codeSystem) {
|
||||
synchronized (this) {
|
||||
Map<String, IBaseResource> codeSystems = myCodeSystems;
|
||||
Map<String, IBaseResource> valueSets = myValueSets;
|
||||
if (codeSystems == null || valueSets == null) {
|
||||
codeSystems = new HashMap<>();
|
||||
valueSets = new HashMap<>();
|
||||
|
||||
initializeResourceLists();
|
||||
for (String next : myTerminologyResources) {
|
||||
loadCodeSystems(codeSystems, valueSets, next);
|
||||
}
|
||||
|
||||
myCodeSystems = codeSystems;
|
||||
myValueSets = valueSets;
|
||||
}
|
||||
|
||||
// System can take the form "http://url|version"
|
||||
String system = theSystem;
|
||||
if (system.contains("|")) {
|
||||
String version = system.substring(system.indexOf('|') + 1);
|
||||
if (version.matches("^[0-9.]+$")) {
|
||||
system = system.substring(0, system.indexOf('|'));
|
||||
}
|
||||
}
|
||||
|
||||
if (codeSystem) {
|
||||
return codeSystems.get(system);
|
||||
} else {
|
||||
return valueSets.get(system);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchStructureDefinition(String theUrl) {
|
||||
String url = theUrl;
|
||||
if (url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) {
|
||||
// no change
|
||||
} else if (url.indexOf('/') == -1) {
|
||||
url = URL_PREFIX_STRUCTURE_DEFINITION + url;
|
||||
} else if (StringUtils.countMatches(url, '/') == 1) {
|
||||
url = URL_PREFIX_STRUCTURE_DEFINITION_BASE + url;
|
||||
}
|
||||
Map<String, IBaseResource> structureDefinitionMap = provideStructureDefinitionMap();
|
||||
IBaseResource retVal = structureDefinitionMap.get(url);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchValueSet(String theUrl) {
|
||||
IBaseResource retVal = fetchCodeSystemOrValueSet(theUrl, false);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
myCodeSystems = null;
|
||||
myStructureDefinitions = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
return myCtx;
|
||||
}
|
||||
|
||||
private Map<String, IBaseResource> provideStructureDefinitionMap() {
|
||||
Map<String, IBaseResource> structureDefinitions = myStructureDefinitions;
|
||||
if (structureDefinitions == null) {
|
||||
structureDefinitions = new HashMap<>();
|
||||
|
||||
initializeResourceLists();
|
||||
for (String next : myStructureDefinitionResources) {
|
||||
loadStructureDefinitions(structureDefinitions, next);
|
||||
}
|
||||
|
||||
myStructureDefinitions = structureDefinitions;
|
||||
}
|
||||
return structureDefinitions;
|
||||
}
|
||||
|
||||
private void loadCodeSystems(Map<String, IBaseResource> theCodeSystems, Map<String, IBaseResource> theValueSets, String theClasspath) {
|
||||
ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath);
|
||||
InputStream inputStream = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
|
||||
InputStreamReader reader = null;
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
reader = new InputStreamReader(inputStream, Constants.CHARSET_UTF8);
|
||||
List<IBaseResource> resources = parseBundle(reader);
|
||||
for (IBaseResource next : resources) {
|
||||
|
||||
RuntimeResourceDefinition nextDef = getFhirContext().getResourceDefinition(next);
|
||||
Map<String, IBaseResource> map = null;
|
||||
switch (nextDef.getName()) {
|
||||
case "CodeSystem":
|
||||
map = theCodeSystems;
|
||||
break;
|
||||
case "ValueSet":
|
||||
map = theValueSets;
|
||||
break;
|
||||
}
|
||||
|
||||
if (map != null) {
|
||||
String urlValueString = getConformanceResourceUrl(next);
|
||||
if (isNotBlank(urlValueString)) {
|
||||
map.put(urlValueString, next);
|
||||
}
|
||||
|
||||
switch (myCtx.getVersion().getVersion()) {
|
||||
case DSTU2:
|
||||
case DSTU2_HL7ORG:
|
||||
|
||||
IPrimitiveType<?> codeSystem = myCtx.newTerser().getSingleValueOrNull(next, "ValueSet.codeSystem.system", IPrimitiveType.class);
|
||||
if (codeSystem != null && isNotBlank(codeSystem.getValueAsString())) {
|
||||
theCodeSystems.put(codeSystem.getValueAsString(), next);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
case DSTU2_1:
|
||||
case DSTU3:
|
||||
case R4:
|
||||
case R5:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
ourLog.warn("Failure closing stream", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ourLog.warn("Unable to load resource: {}", theClasspath);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadStructureDefinitions(Map<String, IBaseResource> theCodeSystems, String theClasspath) {
|
||||
ourLog.info("Loading structure definitions from classpath: {}", theClasspath);
|
||||
try (InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath)) {
|
||||
if (valuesetText != null) {
|
||||
try (InputStreamReader reader = new InputStreamReader(valuesetText, Constants.CHARSET_UTF8)) {
|
||||
|
||||
List<IBaseResource> resources = parseBundle(reader);
|
||||
for (IBaseResource next : resources) {
|
||||
|
||||
String nextType = getFhirContext().getResourceDefinition(next).getName();
|
||||
if ("StructureDefinition".equals(nextType)) {
|
||||
|
||||
String url = getConformanceResourceUrl(next);
|
||||
if (isNotBlank(url)) {
|
||||
theCodeSystems.put(url, next);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ourLog.warn("Unable to load resource: {}", theClasspath);
|
||||
}
|
||||
} catch (IOException theE) {
|
||||
ourLog.warn("Unable to load resource: {}", theClasspath);
|
||||
}
|
||||
}
|
||||
|
||||
private String getConformanceResourceUrl(IBaseResource theResource) {
|
||||
String urlValueString = null;
|
||||
Optional<IBase> urlValue = getFhirContext().getResourceDefinition(theResource).getChildByName("url").getAccessor().getFirstValueOrNull(theResource);
|
||||
if (urlValue.isPresent()) {
|
||||
IPrimitiveType<?> urlValueType = (IPrimitiveType<?>) urlValue.get();
|
||||
urlValueString = urlValueType.getValueAsString();
|
||||
}
|
||||
return urlValueString;
|
||||
}
|
||||
|
||||
private List<IBaseResource> parseBundle(InputStreamReader theReader) {
|
||||
IBaseResource parsedObject = getFhirContext().newXmlParser().parseResource(theReader);
|
||||
if (parsedObject instanceof IBaseBundle) {
|
||||
IBaseBundle bundle = (IBaseBundle) parsedObject;
|
||||
return BundleUtil.toListOfResources(getFhirContext(), bundle);
|
||||
} else {
|
||||
return Collections.singletonList(parsedObject);
|
||||
}
|
||||
}
|
||||
|
||||
static <T extends IBaseResource> List<T> toList(Map<String, IBaseResource> theMap) {
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<>(theMap.values());
|
||||
return (List<T>) Collections.unmodifiableList(retVal);
|
||||
}
|
||||
|
||||
}
|
|
@ -23,12 +23,14 @@ package ca.uhn.fhir.context.support;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -42,33 +44,63 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* various functions that can be provided by validation and terminology
|
||||
* services.
|
||||
* <p>
|
||||
* This interface is invoked directly by internal parts of the HAPI FHIR API, including the
|
||||
* Validator and the FHIRPath evaluator. It is used to supply artifacts required for validation
|
||||
* (e.g. StructureDefinition resources, ValueSet resources, etc.) and also to provide
|
||||
* terminology functions such as code validation, ValueSet expansion, etc.
|
||||
* </p>
|
||||
* <p>
|
||||
* Implementations are not required to implement all of the functions
|
||||
* in this interface; in fact it is expected that most won't. Any
|
||||
* methods which are not implemented may simply return <code>null</code>
|
||||
* and calling code is expected to be able to handle this.
|
||||
* and calling code is expected to be able to handle this. Generally, a
|
||||
* series of implementations of this interface will be joined together using
|
||||
* the
|
||||
* <a href="https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-validation/org/hl7/fhir/common/hapi/validation/ValidationSupportChain2.html">ValidationSupportChain</a>
|
||||
* class.
|
||||
* </p>
|
||||
* <p>
|
||||
* See <a href="https://hapifhir.io/hapi-fhir/docs/validation/validation_support_modules.html">Validation Support Modules</a>
|
||||
* for information on how to assemble and configure implementations of this interface. See also
|
||||
* the <code>org.hl7.fhir.common.hapi.validation.support</code>
|
||||
* <a href="https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-validation/org/hl7/fhir/common/hapi/validation/package-summary.html">package summary</a>
|
||||
* in the <code>hapi-fhir-validation</code> module for many implementations of this interface.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> {
|
||||
public interface IValidationSupport {
|
||||
String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/";
|
||||
|
||||
|
||||
/**
|
||||
* Expands the given portion of a ValueSet
|
||||
*
|
||||
* @param theInclude The portion to include
|
||||
* @return The expansion
|
||||
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
|
||||
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
|
||||
* @param theExpansionOptions If provided (may be <code>null</code>), contains options controlling the expansion
|
||||
* @param theValueSetToExpand The valueset that should be expanded
|
||||
* @return The expansion, or null
|
||||
*/
|
||||
EVS_OUT expandValueSet(FhirContext theContext, EVS_IN theInclude);
|
||||
default ValueSetExpansionOutcome expandValueSet(IValidationSupport theRootValidationSupport, @Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and return all conformance resources associated with this
|
||||
* validation support module. This method may return null if it doesn't
|
||||
* make sense for a given module.
|
||||
*/
|
||||
List<IBaseResource> fetchAllConformanceResources(FhirContext theContext);
|
||||
default List<IBaseResource> fetchAllConformanceResources() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and return all possible structure definitions
|
||||
*/
|
||||
List<SDT> fetchAllStructureDefinitions(FhirContext theContext);
|
||||
default <T extends IBaseResource> List<T> fetchAllStructureDefinitions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a code system by ID
|
||||
|
@ -76,70 +108,146 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||
* @param theSystem The code system
|
||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
||||
*/
|
||||
CST fetchCodeSystem(FhirContext theContext, String theSystem);
|
||||
default IBaseResource fetchCodeSystem(String theSystem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a resource needed by the validation (a StructureDefinition, or a
|
||||
* ValueSet)
|
||||
*
|
||||
* @param theContext The HAPI FHIR Context object current in use by the validator
|
||||
* @param theClass The type of the resource to load
|
||||
* @param theUri The resource URI
|
||||
* @return Returns the resource, or <code>null</code> if no resource with the
|
||||
* given URI can be found
|
||||
*/
|
||||
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
|
||||
default <T extends IBaseResource> T fetchResource(Class<T> theClass, String theUri) {
|
||||
Validate.notNull(theClass, "theClass must not be null or blank");
|
||||
Validate.notBlank(theUri, "theUri must not be null or blank");
|
||||
|
||||
SDT fetchStructureDefinition(FhirContext theCtx, String theUrl);
|
||||
switch (getFhirContext().getResourceDefinition(theClass).getName()) {
|
||||
case "StructureDefinition":
|
||||
return theClass.cast(fetchStructureDefinition(theUri));
|
||||
case "ValueSet":
|
||||
return theClass.cast(fetchValueSet(theUri));
|
||||
case "CodeSystem":
|
||||
return theClass.cast(fetchCodeSystem(theUri));
|
||||
}
|
||||
|
||||
if (theUri.startsWith(URL_PREFIX_VALUE_SET)) {
|
||||
return theClass.cast(fetchValueSet(theUri));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
default IBaseResource fetchStructureDefinition(String theUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if codes in the given code system can be expanded
|
||||
* or validated
|
||||
*
|
||||
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
|
||||
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
|
||||
* @param theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code>
|
||||
* @return Returns <code>true</code> if codes in the given code system can be
|
||||
* validated
|
||||
*/
|
||||
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
|
||||
default boolean isCodeSystemSupported(IValidationSupport theRootValidationSupport, String theSystem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the given ValueSet by URL
|
||||
*/
|
||||
IBaseResource fetchValueSet(FhirContext theContext, String theValueSetUrl);
|
||||
default IBaseResource fetchValueSet(String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given code exists and if possible returns a display
|
||||
* name. This method is called to check codes which are found in "example"
|
||||
* binding fields (e.g. <code>Observation.code</code> in the default profile.
|
||||
*
|
||||
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
|
||||
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
|
||||
* @param theOptions Provides options controlling the validation
|
||||
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
|
||||
* @param theCode The code, e.g. "<code>1234-5</code>"
|
||||
* @param theDisplay The display name, if it should also be validated
|
||||
* @return Returns a validation result object
|
||||
*/
|
||||
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl);
|
||||
default CodeValidationResult validateCode(IValidationSupport theRootValidationSupport, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given code exists and if possible returns a display
|
||||
* name. This method is called to check codes which are found in "example"
|
||||
* binding fields (e.g. <code>Observation.code</code> in the default profile.
|
||||
*
|
||||
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
|
||||
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
|
||||
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
|
||||
* @param theCode The code, e.g. "<code>1234-5</code>"
|
||||
* @param theDisplay The display name, if it should also be validated
|
||||
* @param theValueSet The ValueSet to validate against. Must not be null, and must be a ValueSet resource.
|
||||
* @return Returns a validation result object, or <code>null</code> if this validation support module can not handle this kind of request
|
||||
*/
|
||||
default CodeValidationResult validateCodeInValueSet(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { return null; }
|
||||
default CodeValidationResult validateCodeInValueSet(IValidationSupport theRootValidationSupport, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a code using the system and code value
|
||||
*
|
||||
* @param theContext The FHIR context
|
||||
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
|
||||
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
|
||||
* @param theSystem The CodeSystem URL
|
||||
* @param theCode The code
|
||||
*/
|
||||
LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode);
|
||||
default LookupCodeResult lookupCode(IValidationSupport theRootValidationSupport, String theSystem, String theCode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the given valueset can be validated by the given
|
||||
* validation support module
|
||||
*
|
||||
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
|
||||
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
|
||||
* @param theValueSetUrl The ValueSet canonical URL
|
||||
*/
|
||||
default boolean isValueSetSupported(IValidationSupport theRootValidationSupport, String theValueSetUrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a snapshot from the given differential profile.
|
||||
*
|
||||
* @param theRootValidationSupport The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
|
||||
* other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
|
||||
* @return Returns null if this module does not know how to handle this request
|
||||
*/
|
||||
default IBaseResource generateSnapshot(IValidationSupport theRootValidationSupport, IBaseResource theInput, String theUrl, String theWebUrl, String theProfileName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FHIR Context associated with this module
|
||||
*/
|
||||
FhirContext getFhirContext();
|
||||
|
||||
/**
|
||||
* This method clears any temporary caches within the validation support. It is mainly intended for unit tests,
|
||||
* but could be used in non-test scenarios as well.
|
||||
*/
|
||||
default void invalidateCaches() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
|
||||
class ConceptDesignation {
|
||||
private String myLanguage;
|
||||
|
@ -257,59 +365,83 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||
}
|
||||
}
|
||||
|
||||
enum IssueSeverity {
|
||||
/**
|
||||
* The issue caused the action to fail, and no further checking could be performed.
|
||||
*/
|
||||
FATAL,
|
||||
/**
|
||||
* The issue is sufficiently important to cause the action to fail.
|
||||
*/
|
||||
ERROR,
|
||||
/**
|
||||
* The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired.
|
||||
*/
|
||||
WARNING,
|
||||
/**
|
||||
* The issue has no relation to the degree of success of the action.
|
||||
*/
|
||||
INFORMATION
|
||||
}
|
||||
|
||||
class CodeValidationResult {
|
||||
private IBase myDefinition;
|
||||
private String myCode;
|
||||
private String myMessage;
|
||||
private Enum mySeverity;
|
||||
private IssueSeverity mySeverity;
|
||||
private String myCodeSystemName;
|
||||
private String myCodeSystemVersion;
|
||||
private List<BaseConceptProperty> myProperties;
|
||||
private String myDisplay;
|
||||
|
||||
public CodeValidationResult(IBase theDefinition) {
|
||||
this.myDefinition = theDefinition;
|
||||
}
|
||||
|
||||
public CodeValidationResult(Enum theSeverity, String message) {
|
||||
this.mySeverity = theSeverity;
|
||||
this.myMessage = message;
|
||||
}
|
||||
|
||||
public CodeValidationResult(Enum theSeverity, String theMessage, IBase theDefinition, String theDisplay) {
|
||||
this.mySeverity = theSeverity;
|
||||
this.myMessage = theMessage;
|
||||
this.myDefinition = theDefinition;
|
||||
this.myDisplay = theDisplay;
|
||||
public CodeValidationResult() {
|
||||
super();
|
||||
}
|
||||
|
||||
public String getDisplay() {
|
||||
return myDisplay;
|
||||
}
|
||||
|
||||
public IBase asConceptDefinition() {
|
||||
return myDefinition;
|
||||
public CodeValidationResult setDisplay(String theDisplay) {
|
||||
myDisplay = theDisplay;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return myCode;
|
||||
}
|
||||
|
||||
public CodeValidationResult setCode(String theCode) {
|
||||
myCode = theCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
String getCodeSystemName() {
|
||||
return myCodeSystemName;
|
||||
}
|
||||
|
||||
public void setCodeSystemName(String theCodeSystemName) {
|
||||
public CodeValidationResult setCodeSystemName(String theCodeSystemName) {
|
||||
myCodeSystemName = theCodeSystemName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getCodeSystemVersion() {
|
||||
return myCodeSystemVersion;
|
||||
}
|
||||
|
||||
public void setCodeSystemVersion(String theCodeSystemVersion) {
|
||||
public CodeValidationResult setCodeSystemVersion(String theCodeSystemVersion) {
|
||||
myCodeSystemVersion = theCodeSystemVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return myMessage;
|
||||
}
|
||||
|
||||
public CodeValidationResult setMessage(String theMessage) {
|
||||
myMessage = theMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<BaseConceptProperty> getProperties() {
|
||||
return myProperties;
|
||||
}
|
||||
|
@ -318,12 +450,17 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||
myProperties = theProperties;
|
||||
}
|
||||
|
||||
public Enum getSeverity() {
|
||||
public IssueSeverity getSeverity() {
|
||||
return mySeverity;
|
||||
}
|
||||
|
||||
public CodeValidationResult setSeverity(IssueSeverity theSeverity) {
|
||||
mySeverity = theSeverity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isOk() {
|
||||
return myDefinition != null;
|
||||
return isNotBlank(myCode);
|
||||
}
|
||||
|
||||
public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) {
|
||||
|
@ -339,6 +476,49 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that returns {@link #getSeverity()} as an IssueSeverity code string
|
||||
*/
|
||||
public String getSeverityCode() {
|
||||
String retVal = null;
|
||||
if (getSeverity() != null) {
|
||||
retVal = getSeverity().name().toLowerCase();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an issue severity as a string code. Value must be the name of
|
||||
* one of the enum values in {@link IssueSeverity}. Value is case-insensitive.
|
||||
*/
|
||||
public CodeValidationResult setSeverityCode(@Nonnull String theIssueSeverity) {
|
||||
setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase()));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class ValueSetExpansionOutcome {
|
||||
|
||||
private final IBaseResource myValueSet;
|
||||
private final String myError;
|
||||
|
||||
public ValueSetExpansionOutcome(IBaseResource theValueSet, String theError) {
|
||||
myValueSet = theValueSet;
|
||||
myError = theError;
|
||||
}
|
||||
|
||||
public ValueSetExpansionOutcome(IBaseResource theValueSet) {
|
||||
myValueSet = theValueSet;
|
||||
myError = null;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return myError;
|
||||
}
|
||||
|
||||
public IBaseResource getValueSet() {
|
||||
return myValueSet;
|
||||
}
|
||||
}
|
||||
|
||||
class LookupCodeResult {
|
||||
|
@ -350,7 +530,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||
private boolean myFound;
|
||||
private String mySearchedForCode;
|
||||
private String mySearchedForSystem;
|
||||
private List<IContextValidationSupport.BaseConceptProperty> myProperties;
|
||||
private List<IValidationSupport.BaseConceptProperty> myProperties;
|
||||
private List<ConceptDesignation> myDesignations;
|
||||
|
||||
/**
|
||||
|
@ -367,7 +547,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||
return myProperties;
|
||||
}
|
||||
|
||||
public void setProperties(List<IContextValidationSupport.BaseConceptProperty> theProperties) {
|
||||
public void setProperties(List<IValidationSupport.BaseConceptProperty> theProperties) {
|
||||
myProperties = theProperties;
|
||||
}
|
||||
|
||||
|
@ -466,7 +646,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
for (IContextValidationSupport.BaseConceptProperty next : myProperties) {
|
||||
for (IValidationSupport.BaseConceptProperty next : myProperties) {
|
||||
|
||||
if (!properties.isEmpty()) {
|
||||
if (!properties.contains(next.getPropertyName())) {
|
||||
|
@ -477,11 +657,11 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
|
||||
ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName());
|
||||
|
||||
if (next instanceof IContextValidationSupport.StringConceptProperty) {
|
||||
IContextValidationSupport.StringConceptProperty prop = (IContextValidationSupport.StringConceptProperty) next;
|
||||
if (next instanceof IValidationSupport.StringConceptProperty) {
|
||||
IValidationSupport.StringConceptProperty prop = (IValidationSupport.StringConceptProperty) next;
|
||||
ParametersUtil.addPartString(theContext, property, "value", prop.getValue());
|
||||
} else if (next instanceof IContextValidationSupport.CodingConceptProperty) {
|
||||
IContextValidationSupport.CodingConceptProperty prop = (IContextValidationSupport.CodingConceptProperty) next;
|
||||
} else if (next instanceof IValidationSupport.CodingConceptProperty) {
|
||||
IValidationSupport.CodingConceptProperty prop = (IValidationSupport.CodingConceptProperty) next;
|
||||
ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay());
|
||||
} else {
|
||||
throw new IllegalStateException("Don't know how to handle " + next.getClass());
|
|
@ -0,0 +1,96 @@
|
|||
package ca.uhn.fhir.context.support;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* Options for ValueSet expansion
|
||||
*
|
||||
* @see IValidationSupport
|
||||
*/
|
||||
public class ValueSetExpansionOptions {
|
||||
|
||||
private boolean myFailOnMissingCodeSystem = true;
|
||||
private int myCount = 1000;
|
||||
private int myOffset = 0;
|
||||
|
||||
/**
|
||||
* The number of codes to return.
|
||||
* <p>
|
||||
* Default is 1000
|
||||
* </p>
|
||||
*/
|
||||
public int getCount() {
|
||||
return myCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of codes to return.
|
||||
* <p>
|
||||
* Default is 1000
|
||||
* </p>
|
||||
*/
|
||||
public ValueSetExpansionOptions setCount(int theCount) {
|
||||
Validate.isTrue(theCount >= 0, "theCount must be >= 0");
|
||||
myCount = theCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The code index to start at (i.e the individual code index, not the page number)
|
||||
*/
|
||||
public int getOffset() {
|
||||
return myOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* The code index to start at (i.e the individual code index, not the page number)
|
||||
*/
|
||||
public ValueSetExpansionOptions setOffset(int theOffset) {
|
||||
Validate.isTrue(theOffset >= 0, "theOffset must be >= 0");
|
||||
myOffset = theOffset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the expansion fail if a codesystem is referenced by the valueset, but
|
||||
* it can not be found?
|
||||
* <p>
|
||||
* Default is <code>true</code>
|
||||
* </p>
|
||||
*/
|
||||
public boolean isFailOnMissingCodeSystem() {
|
||||
return myFailOnMissingCodeSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the expansion fail if a codesystem is referenced by the valueset, but
|
||||
* it can not be found?
|
||||
* <p>
|
||||
* Default is <code>true</code>
|
||||
* </p>
|
||||
*/
|
||||
public ValueSetExpansionOptions setFailOnMissingCodeSystem(boolean theFailOnMissingCodeSystem) {
|
||||
myFailOnMissingCodeSystem = theFailOnMissingCodeSystem;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package ca.uhn.fhir.fluentpath;
|
||||
package ca.uhn.fhir.fhirpath;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -23,18 +23,18 @@ package ca.uhn.fhir.fluentpath;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
/**
|
||||
* This exception is thrown if a FluentPath expression can not be executed successfully
|
||||
* This exception is thrown if a FHIRPath expression can not be executed successfully
|
||||
* for any reason
|
||||
*/
|
||||
public class FluentPathExecutionException extends InternalErrorException {
|
||||
public class FhirPathExecutionException extends InternalErrorException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public FluentPathExecutionException(Throwable theCause) {
|
||||
public FhirPathExecutionException(Throwable theCause) {
|
||||
super(theCause);
|
||||
}
|
||||
|
||||
public FluentPathExecutionException(String theMessage) {
|
||||
public FhirPathExecutionException(String theMessage) {
|
||||
super(theMessage);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ca.uhn.fhir.fluentpath;
|
||||
package ca.uhn.fhir.fhirpath;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -25,7 +25,7 @@ import java.util.Optional;
|
|||
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
|
||||
public interface IFluentPath {
|
||||
public interface IFhirPath {
|
||||
|
||||
/**
|
||||
* Apply the given FluentPath expression against the given input and return
|
|
@ -43,6 +43,7 @@ public class HapiLocalizer {
|
|||
private List<ResourceBundle> myBundle = new ArrayList<>();
|
||||
private final Map<String, String> myHardcodedMessages = new HashMap<>();
|
||||
private String[] myBundleNames;
|
||||
private Locale myLocale = Locale.getDefault();
|
||||
|
||||
public HapiLocalizer() {
|
||||
this(HapiLocalizer.class.getPackage().getName() + ".hapi-messages");
|
||||
|
@ -164,6 +165,10 @@ public class HapiLocalizer {
|
|||
}
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return myLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* This <b>global setting</b> causes the localizer to fail if any attempts
|
||||
* are made to retrieve a key that does not exist. This method is primarily for
|
||||
|
|
|
@ -23,11 +23,10 @@ package ca.uhn.fhir.model.api;
|
|||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
|
||||
import ca.uhn.fhir.fhirpath.IFhirPath;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||
import ca.uhn.fhir.fluentpath.IFluentPath;
|
||||
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
|
||||
|
||||
/**
|
||||
|
@ -39,9 +38,7 @@ import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
|
|||
*/
|
||||
public interface IFhirVersion {
|
||||
|
||||
IFluentPath createFluentPathExecutor(FhirContext theFhirContext);
|
||||
|
||||
IContextValidationSupport<?, ?, ?, ?, ?, ?> createValidationSupport();
|
||||
IFhirPath createFhirPathExecutor(FhirContext theFhirContext);
|
||||
|
||||
IBaseResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase);
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
package ca.uhn.fhir.model.api;
|
||||
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
|
||||
/*
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
|
@ -23,6 +20,10 @@ import org.hl7.fhir.r4.model.StructureDefinition;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
public interface IJpaValidationSupportR4 extends IValidationSupport {
|
||||
// nothing yet
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public interface IModelJson {
|
||||
}
|
|
@ -24,7 +24,7 @@ import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
|||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.fluentpath.IFluentPath;
|
||||
import ca.uhn.fhir.fhirpath.IFhirPath;
|
||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
|
@ -120,7 +120,7 @@ public abstract class BaseNarrativeGenerator implements INarrativeGenerator {
|
|||
if (theFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) {
|
||||
return Collections.singletonList(theResource);
|
||||
}
|
||||
IFluentPath fhirPath = theFhirContext.newFluentPath();
|
||||
IFhirPath fhirPath = theFhirContext.newFluentPath();
|
||||
return fhirPath.evaluate(theResource, theContextPath, IBase.class);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,20 +22,29 @@ package ca.uhn.fhir.parser;
|
|||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
|
||||
import ca.uhn.fhir.model.api.*;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
||||
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.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||
import ca.uhn.fhir.parser.json.*;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeArray;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeObject;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeStructure;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeValue;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeWriter;
|
||||
import ca.uhn.fhir.parser.json.jackson.JacksonStructure;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.util.ElementUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.text.WordUtils;
|
||||
|
@ -45,11 +54,17 @@ import java.io.IOException;
|
|||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.ID_DATATYPE;
|
||||
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.PRIMITIVE_DATATYPE;
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use
|
||||
|
@ -147,10 +162,9 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
theEventWriter.beginObject(arrayName);
|
||||
}
|
||||
|
||||
private JsonLikeWriter createJsonWriter(Writer theWriter) {
|
||||
JsonLikeStructure jsonStructure = new GsonStructure();
|
||||
JsonLikeWriter retVal = jsonStructure.getJsonLikeWriter(theWriter);
|
||||
return retVal;
|
||||
private JsonLikeWriter createJsonWriter(Writer theWriter) throws IOException {
|
||||
JsonLikeStructure jsonStructure = new JacksonStructure();
|
||||
return jsonStructure.getJsonLikeWriter(theWriter);
|
||||
}
|
||||
|
||||
public void doEncodeResourceToJsonLikeWriter(IBaseResource theResource, JsonLikeWriter theEventWriter, EncodeContext theEncodeContext) throws IOException {
|
||||
|
@ -168,11 +182,12 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter, EncodeContext theEncodeContext) throws IOException {
|
||||
JsonLikeWriter eventWriter = createJsonWriter(theWriter);
|
||||
doEncodeResourceToJsonLikeWriter(theResource, eventWriter, theEncodeContext);
|
||||
eventWriter.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
|
||||
JsonLikeStructure jsonStructure = new GsonStructure();
|
||||
JsonLikeStructure jsonStructure = new JacksonStructure();
|
||||
jsonStructure.load(theReader);
|
||||
|
||||
T retVal = doParseResource(theResourceType, jsonStructure);
|
||||
|
@ -302,19 +317,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
}
|
||||
case CONTAINED_RESOURCE_LIST:
|
||||
case CONTAINED_RESOURCES: {
|
||||
/*
|
||||
* Disabled per #103 ContainedDt value = (ContainedDt) theNextValue; for (IResource next :
|
||||
* value.getContainedResources()) { if (getContainedResources().getResourceId(next) != null) { continue; }
|
||||
* encodeResourceToJsonStreamWriter(theResDef, next, theWriter, null, true,
|
||||
* fixContainedResourceId(next.getId().getValue())); }
|
||||
*/
|
||||
List<IBaseResource> containedResources = getContainedResources().getContainedResources();
|
||||
if (containedResources.size() > 0) {
|
||||
beginArray(theEventWriter, theChildName);
|
||||
|
||||
for (IBaseResource next : containedResources) {
|
||||
IIdType resourceId = getContainedResources().getResourceId(next);
|
||||
encodeResourceToJsonStreamWriter(theResDef, next, theEventWriter, null, true, fixContainedResourceId(resourceId.getValue()), theEncodeContext);
|
||||
String value = resourceId.getValue();
|
||||
encodeResourceToJsonStreamWriter(theResDef, next, theEventWriter, null, true, fixContainedResourceId(value), theEncodeContext);
|
||||
}
|
||||
|
||||
theEventWriter.endArray();
|
||||
|
@ -344,7 +354,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
|
||||
|
||||
theEncodeContext.pushPath(def.getName(), true);
|
||||
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, false, theEncodeContext);
|
||||
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, theContainedResource, theEncodeContext);
|
||||
theEncodeContext.popPath();
|
||||
|
||||
break;
|
||||
|
@ -418,10 +428,10 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
String currentChildName = null;
|
||||
boolean inArray = false;
|
||||
|
||||
ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<ArrayList<HeldExtension>>(0);
|
||||
ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<ArrayList<HeldExtension>>(0);
|
||||
ArrayList<ArrayList<String>> comments = new ArrayList<ArrayList<String>>(0);
|
||||
ArrayList<String> ids = new ArrayList<String>(0);
|
||||
ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<>(0);
|
||||
ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<>(0);
|
||||
ArrayList<ArrayList<String>> comments = new ArrayList<>(0);
|
||||
ArrayList<String> ids = new ArrayList<>(0);
|
||||
|
||||
int valueIdx = 0;
|
||||
for (IBase nextValue : values) {
|
||||
|
@ -1107,7 +1117,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
} else {
|
||||
// must be a SCALAR
|
||||
theState.enteringNewElement(null, theName);
|
||||
theState.attributeValue("value", theJsonVal.getAsString());
|
||||
String asString = theJsonVal.getAsString();
|
||||
theState.attributeValue("value", asString);
|
||||
parseAlternates(theAlternateVal, theState, theAlternateName, theAlternateName);
|
||||
theState.endingElement();
|
||||
}
|
||||
|
@ -1376,11 +1387,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
}
|
||||
}
|
||||
|
||||
public static Gson newGson() {
|
||||
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||
return gson;
|
||||
}
|
||||
|
||||
private static void write(JsonLikeWriter theWriter, String theName, String theValue) throws IOException {
|
||||
theWriter.write(theName, theValue);
|
||||
}
|
||||
|
|
|
@ -1074,7 +1074,7 @@ class ParserState<T> {
|
|||
*/
|
||||
for (IBaseResource next : myGlobalResources) {
|
||||
IIdType id = next.getIdElement();
|
||||
if (id != null && id.isEmpty() == false) {
|
||||
if (id != null && !id.isEmpty()) {
|
||||
String resName = myContext.getResourceDefinition(next).getName();
|
||||
IIdType idType = id.withResourceType(resName).toUnqualifiedVersionless();
|
||||
idToResource.put(idType.getValueAsString(), next);
|
||||
|
@ -1082,10 +1082,11 @@ class ParserState<T> {
|
|||
}
|
||||
|
||||
for (IBaseReference nextRef : myGlobalReferences) {
|
||||
if (nextRef.isEmpty() == false && nextRef.getReferenceElement() != null) {
|
||||
if (!nextRef.isEmpty() && nextRef.getReferenceElement() != null) {
|
||||
IIdType unqualifiedVersionless = nextRef.getReferenceElement().toUnqualifiedVersionless();
|
||||
IBaseResource target = idToResource.get(unqualifiedVersionless.getValueAsString());
|
||||
if (target != null) {
|
||||
// resource can already be filled with local contained resource by populateTarget()
|
||||
if (target != null && nextRef.getResource() == null) {
|
||||
nextRef.setResource(target);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -287,7 +287,8 @@ public class XmlParser extends BaseParser {
|
|||
for (IBaseResource next : getContainedResources().getContainedResources()) {
|
||||
IIdType resourceId = getContainedResources().getResourceId(next);
|
||||
theEventWriter.writeStartElement("contained");
|
||||
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(resourceId.getValue()), theEncodeContext);
|
||||
String value = resourceId.getValue();
|
||||
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(value), theEncodeContext);
|
||||
theEventWriter.writeEndElement();
|
||||
}
|
||||
break;
|
||||
|
@ -300,7 +301,7 @@ public class XmlParser extends BaseParser {
|
|||
}
|
||||
theEventWriter.writeStartElement(theChildName);
|
||||
theEncodeContext.pushPath(resourceName, true);
|
||||
encodeResourceToXmlStreamWriter(resource, theEventWriter, false, theEncodeContext);
|
||||
encodeResourceToXmlStreamWriter(resource, theEventWriter, theIncludedResource, theEncodeContext);
|
||||
theEncodeContext.popPath();
|
||||
theEventWriter.writeEndElement();
|
||||
break;
|
||||
|
|
|
@ -1,379 +0,0 @@
|
|||
package ca.uhn.fhir.parser.json;
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.PushbackReader;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
public class GsonStructure implements JsonLikeStructure {
|
||||
|
||||
private enum ROOT_TYPE {OBJECT, ARRAY};
|
||||
private ROOT_TYPE rootType = null;
|
||||
private JsonElement nativeRoot = null;
|
||||
private JsonLikeValue jsonLikeRoot = null;
|
||||
private GsonWriter jsonLikeWriter = null;
|
||||
|
||||
public GsonStructure() {
|
||||
super();
|
||||
}
|
||||
|
||||
public void setNativeObject (JsonObject json) {
|
||||
this.rootType = ROOT_TYPE.OBJECT;
|
||||
this.nativeRoot = json;
|
||||
}
|
||||
public void setNativeArray (JsonArray json) {
|
||||
this.rootType = ROOT_TYPE.ARRAY;
|
||||
this.nativeRoot = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeStructure getInstance() {
|
||||
return new GsonStructure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Reader theReader) throws DataFormatException {
|
||||
this.load(theReader, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Reader theReader, boolean allowArray) throws DataFormatException {
|
||||
PushbackReader pbr = new PushbackReader(theReader);
|
||||
int nextInt;
|
||||
try {
|
||||
while(true) {
|
||||
nextInt = pbr.read();
|
||||
if (nextInt == -1) {
|
||||
throw new DataFormatException("Did not find any content to parse");
|
||||
}
|
||||
if (nextInt == '{') {
|
||||
pbr.unread(nextInt);
|
||||
break;
|
||||
}
|
||||
if (Character.isWhitespace(nextInt)) {
|
||||
continue;
|
||||
}
|
||||
if (allowArray) {
|
||||
if (nextInt == '[') {
|
||||
pbr.unread(nextInt);
|
||||
break;
|
||||
}
|
||||
throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char)nextInt + "' (must be '{' or '[')");
|
||||
}
|
||||
throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char)nextInt + "' (must be '{')");
|
||||
}
|
||||
|
||||
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||
if (nextInt == '{') {
|
||||
JsonObject root = gson.fromJson(pbr, JsonObject.class);
|
||||
setNativeObject(root);
|
||||
} else if (nextInt == '[') {
|
||||
JsonArray root = gson.fromJson(pbr, JsonArray.class);
|
||||
setNativeArray(root);
|
||||
}
|
||||
} catch (JsonSyntaxException e) {
|
||||
if (e.getMessage().startsWith("Unexpected char 39")) {
|
||||
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage() + " - This may indicate that single quotes are being used as JSON escapes where double quotes are required", e);
|
||||
}
|
||||
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage(), e);
|
||||
} catch (Exception e) {
|
||||
throw new DataFormatException("Failed to parse JSON content, error was: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter getJsonLikeWriter (Writer writer) {
|
||||
if (null == jsonLikeWriter) {
|
||||
jsonLikeWriter = new GsonWriter(writer);
|
||||
}
|
||||
return jsonLikeWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter getJsonLikeWriter () {
|
||||
if (null == jsonLikeWriter) {
|
||||
jsonLikeWriter = new GsonWriter();
|
||||
}
|
||||
return jsonLikeWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeObject getRootObject() throws DataFormatException {
|
||||
if (rootType == ROOT_TYPE.OBJECT) {
|
||||
if (null == jsonLikeRoot) {
|
||||
jsonLikeRoot = new GsonJsonObject((JsonObject)nativeRoot);
|
||||
}
|
||||
return jsonLikeRoot.getAsObject();
|
||||
}
|
||||
throw new DataFormatException("Content must be a valid JSON Object. It must start with '{'.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeArray getRootArray() throws DataFormatException {
|
||||
if (rootType == ROOT_TYPE.ARRAY) {
|
||||
if (null == jsonLikeRoot) {
|
||||
jsonLikeRoot = new GsonJsonArray((JsonArray)nativeRoot);
|
||||
}
|
||||
return jsonLikeRoot.getAsArray();
|
||||
}
|
||||
throw new DataFormatException("Content must be a valid JSON Array. It must start with '['.");
|
||||
}
|
||||
|
||||
private static class GsonJsonObject extends JsonLikeObject {
|
||||
private JsonObject nativeObject;
|
||||
private Set<String> keySet = null;
|
||||
private Map<String,JsonLikeValue> jsonLikeMap = new LinkedHashMap<String,JsonLikeValue>();
|
||||
|
||||
public GsonJsonObject (JsonObject json) {
|
||||
this.nativeObject = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
if (null == keySet) {
|
||||
Set<Entry<String, JsonElement>> entrySet = nativeObject.entrySet();
|
||||
keySet = new EntryOrderedSet<String>(entrySet.size());
|
||||
for (Entry<String,?> entry : entrySet) {
|
||||
keySet.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
return keySet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeValue get(String key) {
|
||||
JsonLikeValue result = null;
|
||||
if (jsonLikeMap.containsKey(key)) {
|
||||
result = jsonLikeMap.get(key);
|
||||
} else {
|
||||
JsonElement child = nativeObject.get(key);
|
||||
if (child != null) {
|
||||
result = new GsonJsonValue(child);
|
||||
}
|
||||
jsonLikeMap.put(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class GsonJsonArray extends JsonLikeArray {
|
||||
private JsonArray nativeArray;
|
||||
private Map<Integer,JsonLikeValue> jsonLikeMap = new LinkedHashMap<Integer,JsonLikeValue>();
|
||||
|
||||
public GsonJsonArray (JsonArray json) {
|
||||
this.nativeArray = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return nativeArray.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeValue get(int index) {
|
||||
Integer key = Integer.valueOf(index);
|
||||
JsonLikeValue result = null;
|
||||
if (jsonLikeMap.containsKey(key)) {
|
||||
result = jsonLikeMap.get(key);
|
||||
} else {
|
||||
JsonElement child = nativeArray.get(index);
|
||||
if (child != null) {
|
||||
result = new GsonJsonValue(child);
|
||||
}
|
||||
jsonLikeMap.put(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class GsonJsonValue extends JsonLikeValue {
|
||||
private JsonElement nativeValue;
|
||||
private JsonLikeObject jsonLikeObject = null;
|
||||
private JsonLikeArray jsonLikeArray = null;
|
||||
|
||||
public GsonJsonValue (JsonElement json) {
|
||||
this.nativeValue = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
if (nativeValue != null && nativeValue.isJsonPrimitive()) {
|
||||
if (((JsonPrimitive)nativeValue).isNumber()) {
|
||||
return nativeValue.getAsNumber();
|
||||
}
|
||||
if (((JsonPrimitive)nativeValue).isBoolean()) {
|
||||
return Boolean.valueOf(nativeValue.getAsBoolean());
|
||||
}
|
||||
return nativeValue.getAsString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueType getJsonType() {
|
||||
if (null == nativeValue || nativeValue.isJsonNull()) {
|
||||
return ValueType.NULL;
|
||||
}
|
||||
if (nativeValue.isJsonObject()) {
|
||||
return ValueType.OBJECT;
|
||||
}
|
||||
if (nativeValue.isJsonArray()) {
|
||||
return ValueType.ARRAY;
|
||||
}
|
||||
if (nativeValue.isJsonPrimitive()) {
|
||||
return ValueType.SCALAR;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType getDataType() {
|
||||
if (nativeValue != null && nativeValue.isJsonPrimitive()) {
|
||||
if (((JsonPrimitive)nativeValue).isNumber()) {
|
||||
return ScalarType.NUMBER;
|
||||
}
|
||||
if (((JsonPrimitive)nativeValue).isString()) {
|
||||
return ScalarType.STRING;
|
||||
}
|
||||
if (((JsonPrimitive)nativeValue).isBoolean()) {
|
||||
return ScalarType.BOOLEAN;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeArray getAsArray() {
|
||||
if (nativeValue != null && nativeValue.isJsonArray()) {
|
||||
if (null == jsonLikeArray) {
|
||||
jsonLikeArray = new GsonJsonArray((JsonArray)nativeValue);
|
||||
}
|
||||
}
|
||||
return jsonLikeArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeObject getAsObject() {
|
||||
if (nativeValue != null && nativeValue.isJsonObject()) {
|
||||
if (null == jsonLikeObject) {
|
||||
jsonLikeObject = new GsonJsonObject((JsonObject)nativeValue);
|
||||
}
|
||||
}
|
||||
return jsonLikeObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number getAsNumber() {
|
||||
return nativeValue != null ? nativeValue.getAsNumber() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsString() {
|
||||
return nativeValue != null ? nativeValue.getAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAsBoolean() {
|
||||
if (nativeValue != null && nativeValue.isJsonPrimitive() && ((JsonPrimitive)nativeValue).isBoolean()) {
|
||||
return nativeValue.getAsBoolean();
|
||||
}
|
||||
return super.getAsBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
private static class EntryOrderedSet<T> extends AbstractSet<T> {
|
||||
private transient ArrayList<T> data = null;
|
||||
|
||||
public EntryOrderedSet (int initialCapacity) {
|
||||
data = new ArrayList<T>(initialCapacity);
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
public EntryOrderedSet () {
|
||||
data = new ArrayList<T>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return data.contains(o);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") // not really.. just not here
|
||||
public T get(int index) {
|
||||
return data.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T element) {
|
||||
if (data.contains(element)) {
|
||||
return false;
|
||||
}
|
||||
return data.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return data.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return data.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,263 +0,0 @@
|
|||
package ca.uhn.fhir.parser.json;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
public class GsonWriter extends JsonLikeWriter {
|
||||
private static final Logger log = LoggerFactory.getLogger(GsonWriter.class);
|
||||
|
||||
private JsonWriter eventWriter;
|
||||
private enum BlockType {
|
||||
NONE, OBJECT, ARRAY
|
||||
}
|
||||
private BlockType blockType = BlockType.NONE;
|
||||
private Stack<BlockType> blockStack = new Stack<BlockType>();
|
||||
|
||||
public GsonWriter () {
|
||||
super();
|
||||
}
|
||||
public GsonWriter (Writer writer) {
|
||||
setWriter(writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter init() throws IOException {
|
||||
eventWriter = new JsonWriter(getWriter());
|
||||
eventWriter.setSerializeNulls(true);
|
||||
if (isPrettyPrint()) {
|
||||
eventWriter.setIndent(" ");
|
||||
}
|
||||
blockType = BlockType.NONE;
|
||||
blockStack.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter flush() throws IOException {
|
||||
if (blockType != BlockType.NONE) {
|
||||
log.error("JsonLikeStreamWriter.flush() called but JSON document is not finished");
|
||||
}
|
||||
eventWriter.flush();
|
||||
getWriter().flush();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
eventWriter.close();
|
||||
getWriter().close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter beginObject() throws IOException {
|
||||
blockStack.push(blockType);
|
||||
blockType = BlockType.OBJECT;
|
||||
eventWriter.beginObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter beginArray() throws IOException {
|
||||
blockStack.push(blockType);
|
||||
blockType = BlockType.ARRAY;
|
||||
eventWriter.beginArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter beginObject(String name) throws IOException {
|
||||
blockStack.push(blockType);
|
||||
blockType = BlockType.OBJECT;
|
||||
eventWriter.name(name);
|
||||
eventWriter.beginObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter beginArray(String name) throws IOException {
|
||||
blockStack.push(blockType);
|
||||
blockType = BlockType.ARRAY;
|
||||
eventWriter.name(name);
|
||||
eventWriter.beginArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String value) throws IOException {
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(BigInteger value) throws IOException {
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(BigDecimal value) throws IOException {
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(long value) throws IOException {
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(double value) throws IOException {
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(Boolean value) throws IOException {
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(boolean value) throws IOException {
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter writeNull() throws IOException {
|
||||
eventWriter.nullValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, String value) throws IOException {
|
||||
eventWriter.name(name);
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, BigInteger value) throws IOException {
|
||||
eventWriter.name(name);
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, BigDecimal value) throws IOException {
|
||||
eventWriter.name(name);
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, long value) throws IOException {
|
||||
eventWriter.name(name);
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, double value) throws IOException {
|
||||
eventWriter.name(name);
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, Boolean value) throws IOException {
|
||||
eventWriter.name(name);
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, boolean value) throws IOException {
|
||||
eventWriter.name(name);
|
||||
eventWriter.value(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter writeNull(String name) throws IOException {
|
||||
eventWriter.name(name);
|
||||
eventWriter.nullValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter endObject() throws IOException {
|
||||
if (blockType == BlockType.NONE) {
|
||||
log.error("JsonLikeStreamWriter.endObject(); called with no active JSON document");
|
||||
} else {
|
||||
if (blockType != BlockType.OBJECT) {
|
||||
log.error("JsonLikeStreamWriter.endObject(); called outside a JSON object. (Use endArray() instead?)");
|
||||
eventWriter.endArray();
|
||||
} else {
|
||||
eventWriter.endObject();
|
||||
}
|
||||
blockType = blockStack.pop();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter endArray() throws IOException {
|
||||
if (blockType == BlockType.NONE) {
|
||||
log.error("JsonLikeStreamWriter.endArray(); called with no active JSON document");
|
||||
} else {
|
||||
if (blockType != BlockType.ARRAY) {
|
||||
log.error("JsonLikeStreamWriter.endArray(); called outside a JSON array. (Use endObject() instead?)");
|
||||
eventWriter.endObject();
|
||||
} else {
|
||||
eventWriter.endArray();
|
||||
}
|
||||
blockType = blockStack.pop();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter endBlock() throws IOException {
|
||||
if (blockType == BlockType.NONE) {
|
||||
log.error("JsonLikeStreamWriter.endBlock(); called with no active JSON document");
|
||||
} else {
|
||||
if (blockType == BlockType.ARRAY) {
|
||||
eventWriter.endArray();
|
||||
} else {
|
||||
eventWriter.endObject();
|
||||
}
|
||||
blockType = blockStack.pop();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
|
@ -53,20 +53,4 @@ public abstract class JsonLikeObject extends JsonLikeValue {
|
|||
|
||||
public abstract JsonLikeValue get (String key);
|
||||
|
||||
public String getString (String key) {
|
||||
JsonLikeValue value = this.get(key);
|
||||
if (null == value) {
|
||||
throw new NullPointerException("Json object missing element named \""+key+"\"");
|
||||
}
|
||||
return value.getAsString();
|
||||
}
|
||||
|
||||
public String getString (String key, String defaultValue) {
|
||||
String result = defaultValue;
|
||||
JsonLikeValue value = this.get(key);
|
||||
if (value != null) {
|
||||
result = value.getAsString();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,12 @@
|
|||
*/
|
||||
package ca.uhn.fhir.parser.json;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
/**
|
||||
* This interface is the generic representation of any sort of data
|
||||
* structure that looks and smells like JSON. These data structures
|
||||
|
@ -33,7 +34,7 @@ import ca.uhn.fhir.parser.DataFormatException;
|
|||
* @author Bill.Denton
|
||||
*/
|
||||
public interface JsonLikeStructure {
|
||||
public JsonLikeStructure getInstance();
|
||||
JsonLikeStructure getInstance();
|
||||
|
||||
/**
|
||||
* Parse the JSON document into the Json-like structure
|
||||
|
@ -43,10 +44,13 @@ public interface JsonLikeStructure {
|
|||
* process the JSON input stream
|
||||
* @throws DataFormatException when invalid JSON is received
|
||||
*/
|
||||
public void load (Reader theReader) throws DataFormatException;
|
||||
public void load (Reader theReader, boolean allowArray) throws DataFormatException;
|
||||
public JsonLikeObject getRootObject () throws DataFormatException;
|
||||
public JsonLikeArray getRootArray () throws DataFormatException;
|
||||
public JsonLikeWriter getJsonLikeWriter ();
|
||||
public JsonLikeWriter getJsonLikeWriter (Writer writer);
|
||||
void load(Reader theReader) throws DataFormatException;
|
||||
|
||||
void load(Reader theReader, boolean allowArray) throws DataFormatException;
|
||||
|
||||
JsonLikeObject getRootObject() throws DataFormatException;
|
||||
|
||||
JsonLikeWriter getJsonLikeWriter();
|
||||
|
||||
JsonLikeWriter getJsonLikeWriter(Writer writer) throws IOException;
|
||||
}
|
||||
|
|
|
@ -30,54 +30,72 @@ public abstract class JsonLikeWriter {
|
|||
private boolean prettyPrint;
|
||||
private Writer writer;
|
||||
|
||||
public JsonLikeWriter() {
|
||||
super();
|
||||
}
|
||||
|
||||
public boolean isPrettyPrint() {
|
||||
return prettyPrint;
|
||||
}
|
||||
|
||||
public void setPrettyPrint(boolean tf) {
|
||||
prettyPrint = tf;
|
||||
}
|
||||
public boolean isPrettyPrint () {
|
||||
return prettyPrint;
|
||||
|
||||
public Writer getWriter() {
|
||||
return writer;
|
||||
}
|
||||
|
||||
public void setWriter(Writer writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
public Writer getWriter () {
|
||||
return writer;
|
||||
}
|
||||
|
||||
public abstract JsonLikeWriter init() throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter flush() throws IOException;
|
||||
|
||||
public abstract void close() throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter beginObject() throws IOException;
|
||||
public abstract JsonLikeWriter beginArray () throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter beginObject(String name) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter beginArray(String name) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(String value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(BigInteger value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(BigDecimal value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(long value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(double value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(Boolean value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(boolean value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter writeNull() throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(String name, String value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(String name, BigInteger value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(String name, BigDecimal value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(String name, long value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(String name, double value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(String name, Boolean value) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter write(String name, boolean value) throws IOException;
|
||||
public abstract JsonLikeWriter writeNull (String name) throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter endObject() throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter endArray() throws IOException;
|
||||
|
||||
public abstract JsonLikeWriter endBlock() throws IOException;
|
||||
|
||||
public JsonLikeWriter() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,392 @@
|
|||
package ca.uhn.fhir.parser.json.jackson;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeArray;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeObject;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeStructure;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeValue;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeWriter;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.DecimalNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.apache.jena.tdb.setup.BuilderStdDB;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PushbackReader;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public class JacksonStructure implements JsonLikeStructure {
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
|
||||
private JacksonWriter jacksonWriter;
|
||||
private ROOT_TYPE rootType = null;
|
||||
private JsonNode nativeRoot = null;
|
||||
private JsonNode jsonLikeRoot = null;
|
||||
|
||||
public void setNativeObject(ObjectNode objectNode) {
|
||||
this.rootType = ROOT_TYPE.OBJECT;
|
||||
this.nativeRoot = objectNode;
|
||||
}
|
||||
|
||||
public void setNativeArray(ArrayNode arrayNode) {
|
||||
this.rootType = ROOT_TYPE.ARRAY;
|
||||
this.nativeRoot = arrayNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeStructure getInstance() {
|
||||
return new JacksonStructure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Reader theReader) throws DataFormatException {
|
||||
this.load(theReader, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Reader theReader, boolean allowArray) throws DataFormatException {
|
||||
PushbackReader pbr = new PushbackReader(theReader);
|
||||
int nextInt;
|
||||
try {
|
||||
while (true) {
|
||||
nextInt = pbr.read();
|
||||
if (nextInt == -1) {
|
||||
throw new DataFormatException("Did not find any content to parse");
|
||||
}
|
||||
if (nextInt == '{') {
|
||||
pbr.unread(nextInt);
|
||||
break;
|
||||
}
|
||||
if (Character.isWhitespace(nextInt)) {
|
||||
continue;
|
||||
}
|
||||
if (allowArray) {
|
||||
if (nextInt == '[') {
|
||||
pbr.unread(nextInt);
|
||||
break;
|
||||
}
|
||||
throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char) nextInt + "' (must be '{' or '[')");
|
||||
}
|
||||
throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char) nextInt + "' (must be '{')");
|
||||
}
|
||||
|
||||
if (nextInt == '{') {
|
||||
setNativeObject((ObjectNode) OBJECT_MAPPER.readTree(pbr));
|
||||
} else {
|
||||
setNativeArray((ArrayNode) OBJECT_MAPPER.readTree(pbr));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e.getMessage().startsWith("Unexpected char 39")) {
|
||||
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage() + " - " +
|
||||
"This may indicate that single quotes are being used as JSON escapes where double quotes are required", e);
|
||||
}
|
||||
throw new DataFormatException("Failed to parse JSON encoded FHIR content: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter getJsonLikeWriter(Writer writer) throws IOException {
|
||||
if (null == jacksonWriter) {
|
||||
jacksonWriter = new JacksonWriter(OBJECT_MAPPER.getFactory(), writer);
|
||||
}
|
||||
|
||||
return jacksonWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter getJsonLikeWriter() {
|
||||
if (null == jacksonWriter) {
|
||||
jacksonWriter = new JacksonWriter();
|
||||
}
|
||||
return jacksonWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeObject getRootObject() throws DataFormatException {
|
||||
if (rootType == ROOT_TYPE.OBJECT) {
|
||||
if (null == jsonLikeRoot) {
|
||||
jsonLikeRoot = nativeRoot;
|
||||
}
|
||||
|
||||
return new JacksonJsonObject((ObjectNode) jsonLikeRoot);
|
||||
}
|
||||
|
||||
throw new DataFormatException("Content must be a valid JSON Object. It must start with '{'.");
|
||||
}
|
||||
|
||||
private enum ROOT_TYPE {OBJECT, ARRAY}
|
||||
|
||||
private static class JacksonJsonObject extends JsonLikeObject {
|
||||
private final ObjectNode nativeObject;
|
||||
private final Map<String, JsonLikeValue> jsonLikeMap = new LinkedHashMap<>();
|
||||
private Set<String> keySet = null;
|
||||
|
||||
public JacksonJsonObject(ObjectNode json) {
|
||||
this.nativeObject = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
if (null == keySet) {
|
||||
final Iterable<Map.Entry<String, JsonNode>> iterable = nativeObject::fields;
|
||||
keySet = StreamSupport.stream(iterable.spliterator(), false)
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toCollection(EntryOrderedSet::new));
|
||||
}
|
||||
|
||||
return keySet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeValue get(String key) {
|
||||
JsonLikeValue result = null;
|
||||
if (jsonLikeMap.containsKey(key)) {
|
||||
result = jsonLikeMap.get(key);
|
||||
} else {
|
||||
JsonNode child = nativeObject.get(key);
|
||||
if (child != null) {
|
||||
result = new JacksonJsonValue(child);
|
||||
}
|
||||
jsonLikeMap.put(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class EntryOrderedSet<T> extends AbstractSet<T> {
|
||||
private final transient ArrayList<T> data;
|
||||
|
||||
public EntryOrderedSet() {
|
||||
data = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return data.contains(o);
|
||||
}
|
||||
|
||||
public T get(int index) {
|
||||
return data.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T element) {
|
||||
if (data.contains(element)) {
|
||||
return false;
|
||||
}
|
||||
return data.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return data.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return data.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
private static class JacksonJsonArray extends JsonLikeArray {
|
||||
private final ArrayNode nativeArray;
|
||||
private final Map<Integer, JsonLikeValue> jsonLikeMap = new LinkedHashMap<Integer, JsonLikeValue>();
|
||||
|
||||
public JacksonJsonArray(ArrayNode json) {
|
||||
this.nativeArray = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return nativeArray.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeValue get(int index) {
|
||||
Integer key = index;
|
||||
JsonLikeValue result = null;
|
||||
if (jsonLikeMap.containsKey(key)) {
|
||||
result = jsonLikeMap.get(key);
|
||||
} else {
|
||||
JsonNode child = nativeArray.get(index);
|
||||
if (child != null) {
|
||||
result = new JacksonJsonValue(child);
|
||||
}
|
||||
jsonLikeMap.put(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class JacksonJsonValue extends JsonLikeValue {
|
||||
private final JsonNode nativeValue;
|
||||
private JsonLikeObject jsonLikeObject = null;
|
||||
private JsonLikeArray jsonLikeArray = null;
|
||||
|
||||
public JacksonJsonValue(JsonNode jsonNode) {
|
||||
this.nativeValue = jsonNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
if (nativeValue != null && nativeValue.isValueNode()) {
|
||||
if (nativeValue.isNumber()) {
|
||||
return nativeValue.numberValue();
|
||||
}
|
||||
|
||||
if (nativeValue.isBoolean()) {
|
||||
return nativeValue.booleanValue();
|
||||
}
|
||||
|
||||
return nativeValue.asText();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueType getJsonType() {
|
||||
if (null == nativeValue || nativeValue.isNull()) {
|
||||
return ValueType.NULL;
|
||||
}
|
||||
if (nativeValue.isObject()) {
|
||||
return ValueType.OBJECT;
|
||||
}
|
||||
if (nativeValue.isArray()) {
|
||||
return ValueType.ARRAY;
|
||||
}
|
||||
if (nativeValue.isValueNode()) {
|
||||
return ValueType.SCALAR;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType getDataType() {
|
||||
if (nativeValue != null && nativeValue.isValueNode()) {
|
||||
if (nativeValue.isNumber()) {
|
||||
return ScalarType.NUMBER;
|
||||
}
|
||||
if (nativeValue.isTextual()) {
|
||||
return ScalarType.STRING;
|
||||
}
|
||||
if (nativeValue.isBoolean()) {
|
||||
return ScalarType.BOOLEAN;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeArray getAsArray() {
|
||||
if (nativeValue != null && nativeValue.isArray()) {
|
||||
if (null == jsonLikeArray) {
|
||||
jsonLikeArray = new JacksonJsonArray((ArrayNode) nativeValue);
|
||||
}
|
||||
}
|
||||
return jsonLikeArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeObject getAsObject() {
|
||||
if (nativeValue != null && nativeValue.isObject()) {
|
||||
if (null == jsonLikeObject) {
|
||||
jsonLikeObject = new JacksonJsonObject((ObjectNode) nativeValue);
|
||||
}
|
||||
}
|
||||
return jsonLikeObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number getAsNumber() {
|
||||
return nativeValue != null ? nativeValue.numberValue() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsString() {
|
||||
if (nativeValue != null) {
|
||||
if (nativeValue instanceof DecimalNode) {
|
||||
BigDecimal value = nativeValue.decimalValue();
|
||||
return value.toPlainString();
|
||||
}
|
||||
return nativeValue.asText();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAsBoolean() {
|
||||
if (nativeValue != null && nativeValue.isValueNode() && nativeValue.isBoolean()) {
|
||||
return nativeValue.asBoolean();
|
||||
}
|
||||
return super.getAsBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
private static ObjectMapper createObjectMapper() {
|
||||
ObjectMapper retVal = new ObjectMapper();
|
||||
retVal = retVal.setNodeFactory(new JsonNodeFactory(true));
|
||||
retVal = retVal.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
|
||||
retVal = retVal.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
|
||||
retVal = retVal.disable(JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION);
|
||||
retVal = retVal.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
|
||||
retVal = retVal.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
|
||||
retVal = retVal.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
|
||||
return retVal;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
package ca.uhn.fhir.parser.json.jackson;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.parser.json.JsonLikeWriter;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.PrettyPrinter;
|
||||
import com.fasterxml.jackson.core.util.DefaultIndenter;
|
||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||
import com.fasterxml.jackson.core.util.Separators;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class JacksonWriter extends JsonLikeWriter {
|
||||
|
||||
private JsonGenerator myJsonGenerator;
|
||||
|
||||
public JacksonWriter(JsonFactory theJsonFactory, Writer theWriter) throws IOException {
|
||||
myJsonGenerator = theJsonFactory.createGenerator(theWriter);
|
||||
setWriter(theWriter);
|
||||
}
|
||||
|
||||
public JacksonWriter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter init() {
|
||||
if (isPrettyPrint()) {
|
||||
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter() {
|
||||
|
||||
/**
|
||||
* Objects should serialize as
|
||||
* <pre>
|
||||
* {
|
||||
* "key": "value"
|
||||
* }
|
||||
* </pre>
|
||||
* in order to be consistent with Gson behaviour, instead of the jackson default
|
||||
* <pre>
|
||||
* {
|
||||
* "key" : "value"
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
@Override
|
||||
public DefaultPrettyPrinter withSeparators(Separators separators) {
|
||||
_separators = separators;
|
||||
_objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator() + " ";
|
||||
return this;
|
||||
}
|
||||
|
||||
};
|
||||
prettyPrinter = prettyPrinter.withObjectIndenter(new DefaultIndenter(" ", "\n"));
|
||||
|
||||
myJsonGenerator.setPrettyPrinter(prettyPrinter);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter flush() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
myJsonGenerator.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter beginObject() throws IOException {
|
||||
myJsonGenerator.writeStartObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter beginObject(String name) throws IOException {
|
||||
myJsonGenerator.writeObjectFieldStart(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter beginArray(String name) throws IOException {
|
||||
myJsonGenerator.writeArrayFieldStart(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String value) throws IOException {
|
||||
myJsonGenerator.writeObject(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(BigInteger value) throws IOException {
|
||||
myJsonGenerator.writeObject(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(BigDecimal value) throws IOException {
|
||||
myJsonGenerator.writeObject(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(long value) throws IOException {
|
||||
myJsonGenerator.writeObject(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(double value) throws IOException {
|
||||
myJsonGenerator.writeObject(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(Boolean value) throws IOException {
|
||||
myJsonGenerator.writeObject(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(boolean value) throws IOException {
|
||||
myJsonGenerator.writeObject(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter writeNull() throws IOException {
|
||||
myJsonGenerator.writeNull();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, String value) throws IOException {
|
||||
myJsonGenerator.writeObjectField(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, BigInteger value) throws IOException {
|
||||
myJsonGenerator.writeObjectField(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, BigDecimal value) throws IOException {
|
||||
myJsonGenerator.writeObjectField(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, long value) throws IOException {
|
||||
myJsonGenerator.writeObjectField(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, double value) throws IOException {
|
||||
myJsonGenerator.writeObjectField(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, Boolean value) throws IOException {
|
||||
myJsonGenerator.writeObjectField(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter write(String name, boolean value) throws IOException {
|
||||
myJsonGenerator.writeObjectField(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter endObject() throws IOException {
|
||||
myJsonGenerator.writeEndObject();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter endArray() throws IOException {
|
||||
myJsonGenerator.writeEndArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonLikeWriter endBlock() throws IOException {
|
||||
myJsonGenerator.writeEndObject();
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -22,7 +22,14 @@ package ca.uhn.fhir.rest.api;
|
|||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||
|
||||
public class Constants {
|
||||
|
||||
|
@ -252,7 +259,8 @@ public class Constants {
|
|||
* </p>
|
||||
*/
|
||||
public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source";
|
||||
public static final String CODESYSTEM_VALIDATE_NOT_NEEDED = UUID.randomUUID().toString();
|
||||
public static final String PARAM_FHIRPATH = "_fhirpath";
|
||||
public static final String PARAM_TYPE = "_type";
|
||||
|
||||
static {
|
||||
CHARSET_UTF8 = StandardCharsets.UTF_8;
|
||||
|
@ -354,16 +362,7 @@ public class Constants {
|
|||
CORS_ALLWED_METHODS = Collections.unmodifiableSet(corsAllowedMethods);
|
||||
}
|
||||
|
||||
public static boolean codeSystemNotNeeded(String theCodeSystem) {
|
||||
return Constants.CODESYSTEM_VALIDATE_NOT_NEEDED.equals(theCodeSystem);
|
||||
}
|
||||
|
||||
|
||||
public static String codeSystemWithDefaultDescription(String theSystem) {
|
||||
if (codeSystemNotNeeded(theSystem)) {
|
||||
return "(none)";
|
||||
} else {
|
||||
return theSystem;
|
||||
}
|
||||
return defaultIfBlank(theSystem, "(none)");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,13 @@ public interface IBaseOn<T> {
|
|||
*/
|
||||
T onType(Class<? extends IBaseResource> theResourceType);
|
||||
|
||||
/**
|
||||
* Perform the operation across all versions of all resources of the given type on the server
|
||||
*
|
||||
* @param theResourceType The resource type name, e.g. "ValueSet"
|
||||
*/
|
||||
T onType(String theResourceType);
|
||||
|
||||
/**
|
||||
* Perform the operation across all versions of a specific resource (by ID and type) on the server.
|
||||
* Note that <code>theId</code> must be populated with both a resource type and a resource ID at
|
||||
|
|
|
@ -200,6 +200,13 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/
|
|||
return myValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that the parameter to this method <b>must</b> be a resource reference, e.g
|
||||
* <code>123</code> or <code>Patient/123</code> or <code>http://example.com/fhir/Patient/123</code>
|
||||
* or something like this. This is not appropriate for cases where a chain is being used and
|
||||
* the value is for a different type of parameter (e.g. a token). In that case, use one of the
|
||||
* setter constructors.
|
||||
*/
|
||||
public ReferenceParam setValue(String theValue) {
|
||||
IdDt id = new IdDt(theValue);
|
||||
String qualifier= null;
|
||||
|
|
|
@ -238,7 +238,7 @@ public class ParametersUtil {
|
|||
addPart(theContext, theParameter, theName, coding);
|
||||
}
|
||||
|
||||
private static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) {
|
||||
public static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
|
||||
BaseRuntimeChildDefinition partChild = def.getChildByName("part");
|
||||
|
||||
|
@ -252,4 +252,19 @@ public class ParametersUtil {
|
|||
|
||||
partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
|
||||
}
|
||||
|
||||
public static void addPartResource(FhirContext theContext, IBase theParameter, String theName, IBaseResource theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
|
||||
BaseRuntimeChildDefinition partChild = def.getChildByName("part");
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> partChildElem = (BaseRuntimeElementCompositeDefinition<?>) partChild.getChildByName("part");
|
||||
IBase part = partChildElem.newInstance();
|
||||
partChild.getMutator().addValue(theParameter, part);
|
||||
|
||||
IPrimitiveType<String> name = (IPrimitiveType<String>) theContext.getElementDefinition("string").newInstance();
|
||||
name.setValue(theName);
|
||||
partChildElem.getChildByName("name").getMutator().addValue(part, name);
|
||||
|
||||
partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,31 +19,37 @@ package ca.uhn.fhir.util;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import java.lang.reflect.*;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||
|
||||
public class ReflectionUtil {
|
||||
|
||||
private static final ConcurrentHashMap<String, Object> ourFhirServerVersions = new ConcurrentHashMap<String, Object>();
|
||||
private static final ConcurrentHashMap<String, Object> ourFhirServerVersions = new ConcurrentHashMap<>();
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReflectionUtil.class);
|
||||
public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
|
||||
|
||||
public static LinkedHashSet<Method> getDeclaredMethods(Class<?> theClazz) {
|
||||
LinkedHashSet<Method> retVal = new LinkedHashSet<Method>();
|
||||
LinkedHashSet<Method> retVal = new LinkedHashSet<>();
|
||||
for (Method next : theClazz.getDeclaredMethods()) {
|
||||
try {
|
||||
Method method = theClazz.getMethod(next.getName(), next.getParameterTypes());
|
||||
retVal.add(method);
|
||||
} catch (NoSuchMethodException e) {
|
||||
retVal.add(next);
|
||||
} catch (SecurityException e) {
|
||||
} catch (NoSuchMethodException | SecurityException e) {
|
||||
retVal.add(next);
|
||||
}
|
||||
}
|
||||
|
@ -140,43 +146,38 @@ public class ReflectionUtil {
|
|||
public static Object newInstanceOfFhirServerType(String theType) {
|
||||
String errorMessage = "Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!";
|
||||
String wantedType = "ca.uhn.fhir.rest.api.server.IFhirVersionServer";
|
||||
return newInstanceOfType(theType, errorMessage, wantedType);
|
||||
return newInstanceOfType(theType, theType, errorMessage, wantedType, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> ca.uhn.fhir.context.support.IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> newInstanceOfFhirProfileValidationSupport(
|
||||
String theType) {
|
||||
String errorMessage = "Unable to instantiate validation support! Please make sure that hapi-fhir-validation and the appropriate structures JAR are on your classpath!";
|
||||
String wantedType = "ca.uhn.fhir.context.support.IContextValidationSupport";
|
||||
Object fhirServerVersion = newInstanceOfType(theType, errorMessage, wantedType);
|
||||
return (IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>) fhirServerVersion;
|
||||
}
|
||||
|
||||
private static Object newInstanceOfType(String theType, String errorMessage, String wantedType) {
|
||||
Object fhirServerVersion = ourFhirServerVersions.get(theType);
|
||||
private static Object newInstanceOfType(String theKey, String theType, String errorMessage, String wantedType, Class<?>[] theParameterArgTypes, Object[] theConstructorArgs) {
|
||||
Object fhirServerVersion = ourFhirServerVersions.get(theKey);
|
||||
if (fhirServerVersion == null) {
|
||||
try {
|
||||
Class<?> type = Class.forName(theType);
|
||||
Class<?> serverType = Class.forName(wantedType);
|
||||
Validate.isTrue(serverType.isAssignableFrom(type));
|
||||
fhirServerVersion = type.newInstance();
|
||||
fhirServerVersion = type.getConstructor(theParameterArgTypes).newInstance(theConstructorArgs);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException(errorMessage, e);
|
||||
}
|
||||
|
||||
ourFhirServerVersions.put(theType, fhirServerVersion);
|
||||
ourFhirServerVersions.put(theKey, fhirServerVersion);
|
||||
}
|
||||
return fhirServerVersion;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T newInstanceOrReturnNull(String theClassName, Class<T> theType) {
|
||||
return newInstanceOrReturnNull(theClassName, theType, EMPTY_CLASS_ARRAY, EMPTY_OBJECT_ARRAY);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T newInstanceOrReturnNull(String theClassName, Class<T> theType, Class<?>[] theArgTypes, Object[] theArgs) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(theClassName);
|
||||
if (!theType.isAssignableFrom(clazz)) {
|
||||
throw new ConfigurationException(theClassName + " is not assignable to " + theType);
|
||||
}
|
||||
return (T) clazz.newInstance();
|
||||
return (T) clazz.getConstructor(theArgTypes).newInstance(theArgs);
|
||||
} catch (ConfigurationException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
|
@ -185,4 +186,12 @@ public class ReflectionUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean typeExists(String theName) {
|
||||
try {
|
||||
Class.forName(theName);
|
||||
return true;
|
||||
} catch (ClassNotFoundException theE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,7 +112,8 @@ public class TestUtil {
|
|||
* environment
|
||||
*/
|
||||
public static void randomizeLocale() {
|
||||
Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN};
|
||||
// Locale[] availableLocales = {Locale.CANADA, Locale.GERMANY, Locale.TAIWAN};
|
||||
Locale[] availableLocales = {Locale.US};
|
||||
Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
|
||||
ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
|
||||
if (Math.random() < 0.5) {
|
||||
|
|
|
@ -59,7 +59,8 @@ public enum VersionEnum {
|
|||
V4_0_3,
|
||||
V4_1_0,
|
||||
V4_2_0,
|
||||
V4_3_0;
|
||||
V4_3_0, // 4.3.0 was renamed to 5.0.0 during the cycle
|
||||
V5_0_0;
|
||||
|
||||
public static VersionEnum latestVersion() {
|
||||
VersionEnum[] values = VersionEnum.values();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.term;
|
||||
package ca.uhn.fhir.util;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.term;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
@ -28,20 +29,30 @@ public class VersionIndependentConcept implements Comparable<VersionIndependentC
|
|||
|
||||
private final String mySystem;
|
||||
private final String myCode;
|
||||
private final String myDisplay;
|
||||
private int myHashCode;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public VersionIndependentConcept(String theSystem, String theCode) {
|
||||
this(theSystem, theCode, null);
|
||||
}
|
||||
|
||||
public VersionIndependentConcept(String theSystem, String theCode, String theDisplay) {
|
||||
mySystem = theSystem;
|
||||
myCode = theCode;
|
||||
myDisplay = theDisplay;
|
||||
myHashCode = new HashCodeBuilder(17, 37)
|
||||
.append(mySystem)
|
||||
.append(myCode)
|
||||
.toHashCode();
|
||||
}
|
||||
|
||||
public String getDisplay() {
|
||||
return myDisplay;
|
||||
}
|
||||
|
||||
public String getSystem() {
|
||||
return mySystem;
|
||||
}
|
|
@ -83,6 +83,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionMissingUrl=Unable to perfor
|
|||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1}
|
||||
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.cantValidateWithNoResource=No resource supplied for $validate operation (resource is required unless mode is \"delete\")
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.deleteBlockedBecauseDisabled=Resource deletion is not permitted on this server
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithInvalidId=Can not process entity with ID[{0}], this is not a valid FHIR ID
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.incorrectResourceType=Incorrect resource type detected for endpoint, found {0} but expected {1}
|
||||
|
@ -134,3 +135,6 @@ ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateValueSetUrl=Can no
|
|||
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted!
|
||||
|
||||
ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils.failedToApplyPatch=Failed to apply JSON patch to {0}: {1}
|
||||
|
||||
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderReference.invalidTargetTypeForChain=Resource type "{0}" is not a valid target type for reference search parameter: {1}
|
||||
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderReference.invalidResourceType=Invalid/unsupported resource type: "{0}"
|
||||
|
|
|
@ -5,6 +5,7 @@ import static org.junit.Assert.assertNotNull;
|
|||
|
||||
import java.io.StringReader;
|
||||
|
||||
import ca.uhn.fhir.parser.json.jackson.JacksonStructure;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JsonLikeStructureTest {
|
||||
|
@ -39,7 +40,7 @@ public class JsonLikeStructureTest {
|
|||
@Test
|
||||
public void testStructureLoading() {
|
||||
StringReader reader = new StringReader(TEST_STRUCTURELOADING_DATA);
|
||||
JsonLikeStructure jsonStructure = new GsonStructure();
|
||||
JsonLikeStructure jsonStructure = new JacksonStructure();
|
||||
jsonStructure.load(reader);
|
||||
|
||||
JsonLikeObject rootObject = jsonStructure.getRootObject();
|
||||
|
@ -70,7 +71,7 @@ public class JsonLikeStructureTest {
|
|||
@Test
|
||||
public void testJsonAndDataTypes() {
|
||||
StringReader reader = new StringReader(TEST_JSONTYPES_DATA);
|
||||
JsonLikeStructure jsonStructure = new GsonStructure();
|
||||
JsonLikeStructure jsonStructure = new JacksonStructure();
|
||||
jsonStructure.load(reader);
|
||||
|
||||
JsonLikeObject rootObject = jsonStructure.getRootObject();
|
||||
|
|
|
@ -45,4 +45,10 @@ public class ReflectionUtilTest {
|
|||
assertEquals("Failed to instantiate java.util.List", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeExists() {
|
||||
assertFalse(ReflectionUtil.typeExists("ca.Foo"));
|
||||
assertTrue(ReflectionUtil.typeExists(String.class.getName()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-bom</artifactId>
|
||||
<version>4.3.0-SNAPSHOT</version>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>HAPI FHIR BOM</name>
|
||||
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>4.3.0-SNAPSHOT</version>
|
||||
<version>5.0.0-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue