Lots of documentation updates
This commit is contained in:
parent
efb14397df
commit
622e528f43
|
@ -8,9 +8,6 @@
|
|||
<body>
|
||||
<release version="0.5" date="TBD">
|
||||
<action type="add">
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
RESTful search method parameters have been overhauled to reduce confusing duplicate names and
|
||||
having multiple ways of accomplishing the same thing. This means that a number of existing classes
|
||||
have been deprocated in favour of new naming schemes.
|
||||
<![CDATA[<br/><br/>]]>
|
||||
|
@ -25,7 +22,6 @@
|
|||
new parameters when possible.
|
||||
</action>
|
||||
<action type="add">
|
||||
>>>>>>> af3c35cbc07df69c760e200b4a80f4bcc3d183e9
|
||||
Allow server methods to return wildcard generic types (e.g. List<? extends IResource>)
|
||||
</action>
|
||||
<action type="add">
|
||||
|
|
|
@ -45,7 +45,13 @@ public @interface IncludeParam {
|
|||
/**
|
||||
* Optional parameter, if provided the server will only allow the values
|
||||
* within the given set. If an _include parameter is passed to the server
|
||||
* which does not match any allowed values the server will return an error.
|
||||
* which does not match any allowed values the server will return an error.
|
||||
* <p>
|
||||
* You may also pass in a value of "*" which indicates to the server
|
||||
* that any value is allowable and will be passed to this parameter. This is
|
||||
* helpful if you want to explicitly declare support for some includes, but also
|
||||
* allow others implicitly (e.g. imports from other resources)
|
||||
* </p>
|
||||
*/
|
||||
String[] allow() default {};
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ import ca.uhn.fhir.rest.annotation.Validate;
|
|||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||
import ca.uhn.fhir.rest.param.IncludeParameter;
|
||||
import ca.uhn.fhir.rest.server.BundleProviders;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package ca.uhn.fhir.rest.param;
|
||||
package ca.uhn.fhir.rest.method;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -34,11 +34,11 @@ import ca.uhn.fhir.model.api.Include;
|
|||
import ca.uhn.fhir.model.api.PathSpecification;
|
||||
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
|
||||
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||
import ca.uhn.fhir.rest.method.QualifiedParamList;
|
||||
import ca.uhn.fhir.rest.param.BaseQueryParameter;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
public class IncludeParameter extends BaseQueryParameter {
|
||||
class IncludeParameter extends BaseQueryParameter {
|
||||
|
||||
private Set<String> myAllow;
|
||||
private Class<? extends Collection<Include>> myInstantiableCollectionType;
|
||||
|
@ -133,7 +133,9 @@ public class IncludeParameter extends BaseQueryParameter {
|
|||
String value = nextParamList.get(0);
|
||||
if (myAllow != null) {
|
||||
if (!myAllow.contains(value)) {
|
||||
throw new InvalidRequestException("Invalid _include parameter value: '" + value + "'. Valid values are: " + new TreeSet<String>(myAllow));
|
||||
if (!myAllow.contains("*")) {
|
||||
throw new InvalidRequestException("Invalid _include parameter value: '" + value + "'. Valid values are: " + new TreeSet<String>(myAllow));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (retValCollection == null) {
|
|
@ -52,7 +52,6 @@ import ca.uhn.fhir.rest.annotation.VersionIdParam;
|
|||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.param.CollectionBinder;
|
||||
import ca.uhn.fhir.rest.param.IncludeParameter;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
|
|
@ -1167,4 +1167,6 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package example;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
|
@ -27,21 +32,64 @@ Bundle versions = client.history(Patient.class, "1",null,null);
|
|||
// END SNIPPET: simple
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void fluentSearch() {
|
||||
FhirContext ctx = new FhirContext();
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://fhir.healthintersections.com.au/open");
|
||||
{
|
||||
//START SNIPPET: create
|
||||
Patient patient = new Patient();
|
||||
// ..populate the patient object..
|
||||
patient.addIdentifier("urn:system", "12345");
|
||||
patient.addName().addFamily("Smith").addGiven("John");
|
||||
|
||||
//START SNIPPET: fluentExample
|
||||
// Invoke the server create method (and send pretty-printed JSON encoding to the server
|
||||
// instead of the default which is non-pretty printed XML)
|
||||
client.create()
|
||||
.resource(patient)
|
||||
.prettyPrint()
|
||||
.encodedJson()
|
||||
.execute();
|
||||
//END SNIPPET: create
|
||||
}
|
||||
{
|
||||
//START SNIPPET: conformance
|
||||
// Retrieve the server's conformance statement and print its description
|
||||
Conformance conf = client.conformance();
|
||||
System.out.println(conf.getDescription().getValue());
|
||||
//END SNIPPET: conformance
|
||||
}
|
||||
{
|
||||
//START SNIPPET: search
|
||||
Bundle response = client.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.BIRTHDATE.beforeOrEquals().day("2011-01-01"))
|
||||
.and(Patient.PROVIDER.hasChainedProperty(Organization.NAME.matches().value("Health")))
|
||||
.andLogRequestAndResponse(true)
|
||||
.execute();
|
||||
//END SNIPPET: fluentExample
|
||||
//END SNIPPET: search
|
||||
//START SNIPPET: searchPaging
|
||||
if (response.getLinkNext().isEmpty() == false) {
|
||||
|
||||
// load next page
|
||||
Bundle nextPage = client.loadPage()
|
||||
.next(response)
|
||||
.execute();
|
||||
}
|
||||
//END SNIPPET: searchPaging
|
||||
}
|
||||
{
|
||||
//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<IResource> response = client.transaction()
|
||||
.withResources(resources)
|
||||
.execute();
|
||||
//END SNIPPET: transaction
|
||||
}
|
||||
|
||||
|
||||
System.out.println(ctx.newXmlParser().setPrettyPrint(true).encodeBundleToString(response));
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -64,6 +64,8 @@ import ca.uhn.fhir.rest.param.DateParam;
|
|||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
|
@ -180,6 +182,10 @@ public Patient getResourceById(@IdParam IdDt theId) {
|
|||
retVal.addName().addFamily("Smith").addGiven("Tester").addGiven("Q");
|
||||
// ...etc...
|
||||
|
||||
// if you know the version ID of the resource, you should set it and HAPI will
|
||||
// include it in a Content-Location header
|
||||
retVal.setId(new IdDt("Patient", "123", "2"));
|
||||
|
||||
return retVal;
|
||||
}
|
||||
//END SNIPPET: read
|
||||
|
@ -251,10 +257,8 @@ public List<Patient> searchByLastName(@RequiredParam(name=Patient.SP_FAMILY) Str
|
|||
patient.addName().addFamily("Smith").addGiven("Tester").addGiven("Q");
|
||||
// ...etc...
|
||||
|
||||
/*
|
||||
* Every returned resource must have its logical ID set. If the server
|
||||
* supports versioning, that should be set too
|
||||
*/
|
||||
// Every returned resource must have its logical ID set. If the server
|
||||
// supports versioning, that should be set too
|
||||
String logicalId = "4325";
|
||||
String versionId = "2"; // optional
|
||||
patient.setId(new IdDt("Patient", logicalId, versionId));
|
||||
|
@ -325,7 +329,9 @@ public List<Patient> searchWithDocs(
|
|||
|
||||
//START SNIPPET: searchMultiple
|
||||
@Search()
|
||||
public List<Observation> searchByObservationNames( @RequiredParam(name=Observation.SP_NAME) TokenOrListParam theCodings ) {
|
||||
public List<Observation> searchByObservationNames(
|
||||
@RequiredParam(name=Observation.SP_NAME) TokenOrListParam theCodings ) {
|
||||
|
||||
// The list here will contain 0..* codings, and any observations which match any of the
|
||||
// given codings should be returned
|
||||
List<CodingDt> wantedCodings = theCodings.getListAsCodings();
|
||||
|
@ -336,6 +342,32 @@ public List<Observation> searchByObservationNames( @RequiredParam(name=Observati
|
|||
}
|
||||
//END SNIPPET: searchMultiple
|
||||
|
||||
|
||||
//START SNIPPET: searchMultipleAnd
|
||||
@Search()
|
||||
public List<Patient> searchByPatientAddress(
|
||||
@RequiredParam(name=Patient.SP_ADDRESS) StringAndListParam theAddressParts ) {
|
||||
|
||||
// StringAndListParam is a container for 0..* StringOrListParam, which is in turn a
|
||||
// container for 0..* strings. It is a little bit weird to understand at first, but think of the
|
||||
// StringAndListParam to be an AND list with multiple OR lists inside it. So you will need
|
||||
// to return results which match at least one string within every OR list.
|
||||
List<StringOrListParam> wantedCodings = theAddressParts.getValuesAsQueryTokens();
|
||||
for (StringOrListParam nextOrList : wantedCodings) {
|
||||
List<StringParam> queryTokens = nextOrList.getValuesAsQueryTokens();
|
||||
// Only return results that match at least one of the tokens in the list below
|
||||
for (StringParam nextString : queryTokens) {
|
||||
// ....check for match...
|
||||
}
|
||||
}
|
||||
|
||||
List<Patient> retVal = new ArrayList<Patient>();
|
||||
// ...populate...
|
||||
return retVal;
|
||||
}
|
||||
//END SNIPPET: searchMultipleAnd
|
||||
|
||||
|
||||
//START SNIPPET: dates
|
||||
@Search()
|
||||
public List<Patient> searchByObservationNames( @RequiredParam(name=Patient.SP_BIRTHDATE) DateParam theDate ) {
|
||||
|
|
|
@ -57,9 +57,8 @@
|
|||
are very inexpensive to create so you can create a new one for each request if needed
|
||||
(although there is no requirement to do so, clients are reusable and thread-safe as well).
|
||||
</p>
|
||||
|
||||
<subsection name="Search/Query">
|
||||
|
||||
|
||||
<subsection name="Fluent Operations">
|
||||
<p>
|
||||
The generic client supports queries using a fluent interface
|
||||
which is inspired by the fantastic
|
||||
|
@ -69,23 +68,77 @@
|
|||
you to take advantage of intellisense/code completion in your favourite
|
||||
IDE.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following example shows how to query using this interface:
|
||||
Note that most fluent operations end with an <code>execute()</code>
|
||||
statement which actually performs the invocation. You may also invoke
|
||||
several configuration operations just prior to the execute() statement,
|
||||
such as <code>encodedJson()</code> or <code>encodedXml()</code>.
|
||||
</p>
|
||||
</subsection>
|
||||
|
||||
<subsection name="Type - Create">
|
||||
<p>
|
||||
The following example shows how to perform a create
|
||||
operation using the generic client:
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="fluentExample" />
|
||||
<param name="id" value="create" />
|
||||
<param name="file"
|
||||
value="src/site/example/java/example/GenericClientExample.java" />
|
||||
</macro>
|
||||
</subsection>
|
||||
|
||||
<subsection name="Type - Search/Query">
|
||||
<p>
|
||||
The following example shows how to query using the generic client:
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="search" />
|
||||
<param name="file"
|
||||
value="src/site/example/java/example/GenericClientExample.java" />
|
||||
</macro>
|
||||
<p>
|
||||
If the server supports paging results, the client has a page method
|
||||
which can be used to load subsequent pages.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="searchPaging" />
|
||||
<param name="file"
|
||||
value="src/site/example/java/example/GenericClientExample.java" />
|
||||
</macro>
|
||||
</subsection>
|
||||
|
||||
<subsection name="Server - Conformance">
|
||||
<p>
|
||||
To retrieve the server's conformance statement, simply call the <code>conformance()</code>
|
||||
method as shown below.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="conformance" />
|
||||
<param name="file"
|
||||
value="src/site/example/java/example/GenericClientExample.java" />
|
||||
</macro>
|
||||
</subsection>
|
||||
|
||||
<subsection name="Server - Transaction">
|
||||
<p>
|
||||
The following example shows how to execute a transaction using the generic client:
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="transaction" />
|
||||
<param name="file"
|
||||
value="src/site/example/java/example/GenericClientExample.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
</section>
|
||||
|
||||
<section name="The Annotation-Driven Client">
|
||||
|
||||
<p>
|
||||
HAPI also provides a second style of client caled the annotation-driven client.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The design of the annotation-driven client
|
||||
is intended to be similar to that of
|
||||
|
@ -95,10 +148,24 @@
|
|||
annotated methods which HAPI binds to calls against a server.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The annotation-driven client is particularly useful if you have a server that
|
||||
exposes a set of specific operations (search parameter combinations, named queries, etc.)
|
||||
and you want to let developers have a stongly/statically typed interface to that
|
||||
server.
|
||||
</p>
|
||||
<p>
|
||||
There is no difference in terms of capability between the two styles of
|
||||
client. There is simply a difference in programming style and complexity. It
|
||||
is probably safe to say that the generic client is easier to use and leads to
|
||||
more readable code, at the expense of not giving any visibility into the
|
||||
specific capabilities of the server you are interacting with.
|
||||
</p>
|
||||
|
||||
<subsection name="Defining A Restful Client Interface">
|
||||
|
||||
<p>
|
||||
The first step in creating a FHIR RESTful Client is to define a
|
||||
The first step in creating an annotation-driven client is to define a
|
||||
restful client interface.
|
||||
</p>
|
||||
|
||||
|
@ -134,7 +201,7 @@
|
|||
<macro name="snippet">
|
||||
<param name="id" value="provider" />
|
||||
<param name="file"
|
||||
value="src/site/example/java/example/RestfulClientImpl.java" />
|
||||
value="src/site/example/java/example/IRestfulClient.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
|
|
|
@ -466,14 +466,14 @@
|
|||
</p>
|
||||
|
||||
</subsection>
|
||||
<subsection name="Search Parameters: Introduction (String)">
|
||||
<subsection name="Search Parameters: String Introduction">
|
||||
|
||||
<p>
|
||||
To allow a search using given search parameters, add one or more parameters
|
||||
to your search method and tag these parameters as either
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/RequiredParam.html">@RequiredParam</a>
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/annotation/RequiredParam.html">@RequiredParam</a>
|
||||
or
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/OptionalParam.html">@OptionalParam</a>
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/annotation/OptionalParam.html">@OptionalParam</a>
|
||||
.
|
||||
</p>
|
||||
|
||||
|
@ -734,13 +734,13 @@
|
|||
may (or may not) be optional.
|
||||
To add a second required parameter, annotate the
|
||||
parameter with
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/RequiredParam.html">@RequiredParam</a>
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/annotation/RequiredParam.html">@RequiredParam</a>
|
||||
.
|
||||
To add an optional parameter (which will be passed in as
|
||||
<code>null</code>
|
||||
if no value
|
||||
is supplied), annotate the method with
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/OptionalParam.html">@OptionalParam</a>
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/annotation/OptionalParam.html">@OptionalParam</a>
|
||||
.
|
||||
</p>
|
||||
|
||||
|
@ -821,13 +821,31 @@
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
It is worth noting that according to the FHIR specification, you can have an
|
||||
AND relationship combining multiple OR relationships, but not vice-versa. In
|
||||
other words, it's possible to support a search like
|
||||
<code>("name" = ("joe" or "john")) AND ("age" = (11 or 12))</code> but not
|
||||
a search like
|
||||
<code>("language" = ("en" AND "fr") OR ("address" = ("Canada" AND "Quebec"))</code>
|
||||
</p>
|
||||
|
||||
<h4>OR Relationship Query Parameters</h4>
|
||||
|
||||
<p>
|
||||
To accept a composite parameter, use a parameter type which implements the
|
||||
<a href="./apidocs/ca/uhn/fhir/model/api/IQueryParameterOr.html">IQueryParameterOr</a>
|
||||
interface. For example, to accept searches for
|
||||
Observations matching any of a collection of names:
|
||||
interface.
|
||||
</p>
|
||||
<p>
|
||||
Each parameter type (StringParam, TokenParam, etc.) has a corresponding parameter
|
||||
which accepts an OR list of parameters. These types are called "[type]OrListParam", for example:
|
||||
StringOrListParam and TokenOrListParam.
|
||||
</p>
|
||||
<p>
|
||||
The following example shows a search for Observation by name, where a list of
|
||||
names may be passed in (and the expectation is that the server will return Observations
|
||||
that match any of these names):
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
|
@ -848,12 +866,32 @@
|
|||
<a href="./apidocs/ca/uhn/fhir/model/api/IQueryParameterAnd.html">IQueryParameterAnd</a>
|
||||
interface.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
See the section below on
|
||||
<a href="#DATE_RANGES">date ranges</a>
|
||||
for
|
||||
one example of an AND parameter.
|
||||
An example follows which shows a search for Patients by address, where multiple string
|
||||
lists may be supplied by the client. For example, the client might request that the
|
||||
address match <code>("Montreal" OR "Sherbrooke") AND ("Quebec" OR "QC")</code> using
|
||||
the following query:
|
||||
<br/>
|
||||
<code>http://fhir.example.com/Patient?address=Montreal,Sherbrooke&address=Quebec,QC</code>
|
||||
</p>
|
||||
<p>
|
||||
The following code shows how to receive this parameter using a
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/param/StringAndListParam.html">StringAndListParameter</a>,
|
||||
which can handle an AND list of multiple OR lists of strings.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="searchMultipleAnd" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
<h4>AND Relationship Query Parameters for Dates</h4>
|
||||
|
||||
<p>
|
||||
Dates are a bit of a special case, since it is a common scenario to want to match
|
||||
a date range (which is really just an AND query on two qualified date parameters).
|
||||
See the section below on <a href="#DATE_RANGES">date ranges</a>
|
||||
for an example of a DateRangeParameter.
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
|
|
|
@ -910,7 +910,7 @@ public abstract class BaseFhirDao implements IDao {
|
|||
};
|
||||
}
|
||||
|
||||
protected void loadResourcesById(Set<IdDt> theIncludePids, List<IResource> theResourceListToPopulate) {
|
||||
protected List<IResource> loadResourcesById(Set<IdDt> theIncludePids) {
|
||||
Set<Long> pids = new HashSet<Long>();
|
||||
for (IdDt next : theIncludePids) {
|
||||
pids.add(next.getIdPartAsLong());
|
||||
|
@ -926,10 +926,13 @@ public abstract class BaseFhirDao implements IDao {
|
|||
// }
|
||||
TypedQuery<ResourceTable> q = myEntityManager.createQuery(cq);
|
||||
|
||||
ArrayList<IResource> retVal = new ArrayList<IResource>();
|
||||
for (ResourceTable next : q.getResultList()) {
|
||||
IResource resource = toResource(next);
|
||||
theResourceListToPopulate.add(resource);
|
||||
retVal.add(resource);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected String normalizeString(String theString) {
|
||||
|
|
|
@ -7,7 +7,21 @@ public class DaoConfig {
|
|||
private int myHardSearchLimit = 1000;
|
||||
private int myHardTagListLimit = 1000;
|
||||
private ResourceEncodingEnum myResourceEncoding=ResourceEncodingEnum.JSONC;
|
||||
private int myIncludeLimit = 2000;
|
||||
|
||||
/**
|
||||
* This is the maximum number of resources that will be added to a single page of
|
||||
* returned resources. Because of includes with wildcards and other possibilities it is possible for a client to make
|
||||
* requests that include very large amounts of data, so this hard limit can be imposed to prevent runaway
|
||||
* requests.
|
||||
*/
|
||||
public void setIncludeLimit(int theIncludeLimit) {
|
||||
myIncludeLimit = theIncludeLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #setIncludeLimit(int)}
|
||||
*/
|
||||
public int getHardSearchLimit() {
|
||||
return myHardSearchLimit;
|
||||
}
|
||||
|
@ -32,4 +46,8 @@ public class DaoConfig {
|
|||
myResourceEncoding = theResourceEncoding;
|
||||
}
|
||||
|
||||
public int getIncludeLimit() {
|
||||
return myIncludeLimit;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -63,6 +63,8 @@ import ca.uhn.fhir.model.dstu.composite.CodingDt;
|
|||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
|
@ -83,6 +85,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import example.QuickUsage.MyClientInterface;
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements IFhirResourceDao<T> {
|
||||
|
@ -153,10 +156,10 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
Predicate lt = builder.greaterThanOrEqualTo(from.<Date> get("myValueHigh"), lowerBound);
|
||||
lb = builder.or(gt, lt);
|
||||
|
||||
// Predicate gin = builder.isNull(from.get("myValueLow"));
|
||||
// Predicate lbo = builder.or(gt, gin);
|
||||
// Predicate lin = builder.isNull(from.get("myValueHigh"));
|
||||
// Predicate hbo = builder.or(lt, lin);
|
||||
// Predicate gin = builder.isNull(from.get("myValueLow"));
|
||||
// Predicate lbo = builder.or(gt, gin);
|
||||
// Predicate lin = builder.isNull(from.get("myValueHigh"));
|
||||
// Predicate hbo = builder.or(lt, lin);
|
||||
// lb = builder.and(lbo, hbo);
|
||||
}
|
||||
|
||||
|
@ -165,11 +168,11 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
Predicate gt = builder.lessThanOrEqualTo(from.<Date> get("myValueLow"), upperBound);
|
||||
Predicate lt = builder.lessThanOrEqualTo(from.<Date> get("myValueHigh"), upperBound);
|
||||
ub = builder.or(gt, lt);
|
||||
|
||||
// Predicate gin = builder.isNull(from.get("myValueLow"));
|
||||
// Predicate lbo = builder.or(gt, gin);
|
||||
// Predicate lin = builder.isNull(from.get("myValueHigh"));
|
||||
// Predicate ubo = builder.or(lt, lin);
|
||||
|
||||
// Predicate gin = builder.isNull(from.get("myValueLow"));
|
||||
// Predicate lbo = builder.or(gt, gin);
|
||||
// Predicate lin = builder.isNull(from.get("myValueHigh"));
|
||||
// Predicate ubo = builder.or(lt, lin);
|
||||
// ub = builder.and(ubo, lbo);
|
||||
|
||||
}
|
||||
|
@ -207,9 +210,9 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
return found;
|
||||
}
|
||||
|
||||
// private Set<Long> addPredicateComposite(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
|
||||
// }
|
||||
|
||||
// private Set<Long> addPredicateComposite(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
|
||||
// }
|
||||
|
||||
private Set<Long> addPredicateQuantity(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
|
||||
if (theList == null || theList.isEmpty()) {
|
||||
return thePids;
|
||||
|
@ -228,7 +231,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
String unitsValue;
|
||||
QuantityCompararatorEnum cmpValue;
|
||||
BigDecimal valueValue;
|
||||
boolean approx=false;
|
||||
boolean approx = false;
|
||||
|
||||
if (params instanceof QuantityDt) {
|
||||
QuantityDt param = (QuantityDt) params;
|
||||
|
@ -441,7 +444,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
}
|
||||
|
||||
if (rawSearchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed ("
|
||||
+ ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
||||
}
|
||||
|
||||
String likeExpression = normalizeString(rawSearchTerm);
|
||||
|
@ -573,10 +577,12 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
}
|
||||
|
||||
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
|
||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed ("
|
||||
+ ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
|
||||
}
|
||||
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
|
||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH
|
||||
+ "): " + code);
|
||||
}
|
||||
|
||||
ArrayList<Predicate> singleCodePredicates = (new ArrayList<Predicate>());
|
||||
|
@ -645,7 +651,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
if (theResource.getId().isEmpty() == false) {
|
||||
if (isValidPid(theResource.getId())) {
|
||||
throw new UnprocessableEntityException("This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID");
|
||||
throw new UnprocessableEntityException(
|
||||
"This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID");
|
||||
}
|
||||
createForcedIdIfNeeded(entity, theResource.getId());
|
||||
|
||||
|
@ -729,7 +736,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
final T current = currentTmp;
|
||||
|
||||
String querySring = "SELECT count(h) FROM ResourceHistoryTable h " + "WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE" + " AND h.myUpdated < :END" + (theSince != null ? " AND h.myUpdated >= :SINCE" : "");
|
||||
String querySring = "SELECT count(h) FROM ResourceHistoryTable h " + "WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE" + " AND h.myUpdated < :END"
|
||||
+ (theSince != null ? " AND h.myUpdated >= :SINCE" : "");
|
||||
TypedQuery<Long> countQuery = myEntityManager.createQuery(querySring, Long.class);
|
||||
countQuery.setParameter("PID", theId.getIdPartAsLong());
|
||||
countQuery.setParameter("RESTYPE", resourceType);
|
||||
|
@ -767,8 +775,9 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
retVal.add(current);
|
||||
}
|
||||
|
||||
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery("SELECT h FROM ResourceHistoryTable h WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE AND h.myUpdated < :END " + (theSince != null ? " AND h.myUpdated >= :SINCE" : "")
|
||||
+ " ORDER BY h.myUpdated ASC", ResourceHistoryTable.class);
|
||||
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(
|
||||
"SELECT h FROM ResourceHistoryTable h WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE AND h.myUpdated < :END "
|
||||
+ (theSince != null ? " AND h.myUpdated >= :SINCE" : "") + " ORDER BY h.myUpdated ASC", ResourceHistoryTable.class);
|
||||
q.setParameter("PID", theId.getIdPartAsLong());
|
||||
q.setParameter("RESTYPE", resourceType);
|
||||
q.setParameter("END", end.getValue(), TemporalType.TIMESTAMP);
|
||||
|
@ -837,7 +846,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
throw new ConfigurationException("Unknown search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName + "]");
|
||||
}
|
||||
if (sp.getParamType() != SearchParamTypeEnum.TOKEN) {
|
||||
throw new ConfigurationException("Search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName + "] is not a token type, only token is supported");
|
||||
throw new ConfigurationException("Search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName
|
||||
+ "] is not a token type, only token is supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -864,7 +874,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
private void validateResourceType(BaseHasResource entity) {
|
||||
if (!myResourceName.equals(entity.getResourceType())) {
|
||||
throw new ResourceNotFoundException("Resource with ID " + entity.getIdDt().getIdPart() + " exists but it is not of type " + myResourceName + ", found resource of type " + entity.getResourceType());
|
||||
throw new ResourceNotFoundException("Resource with ID " + entity.getIdDt().getIdPart() + " exists but it is not of type " + myResourceName + ", found resource of type "
|
||||
+ entity.getResourceType());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -888,7 +899,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
if (entity == null) {
|
||||
if (theId.hasVersionIdPart()) {
|
||||
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery("SELECT t from ResourceHistoryTable t WHERE t.myResourceId = :RID AND t.myResourceType = :RTYP AND t.myResourceVersion = :RVER", ResourceHistoryTable.class);
|
||||
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(
|
||||
"SELECT t from ResourceHistoryTable t WHERE t.myResourceId = :RID AND t.myResourceType = :RTYP AND t.myResourceVersion = :RVER", ResourceHistoryTable.class);
|
||||
q.setParameter("RID", theId.getIdPartAsLong());
|
||||
q.setParameter("RTYP", myResourceName);
|
||||
q.setParameter("RVER", theId.getVersionIdPartAsLong());
|
||||
|
@ -993,38 +1005,61 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
|
||||
// Load _include resources
|
||||
if (theParams.getIncludes() != null && theParams.getIncludes().isEmpty() == false) {
|
||||
Set<IdDt> includePids = new HashSet<IdDt>();
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (Include next : theParams.getIncludes()) {
|
||||
for (IResource nextResource : retVal) {
|
||||
assert myResourceType.isAssignableFrom(nextResource.getClass());
|
||||
Set<IdDt> previouslyLoadedPids = new HashSet<IdDt>();
|
||||
|
||||
List<Object> values = t.getValues(nextResource, next.getValue());
|
||||
for (Object object : values) {
|
||||
if (object == null) {
|
||||
Set<IdDt> includePids = new HashSet<IdDt>();
|
||||
List<IResource> resources = retVal;
|
||||
do {
|
||||
includePids.clear();
|
||||
|
||||
FhirTerser t = getContext().newTerser();
|
||||
for (Include next : theParams.getIncludes()) {
|
||||
for (IResource nextResource : resources) {
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(nextResource);
|
||||
if (!next.getValue().startsWith(def.getName() + ".")) {
|
||||
continue;
|
||||
}
|
||||
if (!(object instanceof ResourceReferenceDt)) {
|
||||
throw new InvalidRequestException("Path '" + next.getValue() + "' produced non ResourceReferenceDt value: " + object.getClass());
|
||||
|
||||
List<Object> values = t.getValues(nextResource, next.getValue());
|
||||
for (Object object : values) {
|
||||
if (object == null) {
|
||||
continue;
|
||||
}
|
||||
if (!(object instanceof ResourceReferenceDt)) {
|
||||
throw new InvalidRequestException("Path '" + next.getValue() + "' produced non ResourceReferenceDt value: " + object.getClass());
|
||||
}
|
||||
ResourceReferenceDt rr = (ResourceReferenceDt) object;
|
||||
if (rr.getReference().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (rr.getReference().isLocal()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IdDt nextId = rr.getReference().toUnqualified();
|
||||
if (!previouslyLoadedPids.contains(nextId)) {
|
||||
includePids.add(nextId);
|
||||
previouslyLoadedPids.add(nextId);
|
||||
}
|
||||
}
|
||||
ResourceReferenceDt rr = (ResourceReferenceDt) object;
|
||||
if (rr.getReference().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (rr.getReference().isLocal()) {
|
||||
continue;
|
||||
}
|
||||
includePids.add(rr.getReference().toUnqualified());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!includePids.isEmpty()) {
|
||||
ourLog.info("Loading {} included resources", includePids.size());
|
||||
loadResourcesById(includePids, retVal);
|
||||
if (!includePids.isEmpty()) {
|
||||
ourLog.info("Loading {} included resources", includePids.size());
|
||||
resources = loadResourcesById(includePids);
|
||||
retVal.addAll(resources);
|
||||
}
|
||||
} while (includePids.size() > 0 && previouslyLoadedPids.size() < getConfig().getIncludeLimit());
|
||||
|
||||
if (previouslyLoadedPids.size() >= getConfig().getIncludeLimit()) {
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDetails("Not all _include resources were actually included as the request surpassed the limit of " + getConfig().getIncludeLimit() + " resources");
|
||||
retVal.add(0, oo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
|
@ -1181,8 +1216,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
}
|
||||
|
||||
/**
|
||||
* If set, the given param will be treated as a secondary primary key, and multiple resources will not be able to
|
||||
* share the same value.
|
||||
* If set, the given param will be treated as a secondary primary key, and multiple resources will not be able to share the same value.
|
||||
*/
|
||||
public void setSecondaryPrimaryKeyParamName(String theSecondaryPrimaryKeyParamName) {
|
||||
mySecondaryPrimaryKeyParamName = theSecondaryPrimaryKeyParamName;
|
||||
|
|
|
@ -138,6 +138,7 @@
|
|||
<packageBase>ca.uhn.test.jpasrv</packageBase>
|
||||
<baseResourceNames>
|
||||
<baseResourceName>device</baseResourceName>
|
||||
<baseResourceName>encounter</baseResourceName>
|
||||
<baseResourceName>diagnosticreport</baseResourceName>
|
||||
<baseResourceName>location</baseResourceName>
|
||||
<baseResourceName>observation</baseResourceName>
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
<bean id="myDiagnosticReportDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.DiagnosticReport"/>
|
||||
</bean>
|
||||
<bean id="myLocationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Location"/>
|
||||
</bean>
|
||||
<bean id="myPatientDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Patient"/>
|
||||
</bean>
|
||||
|
@ -33,12 +36,12 @@
|
|||
<bean id="myOrganizationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Organization"/>
|
||||
</bean>
|
||||
<bean id="myLocationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Location"/>
|
||||
</bean>
|
||||
<bean id="myQuestionnaireDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Questionnaire"/>
|
||||
</bean>
|
||||
<bean id="myEncounterDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
|
||||
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Encounter"/>
|
||||
</bean>
|
||||
|
||||
<bean id="myPersistenceDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true">
|
||||
<property name="url" value="jdbc:derby:memory:myUnitTestDB;create=true" />
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package ca.uhn.fhir.jpa.test;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
|
@ -17,31 +22,39 @@ import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
|||
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.dstu.composite.PeriodDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Encounter;
|
||||
import ca.uhn.fhir.model.dstu.resource.Location;
|
||||
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
|
||||
import ca.uhn.fhir.model.dstu.valueset.EncounterClassEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.EncounterStateEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.test.jpasrv.EncounterResourceProvider;
|
||||
import ca.uhn.test.jpasrv.LocationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.ObservationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.OrganizationResourceProvider;
|
||||
import ca.uhn.test.jpasrv.PatientResourceProvider;
|
||||
|
||||
public class CompleteResourceProviderTest {
|
||||
|
||||
private static IFhirResourceDao<Observation> observationDao;
|
||||
private static ClassPathXmlApplicationContext ourAppCtx;
|
||||
private static FhirContext ourCtx;
|
||||
|
||||
private static IGenericClient ourClient;
|
||||
private static FhirContext ourCtx;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompleteResourceProviderTest.class);
|
||||
private static Server ourServer;
|
||||
private static IFhirResourceDao<Patient> patientDao;
|
||||
private static IFhirResourceDao<Questionnaire> questionnaireDao;
|
||||
private static IGenericClient ourClient;
|
||||
private static IFhirResourceDao<Observation> observationDao;
|
||||
|
||||
// private static JpaConformanceProvider ourConfProvider;
|
||||
|
||||
|
@ -66,23 +79,7 @@ public class CompleteResourceProviderTest {
|
|||
//
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testSearchByIdentifier() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier01");
|
||||
p1.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven01");
|
||||
IdDt p1Id = ourClient.create(p1).getId();
|
||||
|
||||
Patient p2 = new Patient();
|
||||
p2.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier02");
|
||||
p2.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven02");
|
||||
ourClient.create(p2).getId();
|
||||
|
||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testSearchByIdentifier01")).encodedJson().prettyPrint().execute();
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
}
|
||||
private static IFhirResourceDao<Questionnaire> questionnaireDao;
|
||||
|
||||
@Test
|
||||
public void testCreateWithId() {
|
||||
|
@ -112,52 +109,21 @@ public class CompleteResourceProviderTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByIdentifierWithoutSystem() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01");
|
||||
IdDt p1Id = ourClient.create(p1).getId();
|
||||
|
||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint()
|
||||
.execute();
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByResourceChain() {
|
||||
Organization o1 = new Organization();
|
||||
o1.setName("testSearchByResourceChainName01");
|
||||
IdDt o1id = ourClient.create(o1).getId();
|
||||
|
||||
public void testInsertBadReference() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
|
||||
p1.addName().addFamily("testSearchByResourceChainFamily01").addGiven("testSearchByResourceChainGiven01");
|
||||
p1.setManagingOrganization(new ResourceReferenceDt(o1id));
|
||||
IdDt p1Id = ourClient.create(p1).getId();
|
||||
p1.setManagingOrganization(new ResourceReferenceDt("Organization/132312323"));
|
||||
|
||||
//@formatter:off
|
||||
Bundle actual = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.PROVIDER.hasId(o1id.getIdPart()))
|
||||
.encodedJson().andLogRequestAndResponse(true).prettyPrint().execute();
|
||||
//@formatter:on
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
//@formatter:off
|
||||
actual = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.PROVIDER.hasId(o1id.getValue()))
|
||||
.encodedJson().andLogRequestAndResponse(true).prettyPrint().execute();
|
||||
//@formatter:on
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
try {
|
||||
ourClient.create(p1).getId();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Organization/132312323"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompleteResourceProviderTest.class);
|
||||
|
||||
@Test
|
||||
public void testInsertUpdatesConformance() {
|
||||
// Conformance conf = ourConfProvider.getServerConformance();
|
||||
|
@ -197,22 +163,6 @@ public class CompleteResourceProviderTest {
|
|||
// assertEquals(initial+1, number.getValueAsInteger());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertBadReference() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
|
||||
p1.addName().addFamily("testSearchByResourceChainFamily01").addGiven("testSearchByResourceChainGiven01");
|
||||
p1.setManagingOrganization(new ResourceReferenceDt("Organization/132312323"));
|
||||
|
||||
try {
|
||||
ourClient.create(p1).getId();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Organization/132312323"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveAndRetrieveExistingNarrative() {
|
||||
Patient p1 = new Patient();
|
||||
|
@ -236,6 +186,108 @@ public class CompleteResourceProviderTest {
|
|||
assertThat(actual.getText().getDiv().getValueAsString(), containsString("<td>Identifier</td><td>testSearchByResourceChain01</td>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByIdentifier() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier01");
|
||||
p1.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven01");
|
||||
IdDt p1Id = ourClient.create(p1).getId();
|
||||
|
||||
Patient p2 = new Patient();
|
||||
p2.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier02");
|
||||
p2.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven02");
|
||||
ourClient.create(p2).getId();
|
||||
|
||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testSearchByIdentifier01")).encodedJson().prettyPrint().execute();
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByIdentifierWithoutSystem() {
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01");
|
||||
IdDt p1Id = ourClient.create(p1).getId();
|
||||
|
||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint()
|
||||
.execute();
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeepChaining() {
|
||||
// ourClient = ourCtx.newRestfulGenericClient("http://fhir.healthintersections.com.au/open");
|
||||
// ourClient = ourCtx.newRestfulGenericClient("https://fhir.orionhealth.com/blaze/fhir");
|
||||
// ourClient = ourCtx.newRestfulGenericClient("http://spark.furore.com/fhir");
|
||||
// ourClient.registerInterceptor(new LoggingInterceptor(true));
|
||||
|
||||
Location l1 = new Location();
|
||||
l1.getName().setValue("testDeepChainingL1");
|
||||
IdDt l1id = ourClient.create().resource(l1).execute().getId();
|
||||
|
||||
Location l2 = new Location();
|
||||
l2.getName().setValue("testDeepChainingL2");
|
||||
l2.getPartOf().setReference(l1id.toVersionless().toUnqualified());
|
||||
IdDt l2id = ourClient.create().resource(l2).execute().getId();
|
||||
|
||||
Encounter e1 = new Encounter();
|
||||
e1.addIdentifier().setSystem("urn:foo").setValue("testDeepChainingE1");
|
||||
e1.getStatus().setValueAsEnum(EncounterStateEnum.IN_PROGRESS);
|
||||
e1.getClassElement().setValueAsEnum(EncounterClassEnum.HOME);
|
||||
ca.uhn.fhir.model.dstu.resource.Encounter.Location location = e1.addLocation();
|
||||
location.getLocation().setReference(l2id.toUnqualifiedVersionless());
|
||||
location.setPeriod(new PeriodDt().setStartWithSecondsPrecision(new Date()).setEndWithSecondsPrecision(new Date()));
|
||||
IdDt e1id = ourClient.create().resource(e1).execute().getId();
|
||||
|
||||
//@formatter:off
|
||||
Bundle res = ourClient.search()
|
||||
.forResource(Encounter.class)
|
||||
.where(Encounter.IDENTIFIER.exactly().systemAndCode("urn:foo", "testDeepChainingE1"))
|
||||
.include(Encounter.INCLUDE_LOCATION_LOCATION)
|
||||
.include(Location.INCLUDE_PARTOF)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
|
||||
assertEquals(3, res.size());
|
||||
assertEquals(1, res.getResources(Encounter.class).size());
|
||||
assertEquals(e1id.toUnqualifiedVersionless(), res.getResources(Encounter.class).get(0).getId().toUnqualifiedVersionless());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByResourceChain() {
|
||||
Organization o1 = new Organization();
|
||||
o1.setName("testSearchByResourceChainName01");
|
||||
IdDt o1id = ourClient.create(o1).getId();
|
||||
|
||||
Patient p1 = new Patient();
|
||||
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
|
||||
p1.addName().addFamily("testSearchByResourceChainFamily01").addGiven("testSearchByResourceChainGiven01");
|
||||
p1.setManagingOrganization(new ResourceReferenceDt(o1id));
|
||||
IdDt p1Id = ourClient.create(p1).getId();
|
||||
|
||||
//@formatter:off
|
||||
Bundle actual = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.PROVIDER.hasId(o1id.getIdPart()))
|
||||
.encodedJson().andLogRequestAndResponse(true).prettyPrint().execute();
|
||||
//@formatter:on
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
//@formatter:off
|
||||
actual = ourClient.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.PROVIDER.hasId(o1id.getValue()))
|
||||
.encodedJson().andLogRequestAndResponse(true).prettyPrint().execute();
|
||||
//@formatter:on
|
||||
assertEquals(1, actual.size());
|
||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
|
@ -259,12 +311,21 @@ public class CompleteResourceProviderTest {
|
|||
ObservationResourceProvider observationRp = new ObservationResourceProvider();
|
||||
observationRp.setDao(observationDao);
|
||||
|
||||
IFhirResourceDao<Location> locationDao = (IFhirResourceDao<Location>) ourAppCtx.getBean("myLocationDao", IFhirResourceDao.class);
|
||||
LocationResourceProvider locationRp = new LocationResourceProvider();
|
||||
locationRp.setDao(locationDao);
|
||||
|
||||
IFhirResourceDao<Encounter> encounterDao = (IFhirResourceDao<Encounter>) ourAppCtx.getBean("myEncounterDao", IFhirResourceDao.class);
|
||||
EncounterResourceProvider encounterRp = new EncounterResourceProvider();
|
||||
encounterRp.setDao(encounterDao);
|
||||
|
||||
IFhirResourceDao<Organization> organizationDao = (IFhirResourceDao<Organization>) ourAppCtx.getBean("myOrganizationDao", IFhirResourceDao.class);
|
||||
OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
|
||||
organizationRp.setDao(organizationDao);
|
||||
|
||||
RestfulServer restServer = new RestfulServer();
|
||||
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
|
||||
|
||||
restServer.setResourceProviders(encounterRp, locationRp, patientRp, questionnaireRp, observationRp, organizationRp);
|
||||
restServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
IFhirSystemDao systemDao = (IFhirSystemDao) ourAppCtx.getBean("mySystemDao", IFhirSystemDao.class);
|
||||
|
@ -292,7 +353,7 @@ public class CompleteResourceProviderTest {
|
|||
ourCtx = restServer.getFhirContext();
|
||||
|
||||
ourClient = ourCtx.newRestfulGenericClient(serverBase);
|
||||
ourClient.setLogRequestAndResponse(true);
|
||||
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
|
|||
"${include.path}" #{if}($foreach.hasNext || $haveMore), #{end}
|
||||
#end
|
||||
#end
|
||||
#{if}($searchParamsReference.empty == false), #{end}"*"
|
||||
})
|
||||
Set<Include> theIncludes
|
||||
) {
|
||||
|
|
Loading…
Reference in New Issue