This commit is contained in:
lmds1 2014-09-24 15:52:57 -04:00
commit 8c45f32ce3
37 changed files with 2840 additions and 720 deletions

View File

@ -5,6 +5,7 @@ import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.IRestfulClientFactory; import ca.uhn.fhir.rest.client.IRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor;
import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
@ -45,6 +46,27 @@ public class ClientExamples {
// END SNIPPET: security // END SNIPPET: security
} }
@SuppressWarnings("unused")
public void createSecurityBearer() {
// START SNIPPET: securityBearer
// Create a context and get the client factory so it can be configured
FhirContext ctx = new FhirContext();
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") @SuppressWarnings("unused")
public void createLogging() { public void createLogging() {
{ {

View File

@ -24,10 +24,7 @@ String serverBase = "http://fhirtest.uhn.ca/base";
IGenericClient client = ctx.newRestfulGenericClient(serverBase); IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// Perform a search // Perform a search
Bundle results = client.search() Bundle results = client.search().forResource(Patient.class).where(Patient.FAMILY.matches().value("duck")).execute();
.forResource(Patient.class)
.where(Patient.FAMILY.matches().value("duck"))
.execute();
System.out.println("Found " + results.size() + " patients named 'duck'"); System.out.println("Found " + results.size() + " patients named 'duck'");
// END SNIPPET: simple // END SNIPPET: simple
@ -44,13 +41,10 @@ Patient patient = new Patient();
patient.addIdentifier("urn:system", "12345"); patient.addIdentifier("urn:system", "12345");
patient.addName().addFamily("Smith").addGiven("John"); patient.addName().addFamily("Smith").addGiven("John");
// Invoke the server create method (and send pretty-printed JSON encoding to the server // Invoke the server create method (and send pretty-printed JSON
// encoding to the server
// instead of the default which is non-pretty printed XML) // instead of the default which is non-pretty printed XML)
client.create() client.create().resource(patient).prettyPrint().encodedJson().execute();
.resource(patient)
.prettyPrint()
.encodedJson()
.execute();
// END SNIPPET: create // END SNIPPET: create
} }
{ {
@ -60,31 +54,31 @@ Patient patient = new Patient();
patient.addIdentifier("urn:system", "12345"); patient.addIdentifier("urn:system", "12345");
patient.addName().addFamily("Smith").addGiven("John"); patient.addName().addFamily("Smith").addGiven("John");
// To update a resource, it should have an ID set (if the resource object // 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 // comes from the results of a previous read or search, it will already
// have one though) // have one though)
patient.setId("Patient/123"); patient.setId("Patient/123");
// Invoke the server create method (and send pretty-printed JSON encoding to the server // Invoke the server create method (and send pretty-printed JSON
// encoding to the server
// instead of the default which is non-pretty printed XML) // instead of the default which is non-pretty printed XML)
client.update() client.update().resource(patient).execute();
.resource(patient)
.execute();
// END SNIPPET: update // END SNIPPET: update
} }
{ {
// START SNIPPET: conformance // START SNIPPET: conformance
// Retrieve the server's conformance statement and print its description // Retrieve the server's conformance statement and print its
// description
Conformance conf = client.conformance(); Conformance conf = client.conformance();
System.out.println(conf.getDescription().getValue()); System.out.println(conf.getDescription().getValue());
// END SNIPPET: conformance // END SNIPPET: conformance
} }
{ {
// START SNIPPET: delete // START SNIPPET: delete
// Retrieve the server's conformance statement and print its description // Retrieve the server's conformance statement and print its
OperationOutcome outcome = client.delete() // description
.resourceById(new IdDt("Patient", "1234")) OperationOutcome outcome = client.delete().resourceById(new IdDt("Patient", "1234")).execute();
.execute();
// outcome may be null if the server didn't return one // outcome may be null if the server didn't return one
if (outcome != null) { if (outcome != null) {
@ -133,8 +127,7 @@ response = client.search()
.and(Patient.BIRTHDATE.after().day("2011-01-01")) .and(Patient.BIRTHDATE.after().day("2011-01-01"))
.include(Patient.INCLUDE_MANAGINGORGANIZATION) .include(Patient.INCLUDE_MANAGINGORGANIZATION)
.sort().ascending(Patient.BIRTHDATE) .sort().ascending(Patient.BIRTHDATE)
.sort().descending(Patient.NAME) .sort().descending(Patient.NAME).limitTo(123)
.limitTo(123)
.execute(); .execute();
// END SNIPPET: searchAdv // END SNIPPET: searchAdv
@ -146,14 +139,12 @@ response = client.search()
.execute(); .execute();
// END SNIPPET: searchPost // END SNIPPET: searchPost
// START SNIPPET: searchComposite // START SNIPPET: searchComposite
response = client.search() response = client.search()
.forResource("Observation") .forResource("Observation")
.where(Observation.NAME_VALUE_DATE .where(Observation.NAME_VALUE_DATE
.withLeft(Observation.NAME.exactly().code("FOO$BAR")) .withLeft(Observation.NAME.exactly().code("FOO$BAR"))
.withRight(Observation.VALUE_DATE.exactly().day("2001-01-01")) .withRight(Observation.VALUE_DATE.exactly().day("2001-01-01")))
)
.execute(); .execute();
// END SNIPPET: searchComposite // END SNIPPET: searchComposite
@ -161,28 +152,46 @@ response = client.search()
if (response.getLinkNext().isEmpty() == false) { if (response.getLinkNext().isEmpty() == false) {
// load next page // load next page
Bundle nextPage = client.loadPage() Bundle nextPage = client.loadPage().next(response).execute();
.next(response)
.execute();
} }
// END SNIPPET: searchPaging // END SNIPPET: searchPaging
} }
{ {
// START SNIPPET: transaction // START SNIPPET: transaction
List<IResource> resources = new ArrayList<IResource>(); 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 .. // .. 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() List<IResource> response = client.transaction().withResources(resources).execute();
.withResources(resources)
.execute();
// END SNIPPET: transaction // END SNIPPET: transaction
} }
{
// START SNIPPET: read
IdDt id = new IdDt("Patient", "123");
Patient patient = client.read(Patient.class, id); // search for patient 123
// END SNIPPET: read
}
{
// START SNIPPET: vread
IdDt id = new IdDt("Patient", "123", "888");
Patient patient = client.vread(Patient.class, id); // search for version 888 of patient 123
// END SNIPPET: vread
}
{
// START SNIPPET: readabsolute
IdDt id = new IdDt("http://example.com/fhir/Patient/123");
Patient patient = client.read(Patient.class, id); // search for patient 123 on example.com
// END SNIPPET: readabsolute
}
} }
public static void main(String[] args) { public static void main(String[] args) {
simpleExample(); fluentSearch();
} }
} }

View File

@ -7,8 +7,21 @@
</properties> </properties>
<body> <body>
<release version="0.7" date="TBD"> <release version="0.7" date="TBD">
<action type="add"> <action type="add" dev="suranga">
Documentation update, thanks to Suranga Nath Kasthurirathne of the OpenMRS project. Documentation fixes
</action>
<action type="add" dev="dougmartin">
Add a collection of new methods on the generic client which support the
<![CDATA[
<b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#read(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">read</a></b>,
<b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#vread(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">read</a></b>,
and <b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#search(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">search</a></b>
]]>
operations using an absolute URL. This allows developers to perform these operations using
URLs they obtained from other sources (or external resource references within resources). In
addition, the existing read/vread operations will now access absolute URL references if
they are passed in. Thanks to Doug Martin of the Regenstrief Center for Biomedical Informatics
for contributing this implementation!
</action> </action>
<action type="fix"> <action type="fix">
Server implementation was not correctly figuring out its own FHIR Base URL when deployed Server implementation was not correctly figuring out its own FHIR Base URL when deployed
@ -24,13 +37,23 @@
HAPI now runs successfully on Servlet 2.5 containers (such as Tomcat 6). Thanks to HAPI now runs successfully on Servlet 2.5 containers (such as Tomcat 6). Thanks to
Bernard Gitaadji for reporting and diagnosing the issue! Bernard Gitaadji for reporting and diagnosing the issue!
</action> </action>
<action type="fix">
Summary (in the bundle entry) is now encoded by the XML and JSON parsers if supplied. Thanks to David Hay of
Orion Health for reporting this!
</action>
<action type="fix" issue="24">
Conformance profiles which are automatically generated by the server were missing a few mandatory elements,
which meant that the profile did not correctly validate. Thanks to Bill de Beaubien of Systems Made Simple
for reporting this!
</action>
<action type="fix"> <action type="fix">
XHTML (in narratives) containing escapable characters (e.g. &lt; or &quot;) will now always have those characters XHTML (in narratives) containing escapable characters (e.g. &lt; or &quot;) will now always have those characters
escaped properly in encoded messages. escaped properly in encoded messages.
</action> </action>
<action type="fix"> <action type="fix">
Summary (in the bundle entry) is now encoded by the XML and JSON parsers if supplied. Thanks to David Hay of Resources containing entities which are not valid in basic XML (e.g. &amp;sect;) will have those
Orion Health for reporting this! entities converted to their equivalent unicode characters when resources are encoded, since FHIR does
not allow extended entities in resource instances.
</action> </action>
</release> </release>
<release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!"> <release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!">

View File

@ -91,4 +91,11 @@ public class DateTimeDt extends BaseDateTimeDt {
} }
} }
/**
* Returns a new instance of DateTimeDt with the current system time and SECOND precision
*/
public static DateTimeDt withCurrentTime() {
return new DateTimeDt(new Date(), TemporalPrecisionEnum.SECOND);
}
} }

View File

@ -36,12 +36,11 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.UrlUtil;
/** /**
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs, * Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs, Resource References, etc. to represent a specific instance of a resource.
* Resource References, etc. to represent a specific instance of a resource.
* *
* <p> * <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters. * limit of 36 characters.
* </p> * </p>
* <p> * <p>
* regex: [a-z0-9\-\.]{1,36} * regex: [a-z0-9\-\.]{1,36}
@ -50,6 +49,7 @@ import ca.uhn.fhir.util.UrlUtil;
@DatatypeDef(name = "id") @DatatypeDef(name = "id")
public class IdDt extends BasePrimitive<String> { public class IdDt extends BasePrimitive<String> {
private String myBaseUrl;
private boolean myHaveComponentParts; private boolean myHaveComponentParts;
private String myResourceType; private String myResourceType;
private String myUnqualifiedId; private String myUnqualifiedId;
@ -64,8 +64,7 @@ public class IdDt extends BasePrimitive<String> {
} }
/** /**
* Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string * Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string representation.
* representation.
*/ */
public IdDt(BigDecimal thePid) { public IdDt(BigDecimal thePid) {
if (thePid != null) { if (thePid != null) {
@ -83,12 +82,11 @@ public class IdDt extends BasePrimitive<String> {
} }
/** /**
* Create a new ID using a string. This String may contain a simple ID (e.g. "1234") or it may contain a complete * Create a new ID using a string. This String may contain a simple ID (e.g. "1234") or it may contain a complete URL (http://example.com/fhir/Patient/1234).
* URL (http://example.com/fhir/Patient/1234).
* *
* <p> * <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters. * limit of 36 characters.
* </p> * </p>
* <p> * <p>
* regex: [a-z0-9\-\.]{1,36} * regex: [a-z0-9\-\.]{1,36}
@ -141,8 +139,14 @@ public class IdDt extends BasePrimitive<String> {
} }
/** /**
* @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is * Creates an ID based on a given URL
* ambiguous) */
public IdDt(UriDt theUrl) {
setValue(theUrl.getValueAsString());
}
/**
* @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is ambiguous)
*/ */
public BigDecimal asBigDecimal() { public BigDecimal asBigDecimal() {
return getIdPartAsBigDecimal(); return getIdPartAsBigDecimal();
@ -162,6 +166,17 @@ public class IdDt extends BasePrimitive<String> {
return ObjectUtils.equals(getResourceType(), theId.getResourceType()) && ObjectUtils.equals(getIdPart(), theId.getIdPart()) && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart()); return ObjectUtils.equals(getResourceType(), theId.getResourceType()) && ObjectUtils.equals(getIdPart(), theId.getIdPart()) && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart());
} }
/**
* Returns the portion of this resource ID which corresponds to the server base URL. For example given the resource ID <code>http://example.com/fhir/Patient/123</code> the base URL would be
* <code>http://example.com/fhir</code>.
* <p>
* This method may return null if the ID contains no base (e.g. "Patient/123")
* </p>
*/
public String getBaseUrl() {
return myBaseUrl;
}
public String getIdPart() { public String getIdPart() {
return myUnqualifiedId; return myUnqualifiedId;
} }
@ -199,8 +214,7 @@ public class IdDt extends BasePrimitive<String> {
} }
/** /**
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a * Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to get just the ID portion.
* simple ID. Use {@link #getIdPart()} to get just the ID portion.
* *
* @see #getIdPart() * @see #getIdPart()
*/ */
@ -241,6 +255,15 @@ public class IdDt extends BasePrimitive<String> {
} }
} }
/**
* Returns true if this ID has a base url
*
* @see #getBaseUrl()
*/
public boolean hasBaseUrl() {
return isNotBlank(myBaseUrl);
}
public boolean hasIdPart() { public boolean hasIdPart() {
return isNotBlank(getIdPart()); return isNotBlank(getIdPart());
} }
@ -254,8 +277,7 @@ public class IdDt extends BasePrimitive<String> {
} }
/** /**
* Returns <code>true</code> if this ID contains an absolute URL (in other words, a URL starting with "http://" or * Returns <code>true</code> if this ID contains an absolute URL (in other words, a URL starting with "http://" or "https://"
* "https://"
*/ */
public boolean isAbsolute() { public boolean isAbsolute() {
if (StringUtils.isBlank(getValue())) { if (StringUtils.isBlank(getValue())) {
@ -265,8 +287,7 @@ public class IdDt extends BasePrimitive<String> {
} }
/** /**
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only * Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only of digits)
* of digits)
*/ */
public boolean isIdPartValidLong() { public boolean isIdPartValidLong() {
String id = getIdPart(); String id = getIdPart();
@ -289,8 +310,7 @@ public class IdDt extends BasePrimitive<String> {
} }
/** /**
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method * Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
* but it is provided for consistency with the rest of the API.
*/ */
@Override @Override
public void setId(IdDt theId) { public void setId(IdDt theId) {
@ -301,8 +321,8 @@ public class IdDt extends BasePrimitive<String> {
* Set the value * Set the value
* *
* <p> * <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters. * limit of 36 characters.
* </p> * </p>
* <p> * <p>
* regex: [a-z0-9\-\.]{1,36} * regex: [a-z0-9\-\.]{1,36}
@ -331,6 +351,7 @@ public class IdDt extends BasePrimitive<String> {
myUnqualifiedVersionId = null; myUnqualifiedVersionId = null;
} }
myBaseUrl = null;
if (idIndex <= 0) { if (idIndex <= 0) {
myResourceType = null; myResourceType = null;
} else { } else {
@ -339,6 +360,11 @@ public class IdDt extends BasePrimitive<String> {
myResourceType = theValue.substring(0, idIndex); myResourceType = theValue.substring(0, idIndex);
} else { } else {
myResourceType = theValue.substring(typeIndex + 1, idIndex); myResourceType = theValue.substring(typeIndex + 1, idIndex);
if (typeIndex > 4) {
myBaseUrl = theValue.substring(0, typeIndex);
}
} }
} }
@ -349,8 +375,8 @@ public class IdDt extends BasePrimitive<String> {
* Set the value * Set the value
* *
* <p> * <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or * <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters. * limit of 36 characters.
* </p> * </p>
* <p> * <p>
* regex: [a-z0-9\-\.]{1,36} * regex: [a-z0-9\-\.]{1,36}
@ -389,10 +415,8 @@ public class IdDt extends BasePrimitive<String> {
} }
/** /**
* Returns a view of this ID as a fully qualified URL, given a server base and resource name (which will only be * Returns a view of this ID as a fully qualified URL, given a server base and resource name (which will only be used if the ID does not already contain those respective parts). Essentially,
* used if the ID does not already contain those respective parts). Essentially, because IdDt can contain either a * because IdDt can contain either a complete URL or a partial one (or even jut a simple ID), this method may be used to translate into a complete URL.
* complete URL or a partial one (or even jut a simple ID), this method may be used to translate into a complete
* URL.
* *
* @param theServerBase * @param theServerBase
* The server base (e.g. "http://example.com/fhir") * The server base (e.g. "http://example.com/fhir")
@ -428,13 +452,11 @@ public class IdDt extends BasePrimitive<String> {
} }
/** /**
* Creates a new instance of this ID which is identical, but refers to the specific version of this resource ID * Creates a new instance of this ID which is identical, but refers to the specific version of this resource ID noted by theVersion.
* noted by theVersion.
* *
* @param theVersion * @param theVersion
* The actual version string, e.g. "1" * The actual version string, e.g. "1"
* @return A new instance of IdDt which is identical, but refers to the specific version of this resource ID noted * @return A new instance of IdDt which is identical, but refers to the specific version of this resource ID noted by theVersion.
* by theVersion.
*/ */
public IdDt withVersion(String theVersion) { public IdDt withVersion(String theVersion) {
Validate.notBlank(theVersion, "Version may not be null or empty"); Validate.notBlank(theVersion, "Version may not be null or empty");

View File

@ -153,11 +153,11 @@ public class InstantDt extends BaseDateTimeDt {
} }
/** /**
* Factory method which creates a new InstantDt and initializes it with the * Factory method which creates a new InstantDt with millisecond precision and initializes it with the
* current time. * current time.
*/ */
public static InstantDt withCurrentTime() { public static InstantDt withCurrentTime() {
return new InstantDt(new Date()); return new InstantDt(new Date(), TemporalPrecisionEnum.MILLI);
} }
} }

View File

@ -32,13 +32,10 @@ import java.util.List;
import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent; import javax.xml.stream.events.XMLEvent;
import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringEscapeUtils;
import org.codehaus.stax2.XMLOutputFactory2;
import org.codehaus.stax2.io.EscapingWriterFactory; import org.codehaus.stax2.io.EscapingWriterFactory;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
@ -46,6 +43,7 @@ import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter; import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.util.XmlUtil;
@DatatypeDef(name = "xhtml") @DatatypeDef(name = "xhtml")
public class XhtmlDt extends BasePrimitive<List<XMLEvent>> { public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
@ -75,6 +73,10 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
* <b>Formatting note:</b> The text will be trimmed {@link String#trim()}. If the text does not start with an HTML tag (generally this would be a div tag), a div tag will be automatically placed * <b>Formatting note:</b> The text will be trimmed {@link String#trim()}. If the text does not start with an HTML tag (generally this would be a div tag), a div tag will be automatically placed
* surrounding the text. * surrounding the text.
* </p> * </p>
* <p>
* Also note that if the parsed text contains any entities (&foo;) which are not a part of the entities defined in core XML (e.g. &sect; which
* is valid in XHTML 1.0 but not in XML 1.0) they will be parsed and converted to their equivalent unicode character.
* </p>
*/ */
@Override @Override
public void setValueAsString(String theValue) throws DataFormatException { public void setValueAsString(String theValue) throws DataFormatException {
@ -94,7 +96,8 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
try { try {
ArrayList<XMLEvent> value = new ArrayList<XMLEvent>(); ArrayList<XMLEvent> value = new ArrayList<XMLEvent>();
XMLEventReader er = XMLInputFactory.newInstance().createXMLEventReader(new StringReader(val)); StringReader reader = new StringReader(val);
XMLEventReader er = XmlUtil.createXmlReader(reader);
boolean first = true; boolean first = true;
while (er.hasNext()) { while (er.hasNext()) {
XMLEvent next = er.nextEvent(); XMLEvent next = er.nextEvent();
@ -123,11 +126,7 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
} }
try { try {
StringWriter w = new StringWriter(); StringWriter w = new StringWriter();
XMLOutputFactory newInstance = XMLOutputFactory.newInstance(); XMLEventWriter ew = XmlUtil.createXmlWriter(w);
newInstance.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
XMLEventWriter ew = newInstance.createXMLEventWriter(w);
for (XMLEvent next : myValue) { for (XMLEvent next : myValue) {
if (next.isCharacters()) { if (next.isCharacters()) {
ew.add(next); ew.add(next);
@ -144,55 +143,6 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
} }
} }
private static class MyEscaper implements EscapingWriterFactory {
@Override
public Writer createEscapingWriterFor(final Writer theW, String theEnc) throws UnsupportedEncodingException {
return new Writer() {
@Override
public void write(char[] theCbuf, int theOff, int theLen) throws IOException {
boolean hasEscapable = false;
for (int i = 0; i < theLen && !hasEscapable; i++) {
char nextChar = theCbuf[i + theOff];
switch (nextChar) {
case '<':
case '>':
case '"':
case '&':
hasEscapable = true;
}
}
if (!hasEscapable) {
theW.write(theCbuf, theOff, theLen);
return;
}
String escaped = StringEscapeUtils.escapeXml10(new String(theCbuf, theOff, theLen));
theW.write(escaped.toCharArray());
}
@Override
public void flush() throws IOException {
theW.flush();
}
@Override
public void close() throws IOException {
theW.close();
}
};
}
@Override
public Writer createEscapingWriterFor(OutputStream theOut, String theEnc) throws UnsupportedEncodingException {
// TODO Auto-generated method stub
return null;
}
}
@Override @Override
public List<XMLEvent> getValue() { public List<XMLEvent> getValue() {
return myValue; return myValue;

View File

@ -20,7 +20,9 @@ package ca.uhn.fhir.parser;
* #L% * #L%
*/ */
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;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
@ -76,6 +78,7 @@ import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper; import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper;
import ca.uhn.fhir.util.PrettyPrintWriterWrapper; import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
import ca.uhn.fhir.util.XmlUtil;
public class XmlParser extends BaseParser implements IParser { public class XmlParser extends BaseParser implements IParser {
static final String RESREF_DISPLAY = "display"; static final String RESREF_DISPLAY = "display";
@ -91,14 +94,10 @@ public class XmlParser extends BaseParser implements IParser {
private FhirContext myContext; private FhirContext myContext;
private boolean myPrettyPrint; private boolean myPrettyPrint;
private XMLInputFactory myXmlInputFactory;
private XMLOutputFactory myXmlOutputFactory;
public XmlParser(FhirContext theContext) { public XmlParser(FhirContext theContext) {
super(theContext); super(theContext);
myContext = theContext; myContext = theContext;
myXmlInputFactory = XMLInputFactory.newInstance();
myXmlOutputFactory = XMLOutputFactory.newInstance();
} }
@Override @Override
@ -264,7 +263,7 @@ public class XmlParser extends BaseParser implements IParser {
private XMLStreamWriter createXmlWriter(Writer theWriter) throws XMLStreamException { private XMLStreamWriter createXmlWriter(Writer theWriter) throws XMLStreamException {
XMLStreamWriter eventWriter; XMLStreamWriter eventWriter;
eventWriter = myXmlOutputFactory.createXMLStreamWriter(theWriter); eventWriter = XmlUtil.createXmlStreamWriter(theWriter);
eventWriter = decorateStreamWriter(eventWriter); eventWriter = decorateStreamWriter(eventWriter);
return eventWriter; return eventWriter;
} }
@ -329,15 +328,23 @@ public class XmlParser extends BaseParser implements IParser {
} }
private XMLEventReader createStreamReader(Reader theReader) { private XMLEventReader createStreamReader(Reader theReader) {
XMLEventReader streamReader;
try { try {
streamReader = myXmlInputFactory.createXMLEventReader(theReader); return XmlUtil.createXmlReader(theReader);
} catch (XMLStreamException e) { } catch (FactoryConfigurationError e1) {
throw new DataFormatException(e); throw new ConfigurationException("Failed to initialize STaX event factory", e1);
} catch (FactoryConfigurationError e) { } catch (XMLStreamException e1) {
throw new ConfigurationException("Failed to initialize STaX event factory", e); throw new DataFormatException(e1);
} }
return streamReader;
// XMLEventReader streamReader;
// try {
// streamReader = myXmlInputFactory.createXMLEventReader(theReader);
// } catch (XMLStreamException e) {
// throw new DataFormatException(e);
// } catch (FactoryConfigurationError e) {
// throw new ConfigurationException("Failed to initialize STaX event factory", e);
// }
// return streamReader;
} }
private XMLStreamWriter decorateStreamWriter(XMLStreamWriter eventWriter) { private XMLStreamWriter decorateStreamWriter(XMLStreamWriter eventWriter) {
@ -637,7 +644,7 @@ public class XmlParser extends BaseParser implements IParser {
return; return;
} }
boolean firstEvent = true; boolean firstElement = true;
for (XMLEvent event : theDt.getValue()) { for (XMLEvent event : theDt.getValue()) {
switch (event.getEventType()) { switch (event.getEventType()) {
case XMLStreamConstants.ATTRIBUTE: case XMLStreamConstants.ATTRIBUTE:
@ -677,7 +684,7 @@ public class XmlParser extends BaseParser implements IParser {
break; break;
case XMLStreamConstants.START_ELEMENT: case XMLStreamConstants.START_ELEMENT:
StartElement se = event.asStartElement(); StartElement se = event.asStartElement();
if (firstEvent) { if (firstElement) {
if (StringUtils.isBlank(se.getName().getPrefix())) { if (StringUtils.isBlank(se.getName().getPrefix())) {
String namespaceURI = se.getName().getNamespaceURI(); String namespaceURI = se.getName().getNamespaceURI();
if (StringUtils.isBlank(namespaceURI)) { if (StringUtils.isBlank(namespaceURI)) {
@ -691,6 +698,7 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeStartElement(prefix, se.getName().getLocalPart(), namespaceURI); theEventWriter.writeStartElement(prefix, se.getName().getLocalPart(), namespaceURI);
theEventWriter.writeNamespace(prefix, namespaceURI); theEventWriter.writeNamespace(prefix, namespaceURI);
} }
firstElement = false;
} else { } else {
if (isBlank(se.getName().getPrefix())) { if (isBlank(se.getName().getPrefix())) {
if (isBlank(se.getName().getNamespaceURI())) { if (isBlank(se.getName().getNamespaceURI())) {
@ -721,7 +729,6 @@ public class XmlParser extends BaseParser implements IParser {
break; break;
} }
firstEvent = false;
} }
} }

View File

@ -20,7 +20,8 @@ package ca.uhn.fhir.rest.client;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.*; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
@ -47,6 +48,7 @@ import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome; import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
@ -73,7 +75,6 @@ import ca.uhn.fhir.rest.method.DeleteMethodBinding;
import ca.uhn.fhir.rest.method.HistoryMethodBinding; import ca.uhn.fhir.rest.method.HistoryMethodBinding;
import ca.uhn.fhir.rest.method.HttpDeleteClientInvocation; import ca.uhn.fhir.rest.method.HttpDeleteClientInvocation;
import ca.uhn.fhir.rest.method.HttpGetClientInvocation; import ca.uhn.fhir.rest.method.HttpGetClientInvocation;
import ca.uhn.fhir.rest.method.HttpPostClientInvocation;
import ca.uhn.fhir.rest.method.HttpSimpleGetClientInvocation; import ca.uhn.fhir.rest.method.HttpSimpleGetClientInvocation;
import ca.uhn.fhir.rest.method.IClientResponseHandler; import ca.uhn.fhir.rest.method.IClientResponseHandler;
import ca.uhn.fhir.rest.method.MethodUtil; import ca.uhn.fhir.rest.method.MethodUtil;
@ -87,8 +88,16 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
/**
* @author James Agnew
* @author Doug Martin (Regenstrief Center for Biomedical Informatics)
*/
public class GenericClient extends BaseClient implements IGenericClient { public class GenericClient extends BaseClient implements IGenericClient {
private static final String I18N_CANNOT_DETEMINE_RESOURCE_TYPE = "ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri";
private static final String I18N_INCOMPLETE_URI_FOR_READ = "ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead";
private static final String I18N_NO_VERSION_ID_FOR_VREAD = "ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClient.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClient.class);
private FhirContext myContext; private FhirContext myContext;
@ -144,6 +153,19 @@ public class GenericClient extends BaseClient implements IGenericClient {
return new DeleteInternal(); return new DeleteInternal();
} }
// public IResource read(UriDt url) {
// return read(inferResourceClass(url), url);
// }
//
// @SuppressWarnings("unchecked")
// public <T extends IResource> T read(final Class<T> theType, UriDt url) {
// return (T) invoke(theType, url, new ResourceResponseHandler<T>(theType));
// }
//
// public Bundle search(UriDt url) {
// return search(inferResourceClass(url), url);
// }
@Override @Override
public MethodOutcome delete(final Class<? extends IResource> theType, IdDt theId) { public MethodOutcome delete(final Class<? extends IResource> theType, IdDt theId) {
HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(theId.withResourceType(toResourceName(theType))); HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(theId.withResourceType(toResourceName(theType)));
@ -166,6 +188,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
return myLastRequest; return myLastRequest;
} }
protected String getPreferredId(IResource theResource, String theId) {
if (isNotBlank(theId)) {
return theId;
}
return theResource.getId().getIdPart();
}
@Override @Override
public IGetTags getTags() { public IGetTags getTags() {
return new GetTagsInternal(); return new GetTagsInternal();
@ -206,7 +235,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
throw new IllegalArgumentException("theId does not contain a valid ID, is: " + theId); throw new IllegalArgumentException("theId does not contain a valid ID, is: " + theId);
} }
HttpGetClientInvocation invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType)); HttpGetClientInvocation invocation;
if (theId.hasBaseUrl()) {
invocation = ReadMethodBinding.createAbsoluteReadInvocation(theId.toVersionless());
} else {
invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
}
if (isKeepResponses()) { if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding()); myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
} }
@ -221,6 +255,25 @@ public class GenericClient extends BaseClient implements IGenericClient {
return read(theType, new IdDt(theId)); return read(theType, new IdDt(theId));
} }
@Override
public <T extends IResource> T read(final Class<T> theType, UriDt theUrl) {
return read(theType, new IdDt(theUrl));
}
@Override
public IResource read(UriDt theUrl) {
IdDt id = new IdDt(theUrl);
String resourceType = id.getResourceType();
if (isBlank(resourceType)) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_INCOMPLETE_URI_FOR_READ, theUrl.getValueAsString()));
}
RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceType);
if (def == null) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString()));
}
return read(def.getImplementingClass(), id);
}
@Override @Override
public IUntypedQuery search() { public IUntypedQuery search() {
return new SearchInternal(); return new SearchInternal();
@ -250,6 +303,48 @@ public class GenericClient extends BaseClient implements IGenericClient {
return resp; return resp;
} }
@Override
public <T extends IResource> Bundle search(final Class<T> theType, UriDt theUrl) {
BaseHttpClientInvocation invocation = new HttpGetClientInvocation(theUrl.getValueAsString());
return invokeClient(myContext, new BundleResponseHandler(theType), invocation);
}
@Override
public Bundle search(UriDt theUrl) {
return search(inferResourceClass(theUrl), theUrl);
}
private Class<? extends IResource> inferResourceClass(UriDt theUrl) {
String urlString = theUrl.getValueAsString();
int i = urlString.indexOf('?');
if (i >= 0) {
urlString = urlString.substring(0, i);
}
i = urlString.indexOf("://");
if (i >= 0) {
urlString = urlString.substring(i + 3);
}
String[] pcs = urlString.split("\\/");
for (i = pcs.length - 1; i >= 0; i--) {
String s = pcs[i].trim();
if (!s.isEmpty()) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(s);
if (def != null) {
return def.getImplementingClass();
}
}
}
throw new RuntimeException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString()));
}
/** /**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change! * For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/ */
@ -262,6 +357,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
myLogRequestAndResponse = theLogRequestAndResponse; myLogRequestAndResponse = theLogRequestAndResponse;
} }
private String toResourceName(Class<? extends IResource> theType) {
return myContext.getResourceDefinition(theType).getName();
}
@Override @Override
public ITransaction transaction() { public ITransaction transaction() {
return new TransactionInternal(); return new TransactionInternal();
@ -279,6 +378,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
return resp.toListOfResources(); return resp.toListOfResources();
} }
@Override
public IUpdate update() {
return new UpdateInternal();
}
@Override @Override
public MethodOutcome update(IdDt theIdDt, IResource theResource) { public MethodOutcome update(IdDt theIdDt, IResource theResource) {
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext); BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext);
@ -315,24 +419,43 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
@Override @Override
public <T extends IResource> T vread(final Class<T> theType, IdDt theId, IdDt theVersionId) { public <T extends IResource> T vread(final Class<T> theType, IdDt theId) {
HttpGetClientInvocation invocation = ReadMethodBinding.createVReadInvocation(theId, theVersionId, toResourceName(theType)); String resName = toResourceName(theType);
IdDt id = theId;
if (!id.hasBaseUrl()) {
id = new IdDt(resName, id.getIdPart(), id.getVersionIdPart());
}
if (id.hasVersionIdPart() == false) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_NO_VERSION_ID_FOR_VREAD, theId.getValue()));
}
HttpGetClientInvocation invocation;
if (id.hasBaseUrl()) {
invocation = ReadMethodBinding.createAbsoluteVReadInvocation(id);
} else {
invocation = ReadMethodBinding.createVReadInvocation(id);
}
if (isKeepResponses()) { if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding()); myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
} }
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType, theId); ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType, id);
T resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse); T resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
return resp; return resp;
} }
/* also deprecated in interface */
@Deprecated
@Override @Override
public <T extends IResource> T vread(Class<T> theType, String theId, String theVersionId) { public <T extends IResource> T vread(final Class<T> theType, IdDt theId, IdDt theVersionId) {
return vread(theType, new IdDt(theId), new IdDt(theVersionId)); return vread(theType, theId.withVersion(theVersionId.getIdPart()));
} }
private String toResourceName(Class<? extends IResource> theType) { @Override
return myContext.getResourceDefinition(theType).getName(); public <T extends IResource> T vread(Class<T> theType, String theId, String theVersionId) {
IdDt resId = new IdDt(toResourceName(theType), theId, theVersionId);
return vread(theType, resId);
} }
private abstract class BaseClientExecutable<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> { private abstract class BaseClientExecutable<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> {
@ -340,6 +463,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
private Boolean myPrettyPrint; private Boolean myPrettyPrint;
private boolean myQueryLogRequestAndResponse; private boolean myQueryLogRequestAndResponse;
protected void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
if (!params.containsKey(parameterName)) {
params.put(parameterName, new ArrayList<String>());
}
params.get(parameterName).add(parameterValue);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public T andLogRequestAndResponse(boolean theLogRequestAndResponse) { public T andLogRequestAndResponse(boolean theLogRequestAndResponse) {
@ -347,6 +477,37 @@ public class GenericClient extends BaseClient implements IGenericClient {
return (T) this; return (T) this;
} }
@SuppressWarnings("unchecked")
@Override
public T encodedJson() {
myParamEncoding = EncodingEnum.JSON;
return (T) this;
}
@SuppressWarnings("unchecked")
@Override
public T encodedXml() {
myParamEncoding = EncodingEnum.XML;
return (T) this;
}
protected <Z> Z invoke(Map<String, List<String>> theParams, IClientResponseHandler<Z> theHandler, BaseHttpClientInvocation theInvocation) {
// if (myParamEncoding != null) {
// theParams.put(Constants.PARAM_FORMAT, Collections.singletonList(myParamEncoding.getFormatContentType()));
// }
//
// if (myPrettyPrint != null) {
// theParams.put(Constants.PARAM_PRETTY, Collections.singletonList(myPrettyPrint.toString()));
// }
if (isKeepResponses()) {
myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding());
}
Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse);
return resp;
}
protected IResource parseResourceBody(String theResourceBody) { protected IResource parseResourceBody(String theResourceBody) {
EncodingEnum encoding = null; EncodingEnum encoding = null;
for (int i = 0; i < theResourceBody.length() && encoding == null; i++) { for (int i = 0; i < theResourceBody.length() && encoding == null; i++) {
@ -365,20 +526,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return encoding.newParser(myContext).parseResource(theResourceBody); return encoding.newParser(myContext).parseResource(theResourceBody);
} }
@SuppressWarnings("unchecked")
@Override
public T encodedJson() {
myParamEncoding = EncodingEnum.JSON;
return (T) this;
}
@SuppressWarnings("unchecked")
@Override
public T encodedXml() {
myParamEncoding = EncodingEnum.XML;
return (T) this;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public T prettyPrint() { public T prettyPrint() {
@ -386,30 +533,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return (T) this; return (T) this;
} }
protected void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
if (!params.containsKey(parameterName)) {
params.put(parameterName, new ArrayList<String>());
}
params.get(parameterName).add(parameterValue);
}
protected <Z> Z invoke(Map<String, List<String>> theParams, IClientResponseHandler<Z> theHandler, BaseHttpClientInvocation theInvocation) {
// if (myParamEncoding != null) {
// theParams.put(Constants.PARAM_FORMAT, Collections.singletonList(myParamEncoding.getFormatContentType()));
// }
//
// if (myPrettyPrint != null) {
// theParams.put(Constants.PARAM_PRETTY, Collections.singletonList(myPrettyPrint.toString()));
// }
if (isKeepResponses()) {
myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding());
}
Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse);
return resp;
}
} }
private final class BundleResponseHandler implements IClientResponseHandler<Bundle> { private final class BundleResponseHandler implements IClientResponseHandler<Bundle> {
@ -421,7 +544,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
@Override @Override
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException { public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) { if (respType == null) {
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
@ -484,76 +608,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
private class UpdateInternal extends BaseClientExecutable<IUpdateTyped, MethodOutcome> implements IUpdate, IUpdateTyped {
private IdDt myId;
private String myResourceBody;
private IResource myResource;
@Override
public MethodOutcome execute() {
if (myResource == null) {
myResource = parseResourceBody(myResourceBody);
}
if (myId == null) {
myId = myResource.getId();
}
if (myId == null || myId.hasIdPart() == false) {
throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server");
}
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext);
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
Map<String, List<String>> params = new HashMap<String, List<String>>();
return invoke(params, binding, invocation);
}
@Override
public IUpdateTyped resource(IResource theResource) {
Validate.notNull(theResource, "Resource can not be null");
myResource = theResource;
return this;
}
@Override
public IUpdateTyped resource(String theResourceBody) {
Validate.notBlank(theResourceBody, "Body can not be null or blank");
myResourceBody = theResourceBody;
return this;
}
@Override
public IUpdateTyped withId(IdDt theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (theId.hasIdPart() == false) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue());
}
myId = theId;
return this;
}
@Override
public IUpdateTyped withId(String theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (isBlank(theId)) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId);
}
myId = new IdDt(theId);
return this;
}
}
private class DeleteInternal extends BaseClientExecutable<IDeleteTyped, OperationOutcome> implements IDelete, IDeleteTyped { private class DeleteInternal extends BaseClientExecutable<IDeleteTyped, OperationOutcome> implements IDelete, IDeleteTyped {
private IdDt myId; private IdDt myId;
@ -712,7 +766,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class OperationOutcomeResponseHandler implements IClientResponseHandler<OperationOutcome> { private final class OperationOutcomeResponseHandler implements IClientResponseHandler<OperationOutcome> {
@Override @Override
public OperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException { public OperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) { if (respType == null) {
return null; return null;
@ -739,7 +794,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
@Override @Override
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException { public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
MethodOutcome response = MethodUtil.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders); MethodOutcome response = MethodUtil.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) { if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) {
response.setCreated(true); response.setCreated(true);
@ -757,7 +813,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
@Override @Override
public List<IResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException { public List<IResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
return new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources(); return new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources();
} }
} }
@ -793,15 +850,15 @@ public class GenericClient extends BaseClient implements IGenericClient {
private class SearchInternal extends BaseClientExecutable<IQuery, Bundle> implements IQuery, IUntypedQuery { private class SearchInternal extends BaseClientExecutable<IQuery, Bundle> implements IQuery, IUntypedQuery {
private String myCompartmentName;
private List<ICriterionInternal> myCriterion = new ArrayList<ICriterionInternal>(); private List<ICriterionInternal> myCriterion = new ArrayList<ICriterionInternal>();
private List<Include> myInclude = new ArrayList<Include>(); private List<Include> myInclude = new ArrayList<Include>();
private Integer myParamLimit; private Integer myParamLimit;
private String myResourceId;
private String myResourceName; private String myResourceName;
private Class<? extends IResource> myResourceType; private Class<? extends IResource> myResourceType;
private List<SortInternal> mySort = new ArrayList<SortInternal>();
private SearchStyleEnum mySearchStyle; private SearchStyleEnum mySearchStyle;
private String myResourceId; private List<SortInternal> mySort = new ArrayList<SortInternal>();
private String myCompartmentName;
public SearchInternal() { public SearchInternal() {
myResourceType = null; myResourceType = null;
@ -856,17 +913,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this; return this;
} }
private void setType(Class<? extends IResource> theResourceType) {
myResourceType = theResourceType;
RuntimeResourceDefinition definition = myContext.getResourceDefinition(theResourceType);
myResourceName = definition.getName();
}
private void setType(String theResourceName) {
myResourceType = myContext.getResourceDefinition(theResourceName).getImplementingClass();
myResourceName = theResourceName;
}
@Override @Override
public IQuery forResource(Class<? extends IResource> theResourceType) { public IQuery forResource(Class<? extends IResource> theResourceType) {
setType(theResourceType); setType(theResourceType);
@ -895,6 +941,17 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this; return this;
} }
private void setType(Class<? extends IResource> theResourceType) {
myResourceType = theResourceType;
RuntimeResourceDefinition definition = myContext.getResourceDefinition(theResourceType);
myResourceName = definition.getName();
}
private void setType(String theResourceName) {
myResourceType = myContext.getResourceDefinition(theResourceName).getImplementingClass();
myResourceName = theResourceName;
}
@Override @Override
public ISort sort() { public ISort sort() {
SortInternal retVal = new SortInternal(this); SortInternal retVal = new SortInternal(this);
@ -903,14 +960,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
@Override @Override
public IQuery where(ICriterion<?> theCriterion) { public IQuery usingStyle(SearchStyleEnum theStyle) {
myCriterion.add((ICriterionInternal) theCriterion); mySearchStyle = theStyle;
return this; return this;
} }
@Override @Override
public IQuery usingStyle(SearchStyleEnum theStyle) { public IQuery where(ICriterion<?> theCriterion) {
mySearchStyle = theStyle; myCriterion.add((ICriterionInternal) theCriterion);
return this; return this;
} }
@ -967,7 +1024,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class TagListResponseHandler implements IClientResponseHandler<TagList> { private final class TagListResponseHandler implements IClientResponseHandler<TagList> {
@Override @Override
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException { public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) { if (respType == null) {
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader); throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
@ -1022,16 +1080,74 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
protected String getPreferredId(IResource theResource, String theId) { private class UpdateInternal extends BaseClientExecutable<IUpdateTyped, MethodOutcome> implements IUpdate, IUpdateTyped {
if (isNotBlank(theId)) {
return theId; private IdDt myId;
private IResource myResource;
private String myResourceBody;
@Override
public MethodOutcome execute() {
if (myResource == null) {
myResource = parseResourceBody(myResourceBody);
} }
return theResource.getId().getIdPart(); if (myId == null) {
myId = myResource.getId();
}
if (myId == null || myId.hasIdPart() == false) {
throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server");
}
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext);
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
Map<String, List<String>> params = new HashMap<String, List<String>>();
return invoke(params, binding, invocation);
} }
@Override @Override
public IUpdate update() { public IUpdateTyped resource(IResource theResource) {
return new UpdateInternal(); Validate.notNull(theResource, "Resource can not be null");
myResource = theResource;
return this;
}
@Override
public IUpdateTyped resource(String theResourceBody) {
Validate.notBlank(theResourceBody, "Body can not be null or blank");
myResourceBody = theResourceBody;
return this;
}
@Override
public IUpdateTyped withId(IdDt theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (theId.hasIdPart() == false) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue());
}
myId = theId;
return this;
}
@Override
public IUpdateTyped withId(String theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (isBlank(theId)) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId);
}
myId = new IdDt(theId);
return this;
}
} }
} }

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IRestfulClient; import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.gclient.ICreate; import ca.uhn.fhir.rest.gclient.ICreate;
@ -40,22 +41,16 @@ import ca.uhn.fhir.rest.gclient.IUntypedQuery;
import ca.uhn.fhir.rest.gclient.IUpdate; import ca.uhn.fhir.rest.gclient.IUpdate;
public interface IGenericClient { public interface IGenericClient {
/**
* Register a new interceptor for this client. An interceptor can be used to add additional
* logging, or add security headers, or pre-process responses, etc.
*/
void registerInterceptor(IClientInterceptor theInterceptor);
/**
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
*/
void unregisterInterceptor(IClientInterceptor theInterceptor);
/** /**
* Retrieves and returns the server conformance statement * Retrieves and returns the server conformance statement
*/ */
Conformance conformance(); Conformance conformance();
/**
* Fluent method for the "create" operation, which creates a new resource instance on the server
*/
ICreate create();
/** /**
* Implementation of the "type create" method. * Implementation of the "type create" method.
* *
@ -68,16 +63,9 @@ public interface IGenericClient {
MethodOutcome create(IResource theResource); MethodOutcome create(IResource theResource);
/** /**
* Implementation of the "transaction" method. * Fluent method for the "delete" operation, which performs a logical delete on a server resource
*
* @param theResources
* The resources to create/update in a single transaction
* @return A list of resource stubs (<b>these will not be fully populated</b>) containing IDs and other
* {@link IResource#getResourceMetadata() metadata}
* @deprecated Use {@link #transaction()}
*
*/ */
List<IResource> transaction(List<IResource> theResources); IDelete delete();
/** /**
* Implementation of the "delete instance" method. * Implementation of the "delete instance" method.
@ -103,21 +91,24 @@ public interface IGenericClient {
*/ */
MethodOutcome delete(Class<? extends IResource> theType, String theId); MethodOutcome delete(Class<? extends IResource> theType, String theId);
/**
* Fluent method for the "get tags" operation
*/
IGetTags getTags();
/** /**
* Implementation of the "history instance" method. * Implementation of the "history instance" method.
* *
* @param theType * @param theType
* The type of resource to return the history for, or * The type of resource to return the history for, or <code>null<code> to search for history across all resources
* <code>null<code> to search for history across all resources
* @param theId * @param theId
* The ID of the resource to return the history for, or <code>null</code> to search for all resource * The ID of the resource to return the history for, or <code>null</code> to search for all resource instances. Note that if this param is not null, <code>theType</code> must also not
* instances. Note that if this param is not null, <code>theType</code> must also not be null * be null
* @param theSince * @param theSince
* If not null, request that the server only return resources updated since this time * If not null, request that the server only return resources updated since this time
* @param theLimit * @param theLimit
* If not null, request that the server return no more than this number of resources. Note that the * If not null, request that the server return no more than this number of resources. Note that the server may return less even if more are available, but should not return more
* server may return less even if more are available, but should not return more according to the FHIR * according to the FHIR specification.
* specification.
* @return A bundle containing returned resources * @return A bundle containing returned resources
*/ */
<T extends IResource> Bundle history(Class<T> theType, IdDt theIdDt, DateTimeDt theSince, Integer theLimit); <T extends IResource> Bundle history(Class<T> theType, IdDt theIdDt, DateTimeDt theSince, Integer theLimit);
@ -126,28 +117,38 @@ public interface IGenericClient {
* Implementation of the "history instance" method. * Implementation of the "history instance" method.
* *
* @param theType * @param theType
* The type of resource to return the history for, or * The type of resource to return the history for, or <code>null<code> to search for history across all resources
* <code>null<code> to search for history across all resources
* @param theId * @param theId
* The ID of the resource to return the history for, or <code>null</code> to search for all resource * The ID of the resource to return the history for, or <code>null</code> to search for all resource instances. Note that if this param is not null, <code>theType</code> must also not
* instances. Note that if this param is not null, <code>theType</code> must also not be null * be null
* @param theSince * @param theSince
* If not null, request that the server only return resources updated since this time * If not null, request that the server only return resources updated since this time
* @param theLimit * @param theLimit
* If not null, request that the server return no more than this number of resources. Note that the * If not null, request that the server return no more than this number of resources. Note that the server may return less even if more are available, but should not return more
* server may return less even if more are available, but should not return more according to the FHIR * according to the FHIR specification.
* specification.
* @return A bundle containing returned resources * @return A bundle containing returned resources
*/ */
<T extends IResource> Bundle history(Class<T> theType, String theIdDt, DateTimeDt theSince, Integer theLimit); <T extends IResource> Bundle history(Class<T> theType, String theIdDt, DateTimeDt theSince, Integer theLimit);
/** /**
* Implementation of the "instance read" method. * Loads the previous/next bundle of resources from a paged set, using the link specified in the "link type=next" tag within the atom bundle.
*
* @see Bundle#getLinkNext()
*/
IGetPage loadPage();
/**
* Implementation of the "instance read" method. This method will only ever do a "read" for the latest version of a given resource instance, even if the ID passed in contains a version. If you
* wish to request a specific version of a resource (the "vread" operation), use {@link #vread(Class, IdDt)} instead.
* <p>
* Note that if an absolute resource ID is passed in (i.e. a URL containing a protocol and host as well as the resource type and ID) the server base for the client will be ignored, and the URL
* passed in will be queried.
* </p>
* *
* @param theType * @param theType
* The type of resource to load * The type of resource to load
* @param theId * @param theId
* The ID to load * The ID to load, including the resource ID and the resource version ID. Valid values include "Patient/123/_history/222", or "http://example.com/fhir/Patient/123/_history/222"
* @return The resource * @return The resource
*/ */
<T extends IResource> T read(Class<T> theType, IdDt theId); <T extends IResource> T read(Class<T> theType, IdDt theId);
@ -163,6 +164,31 @@ public interface IGenericClient {
*/ */
<T extends IResource> T read(Class<T> theType, String theId); <T extends IResource> T read(Class<T> theType, String theId);
/**
* Perform the "read" operation (retrieve the latest version of a resource instance by ID) using an absolute URL.
*
* @param theType
* The resource type that is being retrieved
* @param theUrl
* The absolute URL, e.g. "http://example.com/fhir/Patient/123"
* @return The returned resource from the server
*/
<T extends IResource> T read(Class<T> theType, UriDt theUrl);
/**
* Perform the "read" operation (retrieve the latest version of a resource instance by ID) using an absolute URL.
*
* @param theUrl
* The absolute URL, e.g. "http://example.com/fhir/Patient/123"
* @return The returned resource from the server
*/
IResource read(UriDt theUrl);
/**
* Register a new interceptor for this client. An interceptor can be used to add additional logging, or add security headers, or pre-process responses, etc.
*/
void registerInterceptor(IClientInterceptor theInterceptor);
IUntypedQuery search(); IUntypedQuery search();
/** /**
@ -175,6 +201,54 @@ public interface IGenericClient {
*/ */
<T extends IResource> Bundle search(Class<T> theType, Map<String, List<IQueryParameterType>> theParams); <T extends IResource> Bundle search(Class<T> theType, Map<String, List<IQueryParameterType>> theParams);
/**
* Perform the "search" operation using an absolute URL.
*
* @param theType
* The primary resource type that is being retrieved
* @param theUrl
* The absolute URL, e.g. "http://example.com/fhir/Patient/123"
* @return The returned bundle from the server
*/
<T extends IResource> Bundle search(Class<T> theType, UriDt theUrl);
Bundle search(UriDt theUrl);
/**
* If set to <code>true</code>, the client will log all requests and all responses. This is probably not a good production setting since it will result in a lot of extra logging, but it can be
* useful for troubleshooting.
*
* @param theLogRequestAndResponse
* Should requests and responses be logged
*/
void setLogRequestAndResponse(boolean theLogRequestAndResponse);
/**
* Send a transaction (collection of resources) to the server to be executed as a single unit
*/
ITransaction transaction();
/**
* Implementation of the "transaction" method.
*
* @param theResources
* The resources to create/update in a single transaction
* @return A list of resource stubs (<b>these will not be fully populated</b>) containing IDs and other {@link IResource#getResourceMetadata() metadata}
* @deprecated Use {@link #transaction()}
*
*/
List<IResource> transaction(List<IResource> theResources);
/**
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
*/
void unregisterInterceptor(IClientInterceptor theInterceptor);
/**
* Fluent method for the "update" operation, which performs a logical delete on a server resource
*/
IUpdate update();
/** /**
* Implementation of the "instance update" method. * Implementation of the "instance update" method.
* *
@ -208,6 +282,21 @@ public interface IGenericClient {
*/ */
MethodOutcome validate(IResource theResource); MethodOutcome validate(IResource theResource);
/**
* Implementation of the "instance vread" method. Note that this method expects <code>theId</code> to contain a resource ID as well as a version ID, and will fail if it does not.
* <p>
* Note that if an absolute resource ID is passed in (i.e. a URL containing a protocol and host as well as the resource type and ID) the server base for the client will be ignored, and the URL
* passed in will be queried.
* </p>
*
* @param theType
* The type of resource to load
* @param theId
* The ID to load, including the resource ID and the resource version ID. Valid values include "Patient/123/_history/222", or "http://example.com/fhir/Patient/123/_history/222"
* @return The resource
*/
<T extends IResource> T vread(Class<T> theType, IdDt theId);
/** /**
* Implementation of the "instance vread" method. * Implementation of the "instance vread" method.
* *
@ -218,6 +307,7 @@ public interface IGenericClient {
* @param theVersionId * @param theVersionId
* The version ID * The version ID
* @return The resource * @return The resource
* @deprecated Deprecated in 0.7 - IdDt can contain an ID and a version, so this class doesn't make a lot of sense
*/ */
<T extends IResource> T vread(Class<T> theType, IdDt theId, IdDt theVersionId); <T extends IResource> T vread(Class<T> theType, IdDt theId, IdDt theVersionId);
@ -234,46 +324,4 @@ public interface IGenericClient {
*/ */
<T extends IResource> T vread(Class<T> theType, String theId, String theVersionId); <T extends IResource> T vread(Class<T> theType, String theId, String theVersionId);
/**
* If set to <code>true</code>, the client will log all requests and all responses. This is probably not a good
* production setting since it will result in a lot of extra logging, but it can be useful for troubleshooting.
*
* @param theLogRequestAndResponse
* Should requests and responses be logged
*/
void setLogRequestAndResponse(boolean theLogRequestAndResponse);
/**
* Fluent method for the "get tags" operation
*/
IGetTags getTags();
/**
* Loads the previous/next bundle of resources from a paged set, using the link specified in the
* "link type=next" tag within the atom bundle.
*
* @see Bundle#getLinkNext()
*/
IGetPage loadPage();
/**
* Send a transaction (collection of resources) to the server to be executed as a single unit
*/
ITransaction transaction();
/**
* Fluent method for the "create" operation, which creates a new resource instance on the server
*/
ICreate create();
/**
* Fluent method for the "delete" operation, which performs a logical delete on a server resource
*/
IDelete delete();
/**
* Fluent method for the "update" operation, which performs a logical delete on a server resource
*/
IUpdate update();
} }

View File

@ -0,0 +1,63 @@
package ca.uhn.fhir.rest.client.interceptor;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 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 org.apache.commons.lang3.Validate;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.server.Constants;
/**
* HTTP interceptor to be used for adding HTTP Authorization using "bearer tokens" to requests. Bearer tokens are used for protocols such as OAUTH2 (see the <a
* href="http://tools.ietf.org/html/rfc6750">RFC 6750</a> specification on bearer token usage for more information).
* <p>
* This interceptor adds a header resembling the following:<br/>
* &nbsp;&nbsp;&nbsp;<code>Authorization: Bearer dsfu9sd90fwp34.erw0-reu</code><br/>
* where the token portion (at the end of the header) is supplied by the invoking code.
* </p>
* <p>
* See the <a href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html#HTTP_Basic_Authorization">HAPI Documentation</a> for information on how to use this class.
* </p>
*/
public class BearerTokenAuthInterceptor implements IClientInterceptor {
private String myToken;
public BearerTokenAuthInterceptor(String theToken) {
Validate.notNull("theToken must not be null");
myToken = theToken;
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
theRequest.addHeader(Constants.HEADER_AUTHORIZATION, (Constants.HEADER_AUTHORIZATION_VALPREFIX_BEARER + myToken));
}
@Override
public void interceptResponse(HttpResponse theResponse) throws IOException {
// nothing
}
}

View File

@ -20,8 +20,6 @@ package ca.uhn.fhir.rest.method;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.*;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -50,11 +48,15 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
/**
* @author James Agnew
* @author Doug Martin (Regenstrief Center for Biomedical Informatics)
*/
abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvocation { abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvocation {
private final FhirContext myContext; private final FhirContext myContext;
private final IResource myResource; private final IResource myResource;
private final String myUrlExtension; private final String myUrlPath;
private final TagList myTagList; private final TagList myTagList;
private final List<IResource> myResources; private final List<IResource> myResources;
private final Bundle myBundle; private final Bundle myBundle;
@ -62,18 +64,18 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
private boolean myContentsIsBundle; private boolean myContentsIsBundle;
private Map<String, List<String>> myParams; private Map<String, List<String>> myParams;
public BaseHttpClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlExtension) { public BaseHttpClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlPath) {
super(); super();
myContext = theContext; myContext = theContext;
myResource = theResource; myResource = theResource;
myUrlExtension = theUrlExtension; myUrlPath = theUrlPath;
myTagList = null; myTagList = null;
myResources = null; myResources = null;
myBundle = null; myBundle = null;
myContents = null; myContents = null;
} }
public BaseHttpClientInvocationWithContents(FhirContext theContext, TagList theTagList, String... theUrlExtension) { public BaseHttpClientInvocationWithContents(FhirContext theContext, TagList theTagList, String... theUrlPath) {
super(); super();
if (theTagList == null) { if (theTagList == null) {
throw new NullPointerException("Tag list must not be null"); throw new NullPointerException("Tag list must not be null");
@ -86,14 +88,14 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myBundle = null; myBundle = null;
myContents = null; myContents = null;
myUrlExtension = StringUtils.join(theUrlExtension, '/'); myUrlPath = StringUtils.join(theUrlPath, '/');
} }
public BaseHttpClientInvocationWithContents(FhirContext theContext, List<IResource> theResources) { public BaseHttpClientInvocationWithContents(FhirContext theContext, List<IResource> theResources) {
myContext = theContext; myContext = theContext;
myResource = null; myResource = null;
myTagList = null; myTagList = null;
myUrlExtension = null; myUrlPath = null;
myResources = theResources; myResources = theResources;
myBundle = null; myBundle = null;
myContents = null; myContents = null;
@ -103,28 +105,28 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myContext = theContext; myContext = theContext;
myResource = null; myResource = null;
myTagList = null; myTagList = null;
myUrlExtension = null; myUrlPath = null;
myResources = null; myResources = null;
myBundle = theBundle; myBundle = theBundle;
myContents = null; myContents = null;
} }
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, boolean theIsBundle, String theUrlExtension) { public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, boolean theIsBundle, String theUrlPath) {
myContext = theContext; myContext = theContext;
myResource = null; myResource = null;
myTagList = null; myTagList = null;
myUrlExtension = theUrlExtension; myUrlPath = theUrlPath;
myResources = null; myResources = null;
myBundle = null; myBundle = null;
myContents = theContents; myContents = theContents;
myContentsIsBundle = theIsBundle; myContentsIsBundle = theIsBundle;
} }
public BaseHttpClientInvocationWithContents(FhirContext theContext, Map<String, List<String>> theParams, String... theUrlExtension) { public BaseHttpClientInvocationWithContents(FhirContext theContext, Map<String, List<String>> theParams, String... theUrlPath) {
myContext = theContext; myContext = theContext;
myResource = null; myResource = null;
myTagList = null; myTagList = null;
myUrlExtension = StringUtils.join(theUrlExtension, '/'); myUrlPath = StringUtils.join(theUrlPath, '/');
myResources = null; myResources = null;
myBundle = null; myBundle = null;
myContents = null; myContents = null;
@ -135,12 +137,17 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
@Override @Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) throws DataFormatException { public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) throws DataFormatException {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
if (myUrlPath == null) {
b.append(theUrlBase);
} else {
if (!myUrlPath.contains("://")) {
b.append(theUrlBase); b.append(theUrlBase);
if (isNotBlank(myUrlExtension)) {
if (!theUrlBase.endsWith("/")) { if (!theUrlBase.endsWith("/")) {
b.append('/'); b.append('/');
} }
b.append(myUrlExtension); }
b.append(myUrlPath);
} }
appendExtraParamsWithQuestionMark(theExtraParams, b, b.indexOf("?") == -1); appendExtraParamsWithQuestionMark(theExtraParams, b, b.indexOf("?") == -1);

View File

@ -35,6 +35,10 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation; import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
/**
* @author James Agnew
* @author Doug Martin (Regenstrief Center for Biomedical Informatics)
*/
public class HttpGetClientInvocation extends BaseHttpClientInvocation { public class HttpGetClientInvocation extends BaseHttpClientInvocation {
private final Map<String, List<String>> myParameters; private final Map<String, List<String>> myParameters;
@ -77,10 +81,13 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
@Override @Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) { public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
if (!myUrlPath.contains("://")) {
b.append(theUrlBase); b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) { if (!theUrlBase.endsWith("/")) {
b.append('/'); b.append('/');
} }
}
b.append(myUrlPath); b.append(myUrlPath);
boolean first = b.indexOf("?") == -1; boolean first = b.indexOf("?") == -1;

View File

@ -142,11 +142,16 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
IdDt id = ((IdDt) theArgs[myIdIndex]); IdDt id = ((IdDt) theArgs[myIdIndex]);
if (myVersionIdIndex == null) { if (myVersionIdIndex == null) {
String resourceName = getResourceName(); String resourceName = getResourceName();
if (id.hasVersionIdPart()) {
retVal = createVReadInvocation(new IdDt(resourceName, id.getIdPart(), id.getVersionIdPart()));
} else {
retVal = createReadInvocation(id, resourceName); retVal = createReadInvocation(id, resourceName);
}
} else { } else {
IdDt vid = ((IdDt) theArgs[myVersionIdIndex]); IdDt vid = ((IdDt) theArgs[myVersionIdIndex]);
String resourceName = getResourceName(); String resourceName = getResourceName();
retVal = createVReadInvocation(id, vid, resourceName);
retVal = createVReadInvocation(new IdDt(resourceName, id.getIdPart(), vid.getVersionIdPart()));
} }
for (int idx = 0; idx < theArgs.length; idx++) { for (int idx = 0; idx < theArgs.length; idx++) {
@ -198,16 +203,20 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
return mySupportsVersion || myVersionIdIndex != null; return mySupportsVersion || myVersionIdIndex != null;
} }
public static HttpGetClientInvocation createReadInvocation(IdDt theId, String resourceName) { public static HttpGetClientInvocation createAbsoluteReadInvocation(IdDt theId) {
if (theId.hasVersionIdPart()) { return new HttpGetClientInvocation(theId.getValue());
return new HttpGetClientInvocation(resourceName, theId.getIdPart(), Constants.URL_TOKEN_HISTORY, theId.getVersionIdPart());
} else {
return new HttpGetClientInvocation(resourceName, theId.getIdPart());
}
} }
public static HttpGetClientInvocation createVReadInvocation(IdDt theId, IdDt vid, String resourceName) { public static HttpGetClientInvocation createAbsoluteVReadInvocation(IdDt theId) {
return new HttpGetClientInvocation(resourceName, theId.getIdPart(), Constants.URL_TOKEN_HISTORY, vid.getIdPart()); return new HttpGetClientInvocation(theId.getValue());
}
public static HttpGetClientInvocation createReadInvocation(IdDt theId, String theResourceName) {
return new HttpGetClientInvocation(new IdDt(theResourceName, theId.getIdPart()).getValue());
}
public static HttpGetClientInvocation createVReadInvocation(IdDt theId) {
return new HttpGetClientInvocation(theId.getValue());
} }
} }

View File

@ -48,6 +48,8 @@ public class Constants {
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
public static final String HEADER_AUTHORIZATION = "Authorization"; public static final String HEADER_AUTHORIZATION = "Authorization";
public static final String HEADER_CATEGORY = "Category"; public static final String HEADER_CATEGORY = "Category";
public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic ";
public static final String HEADER_AUTHORIZATION_VALPREFIX_BEARER = "Bearer ";
public static final String HEADER_CATEGORY_LC = HEADER_CATEGORY.toLowerCase(); public static final String HEADER_CATEGORY_LC = HEADER_CATEGORY.toLowerCase();
public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition"; public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
public static final String HEADER_CONTENT_ENCODING = "Content-Encoding"; public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";

View File

@ -45,6 +45,7 @@ import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.BooleanDt; import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.Metadata; import ca.uhn.fhir.rest.annotation.Metadata;
@ -70,14 +71,24 @@ import ca.uhn.fhir.util.ExtensionConstants;
*/ */
public class ServerConformanceProvider { public class ServerConformanceProvider {
private volatile Conformance myConformance;
private final RestfulServer myRestfulServer;
private boolean myCache = true; private boolean myCache = true;
private volatile Conformance myConformance;
private String myPublisher = "Not provided";
private final RestfulServer myRestfulServer;
public ServerConformanceProvider(RestfulServer theRestfulServer) { public ServerConformanceProvider(RestfulServer theRestfulServer) {
myRestfulServer = theRestfulServer; myRestfulServer = theRestfulServer;
} }
/**
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this
* is a mandatory element, the value should not be null (although this is not enforced). The value defaults
* to "Not provided" but may be set to null, which will cause this element to be omitted.
*/
public String getPublisher() {
return myPublisher;
}
/** /**
* Actually create and return the conformance statement * Actually create and return the conformance statement
* *
@ -91,6 +102,11 @@ public class ServerConformanceProvider {
Conformance retVal = new Conformance(); Conformance retVal = new Conformance();
retVal.setPublisher(myPublisher);
retVal.setDate(DateTimeDt.withCurrentTime());
retVal.setFhirVersion("0.80"); // TODO: pull from model
retVal.setAcceptUnknown(false); // TODO: make this configurable - this is a fairly big effort since the parser needs to be modified to actually allow it
retVal.getImplementation().setDescription(myRestfulServer.getImplementationDescription()); retVal.getImplementation().setDescription(myRestfulServer.getImplementationDescription());
retVal.getSoftware().setName(myRestfulServer.getServerName()); retVal.getSoftware().setName(myRestfulServer.getServerName());
retVal.getSoftware().setVersion(myRestfulServer.getServerVersion()); retVal.getSoftware().setVersion(myRestfulServer.getServerVersion());
@ -177,6 +193,51 @@ public class ServerConformanceProvider {
return retVal; return retVal;
} }
private void handleDynamicSearchMethodBinding(RestResource resource, RuntimeResourceDefinition def, TreeSet<String> includes, DynamicSearchMethodBinding searchMethodBinding) {
includes.addAll(searchMethodBinding.getIncludes());
List<RuntimeSearchParam> searchParameters = new ArrayList<RuntimeSearchParam>();
searchParameters.addAll(searchMethodBinding.getSearchParams());
sortRuntimeSearchParameters(searchParameters);
if (!searchParameters.isEmpty()) {
for (RuntimeSearchParam nextParameter : searchParameters) {
String nextParamName = nextParameter.getName();
// String chain = null;
String nextParamUnchainedName = nextParamName;
if (nextParamName.contains(".")) {
// chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
}
String nextParamDescription = nextParameter.getDescription();
/*
* If the parameter has no description, default to the one from the resource
*/
if (StringUtils.isBlank(nextParamDescription)) {
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
if (paramDef != null) {
nextParamDescription = paramDef.getDescription();
}
}
RestResourceSearchParam param;
param = resource.addSearchParam();
param.setName(nextParamName);
// if (StringUtils.isNotBlank(chain)) {
// param.addChain(chain);
// }
param.setDocumentation(nextParamDescription);
param.setType(nextParameter.getParamType());
}
}
}
private void handleSearchMethodBinding(Rest rest, RestResource resource, String resourceName, RuntimeResourceDefinition def, TreeSet<String> includes, SearchMethodBinding searchMethodBinding) { private void handleSearchMethodBinding(Rest rest, RestResource resource, String resourceName, RuntimeResourceDefinition def, TreeSet<String> includes, SearchMethodBinding searchMethodBinding) {
includes.addAll(searchMethodBinding.getIncludes()); includes.addAll(searchMethodBinding.getIncludes());
@ -251,49 +312,24 @@ public class ServerConformanceProvider {
} }
} }
private void handleDynamicSearchMethodBinding(RestResource resource, RuntimeResourceDefinition def, TreeSet<String> includes, DynamicSearchMethodBinding searchMethodBinding) { /**
includes.addAll(searchMethodBinding.getIncludes()); * Sets the cache property (default is true). If set to true, the same response will be returned for each
* invocation.
List<RuntimeSearchParam> searchParameters = new ArrayList<RuntimeSearchParam>(); * <p>
searchParameters.addAll(searchMethodBinding.getSearchParams()); * See the class documentation for an important note if you are extending this class
sortRuntimeSearchParameters(searchParameters); * </p>
if (!searchParameters.isEmpty()) {
for (RuntimeSearchParam nextParameter : searchParameters) {
String nextParamName = nextParameter.getName();
// String chain = null;
String nextParamUnchainedName = nextParamName;
if (nextParamName.contains(".")) {
// chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
}
String nextParamDescription = nextParameter.getDescription();
/*
* If the parameter has no description, default to the one from the resource
*/ */
if (StringUtils.isBlank(nextParamDescription)) { public void setCache(boolean theCache) {
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName); myCache = theCache;
if (paramDef != null) {
nextParamDescription = paramDef.getDescription();
}
} }
RestResourceSearchParam param; /**
param = resource.addSearchParam(); * Sets the value of the "publisher" that will be placed in the generated conformance statement. As this
* is a mandatory element, the value should not be null (although this is not enforced). The value defaults
param.setName(nextParamName); * to "Not provided" but may be set to null, which will cause this element to be omitted.
// if (StringUtils.isNotBlank(chain)) { */
// param.addChain(chain); public void setPublisher(String thePublisher) {
// } myPublisher = thePublisher;
param.setDocumentation(nextParamDescription);
param.setType(nextParameter.getParamType());
}
}
} }
private void sortRuntimeSearchParameters(List<RuntimeSearchParam> searchParameters) { private void sortRuntimeSearchParameters(List<RuntimeSearchParam> searchParameters) {
@ -319,15 +355,4 @@ public class ServerConformanceProvider {
} }
}); });
} }
/**
* Sets the cache property (default is true). If set to true, the same response will be returned for each
* invocation.
* <p>
* See the class documentation for an important note if you are extending this class
* </p>
*/
public void setCache(boolean theCache) {
myCache = theCache;
}
} }

