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>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>4.3.0-SNAPSHOT</version>
|
<version>5.0.0-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -181,7 +181,16 @@
|
||||||
</reports>
|
</reports>
|
||||||
<configuration>
|
<configuration>
|
||||||
<links>
|
<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>
|
<link>https://docs.oracle.com/javaee/7/api/</link>
|
||||||
</links>
|
</links>
|
||||||
<additionalparam>-Xdoclint:none</additionalparam>
|
<additionalparam>-Xdoclint:none</additionalparam>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>4.3.0-SNAPSHOT</version>
|
<version>5.0.0-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>4.3.0-SNAPSHOT</version>
|
<version>5.0.0-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@
|
||||||
|
|
||||||
<!-- JSON -->
|
<!-- JSON -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- XML -->
|
<!-- XML -->
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
package ca.uhn.fhir.context;
|
package ca.uhn.fhir.context;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.api.AddProfileTagEnum;
|
import ca.uhn.fhir.context.api.AddProfileTagEnum;
|
||||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||||
import ca.uhn.fhir.fluentpath.IFluentPath;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.fhirpath.IFhirPath;
|
||||||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||||
import ca.uhn.fhir.model.api.IElement;
|
import ca.uhn.fhir.model.api.IElement;
|
||||||
import ca.uhn.fhir.model.api.IFhirVersion;
|
import ca.uhn.fhir.model.api.IFhirVersion;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.view.ViewGenerator;
|
import ca.uhn.fhir.model.view.ViewGenerator;
|
||||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
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.api.IVersionSpecificBundleFactory;
|
||||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
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.util.VersionUtil;
|
||||||
import ca.uhn.fhir.validation.FhirValidator;
|
import ca.uhn.fhir.validation.FhirValidator;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
|
||||||
import org.apache.jena.riot.Lang;
|
import org.apache.jena.riot.Lang;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
|
@ -97,7 +103,7 @@ public class FhirContext {
|
||||||
private Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
|
private Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
|
||||||
private volatile IRestfulClientFactory myRestfulClientFactory;
|
private volatile IRestfulClientFactory myRestfulClientFactory;
|
||||||
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
|
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
|
||||||
private IContextValidationSupport<?, ?, ?, ?, ?, ?> myValidationSupport;
|
private IValidationSupport myValidationSupport;
|
||||||
private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap();
|
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
|
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
|
||||||
* for extending the core library.
|
* for extending the core library.
|
||||||
|
@ -543,16 +562,37 @@ public class FhirContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the validation support module configured for this context, creating a default
|
* 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
|
* method
|
||||||
*
|
*
|
||||||
* @see #setValidationSupport(IContextValidationSupport)
|
* @see #setValidationSupport(IValidationSupport)
|
||||||
*/
|
*/
|
||||||
public IContextValidationSupport<?, ?, ?, ?, ?, ?> getValidationSupport() {
|
public IValidationSupport getValidationSupport() {
|
||||||
if (myValidationSupport == null) {
|
IValidationSupport retVal = myValidationSupport;
|
||||||
myValidationSupport = myVersion.createValidationSupport();
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
myValidationSupport = retVal;
|
||||||
}
|
}
|
||||||
return myValidationSupport;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -560,7 +600,7 @@ public class FhirContext {
|
||||||
* is used to supply underlying infrastructure such as conformance resources (StructureDefinition, ValueSet, etc)
|
* 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
|
* 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;
|
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
|
* 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.
|
* configured on the context at the time this method is called.
|
||||||
* <p>
|
* <p>
|
||||||
* In other words, call {@link #setValidationSupport(IContextValidationSupport)} before
|
* In other words, you may wish to call {@link #setValidationSupport(IValidationSupport)} before
|
||||||
* calling {@link #newFluentPath()}
|
* calling {@link #newFluentPath()}
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -600,10 +649,10 @@ public class FhirContext {
|
||||||
* {@link UnsupportedOperationException}
|
* {@link UnsupportedOperationException}
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @since 2.2
|
* @since 5.0.0
|
||||||
*/
|
*/
|
||||||
public IFluentPath newFluentPath() {
|
public IFhirPath newFhirPath() {
|
||||||
return myVersion.createFluentPathExecutor(this);
|
return myVersion.createFhirPathExecutor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -641,7 +690,6 @@ public class FhirContext {
|
||||||
return new RDFParser(this, myParserErrorHandler, Lang.TURTLE);
|
return new RDFParser(this, myParserErrorHandler, Lang.TURTLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new client instance. This method requires an interface which is defined specifically for your use
|
* 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",
|
* 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;
|
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"})
|
@SuppressWarnings({"cast"})
|
||||||
private List<Class<? extends IElement>> toElementList(final Collection<Class<? extends IBaseResource>> theResourceTypes) {
|
private List<Class<? extends IElement>> toElementList(final Collection<Class<? extends IBaseResource>> theResourceTypes) {
|
||||||
if (theResourceTypes == null) {
|
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
|
* #%L
|
||||||
* HAPI FHIR JPA Server
|
* HAPI FHIR - Core Library
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2014 - 2020 University Health Network
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
* %%
|
* %%
|
||||||
|
@ -22,6 +20,17 @@ import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface IJpaValidationSupportR5 extends IValidationSupport {
|
public class ConceptValidationOptions {
|
||||||
// nothing yet
|
|
||||||
|
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.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.util.ParametersUtil;
|
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.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
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
|
* various functions that can be provided by validation and terminology
|
||||||
* services.
|
* services.
|
||||||
* <p>
|
* <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
|
* Implementations are not required to implement all of the functions
|
||||||
* in this interface; in fact it is expected that most won't. Any
|
* in this interface; in fact it is expected that most won't. Any
|
||||||
* methods which are not implemented may simply return <code>null</code>
|
* 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>
|
||||||
|
* <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
|
* Expands the given portion of a ValueSet
|
||||||
*
|
*
|
||||||
* @param theInclude The portion to include
|
* @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
|
||||||
* @return The expansion
|
* 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
|
* Load and return all conformance resources associated with this
|
||||||
* validation support module. This method may return null if it doesn't
|
* validation support module. This method may return null if it doesn't
|
||||||
* make sense for a given module.
|
* make sense for a given module.
|
||||||
*/
|
*/
|
||||||
List<IBaseResource> fetchAllConformanceResources(FhirContext theContext);
|
default List<IBaseResource> fetchAllConformanceResources() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load and return all possible structure definitions
|
* 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
|
* 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
|
* @param theSystem The code system
|
||||||
* @return The valueset (must not be null, but can be an empty ValueSet)
|
* @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
|
* Loads a resource needed by the validation (a StructureDefinition, or a
|
||||||
* ValueSet)
|
* ValueSet)
|
||||||
*
|
*
|
||||||
* @param theContext The HAPI FHIR Context object current in use by the validator
|
* @param theClass The type of the resource to load
|
||||||
* @param theClass The type of the resource to load
|
* @param theUri The resource URI
|
||||||
* @param theUri The resource URI
|
|
||||||
* @return Returns the resource, or <code>null</code> if no resource with the
|
* @return Returns the resource, or <code>null</code> if no resource with the
|
||||||
* given URI can be found
|
* 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
|
* Returns <code>true</code> if codes in the given code system can be expanded
|
||||||
* or validated
|
* or validated
|
||||||
*
|
*
|
||||||
* @param theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code>
|
* @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
|
* @return Returns <code>true</code> if codes in the given code system can be
|
||||||
* validated
|
* validated
|
||||||
*/
|
*/
|
||||||
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
|
default boolean isCodeSystemSupported(IValidationSupport theRootValidationSupport, String theSystem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the given ValueSet by URL
|
* 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
|
* 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"
|
* 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.
|
* binding fields (e.g. <code>Observation.code</code> in the default profile.
|
||||||
*
|
*
|
||||||
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
|
* @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
|
||||||
* @param theCode The code, e.g. "<code>1234-5</code>"
|
* 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 theDisplay The display name, if it should also be validated
|
* @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
|
* @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
|
* 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"
|
* 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.
|
* binding fields (e.g. <code>Observation.code</code> in the default profile.
|
||||||
*
|
*
|
||||||
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
|
* @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
|
||||||
* @param theCode The code, e.g. "<code>1234-5</code>"
|
* 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 theDisplay The display name, if it should also be validated
|
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
|
||||||
* @param theValueSet The ValueSet to validate against. Must not be null, and must be a ValueSet resource.
|
* @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
|
* @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
|
* 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
|
||||||
* @param theSystem The CodeSystem URL
|
* 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 theCode The code
|
* @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 {
|
class ConceptDesignation {
|
||||||
private String myLanguage;
|
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 {
|
class CodeValidationResult {
|
||||||
private IBase myDefinition;
|
private String myCode;
|
||||||
private String myMessage;
|
private String myMessage;
|
||||||
private Enum mySeverity;
|
private IssueSeverity mySeverity;
|
||||||
private String myCodeSystemName;
|
private String myCodeSystemName;
|
||||||
private String myCodeSystemVersion;
|
private String myCodeSystemVersion;
|
||||||
private List<BaseConceptProperty> myProperties;
|
private List<BaseConceptProperty> myProperties;
|
||||||
private String myDisplay;
|
private String myDisplay;
|
||||||
|
|
||||||
public CodeValidationResult(IBase theDefinition) {
|
public CodeValidationResult() {
|
||||||
this.myDefinition = theDefinition;
|
super();
|
||||||
}
|
|
||||||
|
|
||||||
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 String getDisplay() {
|
public String getDisplay() {
|
||||||
return myDisplay;
|
return myDisplay;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IBase asConceptDefinition() {
|
public CodeValidationResult setDisplay(String theDisplay) {
|
||||||
return myDefinition;
|
myDisplay = theDisplay;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return myCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeValidationResult setCode(String theCode) {
|
||||||
|
myCode = theCode;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getCodeSystemName() {
|
String getCodeSystemName() {
|
||||||
return myCodeSystemName;
|
return myCodeSystemName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCodeSystemName(String theCodeSystemName) {
|
public CodeValidationResult setCodeSystemName(String theCodeSystemName) {
|
||||||
myCodeSystemName = theCodeSystemName;
|
myCodeSystemName = theCodeSystemName;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCodeSystemVersion() {
|
public String getCodeSystemVersion() {
|
||||||
return myCodeSystemVersion;
|
return myCodeSystemVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCodeSystemVersion(String theCodeSystemVersion) {
|
public CodeValidationResult setCodeSystemVersion(String theCodeSystemVersion) {
|
||||||
myCodeSystemVersion = theCodeSystemVersion;
|
myCodeSystemVersion = theCodeSystemVersion;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return myMessage;
|
return myMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CodeValidationResult setMessage(String theMessage) {
|
||||||
|
myMessage = theMessage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public List<BaseConceptProperty> getProperties() {
|
public List<BaseConceptProperty> getProperties() {
|
||||||
return myProperties;
|
return myProperties;
|
||||||
}
|
}
|
||||||
|
@ -318,12 +450,17 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
||||||
myProperties = theProperties;
|
myProperties = theProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Enum getSeverity() {
|
public IssueSeverity getSeverity() {
|
||||||
return mySeverity;
|
return mySeverity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CodeValidationResult setSeverity(IssueSeverity theSeverity) {
|
||||||
|
mySeverity = theSeverity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isOk() {
|
public boolean isOk() {
|
||||||
return myDefinition != null;
|
return isNotBlank(myCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) {
|
public LookupCodeResult asLookupCodeResult(String theSearchedForSystem, String theSearchedForCode) {
|
||||||
|
@ -339,6 +476,49 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
||||||
return retVal;
|
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 {
|
class LookupCodeResult {
|
||||||
|
@ -350,7 +530,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
||||||
private boolean myFound;
|
private boolean myFound;
|
||||||
private String mySearchedForCode;
|
private String mySearchedForCode;
|
||||||
private String mySearchedForSystem;
|
private String mySearchedForSystem;
|
||||||
private List<IContextValidationSupport.BaseConceptProperty> myProperties;
|
private List<IValidationSupport.BaseConceptProperty> myProperties;
|
||||||
private List<ConceptDesignation> myDesignations;
|
private List<ConceptDesignation> myDesignations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -367,7 +547,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
||||||
return myProperties;
|
return myProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProperties(List<IContextValidationSupport.BaseConceptProperty> theProperties) {
|
public void setProperties(List<IValidationSupport.BaseConceptProperty> theProperties) {
|
||||||
myProperties = theProperties;
|
myProperties = theProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +646,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (IContextValidationSupport.BaseConceptProperty next : myProperties) {
|
for (IValidationSupport.BaseConceptProperty next : myProperties) {
|
||||||
|
|
||||||
if (!properties.isEmpty()) {
|
if (!properties.isEmpty()) {
|
||||||
if (!properties.contains(next.getPropertyName())) {
|
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");
|
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
|
||||||
ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName());
|
ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName());
|
||||||
|
|
||||||
if (next instanceof IContextValidationSupport.StringConceptProperty) {
|
if (next instanceof IValidationSupport.StringConceptProperty) {
|
||||||
IContextValidationSupport.StringConceptProperty prop = (IContextValidationSupport.StringConceptProperty) next;
|
IValidationSupport.StringConceptProperty prop = (IValidationSupport.StringConceptProperty) next;
|
||||||
ParametersUtil.addPartString(theContext, property, "value", prop.getValue());
|
ParametersUtil.addPartString(theContext, property, "value", prop.getValue());
|
||||||
} else if (next instanceof IContextValidationSupport.CodingConceptProperty) {
|
} else if (next instanceof IValidationSupport.CodingConceptProperty) {
|
||||||
IContextValidationSupport.CodingConceptProperty prop = (IContextValidationSupport.CodingConceptProperty) next;
|
IValidationSupport.CodingConceptProperty prop = (IValidationSupport.CodingConceptProperty) next;
|
||||||
ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay());
|
ParametersUtil.addPartCoding(theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay());
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Don't know how to handle " + next.getClass());
|
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
|
* #%L
|
||||||
|
@ -23,18 +23,18 @@ package ca.uhn.fhir.fluentpath;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
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
|
* for any reason
|
||||||
*/
|
*/
|
||||||
public class FluentPathExecutionException extends InternalErrorException {
|
public class FhirPathExecutionException extends InternalErrorException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public FluentPathExecutionException(Throwable theCause) {
|
public FhirPathExecutionException(Throwable theCause) {
|
||||||
super(theCause);
|
super(theCause);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FluentPathExecutionException(String theMessage) {
|
public FhirPathExecutionException(String theMessage) {
|
||||||
super(theMessage);
|
super(theMessage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.fluentpath;
|
package ca.uhn.fhir.fhirpath;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
|
@ -25,7 +25,7 @@ import java.util.Optional;
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
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
|
* 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 List<ResourceBundle> myBundle = new ArrayList<>();
|
||||||
private final Map<String, String> myHardcodedMessages = new HashMap<>();
|
private final Map<String, String> myHardcodedMessages = new HashMap<>();
|
||||||
private String[] myBundleNames;
|
private String[] myBundleNames;
|
||||||
|
private Locale myLocale = Locale.getDefault();
|
||||||
|
|
||||||
public HapiLocalizer() {
|
public HapiLocalizer() {
|
||||||
this(HapiLocalizer.class.getPackage().getName() + ".hapi-messages");
|
this(HapiLocalizer.class.getPackage().getName() + ".hapi-messages");
|
||||||
|
@ -164,7 +165,11 @@ public class HapiLocalizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Locale getLocale() {
|
||||||
|
return myLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* This <b>global setting</b> causes the localizer to fail if any attempts
|
* 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
|
* are made to retrieve a key that does not exist. This method is primarily for
|
||||||
* unit tests.
|
* unit tests.
|
||||||
|
|
|
@ -23,11 +23,10 @@ package ca.uhn.fhir.model.api;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.fhirpath.IFhirPath;
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.*;
|
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;
|
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,9 +38,7 @@ import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
|
||||||
*/
|
*/
|
||||||
public interface IFhirVersion {
|
public interface IFhirVersion {
|
||||||
|
|
||||||
IFluentPath createFluentPathExecutor(FhirContext theFhirContext);
|
IFhirPath createFhirPathExecutor(FhirContext theFhirContext);
|
||||||
|
|
||||||
IContextValidationSupport<?, ?, ?, ?, ?, ?> createValidationSupport();
|
|
||||||
|
|
||||||
IBaseResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase);
|
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
|
* #%L
|
||||||
* HAPI FHIR JPA Server
|
* HAPI FHIR - Core Library
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2014 - 2020 University Health Network
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
* %%
|
* %%
|
||||||
|
@ -23,6 +20,10 @@ import org.hl7.fhir.r4.model.StructureDefinition;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface IJpaValidationSupportR4 extends IValidationSupport {
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
// nothing yet
|
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.BaseRuntimeElementCompositeDefinition;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
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.narrative.INarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
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)) {
|
if (theFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) {
|
||||||
return Collections.singletonList(theResource);
|
return Collections.singletonList(theResource);
|
||||||
}
|
}
|
||||||
IFluentPath fhirPath = theFhirContext.newFluentPath();
|
IFhirPath fhirPath = theFhirContext.newFluentPath();
|
||||||
return fhirPath.evaluate(theResource, theContextPath, IBase.class);
|
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.*;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
|
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.api.annotation.Child;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
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.ScalarType;
|
||||||
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
|
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.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
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.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.text.WordUtils;
|
import org.apache.commons.text.WordUtils;
|
||||||
|
@ -45,11 +54,17 @@ import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.math.BigDecimal;
|
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.ID_DATATYPE;
|
||||||
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.PRIMITIVE_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
|
* 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);
|
theEventWriter.beginObject(arrayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsonLikeWriter createJsonWriter(Writer theWriter) {
|
private JsonLikeWriter createJsonWriter(Writer theWriter) throws IOException {
|
||||||
JsonLikeStructure jsonStructure = new GsonStructure();
|
JsonLikeStructure jsonStructure = new JacksonStructure();
|
||||||
JsonLikeWriter retVal = jsonStructure.getJsonLikeWriter(theWriter);
|
return jsonStructure.getJsonLikeWriter(theWriter);
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void doEncodeResourceToJsonLikeWriter(IBaseResource theResource, JsonLikeWriter theEventWriter, EncodeContext theEncodeContext) throws IOException {
|
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 {
|
protected void doEncodeResourceToWriter(IBaseResource theResource, Writer theWriter, EncodeContext theEncodeContext) throws IOException {
|
||||||
JsonLikeWriter eventWriter = createJsonWriter(theWriter);
|
JsonLikeWriter eventWriter = createJsonWriter(theWriter);
|
||||||
doEncodeResourceToJsonLikeWriter(theResource, eventWriter, theEncodeContext);
|
doEncodeResourceToJsonLikeWriter(theResource, eventWriter, theEncodeContext);
|
||||||
|
eventWriter.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
|
public <T extends IBaseResource> T doParseResource(Class<T> theResourceType, Reader theReader) {
|
||||||
JsonLikeStructure jsonStructure = new GsonStructure();
|
JsonLikeStructure jsonStructure = new JacksonStructure();
|
||||||
jsonStructure.load(theReader);
|
jsonStructure.load(theReader);
|
||||||
|
|
||||||
T retVal = doParseResource(theResourceType, jsonStructure);
|
T retVal = doParseResource(theResourceType, jsonStructure);
|
||||||
|
@ -302,19 +317,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
case CONTAINED_RESOURCE_LIST:
|
case CONTAINED_RESOURCE_LIST:
|
||||||
case CONTAINED_RESOURCES: {
|
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();
|
List<IBaseResource> containedResources = getContainedResources().getContainedResources();
|
||||||
if (containedResources.size() > 0) {
|
if (containedResources.size() > 0) {
|
||||||
beginArray(theEventWriter, theChildName);
|
beginArray(theEventWriter, theChildName);
|
||||||
|
|
||||||
for (IBaseResource next : containedResources) {
|
for (IBaseResource next : containedResources) {
|
||||||
IIdType resourceId = getContainedResources().getResourceId(next);
|
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();
|
theEventWriter.endArray();
|
||||||
|
@ -344,7 +354,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
|
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
|
||||||
|
|
||||||
theEncodeContext.pushPath(def.getName(), true);
|
theEncodeContext.pushPath(def.getName(), true);
|
||||||
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, false, theEncodeContext);
|
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, theContainedResource, theEncodeContext);
|
||||||
theEncodeContext.popPath();
|
theEncodeContext.popPath();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -418,10 +428,10 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
String currentChildName = null;
|
String currentChildName = null;
|
||||||
boolean inArray = false;
|
boolean inArray = false;
|
||||||
|
|
||||||
ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<ArrayList<HeldExtension>>(0);
|
ArrayList<ArrayList<HeldExtension>> extensions = new ArrayList<>(0);
|
||||||
ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<ArrayList<HeldExtension>>(0);
|
ArrayList<ArrayList<HeldExtension>> modifierExtensions = new ArrayList<>(0);
|
||||||
ArrayList<ArrayList<String>> comments = new ArrayList<ArrayList<String>>(0);
|
ArrayList<ArrayList<String>> comments = new ArrayList<>(0);
|
||||||
ArrayList<String> ids = new ArrayList<String>(0);
|
ArrayList<String> ids = new ArrayList<>(0);
|
||||||
|
|
||||||
int valueIdx = 0;
|
int valueIdx = 0;
|
||||||
for (IBase nextValue : values) {
|
for (IBase nextValue : values) {
|
||||||
|
@ -1107,7 +1117,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
} else {
|
} else {
|
||||||
// must be a SCALAR
|
// must be a SCALAR
|
||||||
theState.enteringNewElement(null, theName);
|
theState.enteringNewElement(null, theName);
|
||||||
theState.attributeValue("value", theJsonVal.getAsString());
|
String asString = theJsonVal.getAsString();
|
||||||
|
theState.attributeValue("value", asString);
|
||||||
parseAlternates(theAlternateVal, theState, theAlternateName, theAlternateName);
|
parseAlternates(theAlternateVal, theState, theAlternateName, theAlternateName);
|
||||||
theState.endingElement();
|
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 {
|
private static void write(JsonLikeWriter theWriter, String theName, String theValue) throws IOException {
|
||||||
theWriter.write(theName, theValue);
|
theWriter.write(theName, theValue);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1074,7 +1074,7 @@ class ParserState<T> {
|
||||||
*/
|
*/
|
||||||
for (IBaseResource next : myGlobalResources) {
|
for (IBaseResource next : myGlobalResources) {
|
||||||
IIdType id = next.getIdElement();
|
IIdType id = next.getIdElement();
|
||||||
if (id != null && id.isEmpty() == false) {
|
if (id != null && !id.isEmpty()) {
|
||||||
String resName = myContext.getResourceDefinition(next).getName();
|
String resName = myContext.getResourceDefinition(next).getName();
|
||||||
IIdType idType = id.withResourceType(resName).toUnqualifiedVersionless();
|
IIdType idType = id.withResourceType(resName).toUnqualifiedVersionless();
|
||||||
idToResource.put(idType.getValueAsString(), next);
|
idToResource.put(idType.getValueAsString(), next);
|
||||||
|
@ -1082,10 +1082,11 @@ class ParserState<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (IBaseReference nextRef : myGlobalReferences) {
|
for (IBaseReference nextRef : myGlobalReferences) {
|
||||||
if (nextRef.isEmpty() == false && nextRef.getReferenceElement() != null) {
|
if (!nextRef.isEmpty() && nextRef.getReferenceElement() != null) {
|
||||||
IIdType unqualifiedVersionless = nextRef.getReferenceElement().toUnqualifiedVersionless();
|
IIdType unqualifiedVersionless = nextRef.getReferenceElement().toUnqualifiedVersionless();
|
||||||
IBaseResource target = idToResource.get(unqualifiedVersionless.getValueAsString());
|
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);
|
nextRef.setResource(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,7 +287,8 @@ public class XmlParser extends BaseParser {
|
||||||
for (IBaseResource next : getContainedResources().getContainedResources()) {
|
for (IBaseResource next : getContainedResources().getContainedResources()) {
|
||||||
IIdType resourceId = getContainedResources().getResourceId(next);
|
IIdType resourceId = getContainedResources().getResourceId(next);
|
||||||
theEventWriter.writeStartElement("contained");
|
theEventWriter.writeStartElement("contained");
|
||||||
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(resourceId.getValue()), theEncodeContext);
|
String value = resourceId.getValue();
|
||||||
|
encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(value), theEncodeContext);
|
||||||
theEventWriter.writeEndElement();
|
theEventWriter.writeEndElement();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -300,7 +301,7 @@ public class XmlParser extends BaseParser {
|
||||||
}
|
}
|
||||||
theEventWriter.writeStartElement(theChildName);
|
theEventWriter.writeStartElement(theChildName);
|
||||||
theEncodeContext.pushPath(resourceName, true);
|
theEncodeContext.pushPath(resourceName, true);
|
||||||
encodeResourceToXmlStreamWriter(resource, theEventWriter, false, theEncodeContext);
|
encodeResourceToXmlStreamWriter(resource, theEventWriter, theIncludedResource, theEncodeContext);
|
||||||
theEncodeContext.popPath();
|
theEncodeContext.popPath();
|
||||||
theEventWriter.writeEndElement();
|
theEventWriter.writeEndElement();
|
||||||
break;
|
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 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,34 +19,38 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.parser.json;
|
package ca.uhn.fhir.parser.json;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface is the generic representation of any sort of data
|
* This interface is the generic representation of any sort of data
|
||||||
* structure that looks and smells like JSON. These data structures
|
* structure that looks and smells like JSON. These data structures
|
||||||
* can be abstractly viewed as a <code.Map</code> or <code>List</code>
|
* can be abstractly viewed as a <code.Map</code> or <code>List</code>
|
||||||
* whose members are other Maps, Lists, or scalars (Strings, Numbers, Boolean)
|
* whose members are other Maps, Lists, or scalars (Strings, Numbers, Boolean)
|
||||||
*
|
*
|
||||||
* @author Bill.Denton
|
* @author Bill.Denton
|
||||||
*/
|
*/
|
||||||
public interface JsonLikeStructure {
|
public interface JsonLikeStructure {
|
||||||
public JsonLikeStructure getInstance();
|
JsonLikeStructure getInstance();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the JSON document into the Json-like structure
|
* Parse the JSON document into the Json-like structure
|
||||||
* so that it can be navigated.
|
* so that it can be navigated.
|
||||||
*
|
*
|
||||||
* @param theReader a <code>Reader</code> that will
|
* @param theReader a <code>Reader</code> that will
|
||||||
* process the JSON input stream
|
* process the JSON input stream
|
||||||
* @throws DataFormatException when invalid JSON is received
|
* @throws DataFormatException when invalid JSON is received
|
||||||
*/
|
*/
|
||||||
public void load (Reader theReader) throws DataFormatException;
|
void load(Reader theReader) throws DataFormatException;
|
||||||
public void load (Reader theReader, boolean allowArray) throws DataFormatException;
|
|
||||||
public JsonLikeObject getRootObject () throws DataFormatException;
|
void load(Reader theReader, boolean allowArray) throws DataFormatException;
|
||||||
public JsonLikeArray getRootArray () throws DataFormatException;
|
|
||||||
public JsonLikeWriter getJsonLikeWriter ();
|
JsonLikeObject getRootObject() throws DataFormatException;
|
||||||
public JsonLikeWriter getJsonLikeWriter (Writer writer);
|
|
||||||
|
JsonLikeWriter getJsonLikeWriter();
|
||||||
|
|
||||||
|
JsonLikeWriter getJsonLikeWriter(Writer writer) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,55 +29,73 @@ public abstract class JsonLikeWriter {
|
||||||
|
|
||||||
private boolean prettyPrint;
|
private boolean prettyPrint;
|
||||||
private Writer writer;
|
private Writer writer;
|
||||||
|
|
||||||
public void setPrettyPrint (boolean tf) {
|
|
||||||
prettyPrint = tf;
|
|
||||||
}
|
|
||||||
public boolean isPrettyPrint () {
|
|
||||||
return prettyPrint;
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
public JsonLikeWriter() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPrettyPrint() {
|
||||||
|
return prettyPrint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrettyPrint(boolean tf) {
|
||||||
|
prettyPrint = tf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Writer getWriter() {
|
||||||
|
return writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWriter(Writer writer) {
|
||||||
|
this.writer = 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 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 endObject() throws IOException;
|
||||||
|
|
||||||
|
public abstract JsonLikeWriter endArray() throws IOException;
|
||||||
|
|
||||||
|
public abstract JsonLikeWriter endBlock() throws IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
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 {
|
public class Constants {
|
||||||
|
|
||||||
|
@ -252,7 +259,8 @@ public class Constants {
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source";
|
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 {
|
static {
|
||||||
CHARSET_UTF8 = StandardCharsets.UTF_8;
|
CHARSET_UTF8 = StandardCharsets.UTF_8;
|
||||||
|
@ -354,16 +362,7 @@ public class Constants {
|
||||||
CORS_ALLWED_METHODS = Collections.unmodifiableSet(corsAllowedMethods);
|
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) {
|
public static String codeSystemWithDefaultDescription(String theSystem) {
|
||||||
if (codeSystemNotNeeded(theSystem)) {
|
return defaultIfBlank(theSystem, "(none)");
|
||||||
return "(none)";
|
|
||||||
} else {
|
|
||||||
return theSystem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,14 @@ public interface IBaseOn<T> {
|
||||||
* Perform the operation across all versions of all resources of the given type on the server
|
* Perform the operation across all versions of all resources of the given type on the server
|
||||||
*/
|
*/
|
||||||
T onType(Class<? extends IBaseResource> theResourceType);
|
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.
|
* 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
|
* 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;
|
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) {
|
public ReferenceParam setValue(String theValue) {
|
||||||
IdDt id = new IdDt(theValue);
|
IdDt id = new IdDt(theValue);
|
||||||
String qualifier= null;
|
String qualifier= null;
|
||||||
|
|
|
@ -238,7 +238,7 @@ public class ParametersUtil {
|
||||||
addPart(theContext, theParameter, theName, coding);
|
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());
|
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
|
||||||
BaseRuntimeChildDefinition partChild = def.getChildByName("part");
|
BaseRuntimeChildDefinition partChild = def.getChildByName("part");
|
||||||
|
|
||||||
|
@ -252,4 +252,19 @@ public class ParametersUtil {
|
||||||
|
|
||||||
partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
|
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.
|
* limitations under the License.
|
||||||
* #L%
|
* #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.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
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 {
|
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);
|
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) {
|
public static LinkedHashSet<Method> getDeclaredMethods(Class<?> theClazz) {
|
||||||
LinkedHashSet<Method> retVal = new LinkedHashSet<Method>();
|
LinkedHashSet<Method> retVal = new LinkedHashSet<>();
|
||||||
for (Method next : theClazz.getDeclaredMethods()) {
|
for (Method next : theClazz.getDeclaredMethods()) {
|
||||||
try {
|
try {
|
||||||
Method method = theClazz.getMethod(next.getName(), next.getParameterTypes());
|
Method method = theClazz.getMethod(next.getName(), next.getParameterTypes());
|
||||||
retVal.add(method);
|
retVal.add(method);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException | SecurityException e) {
|
||||||
retVal.add(next);
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
retVal.add(next);
|
retVal.add(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +99,7 @@ public class ReflectionUtil {
|
||||||
return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]);
|
return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "rawtypes" })
|
@SuppressWarnings({"rawtypes"})
|
||||||
private static Class<?> getGenericCollectionTypeOf(Type theType) {
|
private static Class<?> getGenericCollectionTypeOf(Type theType) {
|
||||||
Class<?> type;
|
Class<?> type;
|
||||||
if (ParameterizedType.class.isAssignableFrom(theType.getClass())) {
|
if (ParameterizedType.class.isAssignableFrom(theType.getClass())) {
|
||||||
|
@ -140,43 +146,38 @@ public class ReflectionUtil {
|
||||||
public static Object newInstanceOfFhirServerType(String theType) {
|
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 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";
|
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")
|
private static Object newInstanceOfType(String theKey, String theType, String errorMessage, String wantedType, Class<?>[] theParameterArgTypes, Object[] theConstructorArgs) {
|
||||||
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(
|
Object fhirServerVersion = ourFhirServerVersions.get(theKey);
|
||||||
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);
|
|
||||||
if (fhirServerVersion == null) {
|
if (fhirServerVersion == null) {
|
||||||
try {
|
try {
|
||||||
Class<?> type = Class.forName(theType);
|
Class<?> type = Class.forName(theType);
|
||||||
Class<?> serverType = Class.forName(wantedType);
|
Class<?> serverType = Class.forName(wantedType);
|
||||||
Validate.isTrue(serverType.isAssignableFrom(type));
|
Validate.isTrue(serverType.isAssignableFrom(type));
|
||||||
fhirServerVersion = type.newInstance();
|
fhirServerVersion = type.getConstructor(theParameterArgTypes).newInstance(theConstructorArgs);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ConfigurationException(errorMessage, e);
|
throw new ConfigurationException(errorMessage, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
ourFhirServerVersions.put(theType, fhirServerVersion);
|
ourFhirServerVersions.put(theKey, fhirServerVersion);
|
||||||
}
|
}
|
||||||
return fhirServerVersion;
|
return fhirServerVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <T> T newInstanceOrReturnNull(String theClassName, Class<T> theType) {
|
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 {
|
try {
|
||||||
Class<?> clazz = Class.forName(theClassName);
|
Class<?> clazz = Class.forName(theClassName);
|
||||||
if (!theType.isAssignableFrom(clazz)) {
|
if (!theType.isAssignableFrom(clazz)) {
|
||||||
throw new ConfigurationException(theClassName + " is not assignable to " + theType);
|
throw new ConfigurationException(theClassName + " is not assignable to " + theType);
|
||||||
}
|
}
|
||||||
return (T) clazz.newInstance();
|
return (T) clazz.getConstructor(theArgTypes).newInstance(theArgs);
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Exception 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
|
* environment
|
||||||
*/
|
*/
|
||||||
public static void randomizeLocale() {
|
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)]);
|
Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
|
||||||
ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
|
ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
|
||||||
if (Math.random() < 0.5) {
|
if (Math.random() < 0.5) {
|
||||||
|
|
|
@ -59,7 +59,8 @@ public enum VersionEnum {
|
||||||
V4_0_3,
|
V4_0_3,
|
||||||
V4_1_0,
|
V4_1_0,
|
||||||
V4_2_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() {
|
public static VersionEnum latestVersion() {
|
||||||
VersionEnum[] values = VersionEnum.values();
|
VersionEnum[] values = VersionEnum.values();
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package ca.uhn.fhir.jpa.term;
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR JPA Server
|
* HAPI FHIR - Core Library
|
||||||
* %%
|
* %%
|
||||||
* Copyright (C) 2014 - 2020 University Health Network
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
* %%
|
* %%
|
||||||
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.term;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
|
@ -28,20 +29,30 @@ public class VersionIndependentConcept implements Comparable<VersionIndependentC
|
||||||
|
|
||||||
private final String mySystem;
|
private final String mySystem;
|
||||||
private final String myCode;
|
private final String myCode;
|
||||||
|
private final String myDisplay;
|
||||||
private int myHashCode;
|
private int myHashCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public VersionIndependentConcept(String theSystem, String theCode) {
|
public VersionIndependentConcept(String theSystem, String theCode) {
|
||||||
|
this(theSystem, theCode, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VersionIndependentConcept(String theSystem, String theCode, String theDisplay) {
|
||||||
mySystem = theSystem;
|
mySystem = theSystem;
|
||||||
myCode = theCode;
|
myCode = theCode;
|
||||||
|
myDisplay = theDisplay;
|
||||||
myHashCode = new HashCodeBuilder(17, 37)
|
myHashCode = new HashCodeBuilder(17, 37)
|
||||||
.append(mySystem)
|
.append(mySystem)
|
||||||
.append(myCode)
|
.append(myCode)
|
||||||
.toHashCode();
|
.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDisplay() {
|
||||||
|
return myDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
public String getSystem() {
|
public String getSystem() {
|
||||||
return mySystem;
|
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.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.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.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.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}
|
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.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.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 java.io.StringReader;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.parser.json.jackson.JacksonStructure;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class JsonLikeStructureTest {
|
public class JsonLikeStructureTest {
|
||||||
|
@ -39,7 +40,7 @@ public class JsonLikeStructureTest {
|
||||||
@Test
|
@Test
|
||||||
public void testStructureLoading() {
|
public void testStructureLoading() {
|
||||||
StringReader reader = new StringReader(TEST_STRUCTURELOADING_DATA);
|
StringReader reader = new StringReader(TEST_STRUCTURELOADING_DATA);
|
||||||
JsonLikeStructure jsonStructure = new GsonStructure();
|
JsonLikeStructure jsonStructure = new JacksonStructure();
|
||||||
jsonStructure.load(reader);
|
jsonStructure.load(reader);
|
||||||
|
|
||||||
JsonLikeObject rootObject = jsonStructure.getRootObject();
|
JsonLikeObject rootObject = jsonStructure.getRootObject();
|
||||||
|
@ -70,7 +71,7 @@ public class JsonLikeStructureTest {
|
||||||
@Test
|
@Test
|
||||||
public void testJsonAndDataTypes() {
|
public void testJsonAndDataTypes() {
|
||||||
StringReader reader = new StringReader(TEST_JSONTYPES_DATA);
|
StringReader reader = new StringReader(TEST_JSONTYPES_DATA);
|
||||||
JsonLikeStructure jsonStructure = new GsonStructure();
|
JsonLikeStructure jsonStructure = new JacksonStructure();
|
||||||
jsonStructure.load(reader);
|
jsonStructure.load(reader);
|
||||||
|
|
||||||
JsonLikeObject rootObject = jsonStructure.getRootObject();
|
JsonLikeObject rootObject = jsonStructure.getRootObject();
|
||||||
|
|
|
@ -45,4 +45,10 @@ public class ReflectionUtilTest {
|
||||||
assertEquals("Failed to instantiate java.util.List", e.getMessage());
|
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>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-bom</artifactId>
|
<artifactId>hapi-fhir-bom</artifactId>
|
||||||
<version>4.3.0-SNAPSHOT</version>
|
<version>5.0.0-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>HAPI FHIR BOM</name>
|
<name>HAPI FHIR BOM</name>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>4.3.0-SNAPSHOT</version>
|
<version>5.0.0-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue