Fix #315 - Allow declaring of custom types for specific profiles

This commit is contained in:
James Agnew 2016-03-22 11:46:08 +01:00
parent 5895881fa5
commit 1adfc4b4d9
58 changed files with 2556 additions and 11061 deletions

View File

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

View File

@ -12,7 +12,7 @@ import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
public class Extensions {
public class ExtensionsDstu2 {
@SuppressWarnings("unused")
public static void main(String[] args) throws DataFormatException, IOException {

View File

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

View File

@ -4,13 +4,14 @@ package example;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.util.ElementUtil;
/**
@ -25,6 +26,8 @@ import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name="Patient", profile="http://example.com/StructureDefinition/mypatient")
public class MyPatient extends Patient {
private static final long serialVersionUID = 1L;
/**
* Each extension is defined in a field. Any valid HAPI Data Type
* can be used for the field type. Note that the [name=""] attribute
@ -34,7 +37,7 @@ public class MyPatient extends Patient {
@Child(name="petName")
@Extension(url="http://example.com/dontuse#petname", definedLocally=false, isModifier=false)
@Description(shortDefinition="The name of the patient's favourite pet")
private StringDt myPetName;
private StringType myPetName;
/**
* The second example extension uses a List type to provide
@ -46,7 +49,7 @@ public class MyPatient extends Patient {
@Child(name="importantDates", max=Child.MAX_UNLIMITED)
@Extension(url="http://example.com/dontuse#importantDates", definedLocally=false, isModifier=true)
@Description(shortDefinition="Some dates of note for this patient")
private List<DateTimeDt> myImportantDates;
private List<DateTimeType> myImportantDates;
/**
* It is important to override the isEmpty() method, adding a check for any
@ -67,28 +70,28 @@ public class MyPatient extends Patient {
********/
/** Getter for important dates */
public List<DateTimeDt> getImportantDates() {
public List<DateTimeType> getImportantDates() {
if (myImportantDates==null) {
myImportantDates = new ArrayList<DateTimeDt>();
myImportantDates = new ArrayList<DateTimeType>();
}
return myImportantDates;
}
/** Getter for pet name */
public StringDt getPetName() {
public StringType getPetName() {
if (myPetName == null) {
myPetName = new StringDt();
myPetName = new StringType();
}
return myPetName;
}
/** Setter for important dates */
public void setImportantDates(List<DateTimeDt> theImportantDates) {
public void setImportantDates(List<DateTimeType> theImportantDates) {
myImportantDates = theImportantDates;
}
/** Setter for pet name */
public void setPetName(StringDt thePetName) {
public void setPetName(StringType thePetName) {
myPetName = thePetName;
}

View File

@ -18,7 +18,7 @@ import ca.uhn.fhir.parser.IParser;
public class MyPatientUse {
@ResourceDef
@ResourceDef()
public static class MyPatient extends Patient {
@Child(name="petName")

View File

@ -6,22 +6,16 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry combineaccessrules="false" kind="src" path="/hapi-fhir-base"/>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -5,6 +5,16 @@
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
@ -13,5 +23,7 @@
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.7

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<installed facet="java" version="1.7"/>
</faceted-project>

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertThat;
import org.hamcrest.core.StringContains;
@ -22,7 +23,10 @@ public class MultiVersionJsonParserTest {
String str = FhirContext.forDstu2().newJsonParser().encodeResourceToString(p);
ourLog.info(str);
assertThat(str, StringContains.containsString("{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://foo#ext\",\"valueQuantity\":{\"value\":2.2}}],\"identifier\":[{\"system\":\"urn:sys\",\"value\":\"001\"}]}"));
assertThat(str, stringContainsInOrder(
"{\"resourceType\":\"Patient\"",
"\"extension\":[{\"url\":\"http://foo#ext\",\"valueQuantity\":{\"value\":2.2}}]",
"\"identifier\":[{\"system\":\"urn:sys\",\"value\":\"001\"}]"));
}
}

View File

@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.client.IRestfulClientFactory;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.VersionUtil;
@ -77,7 +78,9 @@ public class FhirContext {
private static final List<Class<? extends IBaseResource>> EMPTY_LIST = Collections.emptyList();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContext.class);
private AddProfileTagEnum myAddProfileTagWhenEncoding = AddProfileTagEnum.ONLY_FOR_CUSTOM;
private volatile Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinition = Collections.emptyMap();
private Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<String, Class<? extends IBaseResource>>();
private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap();
private HapiLocalizer myLocalizer = new HapiLocalizer();
private volatile Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinition = Collections.emptyMap();
@ -144,6 +147,26 @@ public class FhirContext {
return getLocalizer().getMessage(FhirContext.class, "unknownResourceName", theResourceName, theVersion);
}
/**
* When encoding resources, this setting configures the parser to include
* an entry in the resource's metadata section which indicates which profile(s) the
* resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}.
*
* @see #setAddProfileTagWhenEncoding(AddProfileTagEnum) for more information
*/
public AddProfileTagEnum getAddProfileTagWhenEncoding() {
return myAddProfileTagWhenEncoding;
}
/**
* Returns the default resource type for the given profile
*
* @see #setDefaultTypeForProfile(String, Class)
*/
public Class<? extends IBaseResource> getDefaultTypeForProfile(String theProfile) {
return myDefaultTypeForProfile.get(theProfile);
}
/**
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
* for extending the core library.
@ -298,14 +321,6 @@ public class FhirContext {
return myIdToResourceDefinition.values();
}
/**
* Set the restful client factory
* @param theRestfulClientFactory
*/
public void setRestfulClientFactory(IRestfulClientFactory theRestfulClientFactory) {
this.myRestfulClientFactory = theRestfulClientFactory;
}
/**
* Get the restful client factory. If no factory has been set, this will be initialized with
* a new ApacheRestfulClientFactory.
@ -326,6 +341,17 @@ public class FhirContext {
return myVersion;
}
/**
* Returns <code>true</code> if any default types for specific profiles have been defined
* within this context.
*
* @see #setDefaultTypeForProfile(String, Class)
* @see #getDefaultTypeForProfile(String)
*/
public boolean hasDefaultTypeForProfile() {
return !myDefaultTypeForProfile.isEmpty();
}
/**
* This method should be considered experimental and will likely change in future releases
* of HAPI. Use with caution!
@ -473,6 +499,50 @@ public class FhirContext {
return classToElementDefinition;
}
/**
* When encoding resources, this setting configures the parser to include
* an entry in the resource's metadata section which indicates which profile(s) the
* resource claims to conform to. The default is {@link AddProfileTagEnum#ONLY_FOR_CUSTOM}.
* <p>
* This feature is intended for situations where custom resource types are being used,
* avoiding the need to manually add profile declarations for these custom types.
* </p>
* <p>
* See <a href="http://jamesagnew.gihhub.io/hapi-fhir/doc_extensions.html">Profiling and Extensions</a>
* for more information on using custom types.
* </p>
* <p>
* Note that this feature automatically adds the profile, but leaves any profile tags
* which have been manually added in place as well.
* </p>
*
* @param theAddProfileTagWhenEncoding
* The add profile mode (must not be <code>null</code>)
*/
public void setAddProfileTagWhenEncoding(AddProfileTagEnum theAddProfileTagWhenEncoding) {
Validate.notNull(theAddProfileTagWhenEncoding, "theAddProfileTagWhenEncoding must not be null");
myAddProfileTagWhenEncoding = theAddProfileTagWhenEncoding;
}
/**
* Sets the default type which will be used when parsing a resource that is found to be
* of the given profile.
* <p>
* For example, this method is invoked with the profile string of
* <code>"http://example.com/some_patient_profile"</code> and the type of <code>MyPatient.class</code>,
* if the parser is parsing a resource and finds that it declares that it conforms to that profile,
* the <code>MyPatient</code> type will be used unless otherwise specified.
* </p>
*
* @param theProfile The profile string, e.g. <code>"http://example.com/some_patient_profile"</code>. Must not be <code>null</code> or empty.
* @param theClass The resource type. Must not be <code>null</code> or empty.
*/
public void setDefaultTypeForProfile(String theProfile, Class<? extends IBaseResource> theClass) {
Validate.notBlank(theProfile, "theProfile must not be null or empty");
Validate.notNull(theClass, "theProfile must not be null");
myDefaultTypeForProfile.put(theProfile, theClass);
}
/**
* This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with
* caution
@ -495,6 +565,14 @@ public class FhirContext {
myParserErrorHandler = theParserErrorHandler;
}
/**
* Set the restful client factory
* @param theRestfulClientFactory
*/
public void setRestfulClientFactory(IRestfulClientFactory theRestfulClientFactory) {
this.myRestfulClientFactory = theRestfulClientFactory;
}
@SuppressWarnings({ "cast" })
private List<Class<? extends IElement>> toElementList(Collection<Class<? extends IBaseResource>> theResourceTypes) {
if (theResourceTypes == null) {
@ -508,7 +586,7 @@ public class FhirContext {
}
/**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU1}
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU1 DSTU1}
*/
public static FhirContext forDstu1() {
return new FhirContext(FhirVersionEnum.DSTU1);
@ -521,6 +599,13 @@ public class FhirContext {
return new FhirContext(FhirVersionEnum.DSTU2);
}
/**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2_HL7ORG DSTU2} (using the Reference Implementation Structures)
*/
public static FhirContext forDstu2Hl7Org() {
return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG);
}
/**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU3 DSTU3}
*
@ -530,10 +615,6 @@ public class FhirContext {
return new FhirContext(FhirVersionEnum.DSTU3);
}
public static FhirContext forDstu2Hl7Org() {
return new FhirContext(FhirVersionEnum.DSTU2_HL7ORG);
}
private static Collection<Class<? extends IBaseResource>> toCollection(Class<? extends IBaseResource> theResourceType) {
ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>(1);
retVal.add(theResourceType);

View File

@ -683,6 +683,14 @@ class ModelScanner {
}
}
Class<? extends IBaseResource> builtInType = myNameToResourceType.get(resourceName.toLowerCase());
boolean standardType = builtInType != null && builtInType.equals(theClass) == true;
if (primaryNameProvider) {
if (builtInType != null && builtInType.equals(theClass) == false) {
primaryNameProvider = false;
}
}
String resourceId = resourceDefinition.id();
if (!isBlank(resourceId)) {
if (myIdToResourceDefinition.containsKey(resourceId)) {
@ -691,7 +699,7 @@ class ModelScanner {
}
}
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(myContext, resourceName, theClass, resourceDefinition, isStandardType(theClass));
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(myContext, resourceName, theClass, resourceDefinition, standardType);
myClassToElementDefinitions.put(theClass, resourceDef);
if (primaryNameProvider) {
if (resourceDef.getStructureVersion() == myVersion) {

View File

@ -155,10 +155,6 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
return "Bundle".equals(getName());
}
public boolean isStandardProfile() {
return myResourceProfile.startsWith("http://hl7.org/fhir/profiles");
}
@Override
public void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
super.sealAndInitialize(theContext, theClassToElementDefinitions);

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.api;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
/*
* #%L
* HAPI FHIR - Core Library
@ -62,6 +64,15 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode
*/
CodeDt getLanguage();
/**
* Returns a view of the {@link #getResourceMetadata() resource metadata} map.
* Note that getters from this map return immutable objects, but the <code>addFoo()</code>
* and <code>setFoo()</code> methods may be used to modify metadata.
*
* @since 1.5
*/
IBaseMetaType getMeta();
/**
* Returns the metadata map for this object, creating it if neccesary. Metadata entries are used to get/set feed bundle entries, such as the resource version, or the last updated timestamp.
* <p>
@ -72,10 +83,23 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode
*/
ResourceMetadataMap getResourceMetadata();
/**
* Returns a String representing the name of this Resource. This return value is not used for anything by HAPI itself, but is provided as a convenience to developers using the API.
*
* @return the name of this resource, e.g. "Patient", or "Observation"
*/
String getResourceName();
/**
* Returns the FHIR version represented by this structure
*/
@Override
public ca.uhn.fhir.context.FhirVersionEnum getStructureFhirVersionEnum();
/**
* Returns the narrative block for this resource
*/
BaseNarrativeDt getText();
BaseNarrativeDt<?> getText();
/**
* Sets the ID of this resource. Note that this identifier is the URL (or a portion of the URL) used to access this resource, and is not the same thing as any business identifiers stored within the
@ -99,16 +123,4 @@ public interface IResource extends ICompositeElement, org.hl7.fhir.instance.mode
*/
void setResourceMetadata(ResourceMetadataMap theMap);
/**
* Returns a String representing the name of this Resource. This return value is not used for anything by HAPI itself, but is provided as a convenience to developers using the API.
*
* @return the name of this resource, e.g. "Patient", or "Observation"
*/
String getResourceName();
/**
* Returns the FHIR version represented by this structure
*/
public ca.uhn.fhir.context.FhirVersionEnum getStructureFhirVersionEnum();
}

View File

@ -26,6 +26,7 @@ import java.net.URI;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IBaseCoding;
/**
* A single tag
@ -34,7 +35,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
* {@link #getScheme() scheme} and
* </p>
*/
public class Tag extends BaseElement implements IElement {
public class Tag extends BaseElement implements IElement, IBaseCoding {
private static final long serialVersionUID = 1L;
@ -59,10 +60,6 @@ public class Tag extends BaseElement implements IElement {
private String myScheme;
private String myTerm;
/**
* @deprecated Tags will become immutable in a future release, so this constructor should not be used.
*/
@Deprecated
public Tag() {
}
@ -149,12 +146,7 @@ public class Tag extends BaseElement implements IElement {
/**
* Sets the label and returns a reference to this tag
*
* @deprecated Tags will become immutable in a future release of HAPI in order to facilitate
* ensuring that the TagList acts as an unordered set. Use the constructor to set this field when creating a new
* tag object
*/
@Deprecated
public Tag setLabel(String theLabel) {
myLabel = theLabel;
return this;
@ -162,12 +154,7 @@ public class Tag extends BaseElement implements IElement {
/**
* Sets the scheme and returns a reference to this tag
*
* @deprecated Tags will become immutable in a future release of HAPI in order to facilitate
* ensuring that the TagList acts as an unordered set. Use the constructor to set this field when creating a new
* tag object
*/
@Deprecated
public Tag setScheme(String theScheme) {
myScheme = theScheme;
return this;
@ -175,12 +162,7 @@ public class Tag extends BaseElement implements IElement {
/**
* Sets the term and returns a reference to this tag
*
* @deprecated Tags will become immutable in a future release of HAPI in order to facilitate
* ensuring that the TagList acts as an unordered set. Use the constructor to set this field when creating a new
* tag object
*/
@Deprecated
public Tag setTerm(String theTerm) {
myTerm = theTerm;
return this;
@ -207,4 +189,37 @@ public class Tag extends BaseElement implements IElement {
return b.toString();
}
@Override
public String getCode() {
return getTerm();
}
@Override
public String getDisplay() {
return getLabel();
}
@Override
public String getSystem() {
return getScheme();
}
@Override
public IBaseCoding setCode(String theTerm) {
setTerm(myTerm);
return this;
}
@Override
public IBaseCoding setDisplay(String theLabel) {
setLabel(theLabel);
return this;
}
@Override
public IBaseCoding setSystem(String theScheme) {
setScheme(theScheme);
return this;
}
}

View File

@ -53,6 +53,10 @@ public @interface ResourceDef {
/**
* The URL indicating the profile for this resource definition. If specified, this URL will be
* automatically added to the meta tag when the resource is serialized.
* <p>
* This URL should be fully qualified to indicate the complete URL of
* the profile being used, e.g. <code>http://example.com/fhir/StructureDefiniton/some_profile</code>
* </p>
*/
String profile() default "";

View File

@ -78,12 +78,14 @@ public abstract class BaseParser implements IParser {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
private IIdType myEncodeForceResourceId;
private ContainedResources myContainedResources;
private FhirContext myContext;
private Set<String> myDontEncodeElements;
private boolean myDontEncodeElementsIncludesStars;
private Set<String> myEncodeElements;
private Set<String> myEncodeElementsAppliesToResourceTypes;
private boolean myEncodeElementsIncludesStars;
private IIdType myEncodeForceResourceId;
private IParserErrorHandler myErrorHandler;
private boolean myOmitResourceId;
private String myServerBaseUrl;
@ -91,10 +93,6 @@ public abstract class BaseParser implements IParser {
private boolean mySummaryMode;
private boolean mySuppressNarratives;
private Set<String> myDontEncodeElements;
private boolean myDontEncodeElementsIncludesStars;
/**
* Constructor
*
@ -439,6 +437,47 @@ public abstract class BaseParser implements IParser {
return tags;
}
@SuppressWarnings("deprecation")
protected <T extends IPrimitiveType<String>> List<T> getProfileTagsForEncoding(IBaseResource theResource, List<T> theProfiles) {
switch (myContext.getAddProfileTagWhenEncoding()) {
case NEVER:
return theProfiles;
case ONLY_FOR_CUSTOM:
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
if (resDef.isStandardType()) {
return theProfiles;
}
break;
case ALWAYS:
break;
}
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
RuntimeResourceDefinition nextDef = myContext.getResourceDefinition(theResource);
String profile = nextDef.getResourceProfile(myServerBaseUrl);
if (isNotBlank(profile)) {
for (T next : theProfiles) {
if (profile.equals(next.getValue())) {
return theProfiles;
}
}
List<T> newList = new ArrayList<T>();
newList.addAll(theProfiles);
BaseRuntimeElementDefinition<?> idElement = myContext.getElementDefinition("id");
@SuppressWarnings("unchecked")
T newId = (T) idElement.newInstance();
newId.setValue(profile);
newList.add(newId);
return newList;
}
}
return theProfiles;
}
/**
* If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the encoded
* values.
@ -595,6 +634,17 @@ public abstract class BaseParser implements IParser {
filterCodingsWithNoCodeOrSystem(metaValue.getTag());
filterCodingsWithNoCodeOrSystem(metaValue.getSecurity());
List<? extends IPrimitiveType<String>> newProfileList = getProfileTagsForEncoding(theResource, metaValue.getProfile());
List<? extends IPrimitiveType<String>> oldProfileList = metaValue.getProfile();
if (oldProfileList != newProfileList) {
oldProfileList.clear();
for (IPrimitiveType<String> next : newProfileList) {
if (isNotBlank(next.getValue())) {
metaValue.addProfile(next.getValue());
}
}
}
if (shouldAddSubsettedTag()) {
IBaseCoding coding = metaValue.addTag();
coding.setCode(Constants.TAG_SUBSETTED_CODE);
@ -748,11 +798,11 @@ public abstract class BaseParser implements IParser {
}
protected static <T> List<T> extractMetadataListNotNull(IResource resource, ResourceMetadataKeyEnum<List<T>> key) {
List<T> securityLabels = key.get(resource);
List<? extends T> securityLabels = key.get(resource);
if (securityLabels == null) {
securityLabels = Collections.emptyList();
}
return securityLabels;
return new ArrayList<T>(securityLabels);
}
static boolean hasExtensions(IBase theElement) {

View File

@ -222,7 +222,8 @@ public interface IParser {
* <li><b>Patient</b> - Don't encode patient and all its children</li>
* <li><b>Patient.name</b> - Don't encode the patient's name</li>
* <li><b>Patient.name.family</b> - Don't encode the patient's family name</li>
* <li><b>*.text</b> - Don't encode the text element on any resource (only the very first position may contain a wildcard)</li>
* <li><b>*.text</b> - Don't encode the text element on any resource (only the very first position may contain a
* wildcard)</li>
* </ul>
* <p>
* DSTU2 note: Note that values including meta, such as <code>Patient.meta</code>
@ -244,7 +245,8 @@ public interface IParser {
* <li><b>Patient</b> - Encode patient and all its children</li>
* <li><b>Patient.name</b> - Encode only the patient's name</li>
* <li><b>Patient.name.family</b> - Encode only the patient's family name</li>
* <li><b>*.text</b> - Encode the text element on any resource (only the very first position may contain a wildcard)</li>
* <li><b>*.text</b> - Encode the text element on any resource (only the very first position may contain a
* wildcard)</li>
* </ul>
*
* @param theEncodeElements

View File

@ -838,7 +838,9 @@ public class JsonParser extends BaseParser implements IParser {
// Object securityLabelRawObj =
List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS);
List<IdDt> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES);
List<? extends IIdType> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES);
profiles = super.getProfileTagsForEncoding(resource, profiles);
TagList tags = getMetaTagsForEncoding(resource);
InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
IdDt resourceId = resource.getId();
@ -854,7 +856,7 @@ public class JsonParser extends BaseParser implements IParser {
if (profiles != null && profiles.isEmpty() == false) {
theEventWriter.writeStartArray("profile");
for (IdDt profile : profiles) {
for (IIdType profile : profiles) {
if (profile != null && isNotBlank(profile.getValue())) {
theEventWriter.write(profile.getValue());
}

View File

@ -43,6 +43,7 @@ import org.hl7.fhir.instance.model.api.IBaseElement;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IBaseXhtml;
@ -1433,6 +1434,11 @@ class ParserState<T> {
}
@Override
protected void populateTarget() {
// nothing
}
}
private class ContainedResourcesStateHl7Org extends PreResourceState {
@ -1464,6 +1470,11 @@ class ParserState<T> {
def.getChildByName("contained").getMutator().addValue(preResCurrentElement, res);
}
@Override
protected void populateTarget() {
// nothing
}
}
private class DeclaredExtensionState extends BaseState {
@ -1969,6 +1980,8 @@ class ParserState<T> {
}
}
protected abstract void populateTarget();
public PreResourceState(PreResourceState thePreResourcesState, FhirVersionEnum theParentVersion) {
super(thePreResourcesState);
Validate.notNull(theParentVersion);
@ -2045,6 +2058,40 @@ class ParserState<T> {
public void wereBack() {
final boolean bundle = "Bundle".equals(myContext.getResourceDefinition(myInstance).getName());
if (myContext.hasDefaultTypeForProfile()) {
IBaseMetaType meta = myInstance.getMeta();
Class<? extends IBaseResource> wantedProfileType = null;
String usedProfile = null;
for (IPrimitiveType<String> next : meta.getProfile()) {
if (isNotBlank(next.getValue())) {
wantedProfileType = myContext.getDefaultTypeForProfile(next.getValue());
if (wantedProfileType != null) {
usedProfile = next.getValue();
break;
}
}
}
if (wantedProfileType != null && !wantedProfileType.equals(myInstance.getClass())) {
if (myResourceType == null || myResourceType.isAssignableFrom(wantedProfileType)) {
ourLog.debug("Converting resource of type {} to type defined for profile \"{}\": {}", new Object[] {myInstance.getClass().getName(), usedProfile, wantedProfileType});
/*
* This isn't the most efficient thing really.. If we want a specific
* type we just re-parse into that type. The problem is that we don't know
* until we've parsed the resource which type we want to use because the
* profile declarations are in the text of the resource itself.
*
* At some point it would be good to write code which can present a view
* of one type backed by another type and use that.
*/
IParser parser = myContext.newJsonParser();
String asString = parser.encodeResourceToString(myInstance);
myInstance = parser.parseResource(wantedProfileType, asString);
}
}
}
FhirTerser terser = myContext.newTerser();
terser.visit(myInstance, new IModelVisitor() {
@ -2114,6 +2161,7 @@ class ParserState<T> {
}
populateTarget();
}
}
@ -2141,14 +2189,19 @@ class ParserState<T> {
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
}
// @Override
// public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
// super.enteringNewElement(theNamespaceUri, theLocalPart);
// populateTarget();
// }
@Override
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
super.enteringNewElement(theNamespaceUri, theLocalPart);
protected void populateTarget() {
if (myEntry != null) {
myEntry.setResource((IResource) getCurrentElement());
}
if (myMutator != null) {
myMutator.addValue(myTarget, getCurrentElement());
myMutator.setValue(myTarget, getCurrentElement());
}
}
@ -2192,10 +2245,9 @@ class ParserState<T> {
}
@Override
public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
super.enteringNewElement(theNamespaceUri, theLocalPart);
protected void populateTarget() {
if (myMutator != null) {
myMutator.addValue(myTarget, getCurrentElement());
myMutator.setValue(myTarget, getCurrentElement());
}
}

View File

@ -132,16 +132,6 @@ public class XmlParser extends BaseParser implements IParser {
} catch (XMLStreamException e1) {
throw new DataFormatException(e1);
}
// 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 createXmlWriter(Writer theWriter) throws XMLStreamException {
@ -433,8 +423,6 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeEndElement();
}
String bundleBaseUrl = theBundle.getLinkBase().getValue();
writeOptionalTagWithValue(theEventWriter, "type", theBundle.getType().getValue());
writeOptionalTagWithValue(theEventWriter, "total", theBundle.getTotalResults().getValueAsString());
@ -841,7 +829,9 @@ public class XmlParser extends BaseParser implements IParser {
versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource);
}
List<BaseCodingDt> securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS);
List<IdDt> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES);
List<? extends IIdType> profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES);
profiles = super.getProfileTagsForEncoding(resource, profiles);
TagList tags = getMetaTagsForEncoding((resource));
if (super.shouldEncodeResourceMeta(resource) && ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) {
@ -851,7 +841,7 @@ public class XmlParser extends BaseParser implements IParser {
writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString());
}
for (IdDt profile : profiles) {
for (IIdType profile : profiles) {
theEventWriter.writeStartElement("profile");
theEventWriter.writeAttribute("value", profile.getValue());
theEventWriter.writeEndElement();

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server;
import ca.uhn.fhir.context.FhirContext;
/*
* #%L
* HAPI FHIR - Core Library
@ -21,9 +23,9 @@ package ca.uhn.fhir.rest.server;
*/
/**
* RESTful server behaviour for automatically adding profile tags
* RESTful server behaviour for automatically adding profile tags when serializing resources
*
* @see RestfulServer#setAddProfileTag(AddProfileTagEnum)
* @see FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)
*/
public enum AddProfileTagEnum {
/**
@ -33,7 +35,13 @@ public enum AddProfileTagEnum {
/**
* Add any profile tags that returned resources appear to conform to
*
* @deprecated This mode causes even FHIR's default profiles to be exported in the
* resource metadata section. This is not generally expected behaviour from other
* systems and it offers no real benefit, so it will be removed at some point. This
* option was deprecated in HAPI 1.5
*/
@Deprecated
ALWAYS,
/**

View File

@ -48,7 +48,9 @@ public interface IRestfulServerDefaults {
/**
* @return Returns the setting for automatically adding profile tags
* @deprecated As of HAPI FHIR 1.5, this property has been moved to {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
*/
@Deprecated
AddProfileTagEnum getAddProfileTag();
/**

View File

@ -40,7 +40,7 @@ public interface IVersionSpecificBundleFactory {
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated);
void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
int theOffset, Integer theCount, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes);
Bundle getDstu1Bundle();

View File

@ -74,6 +74,7 @@ import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.ReflectionUtil;
import ca.uhn.fhir.util.UrlUtil;
import ca.uhn.fhir.util.VersionUtil;
@ -93,7 +94,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
private static final long serialVersionUID = 1L;
private AddProfileTagEnum myAddProfileTag;
private BundleInclusionRule myBundleInclusionRule = BundleInclusionRule.BASED_ON_INCLUDES;
private boolean myDefaultPrettyPrint = false;
private EncodingEnum myDefaultResponseEncoding = EncodingEnum.XML;
@ -369,11 +369,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return count;
}
@Override
public AddProfileTagEnum getAddProfileTag() {
return myAddProfileTag;
}
@Override
public BundleInclusionRule getBundleInclusionRule() {
return myBundleInclusionRule;
@ -1117,10 +1112,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
*
* @param theAddProfileTag
* The behaviour enum (must not be null)
* @deprecated As of HAPI FHIR 1.5, this property has been moved to {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
*/
@Deprecated
@CoverageIgnore
public void setAddProfileTag(AddProfileTagEnum theAddProfileTag) {
Validate.notNull(theAddProfileTag, "theAddProfileTag must not be null");
myAddProfileTag = theAddProfileTag;
myFhirContext.setAddProfileTagWhenEncoding(theAddProfileTag);
}
/**
@ -1394,4 +1392,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return nextString.length() > 0 && (nextString.charAt(0) == '_' || nextString.charAt(0) == '$' || nextString.equals(Constants.URL_TOKEN_METADATA));
}
/**
* @deprecated As of HAPI FHIR 1.5, this property has been moved to {@link FhirContext#setAddProfileTagWhenEncoding(AddProfileTagEnum)}
*/
@Override
@Deprecated
public AddProfileTagEnum getAddProfileTag() {
return myFhirContext.getAddProfileTagWhenEncoding();
}
}

View File

@ -26,7 +26,6 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
@ -49,15 +48,12 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.IParser;
@ -77,36 +73,6 @@ public class RestfulServerUtils {
private static final HashSet<String> TEXT_ENCODE_ELEMENTS = new HashSet<String>(Arrays.asList("Bundle", "*.text"));
public static void addProfileToBundleEntry(FhirContext theContext, IBaseResource theResource, String theServerBase) {
if (theResource instanceof IResource) {
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(theResource);
String profile = nextDef.getResourceProfile(theServerBase);
if (isNotBlank(profile)) {
List<IdDt> newList = new ArrayList<IdDt>();
List<IdDt> existingList = ResourceMetadataKeyEnum.PROFILES.get((IResource) theResource);
if (existingList != null) {
newList.addAll(existingList);
}
newList.add(new IdDt(profile));
ResourceMetadataKeyEnum.PROFILES.put((IResource) theResource, newList);
}
} else {
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get((IResource) theResource);
if (tl == null) {
tl = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put((IResource) theResource, tl);
}
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(theResource);
String profile = nextDef.getResourceProfile(theServerBase);
if (isNotBlank(profile)) {
tl.add(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
}
}
}
}
public static void configureResponseParser(RequestDetails theRequestDetails, IParser parser) {
// Pretty print
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theRequestDetails.getServer(), theRequestDetails);
@ -608,13 +574,6 @@ public class RestfulServerUtils {
}
}
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(theResource);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
addProfileToBundleEntry(theServer.getFhirContext(), theResource, serverBase);
}
}
String contentType;
if (theResource instanceof IBaseBinary && responseEncoding == null) {
IBaseBinary bin = (IBaseBinary) theResource;

View File

@ -55,8 +55,6 @@ public interface IAnyResource extends IBaseResource {
IPrimitiveType<String> getLanguageElement();
IBaseMetaType getMeta();
public Object getUserData(String name);
@Override

View File

@ -39,6 +39,8 @@ import ca.uhn.fhir.model.api.Include;
*/
public interface IBaseResource extends IBase, IElement {
IBaseMetaType getMeta();
/**
* Include constant for <code>*</code> (return all includes)
*/

View File

@ -32,7 +32,7 @@ package org.hl7.fhir.instance.model.api;
* which version of the strctures your application is using.
* </p>
*/
public interface IIdType extends IBase {
public interface IIdType extends IPrimitiveType<String> {
void applyTo(IBaseResource theResource);

View File

@ -36,5 +36,5 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@ -1,5 +1,10 @@
package ca.uhn.fhir.model.dstu.resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/*
* #%L
* HAPI FHIR Structures - DSTU1 (FHIR v0.80)
@ -23,35 +28,31 @@ package ca.uhn.fhir.model.dstu.resource;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.api.BaseElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.util.ElementUtil;
public abstract class BaseResource extends BaseElement implements IResource {
/**
* Search parameter constant for <b>_language</b>
*/
@SearchParamDefinition(name="_language", path="", description="The language of the resource", type="string" )
public static final String SP_RES_LANGUAGE = "_language";
/**
* Search parameter constant for <b>_id</b>
*/
@SearchParamDefinition(name="_id", path="", description="The ID of the resource", type="string" )
public static final String SP_RES_ID = "_id";
/**
* <b>Fluent Client</b> search parameter constant for <b>_id</b>
* <p>
@ -63,9 +64,24 @@ public abstract class BaseResource extends BaseElement implements IResource {
public static final StringClientParam RES_ID = new StringClientParam(BaseResource.SP_RES_ID);
/**
* Search parameter constant for <b>_id</b>
*/
@SearchParamDefinition(name="_id", path="", description="The ID of the resource", type="string" )
public static final String SP_RES_ID = "_id";
/**
* Search parameter constant for <b>_language</b>
*/
@SearchParamDefinition(name="_language", path="", description="The language of the resource", type="string" )
public static final String SP_RES_LANGUAGE = "_language";
@Child(name = "contained", order = 2, min = 0, max = 1)
private ContainedDt myContained;
private IdDt myId;
@Child(name = "language", order = 0, min = 0, max = Child.MAX_UNLIMITED)
@ -104,6 +120,216 @@ public abstract class BaseResource extends BaseElement implements IResource {
return myLanguage;
}
@Override
public IBaseMetaType getMeta() {
return new IBaseMetaType() {
private static final long serialVersionUID = 1L;
@Override
public IBaseMetaType addProfile(String theProfile) {
ArrayList<IdDt> newTagList = new ArrayList<IdDt>();
List<IdDt> existingTagList = ResourceMetadataKeyEnum.PROFILES.get(BaseResource.this);
if (existingTagList != null) {
newTagList.addAll(existingTagList);
}
ResourceMetadataKeyEnum.PROFILES.put(BaseResource.this, newTagList);
IdDt tag = new IdDt(theProfile);
newTagList.add(tag);
return this;
}
@Override
public IBaseCoding addSecurity() {
List<BaseCodingDt> tagList = ResourceMetadataKeyEnum.SECURITY_LABELS.get(BaseResource.this);
if (tagList == null) {
tagList = new ArrayList<BaseCodingDt>();
ResourceMetadataKeyEnum.SECURITY_LABELS.put(BaseResource.this, tagList);
}
CodingDt tag = new CodingDt();
tagList.add(tag);
return asBaseCoding(tag);
}
@Override
public IBaseCoding addTag() {
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(BaseResource.this);
if (tagList == null) {
tagList = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put(BaseResource.this, tagList);
}
Tag tag = new Tag();
tagList.add(tag);
return tag;
}
/**
* This view is used because the old DSTU1 BaseCodingDt can't implements IBaseCoding
*/
private IBaseCoding asBaseCoding(final BaseCodingDt theCoding) {
return new IBaseCoding() {
private static final long serialVersionUID = 1L;
@Override
public String getCode() {
return theCoding.getCodeElement().getValue();
}
@Override
public String getDisplay() {
return theCoding.getDisplayElement().getValue();
}
@Override
public List<String> getFormatCommentsPost() {
return Collections.emptyList();
}
@Override
public List<String> getFormatCommentsPre() {
return Collections.emptyList();
}
@Override
public String getSystem() {
return theCoding.getSystemElement().getValue();
}
@Override
public boolean hasFormatComment() {
return false;
}
@Override
public boolean isEmpty() {
return ElementUtil.isEmpty(getSystem(), getCode(), getDisplay());
}
@Override
public IBaseCoding setCode(String theTerm) {
theCoding.setCode(theTerm);
return this;
}
@Override
public IBaseCoding setDisplay(String theLabel) {
theCoding.setDisplay(theLabel);
return this;
}
@Override
public IBaseCoding setSystem(String theScheme) {
theCoding.setSystem(theScheme);
return this;
}
};
}
@Override
public List<String> getFormatCommentsPost() {
return Collections.emptyList();
}
@Override
public List<String> getFormatCommentsPre() {
return Collections.emptyList();
}
@Override
public Date getLastUpdated() {
InstantDt lu = ResourceMetadataKeyEnum.UPDATED.get(BaseResource.this);
if (lu != null) {
return lu.getValue();
}
return null;
}
@Override
public List<? extends IPrimitiveType<String>> getProfile() {
ArrayList<IPrimitiveType<String>> retVal = new ArrayList<IPrimitiveType<String>>();
List<IdDt> profilesList = ResourceMetadataKeyEnum.PROFILES.get(BaseResource.this);
if (profilesList == null) {
return Collections.emptyList();
}
for (IdDt next : profilesList) {
retVal.add(next);
}
return Collections.unmodifiableList(retVal);
}
@Override
public List<? extends IBaseCoding> getSecurity() {
ArrayList<IBaseCoding> retVal = new ArrayList<IBaseCoding>();
List<BaseCodingDt> labelsList = ResourceMetadataKeyEnum.SECURITY_LABELS.get(BaseResource.this);
if (labelsList == null) {
return Collections.emptyList();
}
for (BaseCodingDt next : labelsList) {
retVal.add(asBaseCoding(next));
}
return Collections.unmodifiableList(retVal);
}
@Override
public IBaseCoding getSecurity(String theSystem, String theCode) {
for (BaseCodingDt next : ResourceMetadataKeyEnum.SECURITY_LABELS.get(BaseResource.this)) {
if (theSystem.equals(next.getSystemElement().getValue()) && theCode.equals(next.getCodeElement().getValue())) {
return asBaseCoding(next);
}
}
return null;
}
@Override
public List<? extends IBaseCoding> getTag() {
ArrayList<IBaseCoding> retVal = new ArrayList<IBaseCoding>();
for (Tag next : ResourceMetadataKeyEnum.TAG_LIST.get(BaseResource.this)) {
retVal.add(next);
}
return Collections.unmodifiableList(retVal);
}
@Override
public IBaseCoding getTag(String theSystem, final String theCode) {
for (final Tag next : ResourceMetadataKeyEnum.TAG_LIST.get(BaseResource.this)) {
if (next.getScheme().equals(theSystem) && next.getTerm().equals(theCode)) {
return next;
}
}
return null;
}
@Override
public String getVersionId() {
return getId().getVersionIdPart();
}
@Override
public boolean hasFormatComment() {
return false;
}
@Override
public boolean isEmpty() {
return getResourceMetadata().isEmpty();
}
@Override
public IBaseMetaType setLastUpdated(Date theHeaderDateValue) {
ResourceMetadataKeyEnum.UPDATED.put(BaseResource.this, new InstantDt(theHeaderDateValue));
return this;
}
@Override
public IBaseMetaType setVersionId(String theVersionId) {
setId(getId().withVersion(theVersionId));
return this;
}
};
}
@Override
public ResourceMetadataMap getResourceMetadata() {
if (myResourceMetadata == null) {
@ -120,10 +346,23 @@ public abstract class BaseResource extends BaseElement implements IResource {
return myText;
}
/**
* Intended to be called by extending classes {@link #isEmpty()} implementations, returns <code>true</code> if all
* content in this superclass instance is empty per the semantics of {@link #isEmpty()}.
*/
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
}
public void setContained(ContainedDt theContained) {
myContained = theContained;
}
public void setId(IdDt theId) {
myId = theId;
}
public BaseResource setId(IIdType theId) {
if (theId instanceof IdDt) {
myId = (IdDt) theId;
@ -133,10 +372,6 @@ public abstract class BaseResource extends BaseElement implements IResource {
return this;
}
public void setId(IdDt theId) {
myId = theId;
}
public BaseResource setId(String theId) {
if (theId == null) {
myId = null;
@ -168,13 +403,4 @@ public abstract class BaseResource extends BaseElement implements IResource {
return b.toString();
}
/**
* Intended to be called by extending classes {@link #isEmpty()} implementations, returns <code>true</code> if all
* content in this superclass instance is empty per the semantics of {@link #isEmpty()}.
*/
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
}
}

View File

@ -19,8 +19,7 @@ package ca.uhn.fhir.rest.server;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList;
import java.util.Date;
@ -29,7 +28,6 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -43,6 +41,8 @@ import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt;
@ -53,7 +53,6 @@ import ca.uhn.fhir.util.ResourceReferenceInfo;
public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu1BundleFactory.class);
private Bundle myBundle;
private FhirContext myContext;
@ -186,7 +185,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
}
@Override
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
int numToReturn;
String searchId = null;
@ -224,16 +223,11 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)");
}
}
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
addProfileIfNeeded(theServer, theServerBase, next);
}
}
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
for (IBaseResource nextRes : resourceList) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
}
}
}
addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished());
@ -256,6 +250,23 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
}
}
private void addProfileIfNeeded(IRestfulServer<?> theServer, String theServerBase, IBaseResource nextRes) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardType()) {
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get((IResource) nextRes);
if (tl == null) {
tl = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put((IResource) nextRes, tl);
}
RuntimeResourceDefinition nextDef = myContext.getResourceDefinition(nextRes);
String profile = nextDef.getResourceProfile(theServerBase);
if (isNotBlank(profile)) {
tl.add(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
}
}
}
@Override
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults,
BundleTypeEnum theBundleType) {

View File

@ -214,9 +214,6 @@ public class CustomTypeTest {
private static boolean ourReturnExtended = false;
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class DummyPatientResourceProvider implements IResourceProvider {
@Search

View File

@ -7,6 +7,7 @@ import java.util.List;
import javax.servlet.ServletException;
import org.hamcrest.core.StringContains;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.Test;
@ -299,6 +300,12 @@ public class ServerInvalidDefinitionTest {
public List<String> getFormatCommentsPost() {
return null;
}
@Override
public IBaseMetaType getMeta() {
// TODO Auto-generated method stub
return null;
}
}.getClass();
}

View File

@ -1,5 +1,10 @@
package ca.uhn.fhir.model.dstu2.resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/*
* #%L
* HAPI FHIR Structures - DSTU2 (FHIR v1.0.0)
@ -23,35 +28,31 @@ package ca.uhn.fhir.model.dstu2.resource;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.api.BaseElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.ContainedDt;
import ca.uhn.fhir.model.dstu2.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.util.ElementUtil;
public abstract class BaseResource extends BaseElement implements IResource {
/**
* Search parameter constant for <b>_language</b>
*/
@SearchParamDefinition(name="_language", path="", description="The language of the resource", type="string" )
public static final String SP_RES_LANGUAGE = "_language";
/**
* Search parameter constant for <b>_id</b>
*/
@SearchParamDefinition(name="_id", path="", description="The ID of the resource", type="string" )
public static final String SP_RES_ID = "_id";
/**
* <b>Fluent Client</b> search parameter constant for <b>_id</b>
* <p>
@ -62,10 +63,23 @@ public abstract class BaseResource extends BaseElement implements IResource {
*/
public static final StringClientParam RES_ID = new StringClientParam(BaseResource.SP_RES_ID);
/**
* Search parameter constant for <b>_id</b>
*/
@SearchParamDefinition(name="_id", path="", description="The ID of the resource", type="string" )
public static final String SP_RES_ID = "_id";
/**
* Search parameter constant for <b>_language</b>
*/
@SearchParamDefinition(name="_language", path="", description="The language of the resource", type="string" )
public static final String SP_RES_LANGUAGE = "_language";
@Child(name = "contained", order = 2, min = 0, max = 1)
private ContainedDt myContained;
private IdDt myId;
@Child(name = "language", order = 0, min = 0, max = Child.MAX_UNLIMITED)
@ -104,6 +118,157 @@ public abstract class BaseResource extends BaseElement implements IResource {
return myLanguage;
}
@Override
public IBaseMetaType getMeta() {
return new IBaseMetaType() {
private static final long serialVersionUID = 1L;
@Override
public IBaseMetaType addProfile(String theProfile) {
ArrayList<IdDt> newTagList = new ArrayList<IdDt>();
List<IdDt> existingTagList = ResourceMetadataKeyEnum.PROFILES.get(BaseResource.this);
if (existingTagList != null) {
newTagList.addAll(existingTagList);
}
ResourceMetadataKeyEnum.PROFILES.put(BaseResource.this, newTagList);
IdDt tag = new IdDt(theProfile);
newTagList.add(tag);
return this;
}
@Override
public IBaseCoding addSecurity() {
List<BaseCodingDt> tagList = ResourceMetadataKeyEnum.SECURITY_LABELS.get(BaseResource.this);
if (tagList == null) {
tagList = new ArrayList<BaseCodingDt>();
ResourceMetadataKeyEnum.SECURITY_LABELS.put(BaseResource.this, tagList);
}
CodingDt tag = new CodingDt();
tagList.add(tag);
return tag;
}
@Override
public IBaseCoding addTag() {
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(BaseResource.this);
if (tagList == null) {
tagList = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put(BaseResource.this, tagList);
}
Tag tag = new Tag();
tagList.add(tag);
return tag;
}
@Override
public List<String> getFormatCommentsPost() {
return Collections.emptyList();
}
@Override
public List<String> getFormatCommentsPre() {
return Collections.emptyList();
}
@Override
public Date getLastUpdated() {
InstantDt lu = ResourceMetadataKeyEnum.UPDATED.get(BaseResource.this);
if (lu != null) {
return lu.getValue();
}
return null;
}
@Override
public List<? extends IPrimitiveType<String>> getProfile() {
ArrayList<IPrimitiveType<String>> retVal = new ArrayList<IPrimitiveType<String>>();
List<IdDt> profilesList = ResourceMetadataKeyEnum.PROFILES.get(BaseResource.this);
if (profilesList == null) {
return Collections.emptyList();
}
for (IdDt next : profilesList) {
retVal.add(next);
}
return Collections.unmodifiableList(retVal);
}
@Override
public List<? extends IBaseCoding> getSecurity() {
ArrayList<CodingDt> retVal = new ArrayList<CodingDt>();
List<BaseCodingDt> labelsList = ResourceMetadataKeyEnum.SECURITY_LABELS.get(BaseResource.this);
if (labelsList == null) {
return Collections.emptyList();
}
for (BaseCodingDt next : labelsList) {
retVal.add(new CodingDt(next.getSystemElement().getValue(), next.getCodeElement().getValue()).setDisplay(next.getDisplayElement().getValue()));
}
return Collections.unmodifiableList(retVal);
}
@Override
public IBaseCoding getSecurity(String theSystem, String theCode) {
for (IBaseCoding next : getSecurity()) {
if (theSystem.equals(next.getSystem()) && theCode.equals(next.getCode())) {
return next;
}
}
return null;
}
@Override
public List<? extends IBaseCoding> getTag() {
ArrayList<IBaseCoding> retVal = new ArrayList<IBaseCoding>();
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(BaseResource.this);
if (tagList == null) {
return Collections.emptyList();
}
for (Tag next : tagList) {
retVal.add(next);
}
return Collections.unmodifiableList(retVal);
}
@Override
public IBaseCoding getTag(String theSystem, String theCode) {
for (IBaseCoding next : getTag()) {
if (next.getSystem().equals(theSystem) && next.getCode().equals(theCode)) {
return next;
}
}
return null;
}
@Override
public String getVersionId() {
return getId().getVersionIdPart();
}
@Override
public boolean hasFormatComment() {
return false;
}
@Override
public boolean isEmpty() {
return getResourceMetadata().isEmpty();
}
@Override
public IBaseMetaType setLastUpdated(Date theHeaderDateValue) {
ResourceMetadataKeyEnum.UPDATED.put(BaseResource.this, new InstantDt(theHeaderDateValue));
return this;
}
@Override
public IBaseMetaType setVersionId(String theVersionId) {
setId(getId().withVersion(theVersionId));
return this;
}
};
}
@Override
public ResourceMetadataMap getResourceMetadata() {
if (myResourceMetadata == null) {
@ -120,6 +285,15 @@ public abstract class BaseResource extends BaseElement implements IResource {
return myText;
}
/**
* Intended to be called by extending classes {@link #isEmpty()} implementations, returns <code>true</code> if all
* content in this superclass instance is empty per the semantics of {@link #isEmpty()}.
*/
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
}
public void setContained(ContainedDt theContained) {
myContained = theContained;
}
@ -168,13 +342,4 @@ public abstract class BaseResource extends BaseElement implements IResource {
return b.toString();
}
/**
* Intended to be called by extending classes {@link #isEmpty()} implementations, returns <code>true</code> if all
* content in this superclass instance is empty per the semantics of {@link #isEmpty()}.
*/
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
}
}

View File

@ -35,7 +35,6 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
@ -51,7 +50,6 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.BundleInclusionRule;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -65,7 +63,6 @@ import ca.uhn.fhir.util.ResourceReferenceInfo;
public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu2BundleFactory.class);
private Bundle myBundle;
private FhirContext myContext;
private String myBase;
@ -302,7 +299,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
}
@Override
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
myBase = theServerBase;
@ -352,15 +349,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
}
}
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
for (IBaseResource nextRes : resourceList) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
}
}
}
addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished());

View File

@ -0,0 +1,510 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.util.ElementUtil;
public class CustomTypeDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeDstu2Test.class);
private static final FhirContext ourCtx = FhirContext.forDstu2();
@Test
public void testAccessEmptyMetaLists() {
Patient p = new Patient();
assertThat(p.getMeta().getProfile(), empty());
assertThat(p.getMeta().getFormatCommentsPost(), empty());
assertThat(p.getMeta().getFormatCommentsPre(), empty());
assertThat(p.getMeta().getLastUpdated(), nullValue());
assertThat(p.getMeta().getSecurity(), empty());
assertThat(p.getMeta().getSecurity("foo", "bar"), nullValue());
assertThat(p.getMeta().getTag(), empty());
assertThat(p.getMeta().getTag("foo", "bar"), nullValue());
assertThat(p.getMeta().getVersionId(), nullValue());
}
@Test
public void testEncodeCompleteMetaLists() {
Patient p = new Patient();
p.getMeta().addProfile("http://foo/profile1");
p.getMeta().addProfile("http://foo/profile2");
p.getMeta().addSecurity().setSystem("SEC_S1").setCode("SEC_C1").setDisplay("SED_D1");
p.getMeta().addSecurity().setSystem("SEC_S2").setCode("SEC_C2").setDisplay("SED_D2");
p.getMeta().addTag().setSystem("TAG_S1").setCode("TAG_C1").setDisplay("TAG_D1");
p.getMeta().addTag().setSystem("TAG_S2").setCode("TAG_C2").setDisplay("TAG_D2");
String out = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(out);
//@formatter:off
assertThat(out, stringContainsInOrder(
"<meta>",
"<profile value=\"http://foo/profile1\"/>",
"<profile value=\"http://foo/profile2\"/>",
"<security>",
"<system value=\"SEC_S1\"/>",
"<code value=\"SEC_C1\"/>",
"<display value=\"SED_D1\"/>",
"</security>",
"<security>",
"<system value=\"SEC_S2\"/>",
"<code value=\"SEC_C2\"/>",
"<display value=\"SED_D2\"/>",
"</security>",
"<tag>",
"<system value=\"TAG_S1\"/>",
"<display value=\"TAG_D1\"/>",
"</tag>",
"<tag>",
"<system value=\"TAG_S2\"/>",
"<display value=\"TAG_D2\"/>",
"</tag>",
"</meta>"));
//@formatter:on
}
@Before
public void before() {
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
}
@Test
public void testEncodeWithCustomType() {
MyCustomPatient patient = new MyCustomPatient();
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().addFamily("Rossi").addGiven("Mario");
patient.setInsulinLevel(new QuantityDt());
patient.setGlucoseLevel(new QuantityDt());
patient.setHbA1c(new QuantityDt());
patient.setBloodPressure(new QuantityDt());
patient.setCholesterol(new QuantityDt());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>"));
//@formatter:on
//@formatter:off
assertThat(messageString, not(stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"", "/>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>")));
//@formatter:on
}
@Test
public void testEncodeWithCustomTypeAndAutoInsertedProfile() {
MyCustomPatient patient = new MyCustomPatient();
patient.getMeta().addProfile("http://example.com/foo");
patient.getMeta().addProfile("http://example.com/bar");
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().addFamily("Rossi").addGiven("Mario");
patient.setInsulinLevel(new QuantityDt());
patient.setGlucoseLevel(new QuantityDt());
patient.setHbA1c(new QuantityDt());
patient.setBloodPressure(new QuantityDt());
patient.setCholesterol(new QuantityDt());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"/>",
"<profile value=\"http://example.com/bar\"/>",
"</meta>"));
//@formatter:on
//@formatter:off
assertThat(messageString, not(stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"", "/>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>")));
//@formatter:on
}
@Test
public void testEncodeNormalType() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().addFamily("Rossi").addGiven("Mario");
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
assertThat(messageString, not(containsString("<profile")));
}
@Test
public void parseResourceWithNoDirective() {
String input = createResource(true);
FhirContext ctx = FhirContext.forDstu2();
Patient parsed = (Patient) ctx.newXmlParser().parseResource(input);
assertEquals(1, parsed.getMeta().getProfile().size());
assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue());
List<ExtensionDt> exts = parsed.getUndeclaredExtensionsByUrl("http://example.com/Weight");
assertEquals(1, exts.size());
assertEquals("185 cm", ((StringDt)exts.get(0).getValueAsPrimitive()).getValue());
}
@Test
public void parseResourceWithDirective() {
String input = createResource(true);
FhirContext ctx = FhirContext.forDstu2();
ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class);
MyCustomPatient parsed = (MyCustomPatient) ctx.newXmlParser().parseResource(input);
assertEquals(1, parsed.getMeta().getProfile().size());
assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue());
List<ExtensionDt> exts = parsed.getUndeclaredExtensionsByUrl("http://example.com/Weight");
assertEquals(0, exts.size());
assertEquals("185 cm", parsed.getWeight().getValue());
}
@Test
public void parseBundleWithResourceDirective() {
String input = createBundle(createResource(false), createResource(true));
FhirContext ctx = FhirContext.forDstu2();
ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class);
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, input);
Patient res0 = (Patient) bundle.getEntry().get(0).getResource();
assertEquals(0, res0.getMeta().getProfile().size());
List<ExtensionDt> exts = res0.getUndeclaredExtensionsByUrl("http://example.com/Weight");
assertEquals(1, exts.size());
assertEquals("185 cm", ((StringDt)exts.get(0).getValueAsPrimitive()).getValue());
MyCustomPatient res1 = (MyCustomPatient) bundle.getEntry().get(1).getResource();
assertEquals(1, res1.getMeta().getProfile().size());
assertEquals("http://example.com/foo", res1.getMeta().getProfile().get(0).getValue());
exts = res1.getUndeclaredExtensionsByUrl("http://example.com/Weight");
assertEquals(0, exts.size());
assertEquals("185 cm", res1.getWeight().getValue());
}
private String createBundle(String... theResources) {
StringBuilder b = new StringBuilder();
b.append("<Bundle xmlns=\"http://hl7.org/fhir\">\n");
for (String next : theResources) {
b.append(" <entry>\n");
b.append(" <resource>\n");
b.append(next);
b.append(" </resource>\n");
b.append(" </entry>\n");
}
b.append("</Bundle>");
return b.toString();
}
private String createResource(boolean theWithProfile) {
StringBuilder b = new StringBuilder();
b.append("<Patient xmlns=\"http://hl7.org/fhir\">\n");
if (theWithProfile) {
b.append(" <meta>\n");
b.append(" <profile value=\"http://example.com/foo\"/>\n");
b.append(" </meta>\n");
}
b.append(" <extension url=\"http://example.com/BloodPressure\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"110\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmHg\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n");
b.append(" <valueDateTime value=\"2010-01-02\"/>\n");
b.append(" </modifierExtension>\n");
b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n");
b.append(" <valueDateTime value=\"2014-01-26T11:11:11\"/>\n");
b.append(" </modifierExtension>\n");
b.append(" <extension url=\"http://example.com/Cholesterol\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"2\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmol/l\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Glucose\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"95\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mg/dl\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/HbA1c\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"48\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmol/mol\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Insuline\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"125\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"pmol/l\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Weight\">\n");
b.append(" <valueString value=\"185 cm\"/>\n");
b.append(" </extension>\n");
b.append(" <identifier>\n");
b.append(" <system value=\"urn:system\"/>\n");
b.append(" <value value=\"1234\"/>\n");
b.append(" </identifier>\n");
b.append(" <name>\n");
b.append(" <family value=\"Rossi\"/>\n");
b.append(" <given value=\"Mario\"/>\n");
b.append(" </name>\n");
b.append("</Patient>");
String input =
b.toString();
return input;
}
@ResourceDef(name = "Patient", profile = "http://example.com/foo")
public static class MyCustomPatient extends Patient {
private static final long serialVersionUID = 1L;
@Child(name = "bloodPressure") // once every 3 month. The average target is 130/80 mmHg or less
@Extension(url = "http://example.com/BloodPressure", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's blood pressure")
private QuantityDt myBloodPressure;
// Dates of periodic tests
@Child(name = "CheckDates", max = Child.MAX_UNLIMITED)
@Extension(url = "http://example.com/diabetes2", definedLocally = false, isModifier = true)
@Description(shortDefinition = "Dates of periodic tests")
private List<DateTimeDt> myCheckDates;
@Child(name = "cholesterol") // once a year. The target is triglycerides =< 2 mmol/l e cholesterol =< 4 mmol/l
@Extension(url = "http://example.com/Cholesterol", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's cholesterol")
private QuantityDt myCholesterol;
@Child(name = "glucoseLevel") // fingerprick test
@Extension(url = "http://example.com/Glucose", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's blood glucose")
private QuantityDt myGlucoseLevel;
// Periodic Tests
@Child(name = "hbA1c") // once every 6 month. The average target is 53 mmol/mol (or 7%) or less.
@Extension(url = "http://example.com/HbA1c", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's glucose")
private QuantityDt myHbA1c;
@Child(name = "Height")
@Extension(url = "http://example.com/Height", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The patient's height in cm")
private StringDt myHeight;
@Child(name = "insulinLevel") // Normal range is [43,208] pmol/l
@Extension(url = "http://example.com/Insuline", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's insulin")
private QuantityDt myInsulinLevel;
// Other parameters
@Child(name = "weight")
@Extension(url = "http://example.com/Weight", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The patient's weight in Kg")
private StringDt myWeight;
public QuantityDt Cholesterol() {
if (myCholesterol == null) {
myCholesterol = new QuantityDt();
}
myCholesterol.getValue();
myCholesterol.getSystem();
myCholesterol.getCode();
return myCholesterol;
}
public QuantityDt getBloodPressure() {
if (myBloodPressure == null) {
myBloodPressure = new QuantityDt();
}
myBloodPressure.getValue();
myBloodPressure.getSystem();
myBloodPressure.getCode();
return myBloodPressure;
}
public List<DateTimeDt> getCheckDates() {
if (myCheckDates == null) {
myCheckDates = new ArrayList<DateTimeDt>();
}
return myCheckDates;
}
public QuantityDt getGlucoseLevel() {
if (myGlucoseLevel == null) {
myGlucoseLevel = new QuantityDt();
}
myGlucoseLevel.getValue();
myGlucoseLevel.getSystem();
myGlucoseLevel.getCode();
return myGlucoseLevel;
}
public QuantityDt getHbA1c() {
if (myHbA1c == null) {
myHbA1c = new QuantityDt();
}
myHbA1c.getValue();
myHbA1c.getSystem();
myHbA1c.getCode();
return myHbA1c;
}
public StringDt getHeight() {
if (myHeight == null) {
myHeight = new StringDt();
}
return myHeight;
}
public QuantityDt getInsulinLevel() {
if (myInsulinLevel == null) {
myInsulinLevel = new QuantityDt();
}
myInsulinLevel.getValue();
myInsulinLevel.getSystem();
myInsulinLevel.getCode();
return myInsulinLevel;
}
public StringDt getWeight() {
if (myWeight == null) {
myWeight = new StringDt();
}
return myWeight;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myInsulinLevel, myGlucoseLevel, myHbA1c, myBloodPressure, myCholesterol, myWeight, myHeight, myCheckDates);
}
public void setBloodPressure(QuantityDt bloodPressure) {
myBloodPressure = bloodPressure;
myBloodPressure.setValue(110);
myBloodPressure.setSystem("http://unitsofmeasure.org");
myBloodPressure.setCode("mmHg");
}
public void setCheckDates(List<DateTimeDt> theCheckDates) {
myCheckDates = theCheckDates;
myCheckDates.add(new DateTimeDt("2010-01-02"));
}
public void setCholesterol(QuantityDt cholesterol) {
myCholesterol = cholesterol;
myCholesterol.setValue(2);
myCholesterol.setSystem("http://unitsofmeasure.org");
myCholesterol.setCode("mmol/l");
}
public void setGlucoseLevel(QuantityDt glucoseLevel) {
myGlucoseLevel = glucoseLevel;
myGlucoseLevel.setValue(95);
myGlucoseLevel.setSystem("http://unitsofmeasure.org");
myGlucoseLevel.setCode("mg/dl");
}
public void setHbA1c(QuantityDt hba1c) {
myHbA1c = hba1c;
myHbA1c.setValue(48);
myHbA1c.setSystem("http://unitsofmeasure.org");
myHbA1c.setCode("mmol/mol");
}
public void setHeight(StringDt height) {
myHeight = height;
}
// Setter/Getter methods
public void setInsulinLevel(QuantityDt insulinLevel) {
myInsulinLevel = insulinLevel;
myInsulinLevel.setValue(125);
myInsulinLevel.setSystem("http://unitsofmeasure.org");
myInsulinLevel.setCode("pmol/l");
}
public void setWeight(StringDt weight) {
myWeight = weight;
}
}
}

View File

@ -1,228 +0,0 @@
package ca.uhn.fhir.parser;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.util.ElementUtil;
public class CustomTypeTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeTest.class);
@Test
public void testEncode() {
FhirContext ctx = FhirContext.forDstu2();
MyCustomPatient patient = new MyCustomPatient();
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().addFamily("Rossi").addGiven("Mario");
patient.setInsulinLevel(new QuantityDt());
patient.setGlucoseLevel(new QuantityDt());
patient.setHbA1c(new QuantityDt());
patient.setBloodPressure(new QuantityDt());
patient.setCholesterol(new QuantityDt());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
IParser p = ctx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
}
@ResourceDef(name = "Patient")
public static class MyCustomPatient extends Patient {
private static final long serialVersionUID = 1L;
@Child(name = "bloodPressure") // once every 3 month. The average target is 130/80 mmHg or less
@Extension(url = "http://example.com/BloodPressure", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's blood pressure")
private QuantityDt myBloodPressure;
// Dates of periodic tests
@Child(name = "CheckDates", max = Child.MAX_UNLIMITED)
@Extension(url = "http://example.com/diabetes2", definedLocally = false, isModifier = true)
@Description(shortDefinition = "Dates of periodic tests")
private List<DateTimeDt> myCheckDates;
@Child(name = "cholesterol") // once a year. The target is triglycerides =< 2 mmol/l e cholesterol =< 4 mmol/l
@Extension(url = "http://example.com/Cholesterol", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's cholesterol")
private QuantityDt myCholesterol;
@Child(name = "glucoseLevel") // fingerprick test
@Extension(url = "http://example.com/Glucose", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's blood glucose")
private QuantityDt myGlucoseLevel;
// Periodic Tests
@Child(name = "hbA1c") // once every 6 month. The average target is 53 mmol/mol (or 7%) or less.
@Extension(url = "http://example.com/HbA1c", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's glucose")
private QuantityDt myHbA1c;
@Child(name = "Height")
@Extension(url = "http://example.com/Height", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The patient's height in cm")
private StringDt myHeight;
@Child(name = "insulinLevel") // Normal range is [43,208] pmol/l
@Extension(url = "http://example.com/Insuline", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The value of the patient's insulin")
private QuantityDt myInsulinLevel;
// Other parameters
@Child(name = "weight")
@Extension(url = "http://example.com/Weight", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The patient's weight in Kg")
private StringDt myWeight;
public QuantityDt Cholesterol() {
if (myCholesterol == null) {
myCholesterol = new QuantityDt();
}
myCholesterol.getValue();
myCholesterol.getSystem();
myCholesterol.getCode();
return myCholesterol;
}
public QuantityDt getBloodPressure() {
if (myBloodPressure == null) {
myBloodPressure = new QuantityDt();
}
myBloodPressure.getValue();
myBloodPressure.getSystem();
myBloodPressure.getCode();
return myBloodPressure;
}
public List<DateTimeDt> getCheckDates() {
if (myCheckDates == null) {
myCheckDates = new ArrayList<DateTimeDt>();
}
return myCheckDates;
}
public QuantityDt getGlucoseLevel() {
if (myGlucoseLevel == null) {
myGlucoseLevel = new QuantityDt();
}
myGlucoseLevel.getValue();
myGlucoseLevel.getSystem();
myGlucoseLevel.getCode();
return myGlucoseLevel;
}
public QuantityDt getHbA1c() {
if (myHbA1c == null) {
myHbA1c = new QuantityDt();
}
myHbA1c.getValue();
myHbA1c.getSystem();
myHbA1c.getCode();
return myHbA1c;
}
public StringDt getHeight() {
if (myHeight == null) {
myHeight = new StringDt();
}
return myHeight;
}
public QuantityDt getInsulinLevel() {
if (myInsulinLevel == null) {
myInsulinLevel = new QuantityDt();
}
myInsulinLevel.getValue();
myInsulinLevel.getSystem();
myInsulinLevel.getCode();
return myInsulinLevel;
}
public StringDt getWeight() {
if (myWeight == null) {
myWeight = new StringDt();
}
return myWeight;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myInsulinLevel, myGlucoseLevel, myHbA1c, myBloodPressure, myCholesterol, myWeight, myHeight, myCheckDates);
}
public void setBloodPressure(QuantityDt bloodPressure) {
myBloodPressure = bloodPressure;
myBloodPressure.setValue(110);
myBloodPressure.setSystem("http://unitsofmeasure.org");
myBloodPressure.setCode("mmHg");
}
public void setCheckDates(List<DateTimeDt> theCheckDates) {
myCheckDates = theCheckDates;
myCheckDates.add(new DateTimeDt("2010-01-02"));
}
public void setCholesterol(QuantityDt cholesterol) {
myCholesterol = cholesterol;
myCholesterol.setValue(2);
myCholesterol.setSystem("http://unitsofmeasure.org");
myCholesterol.setCode("mmol/l");
}
public void setGlucoseLevel(QuantityDt glucoseLevel) {
myGlucoseLevel = glucoseLevel;
myGlucoseLevel.setValue(95);
myGlucoseLevel.setSystem("http://unitsofmeasure.org");
myGlucoseLevel.setCode("mg/dl");
}
public void setHbA1c(QuantityDt hba1c) {
myHbA1c = hba1c;
myHbA1c.setValue(48);
myHbA1c.setSystem("http://unitsofmeasure.org");
myHbA1c.setCode("mmol/mol");
}
public void setHeight(StringDt height) {
myHeight = height;
}
// Setter/Getter methods
public void setInsulinLevel(QuantityDt insulinLevel) {
myInsulinLevel = insulinLevel;
myInsulinLevel.setValue(125);
myInsulinLevel.setSystem("http://unitsofmeasure.org");
myInsulinLevel.setCode("pmol/l");
}
public void setWeight(StringDt weight) {
myWeight = weight;
}
}
}

View File

@ -94,6 +94,24 @@ public class JsonParserDstu2Test {
assertEquals(true, obs.getReadOnly().getValue().booleanValue());
}
@Test
public void testParseBundleWithCustomObservationType() {
ReportObservation obs = new ReportObservation();
obs.setReadOnly(true);
IParser p = ourCtx.newJsonParser();
// p.set
p.setParserErrorHandler(mock(IParserErrorHandler.class, new ThrowsException(new IllegalStateException())));
String encoded = p.encodeResourceToString(obs);
ourLog.info(encoded);
obs = p.parseResource(ReportObservation.class, encoded);
assertEquals(true, obs.getReadOnly().getValue().booleanValue());
}
@Test
public void testEncodeAndParseExtensions() throws Exception {

View File

@ -9,7 +9,7 @@ import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name = "Observation", id = "reportobservation")
@ResourceDef(name = "Observation", id = "http://example.com/reportobservation")
public class ReportObservation extends Observation {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,4 @@
<Bundle
xmlns="http://hl7.org/fhir">
<Bundle xmlns="http://hl7.org/fhir">
<id value="487d3669-0e1c-4867-b124-400d1849548d"></id>
<type value="searchset"></type>
<base value="http://localhost:19080/fhir/dstu1"></base>
@ -17,8 +16,7 @@
<url value="Observation"></url>
</link>
<resource>
<Observation
xmlns="http://hl7.org/fhir">
<Observation xmlns="http://hl7.org/fhir">
<id value="0d87f02c-da2c-4551-9ead-2956f0165a4f"></id>
<extension url="http://orionhealth.com/fhir/extensions#created-by"></extension>
<extension url="http://orionhealth.com/fhir/extensions#last-modified-by"></extension>
@ -46,8 +44,7 @@
<url value="Observation"></url>
</link>
<resource>
<Observation
xmlns="http://hl7.org/fhir">
<Observation xmlns="http://hl7.org/fhir">
<id value="c54ac0cc-a99f-40aa-9541-c5aa853a2e88"></id>
<extension url="http://orionhealth.com/fhir/extensions#created-by"></extension>
<extension url="http://orionhealth.com/fhir/extensions#last-modified-by"></extension>

View File

@ -31,27 +31,24 @@ import java.util.UUID;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode;
import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.BundleInclusionRule;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -65,7 +62,6 @@ import ca.uhn.fhir.util.ResourceReferenceInfo;
public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu3BundleFactory.class);
private Bundle myBundle;
private FhirContext myContext;
private String myBase;
@ -308,7 +304,7 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
}
@Override
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
myBase = theServerBase;
@ -358,15 +354,6 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
}
}
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
for (IBaseResource nextRes : resourceList) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
}
}
}
addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished());

View File

@ -9,12 +9,24 @@ import org.junit.Test;
import ca.uhn.fhir.context.BaseRuntimeChildDatatypeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
public class FhirContextDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirContextDstu3Test.class);
private FhirContext ourCtx = FhirContext.forDstu3();
@Test
public void testCustomTypeDoesntBecomeDefault() {
FhirContext ctx = FhirContext.forDstu3();
MyPatientWithExtensions pt = new MyPatientWithExtensions();
pt.addName().addGiven("FOO");
ctx.newXmlParser().encodeResourceToString(pt);
assertEquals(Patient.class, ctx.getResourceDefinition("Patient").getImplementingClass());
}
@Test
public void testQueryBoundCode() {
RuntimeResourceDefinition patientType = ourCtx.getResourceDefinition(Patient.class);

View File

@ -1,11 +1,20 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Quantity;
import org.hl7.fhir.dstu3.model.StringType;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
@ -13,17 +22,215 @@ import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.util.ElementUtil;
public class CustomTypeDstu3Test {
private static final FhirContext ourCtx = FhirContext.forDstu3();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeDstu3Test.class);
@Before
public void before() {
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
}
private String createBundle(String... theResources) {
StringBuilder b = new StringBuilder();
b.append("<Bundle xmlns=\"http://hl7.org/fhir\">\n");
for (String next : theResources) {
b.append(" <entry>\n");
b.append(" <resource>\n");
b.append(next);
b.append(" </resource>\n");
b.append(" </entry>\n");
}
b.append("</Bundle>");
return b.toString();
}
private String createResource(boolean theWithProfile) {
StringBuilder b = new StringBuilder();
b.append("<Patient xmlns=\"http://hl7.org/fhir\">\n");
if (theWithProfile) {
b.append(" <meta>\n");
b.append(" <profile value=\"http://example.com/foo\"/>\n");
b.append(" </meta>\n");
}
b.append(" <extension url=\"http://example.com/BloodPressure\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"110\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmHg\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n");
b.append(" <valueDateTime value=\"2010-01-02\"/>\n");
b.append(" </modifierExtension>\n");
b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n");
b.append(" <valueDateTime value=\"2014-01-26T11:11:11\"/>\n");
b.append(" </modifierExtension>\n");
b.append(" <extension url=\"http://example.com/Cholesterol\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"2\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmol/l\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Glucose\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"95\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mg/dl\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/HbA1c\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"48\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"mmol/mol\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Insuline\">\n");
b.append(" <valueQuantity>\n");
b.append(" <value value=\"125\"/>\n");
b.append(" <system value=\"http://unitsofmeasure.org\"/>\n");
b.append(" <code value=\"pmol/l\"/>\n");
b.append(" </valueQuantity>\n");
b.append(" </extension>\n");
b.append(" <extension url=\"http://example.com/Weight\">\n");
b.append(" <valueString value=\"185 cm\"/>\n");
b.append(" </extension>\n");
b.append(" <identifier>\n");
b.append(" <system value=\"urn:system\"/>\n");
b.append(" <value value=\"1234\"/>\n");
b.append(" </identifier>\n");
b.append(" <name>\n");
b.append(" <family value=\"Rossi\"/>\n");
b.append(" <given value=\"Mario\"/>\n");
b.append(" </name>\n");
b.append("</Patient>");
String input =
b.toString();
return input;
}
@Test
public void testEncode() {
public void parseBundleWithResourceDirective() {
String input = createBundle(createResource(false), createResource(true));
FhirContext ctx = FhirContext.forDstu3();
ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class);
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, input);
Patient res0 = (Patient) bundle.getEntry().get(0).getResource();
assertEquals(0, res0.getMeta().getProfile().size());
List<org.hl7.fhir.dstu3.model.Extension> exts = res0.getExtensionsByUrl("http://example.com/Weight");
assertEquals(1, exts.size());
assertEquals("185 cm", ((StringType)exts.get(0).getValue()).getValue());
MyCustomPatient res1 = (MyCustomPatient) bundle.getEntry().get(1).getResource();
assertEquals(1, res1.getMeta().getProfile().size());
assertEquals("http://example.com/foo", res1.getMeta().getProfile().get(0).getValue());
exts = res1.getExtensionsByUrl("http://example.com/Weight");
assertEquals(0, exts.size());
assertEquals("185 cm", res1.getWeight().getValue());
}
@Test
public void parseResourceWithDirective() {
String input = createResource(true);
FhirContext ctx = FhirContext.forDstu3();
ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class);
MyCustomPatient parsed = (MyCustomPatient) ctx.newXmlParser().parseResource(input);
assertEquals(1, parsed.getMeta().getProfile().size());
assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue());
List<org.hl7.fhir.dstu3.model.Extension> exts = parsed.getExtensionsByUrl("http://example.com/Weight");
assertEquals(0, exts.size());
assertEquals("185 cm", parsed.getWeight().getValue());
}
@Test
public void parseResourceWithNoDirective() {
String input = createResource(true);
FhirContext ctx = FhirContext.forDstu3();
Patient parsed = (Patient) ctx.newXmlParser().parseResource(input);
assertEquals(1, parsed.getMeta().getProfile().size());
assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue());
List<org.hl7.fhir.dstu3.model.Extension> exts = parsed.getExtensionsByUrl("http://example.com/Weight");
assertEquals(1, exts.size());
assertEquals("185 cm", ((StringType)exts.get(0).getValue()).getValue());
}
@Test
public void testAccessEmptyMetaLists() {
Patient p = new Patient();
assertThat(p.getMeta().getProfile(), empty());
assertThat(p.getMeta().getFormatCommentsPost(), empty());
assertThat(p.getMeta().getFormatCommentsPre(), empty());
assertThat(p.getMeta().getLastUpdated(), nullValue());
assertThat(p.getMeta().getSecurity(), empty());
assertThat(p.getMeta().getSecurity("foo", "bar"), nullValue());
assertThat(p.getMeta().getTag(), empty());
assertThat(p.getMeta().getTag("foo", "bar"), nullValue());
assertThat(p.getMeta().getVersionId(), nullValue());
}
@Test
public void testEncodeCompleteMetaLists() {
Patient p = new Patient();
p.getMeta().addProfile("http://foo/profile1");
p.getMeta().addProfile("http://foo/profile2");
p.getMeta().addSecurity().setSystem("SEC_S1").setCode("SEC_C1").setDisplay("SED_D1");
p.getMeta().addSecurity().setSystem("SEC_S2").setCode("SEC_C2").setDisplay("SED_D2");
p.getMeta().addTag().setSystem("TAG_S1").setCode("TAG_C1").setDisplay("TAG_D1");
p.getMeta().addTag().setSystem("TAG_S2").setCode("TAG_C2").setDisplay("TAG_D2");
String out = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
ourLog.info(out);
//@formatter:off
assertThat(out, stringContainsInOrder(
"<meta>",
"<profile value=\"http://foo/profile1\"/>",
"<profile value=\"http://foo/profile2\"/>",
"<security>",
"<system value=\"SEC_S1\"/>",
"<code value=\"SEC_C1\"/>",
"<display value=\"SED_D1\"/>",
"</security>",
"<security>",
"<system value=\"SEC_S2\"/>",
"<code value=\"SEC_C2\"/>",
"<display value=\"SED_D2\"/>",
"</security>",
"<tag>",
"<system value=\"TAG_S1\"/>",
"<display value=\"TAG_D1\"/>",
"</tag>",
"<tag>",
"<system value=\"TAG_S2\"/>",
"<display value=\"TAG_D2\"/>",
"</tag>",
"</meta>"));
//@formatter:on
}
@Test
public void testEncodeWithCustomType() {
MyCustomPatient patient = new MyCustomPatient();
@ -36,17 +243,76 @@ public class CustomTypeDstu3Test {
patient.setCholesterol(new Quantity());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeType>());
patient.getCheckDates().add(new DateTimeType("2014-01-26T11:11:11"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
IParser p = ctx.newXmlParser().setPrettyPrint(true);
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>"));
//@formatter:on
//@formatter:off
assertThat(messageString, not(stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"", "/>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>")));
//@formatter:on
}
@ResourceDef(name = "Patient")
@Test
public void testEncodeWithCustomTypeAndAutoInsertedProfile() {
MyCustomPatient patient = new MyCustomPatient();
patient.getMeta().addProfile("http://example.com/foo");
patient.getMeta().addProfile("http://example.com/bar");
patient.addIdentifier().setSystem("urn:system").setValue("1234");
patient.addName().addFamily("Rossi").addGiven("Mario");
patient.setInsulinLevel(new Quantity());
patient.setGlucoseLevel(new Quantity());
patient.setHbA1c(new Quantity());
patient.setBloodPressure(new Quantity());
patient.setCholesterol(new Quantity());
patient.setWeight(new StringDt("80 kg"));
patient.setWeight(new StringDt("185 cm"));
patient.setCheckDates(new ArrayList<DateTimeDt>());
patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11"));
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(patient);
ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"/>",
"<profile value=\"http://example.com/bar\"/>",
"</meta>"));
//@formatter:on
//@formatter:off
assertThat(messageString, not(stringContainsInOrder(
"<meta>",
"<profile value=\"http://example.com/foo\"", "/>",
"<profile value=\"http://example.com/foo\"/>",
"</meta>")));
//@formatter:on
}
@ResourceDef(name = "Patient", profile = "http://example.com/foo")
public static class MyCustomPatient extends Patient {
private static final long serialVersionUID = 1L;
@ -60,7 +326,7 @@ public class CustomTypeDstu3Test {
@Child(name = "CheckDates", max = Child.MAX_UNLIMITED)
@Extension(url = "http://example.com/diabetes2", definedLocally = false, isModifier = true)
@Description(shortDefinition = "Dates of periodic tests")
private List<DateTimeType> myCheckDates;
private List<DateTimeDt> myCheckDates;
@Child(name = "cholesterol") // once a year. The target is triglycerides =< 2 mmol/l e cholesterol =< 4 mmol/l
@Extension(url = "http://example.com/Cholesterol", definedLocally = false, isModifier = false)
@ -116,9 +382,9 @@ public class CustomTypeDstu3Test {
return myBloodPressure;
}
public List<DateTimeType> getCheckDates() {
public List<DateTimeDt> getCheckDates() {
if (myCheckDates == null) {
myCheckDates = new ArrayList<DateTimeType>();
myCheckDates = new ArrayList<DateTimeDt>();
}
return myCheckDates;
}
@ -182,9 +448,9 @@ public class CustomTypeDstu3Test {
myBloodPressure.setCode("mmHg");
}
public void setCheckDates(List<DateTimeType> theCheckDates) {
public void setCheckDates(List<DateTimeDt> theCheckDates) {
myCheckDates = theCheckDates;
myCheckDates.add(new DateTimeType("2010-01-02"));
myCheckDates.add(new DateTimeDt("2010-01-02"));
}
public void setCholesterol(Quantity cholesterol) {

View File

@ -0,0 +1,116 @@
package ca.uhn.fhir.rest.client;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hl7.fhir.dstu3.model.Binary;
import org.hl7.fhir.dstu3.model.Conformance;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Patient;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.VersionUtil;
public class ClientWithCustomTypeDstu3Test {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ClientWithCustomTypeDstu3Test.class);
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
private byte[] extractBodyAsByteArray(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
byte[] body = IOUtils.toByteArray(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent());
return body;
}
private String extractBodyAsString(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent(), "UTF-8");
return body;
}
@Test
public void testReadCustomType() throws Exception {
IParser p = ourCtx.newXmlParser();
MyPatientWithExtensions response = new MyPatientWithExtensions();
response.addName().addFamily("FAMILY");
response.getStringExt().setValue("STRINGVAL");
response.getDateExt().setValueAsString("2011-01-02");
final String respString = p.encodeResourceToString(response);
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()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
MyPatientWithExtensions value = client
.read()
.resource(MyPatientWithExtensions.class)
.withId("123")
.execute();
//@formatter:on
HttpUriRequest request = capt.getAllValues().get(0);
assertEquals("http://example.com/fhir/Patient/123", request.getURI().toASCIIString());
assertEquals("GET", request.getMethod());
assertEquals(1, value.getName().size());
assertEquals("FAMILY", value.getName().get(0).getFamilyAsSingleString());
assertEquals("STRINGVAL", value.getStringExt().getValue());
assertEquals("2011-01-02", value.getDateExt().getValueAsString());
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu3();
}
}

View File

@ -0,0 +1,94 @@
package ca.uhn.fhir.rest.client;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.dstu3.model.BaseResource;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.HumanName;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.ResourceType;
import org.hl7.fhir.dstu3.model.StringType;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.ElementUtil;
@ResourceDef(name="Patient", profile="http://example.com/StructureDefinition/patient_with_extensions")
public class MyPatientWithExtensions extends DomainResource {
private static final long serialVersionUID = 1L;
@Extension(url = "http://example.com/ext/date", definedLocally = false, isModifier = true)
@Child(name = "modExt")
private DateType myDateExt;
@Extension(url = "http://example.com/ext/string", definedLocally = false, isModifier = false)
@Child(name = "extAtt")
private StringType myStringExt;
@Child(name = "name", type = {HumanName.class}, min=0, max=Child.MAX_UNLIMITED, modifier=false, summary=true)
@Description(shortDefinition="A name associated with the patient", formalDefinition="A name associated with the individual." )
private List<HumanName> myName;
public List<HumanName> getName() {
if (myName == null) {
myName = new ArrayList<HumanName>();
}
return myName;
}
public void setName(List<HumanName> theName) {
myName = theName;
}
public DateType getDateExt() {
if (myDateExt == null) {
myDateExt = new DateType();
}
return myDateExt;
}
public StringType getStringExt() {
if (myStringExt == null) {
myStringExt = new StringType();
}
return myStringExt;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && ElementUtil.isEmpty(myStringExt, myDateExt);
}
public void setDateExt(DateType theDateExt) {
myDateExt = theDateExt;
}
public void setStringExt(StringType theStringExt) {
myStringExt = theStringExt;
}
@Override
public DomainResource copy() {
return null;
}
@Override
public ResourceType getResourceType() {
return ResourceType.Patient;
}
public HumanName addName() {
HumanName retVal = new HumanName();
getName().add(retVal);
return retVal;
}
}

View File

@ -1,6 +1,8 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@ -10,120 +12,88 @@ import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
import ca.uhn.fhir.util.PortUtil;
public class CreateDstu3Test {
private static CloseableHttpClient ourClient;
private static String ourLastConditionalUrl;
private static FhirContext ourCtx = FhirContext.forDstu3();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateDstu3Test.class);
private static int ourPort;
private static Server ourServer;
private static IdType ourLastId;
private static IdType ourLastIdParam;
private static boolean ourLastRequestWasSearch;
private static FhirContext ourCtx = FhirContext.forDstu3();
@Before
public void before() {
ourLastId = null;
ourLastConditionalUrl = null;
ourLastIdParam = null;
ourLastRequestWasSearch = false;
}
@Test
public void testCreateWithIdInBody() throws Exception {
public void testRead() throws Exception {
Patient patient = new Patient();
patient.setId("2");
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
// httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_format=xml&_pretty=true");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", an ID element must not be supplied in the resource body on a create (POST) operation", oo.getIssue().get(0).getDiagnostics());
assertEquals(200, status.getStatusLine().getStatusCode());
//@formatter:off
assertThat(responseContent, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
"<id value=\"2\"/>",
"<modifierExtension url=\"http://example.com/ext/date\">",
"<valueDate value=\"2011-01-01\"/>",
"</modifierExtension>",
"<meta>",
"<profile value=\"http://example.com/StructureDefinition/patient_with_extensions\"/>",
"</meta>",
"</Patient>"));
//@formatter:on
}
@Test
public void testCreateWithIdInUrl() throws Exception {
public void testSearch() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2");
// httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics());
}
assertEquals(200, status.getStatusLine().getStatusCode());
@Test
public void testCreateWithIdInUrlForConditional() throws Exception {
//@formatter:off
assertThat(responseContent, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
"<id value=\"0\"/>",
"<modifierExtension url=\"http://example.com/ext/date\">",
"<valueDate value=\"2011-01-01\"/>",
"</modifierExtension>",
"<meta>",
"<profile value=\"http://example.com/StructureDefinition/patient_with_extensions\"/>",
"</meta>",
"</Patient>"));
//@formatter:on
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2");
httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics());
assertThat(responseContent, not(containsString("http://hl7.org/fhir/")));
}
@AfterClass
@ -140,6 +110,7 @@ public class CreateDstu3Test {
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
@ -160,18 +131,29 @@ public class CreateDstu3Test {
return Patient.class;
}
@Read()
public MyPatientWithExtensions read(@IdParam IdType theIdParam) {
MyPatientWithExtensions p0 = new MyPatientWithExtensions();
p0.setId(theIdParam);
p0.setDateExt(new DateType("2011-01-01"));
return p0;
}
@Search
public List<IResource> search(@OptionalParam(name = "foo") StringDt theString) {
ourLastRequestWasSearch = true;
return new ArrayList<IResource>();
}
public List<IBaseResource> search() {
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
@Create()
public MethodOutcome createPatient(@ResourceParam Patient thePatient, @ConditionalUrlParam String theConditional, @IdParam IdType theIdParam) {
ourLastConditionalUrl = theConditional;
ourLastId = thePatient.getIdElement();
ourLastIdParam = theIdParam;
return new MethodOutcome(new IdType("Patient/001/_history/002"));
MyPatientWithExtensions p0 = new MyPatientWithExtensions();
p0.setId(new IdType("Patient/0"));
p0.setDateExt(new DateType("2011-01-01"));
retVal.add(p0);
Patient p1 = new Patient();
p1.setId(new IdType("Patient/1"));
p1.addName().addFamily("The Family");
retVal.add(p1);
return retVal;
}
}

View File

@ -0,0 +1,179 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Patient;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.PortUtil;
public class CustomTypeServerDstu3 {
private static CloseableHttpClient ourClient;
private static String ourLastConditionalUrl;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeServerDstu3.class);
private static int ourPort;
private static Server ourServer;
private static IdType ourLastId;
private static IdType ourLastIdParam;
private static boolean ourLastRequestWasSearch;
private static FhirContext ourCtx = FhirContext.forDstu3();
@Before
public void before() {
ourLastId = null;
ourLastConditionalUrl = null;
ourLastIdParam = null;
ourLastRequestWasSearch = false;
}
@Test
public void testCreateWithIdInBody() throws Exception {
Patient patient = new Patient();
patient.setId("2");
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
// httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", an ID element must not be supplied in the resource body on a create (POST) operation", oo.getIssue().get(0).getDiagnostics());
}
@Test
public void testCreateWithIdInUrl() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2");
// httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics());
}
@Test
public void testCreateWithIdInUrlForConditional() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2");
httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics());
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
PatientProvider patientProvider = new PatientProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class PatientProvider implements IResourceProvider {
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Search
public List<IResource> search(@OptionalParam(name = "foo") StringDt theString) {
ourLastRequestWasSearch = true;
return new ArrayList<IResource>();
}
@Create()
public MethodOutcome createPatient(@ResourceParam Patient thePatient, @ConditionalUrlParam String theConditional, @IdParam IdType theIdParam) {
ourLastConditionalUrl = theConditional;
ourLastId = thePatient.getIdElement();
ourLastIdParam = theIdParam;
return new MethodOutcome(new IdType("Patient/001/_history/002"));
}
}
}

View File

@ -48,10 +48,8 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.BundleInclusionRule;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -59,7 +57,6 @@ import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IRestfulServer;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.ResourceReferenceInfo;
@ -298,7 +295,7 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
}
@Override
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult,
public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult,
EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
myBase = theServerBase;
@ -344,15 +341,6 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
}
}
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
for (IBaseResource nextRes : resourceList) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
}
}
}
addResourcesToBundle(resourceList, theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType,
theResult.getPublished());

View File

@ -60,6 +60,7 @@ import org.hl7.fhir.instance.model.StringType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.INarrative;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
@ -67,11 +68,11 @@ import org.xml.sax.SAXException;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.base.composite.BaseNarrativeDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.parser.JsonParserHl7OrgTest.MyPatientWithOneDeclaredAddressExtension;
import ca.uhn.fhir.parser.JsonParserHl7OrgTest.MyPatientWithOneDeclaredEnumerationExtension;
import ca.uhn.fhir.parser.JsonParserHl7OrgTest.MyPatientWithOneDeclaredExtension;
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.Constants;
import net.sf.json.JSON;
import net.sf.json.JSONSerializer;
@ -81,6 +82,11 @@ public class XmlParserHl7OrgDstu2Test {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserHl7OrgDstu2Test.class);
@After
public void after() {
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM);
}
private String fixDivNodeText(String htmlNoNs) {
return htmlNoNs.replace("<div>", "<div xmlns=\"http://www.w3.org/1999/xhtml\">");
}
@ -89,25 +95,6 @@ public class XmlParserHl7OrgDstu2Test {
return htmlNoNs.replace("<div>", "<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">");
}
/**
* See #216 - Profiled datatypes should use their unprofiled parent type as the choice[x] name
*
* Disabled after conversation with Grahame
*/
@Test
@Ignore
public void testEncodeAndParseProfiledDatatypeChoice() throws Exception {
IParser xmlParser = ourCtx.newXmlParser();
String input = IOUtils.toString(XmlParser.class.getResourceAsStream("/medicationstatement_invalidelement.xml"));
MedicationStatement ms = xmlParser.parseResource(MedicationStatement.class, input);
SimpleQuantity q = (SimpleQuantity) ms.getDosage().get(0).getQuantity();
assertEquals("1", q.getValueElement().getValueAsString());
String output = xmlParser.encodeResourceToString(ms);
assertThat(output, containsString("<quantityQuantity><value value=\"1\"/></quantityQuantity>"));
}
@Test
public void testComposition() {
@ -575,6 +562,26 @@ public class XmlParserHl7OrgDstu2Test {
}
/**
* See #216 - Profiled datatypes should use their unprofiled parent type as the choice[x] name
*
* Disabled after conversation with Grahame
*/
@Test
@Ignore
public void testEncodeAndParseProfiledDatatypeChoice() throws Exception {
IParser xmlParser = ourCtx.newXmlParser();
String input = IOUtils.toString(XmlParser.class.getResourceAsStream("/medicationstatement_invalidelement.xml"));
MedicationStatement ms = xmlParser.parseResource(MedicationStatement.class, input);
SimpleQuantity q = (SimpleQuantity) ms.getDosage().get(0).getQuantity();
assertEquals("1", q.getValueElement().getValueAsString());
String output = xmlParser.encodeResourceToString(ms);
assertThat(output, containsString("<quantityQuantity><value value=\"1\"/></quantityQuantity>"));
}
@Test
public void testEncodeBinaryResource() {
@ -587,7 +594,6 @@ public class XmlParserHl7OrgDstu2Test {
}
@Test
public void testEncodeBinaryWithNoContentType() {
Binary b = new Binary();
@ -613,7 +619,6 @@ public class XmlParserHl7OrgDstu2Test {
assertThat(val, containsString("home"));
assertThat(val, containsString("male"));
}
@Test
public void testEncodeBundle() throws InterruptedException {
Bundle b = new Bundle();
@ -650,6 +655,7 @@ public class XmlParserHl7OrgDstu2Test {
assertThat(bundleString, StringContainsInOrder.stringContainsInOrder(strings));
}
@Test
public void testEncodeBundleCategory() {
@ -718,6 +724,7 @@ public class XmlParserHl7OrgDstu2Test {
}
@Test
public void testEncodeContainedWithNarrativeIsSuppresed() throws Exception {
IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
@ -745,7 +752,6 @@ public class XmlParserHl7OrgDstu2Test {
}
@Test
public void testEncodeDeclaredExtensionWithAddressContent() {
IParser parser = ourCtx.newXmlParser();
@ -1110,6 +1116,7 @@ public class XmlParserHl7OrgDstu2Test {
}
@Test
public void testExtensionOnComposite() throws Exception {
@ -1133,7 +1140,6 @@ public class XmlParserHl7OrgDstu2Test {
}
@Test
public void testExtensionOnPrimitive() throws Exception {
@ -1219,6 +1225,7 @@ public class XmlParserHl7OrgDstu2Test {
@Test
public void testLoadAndEncodeDeclaredExtensions() throws ConfigurationException, DataFormatException, SAXException, IOException {
IParser p = ourCtx.newXmlParser();
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER);
//@formatter:off
String msg = "<ResourceWithExtensionsA xmlns=\"http://hl7.org/fhir\">\n" +

View File

@ -26,9 +26,6 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.util.PortUtil;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class BundleTypeInResponseHl7OrgTest {
private static CloseableHttpClient ourClient;

View File

@ -1,180 +0,0 @@
package ca.uhn.fhir.model.dstu2.resource;
/*
* #%L
* HAPI FHIR Structures - DSTU2 (FHIR v0.4.0)
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.model.api.BaseElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
import ca.uhn.fhir.model.base.resource.ResourceMetadataMap;
import ca.uhn.fhir.model.dstu2.composite.ContainedDt;
import ca.uhn.fhir.model.dstu2.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.gclient.StringClientParam;
import ca.uhn.fhir.util.ElementUtil;
public abstract class BaseResource extends BaseElement implements IResource {
/**
* Search parameter constant for <b>_language</b>
*/
@SearchParamDefinition(name="_language", path="", description="The language of the resource", type="string" )
public static final String SP_RES_LANGUAGE = "_language";
/**
* Search parameter constant for <b>_id</b>
*/
@SearchParamDefinition(name="_id", path="", description="The ID of the resource", type="string" )
public static final String SP_RES_ID = "_id";
/**
* <b>Fluent Client</b> search parameter constant for <b>_id</b>
* <p>
* Description: <b>the _id of a resource</b><br>
* Type: <b>string</b><br>
* Path: <b>Resource._id</b><br>
* </p>
*/
public static final StringClientParam RES_ID = new StringClientParam(BaseResource.SP_RES_ID);
@Child(name = "contained", order = 2, min = 0, max = 1)
private ContainedDt myContained;
private IdDt myId;
@Child(name = "language", order = 0, min = 0, max = Child.MAX_UNLIMITED)
private CodeDt myLanguage;
private ResourceMetadataMap myResourceMetadata;
@Child(name = "text", order = 1, min = 0, max = 1)
private NarrativeDt myText;
@Override
public ContainedDt getContained() {
if (myContained == null) {
myContained = new ContainedDt();
}
return myContained;
}
public IdDt getId() {
if (myId == null) {
myId = new IdDt();
}
return myId;
}
@Override
public IIdType getIdElement() {
return getId();
}
@Override
public CodeDt getLanguage() {
if (myLanguage == null) {
myLanguage = new CodeDt();
}
return myLanguage;
}
@Override
public ResourceMetadataMap getResourceMetadata() {
if (myResourceMetadata == null) {
myResourceMetadata = new ResourceMetadataMap();
}
return myResourceMetadata;
}
@Override
public NarrativeDt getText() {
if (myText == null) {
myText = new NarrativeDt();
}
return myText;
}
public void setContained(ContainedDt theContained) {
myContained = theContained;
}
public void setId(IdDt theId) {
myId = theId;
}
public BaseResource setId(IIdType theId) {
if (theId instanceof IdDt) {
myId = (IdDt) theId;
} else if (theId != null) {
myId = new IdDt(theId.getValue());
}
return this;
}
public BaseResource setId(String theId) {
if (theId == null) {
myId = null;
} else {
myId = new IdDt(theId);
}
return this;
}
@Override
public void setLanguage(CodeDt theLanguage) {
myLanguage = theLanguage;
}
@Override
public void setResourceMetadata(ResourceMetadataMap theMap) {
Validate.notNull(theMap, "The Map must not be null");
myResourceMetadata = theMap;
}
public void setText(NarrativeDt theText) {
myText = theText;
}
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("id", getId().toUnqualified());
return b.toString();
}
/**
* Intended to be called by extending classes {@link #isEmpty()} implementations, returns <code>true</code> if all
* content in this superclass instance is empty per the semantics of {@link #isEmpty()}.
*/
@Override
protected boolean isBaseEmpty() {
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
}
}

View File

@ -51,6 +51,7 @@
}
#foreach ( $child in $childElements )
#if (${child.methodName} != 'Meta')
/**
* Gets the value(s) for <b>${child.elementName}</b> ($esc.html(${child.shortName})).
* creating it if it does
@ -73,7 +74,7 @@
#end
return ${child.variableName};
}
#end
/**
* Gets the value(s) for <b>${child.elementName}</b> ($esc.html(${child.shortName})).

View File

@ -261,6 +261,44 @@
header containing "application/xml" as some browsers
do.
</action>
<action type="add">
DSTU2 resources now have a
<![CDATA[<code>getMeta()</code>]]> method which returns a
modifiable view of the resource metadata for convenience. This
matches the equivalent method in the DSTU3 structures.
</action>
<action type="add" issue="315">
Add a new method to FhirContext called
<![CDATA[
<code><a href="./apidocs/ca/uhn/fhir/context/FhirContext.html#setDefaultTypeForProfile-java.lang.String-java.lang.Class-">setDefaultTypeForProfile</a></code>
]]>
which can be used to specify that when recources are received which declare
support for specific profiles, a specific custom structures should be used
instead of the default. For example, if you have created a custom Observation
class for a specific profile, you could use this method to cause your custom
type to be used by the parser for resources in a search bundle you receive.
<![CDATA[
<br/><br/>
See the documentation page on
<a href="./doc_extensions.html">Profiles and Extensions</a>
for more information.
]]>
</action>
<action type="fix" issue="315">
Parsing/Encoding a custom resource type which extends a
base type sometimes caused the FhirContext to treat all future
parses of the same resource as using the custom type even when
this was not wanted.
<![CDATA[<br/><br/>]]>
Custom structures may now be explicitly declared by profile
using the
<![CDATA[
<code><a href="./apidocs/ca/uhn/fhir/context/FhirContext.html#setDefaultTypeForProfile-java.lang.String-java.lang.Class-">setDefaultTypeForProfile</a></code>
]]>
method.
<![CDATA[<br/><br/>]]>
This issue was discovered and fixed as a part of the implementation of issue #315.
</action>
</release>
<release version="1.4" date="2016-02-04">
<action type="add">

View File

@ -3,13 +3,20 @@
<properties>
<title>Profiles and Extensions</title>
<author email="jamesagnew@users.sourceforge.net">James Agnew</author>
<author email="jamesagnew@gmail.com">James Agnew</author>
</properties>
<body>
<section name="Undeclared Extensions">
<p class="doc_info_bubble">
Note on FHIR Versions: Because of the differences in the way the structures
work between DSTU2 and DSTU3, we have provided two versions of many of the
examples on this page. See the <a href="./download.html">download page</a>
for more information on FHIR versions.
</p>
<p>
Extensions are a key part of the FHIR specification, providing a standardized
way of placing additional data in a resource.
@ -21,17 +28,40 @@
Undeclared extensions can be added to any of the built in FHIR resource types that come with HAPI-FHIR.
</p>
<p>
<b>DSTU2</b>
</p>
<macro name="snippet">
<param name="id" value="resourceExtension" />
<param name="file" value="examples/src/main/java/example/Extensions.java" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu2.java" />
</macro>
<p>
<b>DSTU3</b>
</p>
<macro name="snippet">
<param name="id" value="resourceExtension" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro>
<p>
Undeclared extensions can also be added to datatypes (composite or primitive).
</p>
<p>
<b>DSTU2</b>
</p>
<macro name="snippet">
<param name="id" value="resourceStringExtension" />
<param name="file" value="examples/src/main/java/example/Extensions.java" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu2.java" />
</macro>
<p>
<b>DSTU3</b>
</p>
<macro name="snippet">
<param name="id" value="resourceStringExtension" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro>
<subsection name="Sub-Extensions">
@ -41,9 +71,21 @@
of a datatype. This is done by adding a child undeclared extension to the
parent extension.
</p>
<p>
<b>DSTU2</b>
</p>
<macro name="snippet">
<param name="id" value="subExtension" />
<param name="file" value="examples/src/main/java/example/Extensions.java" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu2.java" />
</macro>
<p>
<b>DSTU3</b>
</p>
<macro name="snippet">
<param name="id" value="subExtension" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro>
</subsection>
@ -52,11 +94,23 @@
<p>
HAPI provides a few ways of accessing extension values in resources
which are receieved from other sources (i.e. downloaded by a client).
which are received from other sources (i.e. downloaded by a client).
</p>
<p>
<b>DSTU2</b>
</p>
<macro name="snippet">
<param name="id" value="parseExtension" />
<param name="file" value="examples/src/main/java/example/Extensions.java" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu2.java" />
</macro>
<p>
<b>DSTU3</b>
</p>
<macro name="snippet">
<param name="id" value="parseExtension" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro>
</subsection>
@ -119,84 +173,43 @@
<param name="file" value="examples/src/main/java/example/MyPatientUse.java" />
</macro>
<subsection name="Documentation">
<subsection name="Using Custom Types in a Client">
<p>
Note in the example above the use of the <code>@Description</code>
annotation on the extension fields. This documentation is important,
as the FHIR Server will automatically add it to the generated Profile
resources which are exported by the server.
If you are using a client and wish to use a specific custom structure,
you may simply use the custom structure as you would a build in
HAPI type.
</p>
<macro name="snippet">
<param name="id" value="customTypeClientSimple" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro>
<p>
These bits of documentation will then be re-added to any automatically
generated clients built using <a href="./doc_tinder.html">Tinder</a>
against your server.
Sometimes you may not know in advance exactly which
type you will be receiving. For example, there are Patient resources
which conform to several different profiles on a server and you
aren't sure which profile you will get back for a specific read,
you can declare the "primary" type for a given profile.
</p>
<macro name="snippet">
<param name="id" value="customTypeClientDeclared" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro>
</subsection>
<subsection name="Using Custom Types in a Server">
<p>
The following is a snippet from the generated Profile resource
which is automatically created by a HAPI RESTful server using
your new type. (Don't worry if this part doens't make sense to you
yet, this is an advanced feature)
If you are using a client and wish to use a specific custom structure,
you may simply use the custom structure as you would a build in
HAPI type.
</p>
<source><![CDATA[<element>
<path value="Patient.extension"/>
<name value="extension"/>
<slicing>
<discriminator value="url"/>
<ordered value="false"/>
<rules value="open"/>
</slicing>
<definition>
<type>
<code value="Extension"/>
</type>
</definition>
</element>
<element>
<path value="Patient.extension"/>
<name value="extension"/>
<definition>
<short value="The name of the patient's favourite pet"/>
<min value="0"/>
<max value="1"/>
<type>
<code value="Extension"/>
<profile value="http://example.com/dontuse#petname"/>
</type>
<isModifier value="false"/>
</definition>
</element>
<element>
<path value="Patient.modifierExtension"/>
<name value="modifierExtension"/>
<slicing>
<discriminator value="url"/>
<ordered value="false"/>
<rules value="open"/>
</slicing>
<definition>
<type>
<code value="Extension"/>
</type>
</definition>
</element>
<element>
<path value="Patient.modifierExtension"/>
<name value="modifierExtension"/>
<definition>
<short value="Some dates of note for the patient"/>
<min value="0"/>
<max value="*"/>
<type>
<code value="Extension"/>
<profile value="http://example.com/dontuse#importantDates"/>
</type>
<isModifier value="true"/>
</definition>
</element>]]></source>
<macro name="snippet">
<param name="id" value="customTypeClientSimple" />
<param name="file" value="examples/src/main/java/example/ExtensionsDstu3.java" />
</macro>
</subsection>