View File

@ -0,0 +1,252 @@
package ca.uhn.fhir.util;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 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.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLResolver;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.codehaus.stax2.XMLOutputFactory2;
import org.codehaus.stax2.io.EscapingWriterFactory;
import com.ctc.wstx.api.WstxInputProperties;
import com.ctc.wstx.stax.WstxInputFactory;
public class XmlUtil {
private static final Map<String, Integer> VALID_ENTITY_NAMES;
public static void main(String[] args) {
System.out.println(Character.toString((char)167));
}
static {
HashMap<String, Integer> validEntityNames = new HashMap<String, Integer>();
validEntityNames.put("nbsp", 160); // no-break space = non-breaking space, U+00A0 ISOnum -->
validEntityNames.put("iexcl", 161); // inverted exclamation mark, U+00A1 ISOnum -->
validEntityNames.put("cent", 162); // cent sign, U+00A2 ISOnum -->
validEntityNames.put("pound", 163); // pound sign, U+00A3 ISOnum -->
validEntityNames.put("curren", 164); // currency sign, U+00A4 ISOnum -->
validEntityNames.put("yen", 165); // yen sign = yuan sign, U+00A5 ISOnum -->
validEntityNames.put("brvbar", 166); // broken bar = broken vertical bar, U+00A6 ISOnum -->
validEntityNames.put("sect", 167); // section sign, U+00A7 ISOnum -->
validEntityNames.put("uml", 168); // diaeresis = spacing diaeresis, U+00A8 ISOdia -->
validEntityNames.put("copy", 169); // copyright sign, U+00A9 ISOnum -->
validEntityNames.put("ordf", 170); // feminine ordinal indicator, U+00AA ISOnum -->
validEntityNames.put("laquo", 171); // left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum -->
validEntityNames.put("not", 172); // not sign = angled dash, U+00AC ISOnum -->
validEntityNames.put("shy", 173); // soft hyphen = discretionary hyphen, U+00AD ISOnum -->
validEntityNames.put("reg", 174); // registered sign = registered trade mark sign, U+00AE ISOnum -->
validEntityNames.put("macr", 175); // macron = spacing macron = overline = APL overbar, U+00AF ISOdia -->
validEntityNames.put("deg", 176); // degree sign, U+00B0 ISOnum -->
validEntityNames.put("plusmn", 177); // plus-minus sign = plus-or-minus sign, U+00B1 ISOnum -->
validEntityNames.put("sup2", 178); // superscript two = superscript digit two = squared, U+00B2 ISOnum -->
validEntityNames.put("sup3", 179); // superscript three = superscript digit three = cubed, U+00B3 ISOnum -->
validEntityNames.put("acute", 180); // acute accent = spacing acute, U+00B4 ISOdia -->
validEntityNames.put("micro", 181); // micro sign, U+00B5 ISOnum -->
validEntityNames.put("para", 182); // pilcrow sign = paragraph sign, U+00B6 ISOnum -->
validEntityNames.put("middot", 183); // middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum -->
validEntityNames.put("cedil", 184); // cedilla = spacing cedilla, U+00B8 ISOdia -->
validEntityNames.put("sup1", 185); // superscript one = superscript digit one, U+00B9 ISOnum -->
validEntityNames.put("ordm", 186); // masculine ordinal indicator, U+00BA ISOnum -->
validEntityNames.put("raquo", 187); // right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum -->
validEntityNames.put("frac14", 188); // vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum -->
validEntityNames.put("frac12", 189); // vulgar fraction one half = fraction one half, U+00BD ISOnum -->
validEntityNames.put("frac34", 190); // vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum -->
validEntityNames.put("iquest", 191); // inverted question mark = turned question mark, U+00BF ISOnum -->
validEntityNames.put("Agrave", 192); // latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1 -->
validEntityNames.put("Aacute", 193); // latin capital letter A with acute, U+00C1 ISOlat1 -->
validEntityNames.put("Acirc", 194); // latin capital letter A with circumflex, U+00C2 ISOlat1 -->
validEntityNames.put("Atilde", 195); // latin capital letter A with tilde, U+00C3 ISOlat1 -->
validEntityNames.put("Auml", 196); // latin capital letter A with diaeresis, U+00C4 ISOlat1 -->
validEntityNames.put("Aring", 197); // latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 -->
validEntityNames.put("AElig", 198); // latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 -->
validEntityNames.put("Ccedil", 199); // latin capital letter C with cedilla, U+00C7 ISOlat1 -->
validEntityNames.put("Egrave", 200); // latin capital letter E with grave, U+00C8 ISOlat1 -->
validEntityNames.put("Eacute", 201); // latin capital letter E with acute, U+00C9 ISOlat1 -->
validEntityNames.put("Ecirc", 202); // latin capital letter E with circumflex, U+00CA ISOlat1 -->
validEntityNames.put("Euml", 203); // latin capital letter E with diaeresis, U+00CB ISOlat1 -->
validEntityNames.put("Igrave", 204); // latin capital letter I with grave, U+00CC ISOlat1 -->
validEntityNames.put("Iacute", 205); // latin capital letter I with acute, U+00CD ISOlat1 -->
validEntityNames.put("Icirc", 206); // latin capital letter I with circumflex, U+00CE ISOlat1 -->
validEntityNames.put("Iuml", 207); // latin capital letter I with diaeresis, U+00CF ISOlat1 -->
validEntityNames.put("ETH", 208); // latin capital letter ETH, U+00D0 ISOlat1 -->
validEntityNames.put("Ntilde", 209); // latin capital letter N with tilde, U+00D1 ISOlat1 -->
validEntityNames.put("Ograve", 210); // latin capital letter O with grave, U+00D2 ISOlat1 -->
validEntityNames.put("Oacute", 211); // latin capital letter O with acute, U+00D3 ISOlat1 -->
validEntityNames.put("Ocirc", 212); // latin capital letter O with circumflex, U+00D4 ISOlat1 -->
validEntityNames.put("Otilde", 213); // latin capital letter O with tilde, U+00D5 ISOlat1 -->
validEntityNames.put("Ouml", 214); // latin capital letter O with diaeresis, U+00D6 ISOlat1 -->
validEntityNames.put("times", 215); // multiplication sign, U+00D7 ISOnum -->
validEntityNames.put("Oslash", 216); // latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 -->
validEntityNames.put("Ugrave", 217); // latin capital letter U with grave, U+00D9 ISOlat1 -->
validEntityNames.put("Uacute", 218); // latin capital letter U with acute, U+00DA ISOlat1 -->
validEntityNames.put("Ucirc", 219); // latin capital letter U with circumflex, U+00DB ISOlat1 -->
validEntityNames.put("Uuml", 220); // latin capital letter U with diaeresis, U+00DC ISOlat1 -->
validEntityNames.put("Yacute", 221); // latin capital letter Y with acute, U+00DD ISOlat1 -->
validEntityNames.put("THORN", 222); // latin capital letter THORN, U+00DE ISOlat1 -->
validEntityNames.put("szlig", 223); // latin small letter sharp s = ess-zed, U+00DF ISOlat1 -->
validEntityNames.put("agrave", 224); // latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 -->
validEntityNames.put("aacute", 225); // latin small letter a with acute, U+00E1 ISOlat1 -->
validEntityNames.put("acirc", 226); // latin small letter a with circumflex, U+00E2 ISOlat1 -->
validEntityNames.put("atilde", 227); // latin small letter a with tilde, U+00E3 ISOlat1 -->
validEntityNames.put("auml", 228); // latin small letter a with diaeresis, U+00E4 ISOlat1 -->
validEntityNames.put("aring", 229); // latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 -->
validEntityNames.put("aelig", 230); // latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 -->
validEntityNames.put("ccedil", 231); // latin small letter c with cedilla, U+00E7 ISOlat1 -->
validEntityNames.put("egrave", 232); // latin small letter e with grave, U+00E8 ISOlat1 -->
validEntityNames.put("eacute", 233); // latin small letter e with acute, U+00E9 ISOlat1 -->
validEntityNames.put("ecirc", 234); // latin small letter e with circumflex, U+00EA ISOlat1 -->
validEntityNames.put("euml", 235); // latin small letter e with diaeresis, U+00EB ISOlat1 -->
validEntityNames.put("igrave", 236); // latin small letter i with grave, U+00EC ISOlat1 -->
validEntityNames.put("iacute", 237); // latin small letter i with acute, U+00ED ISOlat1 -->
validEntityNames.put("icirc", 238); // latin small letter i with circumflex, U+00EE ISOlat1 -->
validEntityNames.put("iuml", 239); // latin small letter i with diaeresis, U+00EF ISOlat1 -->
validEntityNames.put("eth", 240); // latin small letter eth, U+00F0 ISOlat1 -->
validEntityNames.put("ntilde", 241); // latin small letter n with tilde, U+00F1 ISOlat1 -->
validEntityNames.put("ograve", 242); // latin small letter o with grave, U+00F2 ISOlat1 -->
validEntityNames.put("oacute", 243); // latin small letter o with acute, U+00F3 ISOlat1 -->
validEntityNames.put("ocirc", 244); // latin small letter o with circumflex, U+00F4 ISOlat1 -->
validEntityNames.put("otilde", 245); // latin small letter o with tilde, U+00F5 ISOlat1 -->
validEntityNames.put("ouml", 246); // latin small letter o with diaeresis, U+00F6 ISOlat1 -->
validEntityNames.put("divide", 247); // division sign, U+00F7 ISOnum -->
validEntityNames.put("oslash", 248); // latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 -->
validEntityNames.put("ugrave", 249); // latin small letter u with grave, U+00F9 ISOlat1 -->
validEntityNames.put("uacute", 250); // latin small letter u with acute, U+00FA ISOlat1 -->
validEntityNames.put("ucirc", 251); // latin small letter u with circumflex, U+00FB ISOlat1 -->
validEntityNames.put("uuml", 252); // latin small letter u with diaeresis, U+00FC ISOlat1 -->
validEntityNames.put("yacute", 253); // latin small letter y with acute, U+00FD ISOlat1 -->
validEntityNames.put("thorn", 254); // latin small letter thorn, U+00FE ISOlat1 -->
validEntityNames.put("yuml", 255); // latin small letter y with diaeresis, U+00FF ISOlat1 -->
VALID_ENTITY_NAMES = Collections.unmodifiableMap(validEntityNames);
}
public static XMLEventReader createXmlReader(Reader reader) throws FactoryConfigurationError, XMLStreamException {
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
XMLResolver xmlResolver = new XMLResolver() {
@Override
public Object resolveEntity(String thePublicID, String theSystemID, String theBaseURI, String theNamespace) throws XMLStreamException {
if (thePublicID == null && theSystemID == null) {
if (theNamespace != null && VALID_ENTITY_NAMES.containsKey(theNamespace)) {
return new String(Character.toChars(VALID_ENTITY_NAMES.get(theNamespace)));
}
}
return null;
}
};
// In the following two lines, you can uncomment the first and comment the second to disable
// automatic parsing of extended entities, e.g. &sect;
// inputFactory.setProperty(WstxInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
inputFactory.setProperty(WstxInputProperties.P_UNDECLARED_ENTITY_RESOLVER, xmlResolver);
XMLEventReader er = inputFactory.createXMLEventReader(reader);
return er;
}
public static XMLEventWriter createXmlWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
XMLOutputFactory newInstance = XMLOutputFactory.newInstance();
newInstance.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
XMLEventWriter ew = newInstance.createXMLEventWriter(theWriter);
return ew;
}
public static XMLStreamWriter createXmlStreamWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
XMLOutputFactory newInstance = XMLOutputFactory.newInstance();
newInstance.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
XMLStreamWriter ew = newInstance.createXMLStreamWriter(theWriter);
return ew;
}
public static class MyEscaper implements EscapingWriterFactory {
@Override
public Writer createEscapingWriterFor(OutputStream theOut, String theEnc) throws UnsupportedEncodingException {
return createEscapingWriterFor(new OutputStreamWriter(theOut), theEnc);
}
@Override
public Writer createEscapingWriterFor(final Writer theW, String theEnc) throws UnsupportedEncodingException {
return new Writer() {
@Override
public void close() throws IOException {
theW.close();
}
@Override
public void flush() throws IOException {
theW.flush();
}
@Override
public void write(char[] theCbuf, int theOff, int theLen) throws IOException {
boolean hasEscapable = false;
for (int i = 0; i < theLen && !hasEscapable; i++) {
char nextChar = theCbuf[i + theOff];
switch (nextChar) {
case '<':
case '>':
case '"':
case '&':
hasEscapable = true;
}
}
if (!hasEscapable) {
theW.write(theCbuf, theOff, theLen);
return;
}
String escaped = StringEscapeUtils.escapeXml10(new String(theCbuf, theOff, theLen));
theW.write(escaped.toCharArray());
}
};
}
}
}

View File

@ -76,7 +76,11 @@ class SchemaBaseValidator implements IValidator {
Validator validator = schema.newValidator(); Validator validator = schema.newValidator();
MyErrorHandler handler = new MyErrorHandler(theContext); MyErrorHandler handler = new MyErrorHandler(theContext);
validator.setErrorHandler(handler); validator.setErrorHandler(handler);
validator.validate(new StreamSource(new StringReader(theContext.getXmlEncodedResource()))); String encodedResource = theContext.getXmlEncodedResource();
// ourLog.info(new FhirContext().newXmlParser().setPrettyPrint(true).encodeBundleToString((Bundle) theContext.getResource()));
validator.validate(new StreamSource(new StringReader(encodedResource)));
} catch (SAXException e) { } catch (SAXException e) {
throw new ConfigurationException("Could not apply schema file", e); throw new ConfigurationException("Could not apply schema file", e);
} catch (IOException e) { } catch (IOException e) {

View File

@ -1,4 +1,8 @@
ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread=No version specified in URL for 'vread' operation: {0}
ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead=The given URI is not an absolute URL and is not usable for this operation: {0}
ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable to determine the resource type from the given URI: {0}
ca.uhn.fhir.rest.method.SearchMethodBinding.invalidSpecialParamName=Method [{0}] in provider [{1}] contains search parameter annotated to use name [{2}] - This name is reserved according to the FHIR specification and can not be used as a search parameter name. ca.uhn.fhir.rest.method.SearchMethodBinding.invalidSpecialParamName=Method [{0}] in provider [{1}] contains search parameter annotated to use name [{2}] - This name is reserved according to the FHIR specification and can not be used as a search parameter name.
ca.uhn.fhir.rest.method.SearchMethodBinding.idWithoutCompartment=Method [{0}] in provider [{1}] has an @IdParam parameter. This is only allowable for compartment search (e.g. @Search(compartment="foo") ) ca.uhn.fhir.rest.method.SearchMethodBinding.idWithoutCompartment=Method [{0}] in provider [{1}] has an @IdParam parameter. This is only allowable for compartment search (e.g. @Search(compartment="foo") )
ca.uhn.fhir.rest.method.SearchMethodBinding.idNullForCompartmentSearch=ID parameter can not be null or empty for compartment search ca.uhn.fhir.rest.method.SearchMethodBinding.idNullForCompartmentSearch=ID parameter can not be null or empty for compartment search

View File

@ -191,6 +191,36 @@
</macro> </macro>
</subsection> </subsection>
<subsection name="Instance - Read / VRead">
<p>
Given a resource name and ID, it is simple to retrieve
the latest version of that resource (a 'read')
</p>
<macro name="snippet">
<param name="id" value="read" />
<param name="file"
value="examples/src/main/java/example/GenericClientExample.java" />
</macro>
<p>
By adding a version string, it is also possible to retrieve a
specific version (a 'vread')
</p>
<macro name="snippet">
<param name="id" value="vread" />
<param name="file"
value="examples/src/main/java/example/GenericClientExample.java" />
</macro>
<p>
It is also possible to retrieve a resource given its absolute
URL (this will override the base URL set on the client)
</p>
<macro name="snippet">
<param name="id" value="readabsolute" />
<param name="file"
value="examples/src/main/java/example/GenericClientExample.java" />
</macro>
</subsection>
<subsection name="Instance - Delete"> <subsection name="Instance - Delete">
<p> <p>
The following example shows how to perform a delete The following example shows how to perform a delete

View File

@ -25,7 +25,7 @@
The following section shows some sample interceptors which may be used. The following section shows some sample interceptors which may be used.
</p> </p>
<subsection name="HTTP Basic Authorization"> <subsection name="Security: HTTP Basic Authorization">
<p> <p>
The following example shows how to configure your client to The following example shows how to configure your client to
@ -39,14 +39,29 @@
</subsection> </subsection>
<a name="req_resp_logging"/> <subsection name="Security: HTTP Bearer Token Authorization">
<subsection name="Logging Requests and Responses">
<p> <p>
An interceptor is provided with HAPI FHIR which can be used to The following example shows how to configure your client to
log every transaction. The interceptor can be configured to be extremely inject a bearer token authorization header into every request. This
is used to satisfy servers which are protected using OAUTH2.
</p>
<macro name="snippet">
<param name="id" value="securityBearer" />
<param name="file" value="examples/src/main/java/example/ClientExamples.java" />
</macro>
</subsection>
<a name="req_resp_logging"/>
<subsection name="Logging: Log Requests and Responses">
<p>
The <code>LoggingInterceptor</code> can be used to
log every transaction. The interceptor is flexible and can be configured to be extremely
verbose (logging entire transactions including HTTP headers and payload bodies) verbose (logging entire transactions including HTTP headers and payload bodies)
or simply to log request URLs. or simply to log request URLs, or some combination in between.
</p> </p>
<macro name="snippet"> <macro name="snippet">

View File

@ -17,11 +17,28 @@ public class IdDtTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdDtTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdDtTest.class);
@Test
public void testDetermineBase() {
IdDt rr;
rr = new IdDt("http://foo/fhir/Organization/123");
assertEquals("http://foo/fhir", rr.getBaseUrl());
rr = new IdDt("http://foo/fhir/Organization/123/_history/123");
assertEquals("http://foo/fhir", rr.getBaseUrl());
rr = new IdDt("Organization/123/_history/123");
assertEquals(null, rr.getBaseUrl());
}
@Test @Test
public void testParseValueAbsolute() { public void testParseValueAbsolute() {
Patient patient = new Patient(); Patient patient = new Patient();
IdDt rr = new IdDt(); IdDt rr = new IdDt();
rr.setValue("http://foo/fhir/Organization/123"); rr.setValue("http://foo/fhir/Organization/123");
patient.setManagingOrganization(new ResourceReferenceDt(rr)); patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient); Patient actual = parseAndEncode(patient);
@ -69,7 +86,6 @@ public class IdDtTest {
} }
@Test @Test
public void testParseValueMissingType1() { public void testParseValueMissingType1() {
Patient patient = new Patient(); Patient patient = new Patient();

View File

@ -40,4 +40,29 @@ public class XhtmlDtTest {
} }
@Test
public void testBasicCharacterEntity() {
String input = "amp &amp;";
XhtmlDt x = new XhtmlDt();
x.setValueAsString(input);
assertEquals("<div>amp &amp;</div>", x.getValueAsString());
}
@Test
public void testCharacterEntities() {
String input = "Sect: &sect; uuml: &uuml; &Uuml;";
XhtmlDt x = new XhtmlDt();
x.setValueAsString(input);
// <div>Sect: § uuml: ü Ü</div>
// <div>Sect: &sect; uuml: &uuml; &Uuml;</div>
assertEquals("<div>Sect: § uuml: ü Ü</div>", x.getValueAsString());
}
} }

View File

@ -446,8 +446,13 @@ public class JsonParserTest {
@Test @Test
public void testParseJsonProfile() throws IOException { public void testParseJsonProfile() throws IOException {
parseAndEncode("/patient.profile.json");
parseAndEncode("/alert.profile.json");
}
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/alert.profile.json"));
private void parseAndEncode(String name) throws IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream(name));
ourLog.info(msg); ourLog.info(msg);
IParser p = ourCtx.newJsonParser(); IParser p = ourCtx.newJsonParser();
@ -459,7 +464,7 @@ public class JsonParserTest {
JSON expected = JSONSerializer.toJSON(msg.trim()); JSON expected = JSONSerializer.toJSON(msg.trim());
JSON actual = JSONSerializer.toJSON(encoded.trim()); JSON actual = JSONSerializer.toJSON(encoded.trim());
String exp = expected.toString().replace("\\r\\n", "\\n"); String exp = expected.toString().replace("\\r\\n", "\\n").replace("&sect;", "§");
String act = actual.toString().replace("\\r\\n","\\n"); String act = actual.toString().replace("\\r\\n","\\n");
ourLog.info("Expected: {}", exp); ourLog.info("Expected: {}", exp);

View File

@ -181,6 +181,29 @@ public class XmlParserTest {
} }
@Test
public void testEncodeEscapedChars() {
Patient p = new Patient();
p.addName().addFamily("and <>&ü");
String enc = ourCtx.newXmlParser().encodeResourceToString(p);
ourLog.info(enc);
p = ourCtx.newXmlParser().parseResource(Patient.class, enc);
assertEquals("and <>&ü", p.getNameFirstRep().getFamilyFirstRep().getValue());
p = ourCtx.newXmlParser().parseResource(Patient.class, "<Patient xmlns=\"http://hl7.org/fhir\"><name><family value=\"quot &quot;\"/></name></Patient>");
assertEquals("quot \"", p.getNameFirstRep().getFamilyFirstRep().getValue());
}
@Test
public void testEncodeEscapedExtendedChars() {
Patient p = ourCtx.newXmlParser().parseResource(Patient.class, "<Patient xmlns=\"http://hl7.org/fhir\"><name><family value=\"uuml &uuml;\"/></name></Patient>");
assertEquals("uuml ü", p.getNameFirstRep().getFamilyFirstRep().getValue());
}
@Test @Test
public void testEncodeContainedAndIncludedResources() { public void testEncodeContainedAndIncludedResources() {
@ -316,7 +339,6 @@ public class XmlParserTest {
} }
@Test @Test
public void testEncodePrettyPrint() throws DataFormatException { public void testEncodePrettyPrint() throws DataFormatException {
@ -740,7 +762,9 @@ public class XmlParserTest {
String enc = ourCtx.newXmlParser().encodeResourceToString(patient); String enc = ourCtx.newXmlParser().encodeResourceToString(patient);
assertThat(enc, containsString("<Patient xmlns=\"http://hl7.org/fhir\"><extension url=\"http://example.com/extensions#someext\"><valueDateTime value=\"2011-01-02T11:13:15\"/></extension>")); assertThat(enc, containsString("<Patient xmlns=\"http://hl7.org/fhir\"><extension url=\"http://example.com/extensions#someext\"><valueDateTime value=\"2011-01-02T11:13:15\"/></extension>"));
assertThat(enc, containsString("<extension url=\"http://example.com#parent\"><extension url=\"http://example.com#child\"><valueString value=\"value1\"/></extension><extension url=\"http://example.com#child\"><valueString value=\"value1\"/></extension></extension>")); assertThat(
enc,
containsString("<extension url=\"http://example.com#parent\"><extension url=\"http://example.com#child\"><valueString value=\"value1\"/></extension><extension url=\"http://example.com#child\"><valueString value=\"value1\"/></extension></extension>"));
assertThat(enc, containsString("<given value=\"Joe\"><extension url=\"http://examples.com#givenext\"><valueString value=\"given\"/></extension></given>")); assertThat(enc, containsString("<given value=\"Joe\"><extension url=\"http://examples.com#givenext\"><valueString value=\"given\"/></extension></given>"));
} }
@ -935,7 +959,8 @@ public class XmlParserTest {
assertEquals("256a5231-a2bb-49bd-9fea-f349d428b70d", resource.getId().getIdPart()); assertEquals("256a5231-a2bb-49bd-9fea-f349d428b70d", resource.getId().getIdPart());
msg = msg.replace("<link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d\" rel=\"self\"/>", "<link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d/_history/12345\" rel=\"self\"/>"); msg = msg.replace("<link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d\" rel=\"self\"/>",
"<link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d/_history/12345\" rel=\"self\"/>");
entry = p.parseBundle(msg).getEntries().get(0); entry = p.parseBundle(msg).getEntries().get(0);
resource = (ValueSet) entry.getResource(); resource = (ValueSet) entry.getResource();
assertEquals("256a5231-a2bb-49bd-9fea-f349d428b70d", resource.getId().getIdPart()); assertEquals("256a5231-a2bb-49bd-9fea-f349d428b70d", resource.getId().getIdPart());
@ -1070,9 +1095,10 @@ public class XmlParserTest {
@Test @Test
public void testParseQuery() { public void testParseQuery() {
String msg = "<Query xmlns=\"http://hl7.org/fhir\">\n" + " <text>\n" + " <status value=\"generated\"/>\n" + " <div xmlns=\"http://www.w3.org/1999/xhtml\">[Put rendering here]</div>\n" + " </text>\n" + "\n" String msg = "<Query xmlns=\"http://hl7.org/fhir\">\n" + " <text>\n" + " <status value=\"generated\"/>\n" + " <div xmlns=\"http://www.w3.org/1999/xhtml\">[Put rendering here]</div>\n"
+ " <!-- this is an extermely simple query - a request to execute the query 'example' on the\n" + " responder -->\n" + " <identifier value=\"urn:uuid:42b253f5-fa17-40d0-8da5-44aeb4230376\"/>\n" + " <parameter url=\"http://hl7.org/fhir/query#_query\">\n" + " </text>\n" + "\n" + " <!-- this is an extermely simple query - a request to execute the query 'example' on the\n" + " responder -->\n"
+ " <valueString value=\"example\"/>\n" + " </parameter>\n" + "</Query>"; + " <identifier value=\"urn:uuid:42b253f5-fa17-40d0-8da5-44aeb4230376\"/>\n" + " <parameter url=\"http://hl7.org/fhir/query#_query\">\n" + " <valueString value=\"example\"/>\n"
+ " </parameter>\n" + "</Query>";
Query query = ourCtx.newXmlParser().parseResource(Query.class, msg); Query query = ourCtx.newXmlParser().parseResource(Query.class, msg);
assertEquals("urn:uuid:42b253f5-fa17-40d0-8da5-44aeb4230376", query.getIdentifier().getValueAsString()); assertEquals("urn:uuid:42b253f5-fa17-40d0-8da5-44aeb4230376", query.getIdentifier().getValueAsString());

View File

@ -0,0 +1,87 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.StringReader;
import java.nio.charset.Charset;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor;
import ca.uhn.fhir.rest.server.Constants;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class BasicAuthInterceptorTest {
private static FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
private String crerateMsg() {
//@formatter:off
String msg = "<Patient xmlns=\"http://hl7.org/fhir\">"
+ "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal: 444333333 </div></text>"
+ "<identifier><label value=\"SSN\" /><system value=\"http://orionhealth.com/mrn\" /><value value=\"PRP1660\" /></identifier>"
+ "<name><use value=\"official\" /><family value=\"Cardinal\" /><given value=\"John\" /></name>"
+ "<name><family value=\"Kramer\" /><given value=\"Doe\" /></name>"
+ "<telecom><system value=\"phone\" /><value value=\"555-555-2004\" /><use value=\"work\" /></telecom>"
+ "<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>";
//@formatter:on
return msg;
}
@Test
public void testRequest() throws Exception {
String msg = crerateMsg();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
Header[] headers = new Header[] {};
when(myHttpResponse.getAllHeaders()).thenReturn(headers);
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
ITestClient client = ourCtx.newRestfulClient(ITestClient.class, "http://foo");
client.registerInterceptor(new BasicAuthInterceptor("myuser", "mypass"));
client.getPatientById(new IdDt("111"));
HttpUriRequest req = capt.getValue();
assertEquals("Basic bXl1c2VyOm15cGFzcw==", req.getFirstHeader("Authorization").getValue());
}
@BeforeClass
public static void beforeClass() {
ourCtx = new FhirContext();
}
}

View File

@ -0,0 +1,87 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.StringReader;
import java.nio.charset.Charset;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor;
import ca.uhn.fhir.rest.server.Constants;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class BearerTokenAuthInterceptorTest {
private static FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
private String crerateMsg() {
//@formatter:off
String msg = "<Patient xmlns=\"http://hl7.org/fhir\">"
+ "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal: 444333333 </div></text>"
+ "<identifier><label value=\"SSN\" /><system value=\"http://orionhealth.com/mrn\" /><value value=\"PRP1660\" /></identifier>"
+ "<name><use value=\"official\" /><family value=\"Cardinal\" /><given value=\"John\" /></name>"
+ "<name><family value=\"Kramer\" /><given value=\"Doe\" /></name>"
+ "<telecom><system value=\"phone\" /><value value=\"555-555-2004\" /><use value=\"work\" /></telecom>"
+ "<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>";
//@formatter:on
return msg;
}
@Test
public void testRequest() throws Exception {
String msg = crerateMsg();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
Header[] headers = new Header[] {};
when(myHttpResponse.getAllHeaders()).thenReturn(headers);
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
ITestClient client = ourCtx.newRestfulClient(ITestClient.class, "http://foo");
client.registerInterceptor(new BearerTokenAuthInterceptor("mytoken"));
client.getPatientById(new IdDt("111"));
HttpUriRequest req = capt.getValue();
assertEquals("Bearer mytoken", req.getFirstHeader("Authorization").getValue());
}
@BeforeClass
public static void beforeClass() {
ourCtx = new FhirContext();
}
}

View File

@ -42,6 +42,7 @@ import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
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.model.primitive.UriDt;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.method.SearchStyleEnum; import ca.uhn.fhir.rest.method.SearchStyleEnum;
@ -95,7 +96,6 @@ public class GenericClientTest {
return msg; return msg;
} }
private String getResourceResult() { private String getResourceResult() {
//@formatter:off //@formatter:off
String msg = String msg =
@ -112,7 +112,6 @@ public class GenericClientTest {
return msg; return msg;
} }
@Test @Test
public void testSearchByCompartment() throws Exception { public void testSearchByCompartment() throws Exception {
@ -155,8 +154,6 @@ public class GenericClientTest {
} }
@Test @Test
public void testCreateWithTag() throws Exception { public void testCreateWithTag() throws Exception {
@ -231,7 +228,6 @@ public class GenericClientTest {
assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue()); assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue());
} }
@Test @Test
public void testDelete() throws Exception { public void testDelete() throws Exception {
OperationOutcome oo = new OperationOutcome(); OperationOutcome oo = new OperationOutcome();
@ -300,7 +296,6 @@ public class GenericClientTest {
} }
@Test @Test
public void testRead() throws Exception { public void testRead() throws Exception {
@ -311,11 +306,9 @@ public class GenericClientTest {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
Header[] headers = new Header[] { Header[] headers = new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"), new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
new BasicHeader(Constants.HEADER_CATEGORY, "http://foo/tagdefinition.html; scheme=\"http://hl7.org/fhir/tag\"; label=\"Some tag\"") new BasicHeader(Constants.HEADER_CATEGORY, "http://foo/tagdefinition.html; scheme=\"http://hl7.org/fhir/tag\"; label=\"Some tag\"") };
};
when(myHttpResponse.getAllHeaders()).thenReturn(headers); when(myHttpResponse.getAllHeaders()).thenReturn(headers);
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir"); IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
@ -341,6 +334,64 @@ public class GenericClientTest {
} }
@Test
public void testReadWithAbsoluteUrl() throws Exception {
String msg = getResourceResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
Header[] headers = new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
new BasicHeader(Constants.HEADER_CATEGORY, "http://foo/tagdefinition.html; scheme=\"http://hl7.org/fhir/tag\"; label=\"Some tag\"") };
when(myHttpResponse.getAllHeaders()).thenReturn(headers);
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
Patient response = client.read(Patient.class, new IdDt("http://somebase.com/path/to/base/Patient/1234"));
assertThat(response.getNameFirstRep().getFamilyAsSingleString(), StringContains.containsString("Cardinal"));
assertEquals("http://somebase.com/path/to/base/Patient/1234", capt.getAllValues().get(0).getURI().toString());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
response = client.read(Patient.class, new IdDt("http://somebase.com/path/to/base/Patient/1234/_history/2222"));
assertThat(response.getNameFirstRep().getFamilyAsSingleString(), StringContains.containsString("Cardinal"));
assertEquals("http://somebase.com/path/to/base/Patient/1234", capt.getAllValues().get(1).getURI().toString());
}
@Test
public void testVReadWithAbsoluteUrl() throws Exception {
String msg = getResourceResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
Header[] headers = new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
new BasicHeader(Constants.HEADER_CATEGORY, "http://foo/tagdefinition.html; scheme=\"http://hl7.org/fhir/tag\"; label=\"Some tag\"") };
when(myHttpResponse.getAllHeaders()).thenReturn(headers);
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
Patient response = client.vread(Patient.class, new IdDt("http://somebase.com/path/to/base/Patient/1234/_history/2222"));
assertThat(response.getNameFirstRep().getFamilyAsSingleString(), StringContains.containsString("Cardinal"));
assertEquals("http://somebase.com/path/to/base/Patient/1234/_history/2222", capt.getAllValues().get(0).getURI().toString());
try {
client.vread(Patient.class, new IdDt("http://somebase.com/path/to/base/Patient/1234"));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("No version specified in URL"));
}
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Test @Test
public void testSearchAllResources() throws Exception { public void testSearchAllResources() throws Exception {
@ -365,6 +416,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/?name=james", capt.getValue().getURI().toString()); assertEquals("http://example.com/fhir/?name=james", capt.getValue().getURI().toString());
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Test @Test
public void testSearchByComposite() throws Exception { public void testSearchByComposite() throws Exception {
@ -393,7 +445,6 @@ public class GenericClientTest {
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Test @Test
public void testSearchWithClientEncodingAndPrettyPrintConfig() throws Exception { public void testSearchWithClientEncodingAndPrettyPrintConfig() throws Exception {
@ -420,7 +471,6 @@ public class GenericClientTest {
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Test @Test
public void testSearchByDate() throws Exception { public void testSearchByDate() throws Exception {
@ -448,10 +498,58 @@ public class GenericClientTest {
.execute(); .execute();
//@formatter:on //@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json", capt.getValue().getURI().toString()); assertEquals(
"http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json",
capt.getValue().getURI().toString());
} }
@Test
public void testSearchWithAbsoluteUrl() throws Exception {
String msg = getPatientFeedWithOneResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
Bundle response = client.search(new UriDt("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json"));
assertEquals(
"http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json",
capt.getValue().getURI().toString());
assertEquals(1, response.size());
}
@Test
public void testSearchWithAbsoluteUrlAndType() throws Exception {
String msg = getPatientFeedWithOneResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
Bundle response = client.search(Patient.class, new UriDt("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json"));
assertEquals(
"http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json",
capt.getValue().getURI().toString());
assertEquals(1, response.size());
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Test @Test
public void testSearchByNumberExact() throws Exception { public void testSearchByNumberExact() throws Exception {
@ -657,7 +755,6 @@ public class GenericClientTest {
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Test @Test
public void testSearchUsingPost() throws Exception { public void testSearchUsingPost() throws Exception {
@ -689,7 +786,6 @@ public class GenericClientTest {
assertEquals("name=james", string); assertEquals("name=james", string);
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Test @Test
public void testSearchAutomaticallyUsesPost() throws Exception { public void testSearchAutomaticallyUsesPost() throws Exception {
@ -722,7 +818,6 @@ public class GenericClientTest {
assertEquals("name=" + longValue, string); assertEquals("name=" + longValue, string);
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Test @Test
public void testSearchUsingGetSearch() throws Exception { public void testSearchUsingGetSearch() throws Exception {
@ -818,8 +913,6 @@ public class GenericClientTest {
assertEquals(bundle.getEntries().get(0).getId(), response.getEntries().get(0).getId()); assertEquals(bundle.getEntries().get(0).getId(), response.getEntries().get(0).getId());
} }
@Test @Test
public void testTransactionJson() throws Exception { public void testTransactionJson() throws Exception {
String bundleStr = IOUtils.toString(getClass().getResourceAsStream("/bundle.json")); String bundleStr = IOUtils.toString(getClass().getResourceAsStream("/bundle.json"));
@ -851,7 +944,6 @@ public class GenericClientTest {
assertEquals(bundle.getEntries().get(0).getId(), response.getEntries().get(0).getId()); assertEquals(bundle.getEntries().get(0).getId(), response.getEntries().get(0).getId());
} }
@Test @Test
public void testUpdate() throws Exception { public void testUpdate() throws Exception {

View File

@ -1,15 +1,17 @@
package ca.uhn.fhir.rest.client; package ca.uhn.fhir.rest.client;
import static org.junit.Assert.*; import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.*; import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import java.util.ResourceBundle; import static org.mockito.Mockito.when;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatcher;
@ -23,9 +25,9 @@ import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.ResourceBinding;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.PortUtil;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.Appender; import ch.qos.logback.core.Appender;
@ -33,44 +35,45 @@ import ch.qos.logback.core.Appender;
/** /**
* Created by dsotnikov on 2/25/2014. * Created by dsotnikov on 2/25/2014.
*/ */
public class InterceptorTest { public class LoggingInterceptorTest {
private static int ourPort; private static int ourPort;
private static Server ourServer; private static Server ourServer;
private static FhirContext ourCtx; private static FhirContext ourCtx;
private Appender<ILoggingEvent> myMockAppender;
private Logger myLoggerRoot;
@SuppressWarnings("unchecked")
@Before
public void before() {
/*
* This is a bit funky, but it's useful for verifying that the headers actually get logged
*/
myLoggerRoot = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
myMockAppender = mock(Appender.class);
when(myMockAppender.getName()).thenReturn("MOCK");
myLoggerRoot.addAppender(myMockAppender);
}
@After
public void after() {
myLoggerRoot.detachAppender(myMockAppender);
}
@Test @Test
public void testLogger() throws Exception { public void testLogger() throws Exception {
/*
* This is a bit funky, but we're verifying that the headers actually get logged
*/
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
@SuppressWarnings("unchecked")
Appender<ILoggingEvent> mockAppender = mock(Appender.class);
when(mockAppender.getName()).thenReturn("MOCK");
root.addAppender(mockAppender);
try {
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
client.registerInterceptor(new LoggingInterceptor(true)); client.registerInterceptor(new LoggingInterceptor(true));
Patient patient = client.read(Patient.class, "1"); Patient patient = client.read(Patient.class, "1");
assertFalse(patient.getIdentifierFirstRep().isEmpty()); assertFalse(patient.getIdentifierFirstRep().isEmpty());
// int times = 1; verify(myMockAppender).doAppend(argThat(new ArgumentMatcher<ILoggingEvent>() {
// if (LoggerFactory.getLogger(ResourceBinding.class).isDebugEnabled()) {
// times = 3;
// }
verify(mockAppender).doAppend(argThat(new ArgumentMatcher<ILoggingEvent>() {
@Override @Override
public boolean matches(final Object argument) { public boolean matches(final Object argument) {
return ((LoggingEvent) argument).getFormattedMessage().contains("Content-Type: application/xml+fhir; charset=UTF-8"); return ((LoggingEvent) argument).getFormattedMessage().contains("Content-Type: application/xml+fhir; charset=UTF-8");
} }
})); }));
} finally {
root.detachAppender(mockAppender);
}
} }
@AfterClass @AfterClass

View File

@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
public class ServerConformanceProviderTest { public class ServerConformanceProviderTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerConformanceProviderTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerConformanceProviderTest.class);
private FhirContext myCtx = new FhirContext();
@Test @Test
public void testSearchParameterDocumentation() throws Exception { public void testSearchParameterDocumentation() throws Exception {
@ -59,14 +60,34 @@ public class ServerConformanceProviderTest {
} }
assertTrue(found); assertTrue(found);
Conformance conformance = sc.getServerConformance(); Conformance conformance = sc.getServerConformance();
String conf = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
ourLog.info(conf); ourLog.info(conf);
assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>")); assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>"));
assertThat(conf, containsString("<type value=\"token\"/>")); assertThat(conf, containsString("<type value=\"token\"/>"));
} }
@Test
public void testValidateGeneratedStatement() throws Exception {
RestfulServer rs = new RestfulServer();
rs.setProviders(new MultiOptionalProvider());
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(null);
Conformance conformance = sc.getServerConformance();
myCtx.newValidator().validate(conformance);
}
@Test @Test
public void testMultiOptionalDocumentation() throws Exception { public void testMultiOptionalDocumentation() throws Exception {

File diff suppressed because one or more lines are too long

View File

@ -47,8 +47,6 @@ import com.nimbusds.jwt.SignedJWT;
public class OpenIdConnectBearerTokenServerInterceptor extends InterceptorAdapter { public class OpenIdConnectBearerTokenServerInterceptor extends InterceptorAdapter {
private static final String BEARER_PREFIX = "Bearer ";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OpenIdConnectBearerTokenServerInterceptor.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OpenIdConnectBearerTokenServerInterceptor.class);
@Autowired @Autowired
@ -68,8 +66,6 @@ public class OpenIdConnectBearerTokenServerInterceptor extends InterceptorAdapte
} }
@Override @Override
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException { public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
if (theRequestDetails.getOtherOperationType() == OtherOperationTypeEnum.METADATA) { if (theRequestDetails.getOtherOperationType() == OtherOperationTypeEnum.METADATA) {
@ -89,11 +85,11 @@ public class OpenIdConnectBearerTokenServerInterceptor extends InterceptorAdapte
if (token == null) { if (token == null) {
throw new AuthenticationException("Not authorized (no authorization header found in request)"); throw new AuthenticationException("Not authorized (no authorization header found in request)");
} }
if (!token.startsWith(BEARER_PREFIX)) { if (!token.startsWith(Constants.HEADER_AUTHORIZATION_VALPREFIX_BEARER)) {
throw new AuthenticationException("Not authorized (authorization header does not contain a bearer token)"); throw new AuthenticationException("Not authorized (authorization header does not contain a bearer token)");
} }
token = token.substring(BEARER_PREFIX.length()); token = token.substring(Constants.HEADER_AUTHORIZATION_VALPREFIX_BEARER.length());
SignedJWT idToken; SignedJWT idToken;
try { try {

View File

@ -18,5 +18,11 @@
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/> <classpathentry kind="output" path="target/classes"/>
</classpath> </classpath>

17
pom.xml
View File

@ -56,11 +56,24 @@
</developer> </developer>
<developer> <developer>
<name>Josh Mandel</name> <name>Josh Mandel</name>
<url></url> <organization>Boston Children's Hospital</organization>
</developer> </developer>
<developer> <developer>
<name>Laura MacDougall Sookraj</name> <name>Laura MacDougall Sookraj</name>
<url></url> </developer>
<developer>
<name>David Hay</name>
<organization>Orion Health</organization>
</developer>
<developer>
<id>suranga</id>
<name>Suranga Nath Kasthurirathne</name>
<organization>OpenMRS</organization>
</developer>
<developer>
<id>dougmartin</id>
<name>Doug Martin</name>
<organization>Regenstrief Center for Biomedical Informatics</organization>
</developer> </developer>
</developers> </developers>