Merge branch 'hapi3_refactor' of github.com:jamesagnew/hapi-fhir into hapi3_refactor

This commit is contained in:
James Agnew 2017-08-13 22:42:22 -04:00
commit 95a607d155
89 changed files with 577559 additions and 1163345 deletions

View File

@ -140,6 +140,11 @@ public enum FhirVersionEnum {
String provideVersion();
}
/**
* This class attempts to read the FHIR version from the actual model
* classes in order to supply an accurate version string even over time
*
*/
private static class Dstu3Version implements IVersionProvider {
public Dstu3Version() {
@ -147,7 +152,7 @@ public enum FhirVersionEnum {
Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants");
myVersion = (String) c.getDeclaredField("VERSION").get(null);
} catch (Exception e) {
myVersion = "UNKNOWN";
myVersion = "3.0.1";
}
}
@ -167,7 +172,7 @@ public enum FhirVersionEnum {
Class<?> c = Class.forName("org.hl7.fhir.r4.model.Constants");
myVersion = (String) c.getDeclaredField("VERSION").get(null);
} catch (Exception e) {
myVersion = "UNKNOWN";
myVersion = "4.0.0";
}
}

View File

@ -301,11 +301,16 @@ public class IdDt extends UriDt implements /*IPrimitiveDatatype<String>, */IIdTy
b.append(myResourceType);
}
if (b.length() > 0) {
if (b.length() > 0 && isNotBlank(myUnqualifiedId)) {
b.append('/');
}
if (isNotBlank(myUnqualifiedId)) {
b.append(myUnqualifiedId);
} else if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');
}
b.append(myUnqualifiedId);
if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
@ -381,7 +386,7 @@ public class IdDt extends UriDt implements /*IPrimitiveDatatype<String>, */IIdTy
@Override
public boolean isEmpty() {
return isBlank(getValue());
return super.isBaseEmpty() && isBlank(getValue());
}
@Override
@ -523,7 +528,21 @@ public class IdDt extends UriDt implements /*IPrimitiveDatatype<String>, */IIdTy
if (typeIndex == -1) {
myResourceType = theValue.substring(0, idIndex);
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
if (typeIndex > 0 && '/' == theValue.charAt(typeIndex - 1)) {
typeIndex = theValue.indexOf('/', typeIndex + 1);
}
if (typeIndex >= idIndex) {
// e.g. http://example.org/foo
// 'foo' was the id but we're making that the resource type. Nullify the id part because we don't have an id.
// Also set null value to the super.setValue() and enable myHaveComponentParts so it forces getValue() to properly
// recreate the url
myResourceType = myUnqualifiedId;
myUnqualifiedId = null;
super.setValue(null);
myHaveComponentParts = true;
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
}
if (typeIndex > 4) {
myBaseUrl = theValue.substring(0, typeIndex);

View File

@ -47,6 +47,8 @@ import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType;
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.util.ElementUtil;
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.ID_DATATYPE;
import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.PRIMITIVE_DATATYPE;
/**
* This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use
@ -610,14 +612,28 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
write(theEventWriter, "resourceType", resDef.getName());
if (theResourceId != null && theResourceId.hasIdPart()) {
write(theEventWriter, "id", theResourceId.getIdPart());
if (theResourceId.hasFormatComment()) {
beginObject(theEventWriter, "_id");
writeCommentsPreAndPost(theResourceId, theEventWriter);
theEventWriter.endObject();
}
}
if (theResourceId != null && theResourceId.hasIdPart()) {
write(theEventWriter, "id", theResourceId.getIdPart());
final List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
final List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
// Undeclared extensions
extractUndeclaredExtensions(theResourceId, extensions, modifierExtensions, null, null);
boolean haveExtension = false;
if (!extensions.isEmpty()) {
haveExtension = true;
}
if (theResourceId.hasFormatComment() || haveExtension) {
beginObject(theEventWriter, "_id");
if (theResourceId.hasFormatComment()) {
writeCommentsPreAndPost(theResourceId, theEventWriter);
}
if (haveExtension) {
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
}
theEventWriter.endObject();
}
}
if (theResource instanceof IResource) {
IResource resource = (IResource) theResource;
@ -1013,41 +1029,78 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
}
private void parseExtension(ParserState<?> theState, JsonLikeArray theValues, boolean theIsModifier) {
for (int i = 0; i < theValues.size(); i++) {
JsonLikeObject nextExtObj = JsonLikeValue.asObject(theValues.get(i));
JsonLikeValue jsonElement = nextExtObj.get("url");
String url;
if (null == jsonElement || !(jsonElement.isScalar())) {
String parentElementName;
if (theIsModifier) {
parentElementName = "modifierExtension";
} else {
parentElementName = "extension";
}
getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url");
url = null;
} else {
url = getExtensionUrl(jsonElement.getAsString());
}
theState.enteringNewElementExtension(null, url, theIsModifier, getServerBaseUrl());
for (String next : nextExtObj.keySet()) {
if ("url".equals(next)) {
continue;
} else if ("extension".equals(next)) {
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
parseExtension(theState, jsonVal, false);
} else if ("modifierExtension".equals(next)) {
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
parseExtension(theState, jsonVal, true);
} else {
JsonLikeValue jsonVal = nextExtObj.get(next);
parseChildren(theState, next, jsonVal, null, null, false);
}
}
theState.endingElement();
}
}
private void parseExtension(ParserState<?> theState, JsonLikeArray theValues, boolean theIsModifier) {
int allUnderscoreNames = 0;
int handledUnderscoreNames = 0;
for (int i = 0; i < theValues.size(); i++) {
JsonLikeObject nextExtObj = JsonLikeValue.asObject(theValues.get(i));
JsonLikeValue jsonElement = nextExtObj.get("url");
String url;
if (null == jsonElement || !(jsonElement.isScalar())) {
String parentElementName;
if (theIsModifier) {
parentElementName = "modifierExtension";
} else {
parentElementName = "extension";
}
getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url");
url = null;
} else {
url = getExtensionUrl(jsonElement.getAsString());
}
theState.enteringNewElementExtension(null, url, theIsModifier, getServerBaseUrl());
for (String next : nextExtObj.keySet()) {
if ("url".equals(next)) {
continue;
} else if ("extension".equals(next)) {
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
parseExtension(theState, jsonVal, false);
} else if ("modifierExtension".equals(next)) {
JsonLikeArray jsonVal = JsonLikeValue.asArray(nextExtObj.get(next));
parseExtension(theState, jsonVal, true);
} else if (next.charAt(0) == '_') {
allUnderscoreNames++;
continue;
} else {
JsonLikeValue jsonVal = nextExtObj.get(next);
String alternateName = '_' + next;
JsonLikeValue alternateVal = nextExtObj.get(alternateName);
if (alternateVal != null) {
handledUnderscoreNames++;
}
parseChildren(theState, next, jsonVal, alternateVal, alternateName, false);
}
}
/*
* This happens if an element has an extension but no actual value. I.e.
* if a resource has a "_status" element but no corresponding "status"
* element. This could be used to handle a null value with an extension
* for example.
*/
if (allUnderscoreNames > handledUnderscoreNames) {
for (String alternateName : nextExtObj.keySet()) {
if (alternateName.startsWith("_") && alternateName.length() > 1) {
JsonLikeValue nextValue = nextExtObj.get(alternateName);
if (nextValue != null) {
if (nextValue.isObject()) {
String nextName = alternateName.substring(1);
if (nextExtObj.get(nextName) == null) {
theState.enteringNewElement(null, nextName);
parseAlternates(nextValue, theState, alternateName, alternateName);
theState.endingElement();
}
} else {
getErrorHandler().incorrectJsonType(null, alternateName, ValueType.OBJECT, null, nextValue.getJsonType(), null);
}
}
}
}
}
theState.endingElement();
}
}
private void parseFhirComments(JsonLikeValue theObject, ParserState<?> theState) {
if (theObject.isArray()) {
@ -1314,6 +1367,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
} else {
String childName = myDef.getChildNameByDatatype(myValue.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, myParent, false);
managePrimitiveExtension(myValue, theResDef, theResource, theEventWriter, def, childName);
}
theEventWriter.endObject();
@ -1368,13 +1422,34 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName());
}
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true, myParent, false);
managePrimitiveExtension(value, theResDef, theResource, theEventWriter, childDef, childName);
}
// theEventWriter.name(myUndeclaredExtension.get);
theEventWriter.endObject();
}
}
private void managePrimitiveExtension(final IBase theValue, final RuntimeResourceDefinition theResDef, final IBaseResource theResource, final JsonLikeWriter theEventWriter, final BaseRuntimeElementDefinition<?> def, final String childName) throws IOException {
if (def.getChildType().equals(ID_DATATYPE) || def.getChildType().equals(PRIMITIVE_DATATYPE)) {
final List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
final List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
// Undeclared extensions
extractUndeclaredExtensions(theValue, extensions, modifierExtensions, myParent, null);
// Declared extensions
if (def != null) {
extractDeclaredExtensions(theValue, def, extensions, modifierExtensions, myParent);
}
boolean haveContent = false;
if (!extensions.isEmpty() || !modifierExtensions.isEmpty()) {
haveContent = true;
}
if (haveContent) {
beginObject(theEventWriter, '_' + childName);
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, extensions, modifierExtensions);
theEventWriter.endObject();
}
}
}
}
}

View File

@ -422,6 +422,7 @@ class ParserState<T> {
push(newState);
return;
}
case ID_DATATYPE:
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = primitiveTarget.newInstance(myDefinition.getInstanceConstructorArguments());
@ -1155,13 +1156,19 @@ class ParserState<T> {
String resourceName = myContext.getResourceDefinition(nextResource).getName();
String bundleIdPart = nextResource.getId().getIdPart();
if (isNotBlank(bundleIdPart)) {
// if (isNotBlank(entryBaseUrl)) {
// nextResource.setId(new IdDt(entryBaseUrl, resourceName, bundleIdPart, version));
// } else {
nextResource.setId(new IdDt(null, resourceName, bundleIdPart, version));
// }
// if (isNotBlank(entryBaseUrl)) {
// nextResource.setId(new IdDt(entryBaseUrl, resourceName, bundleIdPart, version));
// } else {
IdDt previousId = nextResource.getId();
nextResource.setId(new IdDt(null, resourceName, bundleIdPart, version));
// Copy extensions
if (!previousId.getAllUndeclaredExtensions().isEmpty()) {
for (final ExtensionDt ext : previousId.getAllUndeclaredExtensions()) {
nextResource.getId().addUndeclaredExtension(ext);
}
}
// }
}
}
}

View File

@ -184,9 +184,9 @@ public class XmlParser extends BaseParser /* implements IParser */ {
heldComments.clear();
}
parserState.endingElement();
// if (parserState.isComplete()) {
// return parserState.getObject();
// }
// if (parserState.isComplete()) {
// return parserState.getObject();
// }
break;
}
case XMLStreamConstants.CHARACTERS: {
@ -227,13 +227,17 @@ public class XmlParser extends BaseParser /* implements IParser */ {
switch (childDef.getChildType()) {
case ID_DATATYPE: {
IIdType value = IIdType.class.cast(theElement);
String encodedValue = "id".equals(childName) ? value.getIdPart() : value.getValue();
theEventWriter.writeStartElement(childName);
theEventWriter.writeAttribute("value", encodedValue);
encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource);
theEventWriter.writeEndElement();
break;
IIdType value = IIdType.class.cast(theElement);
String encodedValue = "id".equals(childName) ? value.getIdPart() : value.getValue();
if (StringUtils.isNotBlank(encodedValue) || super.hasExtensions(value)) {
theEventWriter.writeStartElement(childName);
if (StringUtils.isNotBlank(encodedValue)) {
theEventWriter.writeAttribute("value", encodedValue);
}
encodeExtensionsIfPresent(theResource, theEventWriter, theElement, theIncludedResource);
theEventWriter.writeEndElement();
}
break;
}
case PRIMITIVE_DATATYPE: {
IPrimitiveType<?> pd = IPrimitiveType.class.cast(theElement);
@ -354,8 +358,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
}
if (nextChild instanceof RuntimeChildContainedResources) {
encodeChildElementToStreamWriter(theResource, theEventWriter, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null, theContainedResource,
nextChildElem);
encodeChildElementToStreamWriter(theResource, theEventWriter, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null, theContainedResource, nextChildElem);
} else {
List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
@ -398,8 +401,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
}
}
private void encodeExtension(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theContainedResource, CompositeChildElement nextChildElem, BaseRuntimeChildDefinition nextChild,
IBase nextValue, String childName, String extensionUrl, BaseRuntimeElementDefinition<?> childDef)
private void encodeExtension(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theContainedResource, CompositeChildElement nextChildElem, BaseRuntimeChildDefinition nextChild, IBase nextValue, String childName, String extensionUrl, BaseRuntimeElementDefinition<?> childDef)
throws XMLStreamException {
BaseRuntimeDeclaredChildDefinition extDef = (BaseRuntimeDeclaredChildDefinition) nextChild;
if (extDef.isModifier()) {
@ -434,8 +436,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
}
}
private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource, boolean theSubResource)
throws XMLStreamException, DataFormatException {
private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource, boolean theSubResource) throws XMLStreamException, DataFormatException {
IIdType resourceId = null;
if (StringUtils.isNotBlank(theResource.getIdElement().getIdPart())) {
@ -470,13 +471,15 @@ public class XmlParser extends BaseParser /* implements IParser */ {
theEventWriter.writeDefaultNamespace(FHIR_NS);
if (theResource instanceof IAnyResource) {
// HL7.org Structures
if (theResourceId != null) {
writeCommentsPre(theEventWriter, theResourceId);
writeOptionalTagWithValue(theEventWriter, "id", theResourceId.getIdPart());
writeCommentsPost(theEventWriter, theResourceId);
}
if (theResourceId != null) {
writeCommentsPre(theEventWriter, theResourceId);
theEventWriter.writeStartElement("id");
theEventWriter.writeAttribute("value", theResourceId.getIdPart());
encodeExtensionsIfPresent(theResource, theEventWriter, theResourceId, false);
theEventWriter.writeEndElement();
writeCommentsPost(theEventWriter, theResourceId);
}
encodeCompositeElementToStreamWriter(theResource, theResource, theEventWriter, theContainedResource, new CompositeChildElement(resDef));
@ -485,11 +488,16 @@ public class XmlParser extends BaseParser /* implements IParser */ {
// DSTU2+
IResource resource = (IResource) theResource;
if (theResourceId != null) {
writeCommentsPre(theEventWriter, theResourceId);
writeOptionalTagWithValue(theEventWriter, "id", theResourceId.getIdPart());
writeCommentsPost(theEventWriter, theResourceId);
}
if (theResourceId != null) {
/* writeCommentsPre(theEventWriter, theResourceId);
writeOptionalTagWithValue(theEventWriter, "id", theResourceId.getIdPart());
writeCommentsPost(theEventWriter, theResourceId);*/
theEventWriter.writeStartElement("id");
theEventWriter.writeAttribute("value", theResourceId.getIdPart());
encodeExtensionsIfPresent(theResource, theEventWriter, theResourceId, false);
theEventWriter.writeEndElement();
writeCommentsPost(theEventWriter, theResourceId);
}
InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
IdDt resourceId = resource.getId();

View File

@ -28,20 +28,20 @@ public interface IBaseBundle extends IBaseResource {
* link.type field to indicate that the given link is for
* the next page of results.
*/
public static final String LINK_NEXT = "next";
String LINK_NEXT = "next";
/**
* Constant for links provided in the bundle. This constant is used in the
* link.type field to indicate that the given link is for
* the previous page of results.
*/
public static final String LINK_PREV = "previous";
String LINK_PREV = "previous";
/**
* Constant for links provided in the bundle. This constant is used in the
* link.type field to indicate that the given link is for
* this bundle.
*/
public static final String LINK_SELF = "self";
String LINK_SELF = "self";
}

View File

@ -1755,11 +1755,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
if (myReturnBundleType == null && myContext.getVersion().getVersion().isRi()) {
throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify "
+ "the bundle return type for the client by adding \".returnBundle(org.hl7.fhir.instance.model.Bundle.class)\" to your search method call before the \".execute()\" method");
}
IClientResponseHandler<? extends IBase> binding;
binding = new ResourceResponseHandler(myReturnBundleType, getPreferResponseTypes(myResourceType));

View File

@ -46,7 +46,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
private int myConnectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT;
private int myConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
private FhirContext myContext;
private Map<Class<? extends IRestfulClient>, ClientInvocationHandlerFactory> myInvocationHandlers = new HashMap<Class<? extends IRestfulClient>, ClientInvocationHandlerFactory>();
private Map<Class<? extends IRestfulClient>, ClientInvocationHandlerFactory> myInvocationHandlers = new HashMap<>();
private ServerValidationModeEnum myServerValidationMode = DEFAULT_SERVER_VALIDATION_MODE;
private int mySocketTimeout = DEFAULT_SOCKET_TIMEOUT;
private String myProxyUsername;
@ -82,9 +82,6 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
/**
* Return the proxy username to authenticate with the HTTP proxy
*
* @param The
* proxy username
*/
protected String getProxyUsername() {
return myProxyUsername;
@ -92,9 +89,6 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
/**
* Return the proxy password to authenticate with the HTTP proxy
*
* @param The
* proxy password
*/
protected String getProxyPassword() {
return myProxyPassword;
@ -128,8 +122,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
@SuppressWarnings("unchecked")
private <T extends IRestfulClient> T instantiateProxy(Class<T> theClientType, InvocationHandler theInvocationHandler) {
T proxy = (T) Proxy.newProxyInstance(theClientType.getClassLoader(), new Class[] { theClientType }, theInvocationHandler);
return proxy;
return (T) Proxy.newProxyInstance(theClientType.getClassLoader(), new Class[] { theClientType }, theInvocationHandler);
}
/**
@ -162,9 +155,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
myInvocationHandlers.put(theClientType, invocationHandler);
}
T proxy = instantiateProxy(theClientType, invocationHandler.newInvocationHandler(this));
return proxy;
return instantiateProxy(theClientType, invocationHandler.newInvocationHandler(this));
}
/**
@ -331,13 +322,14 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
FhirVersionEnum serverFhirVersionEnum = null;
if (StringUtils.isBlank(serverFhirVersionString)) {
// we'll be lenient and accept this
ourLog.debug("Server conformance statement does not indicate the FHIR version");
} else {
if (serverFhirVersionString.startsWith("0.4") || serverFhirVersionString.startsWith("0.5") || serverFhirVersionString.startsWith("1.0.")) {
if (serverFhirVersionString.equals(FhirVersionEnum.DSTU2.getFhirVersionString())) {
serverFhirVersionEnum = FhirVersionEnum.DSTU2;
} else if (serverFhirVersionString.startsWith("3.0.")) {
} else if (serverFhirVersionString.equals(FhirVersionEnum.DSTU2_1.getFhirVersionString())) {
serverFhirVersionEnum = FhirVersionEnum.DSTU2_1;
} else if (serverFhirVersionString.equals(FhirVersionEnum.DSTU3.getFhirVersionString())) {
serverFhirVersionEnum = FhirVersionEnum.DSTU3;
} else if (serverFhirVersionString.startsWith("3.1.")) {
serverFhirVersionEnum = FhirVersionEnum.R4;
} else {
// we'll be lenient and accept this
ourLog.debug("Server conformance statement indicates unknown FHIR version: {}", serverFhirVersionString);

View File

@ -264,6 +264,10 @@
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>

View File

@ -197,8 +197,16 @@ public class ResourceLink implements Serializable {
public void setTargetResourceUrl(IIdType theTargetResourceUrl) {
Validate.isTrue(theTargetResourceUrl.hasBaseUrl());
Validate.isTrue(theTargetResourceUrl.hasResourceType());
Validate.isTrue(theTargetResourceUrl.hasIdPart());
if (theTargetResourceUrl.hasIdPart()) {
// do nothing
} else {
// Must have set an url like http://example.org/something
// We treat 'something' as the resource type because of fix for #659. Prior to #659 fix, 'something' was
// treated as the id and 'example.org' was treated as the resource type
// TODO: log a warning?
}
myTargetResourceType = theTargetResourceUrl.getResourceType();
myTargetResourceUrl = theTargetResourceUrl.getValue();
}

View File

@ -80,7 +80,8 @@ public class RestHookSubscriptionDstu2Interceptor extends BaseRestHookSubscripti
*/
private void checkSubscriptions(IIdType idType, String resourceType, RestOperationTypeEnum theOperation) {
//avoid a ConcurrentModificationException by copying to an array
for (Object object : myRestHookSubscriptions.toArray()) {
Object[] subscriptions = myRestHookSubscriptions.toArray();
for (Object object : subscriptions) {
if (object == null) {
continue;
}
@ -330,8 +331,9 @@ public class RestHookSubscriptionDstu2Interceptor extends BaseRestHookSubscripti
Subscription subscription = (Subscription) theResource;
if (subscription.getChannel() != null
&& subscription.getChannel().getTypeElement().getValueAsEnum() == SubscriptionChannelTypeEnum.REST_HOOK
&& subscription.getStatusElement().getValueAsEnum() == SubscriptionStatusEnum.ACTIVE) {
&& subscription.getStatusElement().getValueAsEnum() == SubscriptionStatusEnum.REQUESTED) {
removeLocalSubscription(subscription.getIdElement().getIdPart());
subscription.setStatus(SubscriptionStatusEnum.ACTIVE);
myRestHookSubscriptions.add(subscription);
ourLog.info("Subscription was added. Id: " + subscription.getId());
}

View File

@ -319,8 +319,9 @@ public class RestHookSubscriptionDstu3Interceptor extends BaseRestHookSubscripti
Subscription subscription = (Subscription) theResource;
if (subscription.getChannel() != null
&& subscription.getChannel().getType() == Subscription.SubscriptionChannelType.RESTHOOK
&& subscription.getStatus() == Subscription.SubscriptionStatus.ACTIVE) {
&& subscription.getStatus() == Subscription.SubscriptionStatus.REQUESTED) {
removeLocalSubscription(subscription.getIdElement().getIdPart());
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
myRestHookSubscriptions.add(subscription);
ourLog.info("Subscription was added, id: {} - Have {}", subscription.getIdElement().getIdPart(), myRestHookSubscriptions.size());
}

View File

@ -58,10 +58,10 @@ public class RestHookSubscriptionR4Interceptor extends BaseRestHookSubscriptionI
private final static int MAX_THREADS = 1;
private static final Logger ourLog = LoggerFactory.getLogger(RestHookSubscriptionR4Interceptor.class);
private final List<Subscription> myRestHookSubscriptions = new ArrayList<>();
@Autowired
private FhirContext myFhirContext;
private final List<Subscription> myRestHookSubscriptions = new ArrayList<Subscription>();
@Autowired
@Qualifier("mySubscriptionDaoR4")
@ -314,9 +314,10 @@ public class RestHookSubscriptionR4Interceptor extends BaseRestHookSubscriptionI
Subscription subscription = (Subscription) theResource;
if (subscription.getChannel() != null
&& subscription.getChannel().getType() == Subscription.SubscriptionChannelType.RESTHOOK
&& subscription.getStatus() == Subscription.SubscriptionStatus.ACTIVE) {
&& subscription.getStatus() == Subscription.SubscriptionStatus.REQUESTED) {
removeLocalSubscription(subscription.getIdElement().getIdPart());
myRestHookSubscriptions.add(subscription);
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
ourLog.info("Subscription was added, id: {} - Have {}", subscription.getIdElement().getIdPart(), myRestHookSubscriptions.size());
}
} else {

View File

@ -0,0 +1,152 @@
package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.support.ExecutorSubscribableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.*;
public abstract class BaseSubscriptionInterceptor extends ServerOperationInterceptorAdapter {
private static final Integer MAX_SUBSCRIPTION_RESULTS = 1000;
private SubscribableChannel myProcessingChannel;
private ExecutorService myExecutor;
private boolean myAutoActivateSubscriptions = true;
private int myExecutorThreadCount = 1;
private MessageHandler mySubscriptionActivatingSubscriber;
private MessageHandler mySubscriptionCheckingSubscriber;
private ConcurrentHashMap<String, IBaseResource> myIdToSubscription = new ConcurrentHashMap<>();
private Subscription.SubscriptionChannelType myChannelType = Subscription.SubscriptionChannelType.RESTHOOK;
private Logger ourLog = LoggerFactory.getLogger(BaseSubscriptionInterceptor.class);
protected abstract IFhirResourceDao<?> getSubscriptionDao();
/**
* Read the existing subscriptions from the database
*/
@SuppressWarnings("unused")
@Scheduled(fixedDelay = 10000)
public void initSubscriptions() {
SearchParameterMap map = new SearchParameterMap();
map.add(Subscription.SP_TYPE, new TokenParam(null, myChannelType.toCode()));
map.add(Subscription.SP_STATUS, new TokenParam(null, Subscription.SubscriptionStatus.ACTIVE.toCode()));
map.setLoadSynchronousUpTo(MAX_SUBSCRIPTION_RESULTS);
RequestDetails req = new ServletSubRequestDetails();
req.setSubRequest(true);
IBundleProvider subscriptionBundleList = getSubscriptionDao().search(map, req);
if (subscriptionBundleList.size() >= MAX_SUBSCRIPTION_RESULTS) {
ourLog.error("Currently over " + MAX_SUBSCRIPTION_RESULTS + " subscriptions. Some subscriptions have not been loaded.");
}
List<IBaseResource> resourceList = subscriptionBundleList.getResources(0, subscriptionBundleList.size());
Set<String> allIds = new HashSet<>();
for (IBaseResource resource : resourceList) {
String nextId = resource.getIdElement().getIdPart();
allIds.add(nextId);
myIdToSubscription.put(nextId, resource);
}
for (String next : myIdToSubscription.keySet()) {
if (!allIds.contains(next)) {
myIdToSubscription.remove(next);
}
}
}
@PostConstruct
public void postConstruct() {
RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
ThreadFactory threadFactory = new BasicThreadFactory.Builder()
.namingPattern("subscription-%d")
.daemon(false)
.priority(Thread.NORM_PRIORITY)
.build();
myExecutor = new ThreadPoolExecutor(
myExecutorThreadCount,
myExecutorThreadCount,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1000),
threadFactory,
rejectedExecutionHandler);
if (myProcessingChannel == null) {
myProcessingChannel = new ExecutorSubscribableChannel(myExecutor);
}
if (myAutoActivateSubscriptions) {
if (mySubscriptionActivatingSubscriber == null) {
mySubscriptionActivatingSubscriber = new SubscriptionActivatingSubscriber(getSubscriptionDao(), myIdToSubscription, myChannelType, myProcessingChannel);
}
myProcessingChannel.subscribe(mySubscriptionActivatingSubscriber);
}
if (mySubscriptionCheckingSubscriber == null) {
mySubscriptionCheckingSubscriber = new SubscriptionCheckingSubscriber(getSubscriptionDao(), myIdToSubscription, myChannelType, myProcessingChannel);
}
myProcessingChannel.subscribe(mySubscriptionCheckingSubscriber);
}
@SuppressWarnings("unused")
@PreDestroy
public void preDestroy() {
if (myAutoActivateSubscriptions) {
myProcessingChannel.unsubscribe(mySubscriptionActivatingSubscriber);
}
myProcessingChannel.unsubscribe(mySubscriptionCheckingSubscriber);
}
@Override
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
ResourceModifiedMessage msg = new ResourceModifiedMessage();
msg.setId(theResource.getIdElement());
msg.setOperationType(RestOperationTypeEnum.CREATE);
msg.setNewPayload(theResource);
submitResourceModified(msg);
}
@Override
public void resourceDeleted(RequestDetails theRequest, IBaseResource theResource) {
ResourceModifiedMessage msg = new ResourceModifiedMessage();
msg.setId(theResource.getIdElement());
msg.setOperationType(RestOperationTypeEnum.DELETE);
submitResourceModified(msg);
}
@Override
public void resourceUpdated(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {
ResourceModifiedMessage msg = new ResourceModifiedMessage();
msg.setId(theNewResource.getIdElement());
msg.setOperationType(RestOperationTypeEnum.UPDATE);
msg.setNewPayload(theNewResource);
submitResourceModified(msg);
}
private void submitResourceModified(ResourceModifiedMessage theMsg) {
myProcessingChannel.send(new GenericMessage<>(theMsg));
}
}

View File

@ -0,0 +1,72 @@
package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Subscription;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.SubscribableChannel;
import java.util.concurrent.ConcurrentHashMap;
public abstract class BaseSubscriptionSubscriber implements MessageHandler {
static final String SUBSCRIPTION_STATUS = "Subscription.status";
private static final String SUBSCRIPTION_TYPE = "Subscription.channel.type";
private final IFhirResourceDao mySubscriptionDao;
private final ConcurrentHashMap<String, IBaseResource> myIdToSubscription;
private final Subscription.SubscriptionChannelType myChannelType;
private final SubscribableChannel myProcessingChannel;
/**
* Constructor
*/
public BaseSubscriptionSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, SubscribableChannel theProcessingChannel) {
mySubscriptionDao = theSubscriptionDao;
myIdToSubscription = theIdToSubscription;
myChannelType = theChannelType;
myProcessingChannel = theProcessingChannel;
}
public Subscription.SubscriptionChannelType getChannelType() {
return myChannelType;
}
public FhirContext getContext() {
return getSubscriptionDao().getContext();
}
public ConcurrentHashMap<String, IBaseResource> getIdToSubscription() {
return myIdToSubscription;
}
public SubscribableChannel getProcessingChannel() {
return myProcessingChannel;
}
public IFhirResourceDao getSubscriptionDao() {
return mySubscriptionDao;
}
/**
* Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor?
*/
protected boolean subscriptionTypeApplies(ResourceModifiedMessage theMsg) {
FhirContext ctx = mySubscriptionDao.getContext();
IBaseResource subscription = theMsg.getNewPayload();
return subscriptionTypeApplies(ctx, subscription);
}
/**
* Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor?
*/
protected boolean subscriptionTypeApplies(FhirContext theCtx, IBaseResource theSubscription) {
IPrimitiveType<?> status = theCtx.newTerser().getSingleValueOrNull(theSubscription, SUBSCRIPTION_TYPE, IPrimitiveType.class);
boolean subscriptionTypeApplies = false;
if (getChannelType().toCode().equals(status.getValueAsString())) {
subscriptionTypeApplies = true;
}
return subscriptionTypeApplies;
}
}

View File

@ -0,0 +1,40 @@
package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.io.Serializable;
public class ResourceDeliveryMessage implements Serializable {
private static final long serialVersionUID = 0L;
private IBaseResource mySubscription;
private IBaseResource myPayoad;
private RestOperationTypeEnum myOperationType;
public RestOperationTypeEnum getOperationType() {
return myOperationType;
}
public void setOperationType(RestOperationTypeEnum theOperationType) {
myOperationType = theOperationType;
}
public IBaseResource getPayoad() {
return myPayoad;
}
public void setPayoad(IBaseResource thePayoad) {
myPayoad = thePayoad;
}
public IBaseResource getSubscription() {
return mySubscription;
}
public void setSubscription(IBaseResource theSubscription) {
mySubscription = theSubscription;
}
}

View File

@ -0,0 +1,41 @@
package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import java.io.Serializable;
public class ResourceModifiedMessage implements Serializable {
private static final long serialVersionUID = 0L;
private IIdType myId;
private RestOperationTypeEnum myOperationType;
private IBaseResource myNewPayload;
public IIdType getId() {
return myId;
}
public void setId(IIdType theId) {
myId = theId;
}
public RestOperationTypeEnum getOperationType() {
return myOperationType;
}
public void setOperationType(RestOperationTypeEnum theOperationType) {
myOperationType = theOperationType;
}
public IBaseResource getNewPayload() {
return myNewPayload;
}
public void setNewPayload(IBaseResource theNewPayload) {
myNewPayload = theNewPayload;
}
}

View File

@ -0,0 +1,96 @@
package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
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 org.hl7.fhir.r4.model.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.SubscribableChannel;
import java.util.concurrent.ConcurrentHashMap;
@SuppressWarnings("unchecked")
public class SubscriptionActivatingSubscriber extends BaseSubscriptionSubscriber {
private Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingSubscriber.class);
/**
* Constructor
*/
public SubscriptionActivatingSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, SubscribableChannel theProcessingChannel) {
super(theSubscriptionDao, theIdToSubscription, theChannelType, theProcessingChannel);
}
private void handleCreate(ResourceModifiedMessage theMsg) {
if (!theMsg.getId().getResourceType().equals("Subscription")) {
return;
}
boolean subscriptionTypeApplies = subscriptionTypeApplies(theMsg);
if (subscriptionTypeApplies == false) {
return;
}
FhirContext ctx = getSubscriptionDao().getContext();
IBaseResource subscription = theMsg.getNewPayload();
IPrimitiveType<?> status = ctx.newTerser().getSingleValueOrNull(subscription, SUBSCRIPTION_STATUS, IPrimitiveType.class);
String statusString = status.getValueAsString();
String oldStatus = Subscription.SubscriptionStatus.REQUESTED.toCode();
if (oldStatus.equals(statusString)) {
String newStatus = Subscription.SubscriptionStatus.ACTIVE.toCode();
status.setValueAsString(newStatus);
ourLog.info("Activating subscription {} from status {} to {}", subscription.getIdElement().toUnqualifiedVersionless().getValue(), oldStatus, newStatus);
getSubscriptionDao().update(subscription);
}
}
@Override
public void handleMessage(Message<?> theMessage) throws MessagingException {
if (!(theMessage.getPayload() instanceof ResourceModifiedMessage)) {
return;
}
ResourceModifiedMessage msg = (ResourceModifiedMessage) theMessage.getPayload();
IIdType id = msg.getId();
switch (msg.getOperationType()) {
case DELETE:
getIdToSubscription().remove(id.getIdPart());
return;
case CREATE:
handleCreate(msg);
break;
case UPDATE:
handleUpdate(msg);
break;
}
}
private void handleUpdate(ResourceModifiedMessage theMsg) {
if (!theMsg.getId().getResourceType().equals("Subscription")) {
return;
}
boolean subscriptionTypeApplies = subscriptionTypeApplies(theMsg);
if (subscriptionTypeApplies == false) {
return;
}
FhirContext ctx = getSubscriptionDao().getContext();
IBaseResource subscription = theMsg.getNewPayload();
IPrimitiveType<?> status = ctx.newTerser().getSingleValueOrNull(subscription, SUBSCRIPTION_STATUS, IPrimitiveType.class);
String statusString = status.getValueAsString();
if (Subscription.SubscriptionStatus.ACTIVE.toCode().equals(statusString)) {
getIdToSubscription().put(theMsg.getId().getIdPart(), theMsg.getNewPayload());
}
}
}

View File

@ -0,0 +1,125 @@
package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Subscription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.support.GenericMessage;
import java.util.concurrent.ConcurrentHashMap;
public class SubscriptionCheckingSubscriber extends BaseSubscriptionSubscriber {
private Logger ourLog = LoggerFactory.getLogger(SubscriptionCheckingSubscriber.class);
public SubscriptionCheckingSubscriber(IFhirResourceDao theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, SubscribableChannel theProcessingChannel) {
super(theSubscriptionDao, theIdToSubscription, theChannelType, theProcessingChannel);
}
@Override
public void handleMessage(Message<?> theMessage) throws MessagingException {
if (!(theMessage.getPayload() instanceof ResourceModifiedMessage)) {
return;
}
ResourceModifiedMessage msg = (ResourceModifiedMessage) theMessage.getPayload();
switch (msg.getOperationType()) {
case CREATE:
case UPDATE:
break;
default:
// ignore anything else
return;
}
String resourceType = msg.getId().getResourceType();
String resourceId = msg.getId().getIdPart();
for (IBaseResource nextSubscription : getIdToSubscription().values()) {
String nextSubscriptionId = nextSubscription.getIdElement().toUnqualifiedVersionless().getValue();
IPrimitiveType<?> nextCriteria = getContext().newTerser().getSingleValueOrNull(nextSubscription, "Subscription.criteria", IPrimitiveType.class);
String nextCriteriaString = nextCriteria != null ? nextCriteria.getValueAsString() : null;
if (StringUtils.isBlank(nextCriteriaString)) {
continue;
}
// see if the criteria matches the created object
ourLog.info("Checking subscription {} for {} with criteria {}", nextSubscriptionId, resourceType, nextCriteriaString);
String criteriaResource = nextCriteriaString;
int index = criteriaResource.indexOf("?");
if (index != -1) {
criteriaResource = criteriaResource.substring(0, criteriaResource.indexOf("?"));
}
if (resourceType != null && nextCriteriaString != null && !criteriaResource.equals(resourceType)) {
ourLog.info("Skipping subscription search for {} because it does not match the criteria {}", resourceType, nextCriteriaString);
continue;
}
// run the subscriptions query and look for matches, add the id as part of the criteria to avoid getting matches of previous resources rather than the recent resource
String criteria = nextCriteriaString;
criteria += "&_id=" + resourceType + "/" + resourceId;
criteria = massageCriteria(criteria);
IBundleProvider results = performSearch(criteria);
if (results.size() == 0) {
continue;
}
// should just be one resource as it was filtered by the id
for (IBaseResource nextBase : results.getResources(0, results.size())) {
IAnyResource next = (IAnyResource) nextBase;
ourLog.info("Found match: queueing rest-hook notification for resource: {}", next.getIdElement());
ResourceDeliveryMessage deliveryMsg = new ResourceDeliveryMessage();
deliveryMsg.setPayoad(next);
deliveryMsg.setSubscription(nextSubscription);
deliveryMsg.setOperationType(msg.getOperationType());
getProcessingChannel().send(new GenericMessage<>(deliveryMsg));
}
}
}
/**
* Subclasses may override
*/
protected String massageCriteria(String theCriteria) {
return theCriteria;
}
/**
* Search based on a query criteria
*/
protected IBundleProvider performSearch(String theCriteria) {
RuntimeResourceDefinition responseResourceDef = getSubscriptionDao().validateCriteriaAndReturnResourceDefinition(theCriteria);
SearchParameterMap responseCriteriaUrl = BaseHapiFhirDao.translateMatchUrl(getSubscriptionDao(), getSubscriptionDao().getContext(), theCriteria, responseResourceDef);
RequestDetails req = new ServletSubRequestDetails();
req.setSubRequest(true);
IFhirResourceDao<? extends IBaseResource> responseDao = getSubscriptionDao().getDao(responseResourceDef.getImplementingClass());
responseCriteriaUrl.setLoadSynchronousUpTo(1);
IBundleProvider responseResults = responseDao.search(responseCriteriaUrl, req);
return responseResults;
}
}

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Subscription;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.SubscribableChannel;
import java.util.concurrent.ConcurrentHashMap;
public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionSubscriber {
public SubscriptionDeliveringRestHookSubscriber(IFhirResourceDao theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, SubscribableChannel theProcessingChannel) {
super(theSubscriptionDao, theIdToSubscription, theChannelType, theProcessingChannel);
}
@Override
public void handleMessage(Message<?> theMessage) throws MessagingException {
if (!(theMessage.getPayload() instanceof ResourceDeliveryMessage)) {
return;
}
ResourceDeliveryMessage msg = (ResourceDeliveryMessage) theMessage.getPayload();
if (!subscriptionTypeApplies(getContext(), msg.getSubscription())) {
return;
}
}
}

View File

@ -57,11 +57,8 @@ public class SubscriptionWebsocketHandlerR4 extends TextWebSocketHandler impleme
private static IFhirResourceDaoSubscription<Subscription> ourSubscriptionDao;
private ScheduledFuture<?> myScheduleFuture;
private IState myState = new InitialState();
private IIdType mySubscriptionId;
private Long mySubscriptionPid;
@Autowired

View File

@ -116,7 +116,7 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
server.start();
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
ourClient.registerInterceptor(new LoggingInterceptor(true));
ourClient.registerInterceptor(new LoggingInterceptor());
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
@ -160,4 +160,4 @@ public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}
}

View File

@ -13,6 +13,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.*;
import org.apache.http.entity.*;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
@ -2059,12 +2060,12 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
Observation o = new Observation();
o.getCode().setText("testSearchWithInvalidSort");
myObservationDao.create(o, mySrd);
ourClient
.search()
.forResource(Observation.class)
.sort().ascending(Observation.CODE_VALUE_QUANTITY) // composite sort not supported yet
.prettyPrint()
.execute();
IBaseBundle bundle = ourClient
.search()
.forResource(Observation.class)
.sort().ascending(Observation.CODE_VALUE_QUANTITY) // composite sort not supported yet
.prettyPrint()
.execute();
}
@Test

View File

@ -151,7 +151,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
if (shouldLogClient()) {
ourClient.registerInterceptor(new LoggingInterceptor(true));
ourClient.registerInterceptor(new LoggingInterceptor());
}
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
@ -195,4 +195,4 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}
}

View File

@ -1,23 +1,6 @@
package ca.uhn.fhir.jpa.subscription;
import static org.junit.Assert.*;
import java.util.List;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
@ -38,6 +21,19 @@ import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.PortUtil;
import com.google.common.collect.Lists;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* Test the rest-hook subscriptions
@ -51,12 +47,19 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
private static String ourListenerServerBase;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestDstu2Test.class);
private static List<Observation> ourUpdatedObservations = Lists.newArrayList();
private List<IIdType> mySubscriptionIds = new ArrayList<IIdType>();
@After
public void afterUnregisterRestHookListener() {
for (IIdType next : mySubscriptionIds){
ourClient.delete().resourceById(next).execute();
}
mySubscriptionIds.clear();
myDaoConfig.setAllowMultipleDelete(true);
ourLog.info("Deleting all subscriptions");
ourClient.delete().resourceConditionalByUrl("Subscription?status=active").execute();
ourClient.delete().resourceConditionalByUrl("Observation?code:missing=false").execute();
ourLog.info("Done deleting all subscriptions");
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
@ -77,7 +80,7 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
private Subscription createSubscription(String criteria, String payload, String endpoint) {
Subscription subscription = new Subscription();
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
subscription.setStatus(SubscriptionStatusEnum.ACTIVE);
subscription.setStatus(SubscriptionStatusEnum.REQUESTED);
subscription.setCriteria(criteria);
Channel channel = new Channel();
@ -88,6 +91,7 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
subscription.setId(methodOutcome.getId().getIdPart());
mySubscriptionIds.add(methodOutcome.getId());
return subscription;
}
@ -136,9 +140,9 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
Observation observation2 = sendObservation(code, "SNOMED-CT");
// Should see two subscription notifications
// Should see one subscription notification
Thread.sleep(500);
assertEquals(3, ourCreatedObservations.size());
assertEquals(2, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
ourClient.delete().resourceById(new IdDt("Subscription/" + subscription2.getId())).execute();
@ -147,7 +151,7 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
@ -160,7 +164,7 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
// Should see no subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
@ -174,7 +178,7 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(1, ourUpdatedObservations.size());
Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
@ -222,9 +226,9 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
Observation observation2 = sendObservation(code, "SNOMED-CT");
// Should see two subscription notifications
// Should see one subscription notification
Thread.sleep(500);
assertEquals(3, ourCreatedObservations.size());
assertEquals(ourCreatedObservations.toString(), 2, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
ourClient.delete().resourceById(new IdDt("Subscription/" + subscription2.getId())).execute();
@ -233,7 +237,7 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
@ -246,7 +250,7 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
// Should see no subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
@ -260,7 +264,7 @@ public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(1, ourUpdatedObservations.size());
Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));

View File

@ -1,57 +1,63 @@
package ca.uhn.fhir.jpa.subscription;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.PortUtil;
import com.google.common.collect.Lists;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import com.google.common.collect.Lists;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* Test the rest-hook subscriptions
*/
public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
private static List<String> ourContentTypes = new ArrayList<String>();
private static List<Observation> ourCreatedObservations = Lists.newArrayList();
private static int ourListenerPort;
private static RestfulServer ourListenerRestServer;
private static Server ourListenerServer;
private static String ourListenerServerBase;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestDstu3Test.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestDstu2Test.class);
private static List<Observation> ourUpdatedObservations = Lists.newArrayList();
private List<IIdType> mySubscriptionIds = new ArrayList<>();
private static List<String> ourContentTypes = new ArrayList<>();
@After
public void afterUnregisterRestHookListener() {
for (IIdType next : mySubscriptionIds){
ourClient.delete().resourceById(next).execute();
}
mySubscriptionIds.clear();
myDaoConfig.setAllowMultipleDelete(true);
ourLog.info("Deleting all subscriptions");
ourClient.delete().resourceConditionalByUrl("Subscription?status=active").execute();
ourClient.delete().resourceConditionalByUrl("Observation?code:missing=false").execute();
ourLog.info("Done deleting all subscriptions");
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
ourRestServer.unregisterInterceptor(ourRestHookSubscriptionInterceptor);
}
@ -85,7 +91,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
private Subscription createSubscription(String theCriteria, String thePayload, String theEndpoint) {
Subscription subscription = new Subscription();
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
subscription.setCriteria(theCriteria);
Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
@ -96,6 +102,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
subscription.setId(methodOutcome.getId().getIdPart());
mySubscriptionIds.add(methodOutcome.getId());
return subscription;
}
@ -163,21 +170,20 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
subscriptionTemp.setCriteria(criteria1);
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
Observation observation2 = sendObservation(code, "SNOMED-CT");
// Should see two subscription notifications
// Should see one subscription notification
Thread.sleep(500);
assertEquals(3, ourCreatedObservations.size());
assertEquals(2, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
ourClient.delete().resourceById(new IdDt("Subscription", subscription2.getId())).execute();
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
@ -190,7 +196,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
// Should see no subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
@ -204,7 +210,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(1, ourUpdatedObservations.size());
Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
@ -232,7 +238,6 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
}
@Test
public void testRestHookSubscriptionApplicationXml() throws Exception {
String payload = "application/xml";
@ -258,21 +263,20 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
subscriptionTemp.setCriteria(criteria1);
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
Observation observation2 = sendObservation(code, "SNOMED-CT");
// Should see two subscription notifications
// Should see one subscription notification
Thread.sleep(500);
assertEquals(3, ourCreatedObservations.size());
assertEquals(ourCreatedObservations.toString(), 2, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
ourClient.delete().resourceById(new IdDt("Subscription", subscription2.getId())).execute();
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
@ -285,7 +289,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
// Should see no subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
@ -299,17 +303,17 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(1, ourUpdatedObservations.size());
Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
Assert.assertFalse(observation1.getId().isEmpty());
Assert.assertFalse(observation2.getId().isEmpty());
}
@BeforeClass
public static void startListenerServer() throws Exception {
ourListenerPort = RandomServerPortProvider.findFreePort();
ourListenerPort = PortUtil.findFreePort();
ourListenerRestServer = new RestfulServer(FhirContext.forDstu3());
ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context";

View File

@ -1,57 +1,63 @@
package ca.uhn.fhir.jpa.subscription.r4;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.rest.api.Constants;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.*;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.jpa.subscription.RestHookTestDstu2Test;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.PortUtil;
import com.google.common.collect.Lists;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* Test the rest-hook subscriptions
*/
public class RestHookTestR4Test extends BaseResourceProviderR4Test {
private static List<String> ourContentTypes = new ArrayList<String>();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestDstu2Test.class);
private static List<Observation> ourCreatedObservations = Lists.newArrayList();
private static int ourListenerPort;
private static RestfulServer ourListenerRestServer;
private static Server ourListenerServer;
private static String ourListenerServerBase;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestR4Test.class);
private static List<Observation> ourUpdatedObservations = Lists.newArrayList();
private static List<String> ourContentTypes = new ArrayList<>();
private List<IIdType> mySubscriptionIds = new ArrayList<>();
@After
public void afterUnregisterRestHookListener() {
for (IIdType next : mySubscriptionIds) {
ourClient.delete().resourceById(next).execute();
}
mySubscriptionIds.clear();
myDaoConfig.setAllowMultipleDelete(true);
ourLog.info("Deleting all subscriptions");
ourClient.delete().resourceConditionalByUrl("Subscription?status=active").execute();
ourClient.delete().resourceConditionalByUrl("Observation?code:missing=false").execute();
ourLog.info("Done deleting all subscriptions");
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
ourRestServer.unregisterInterceptor(ourRestHookSubscriptionInterceptor);
}
@ -66,7 +72,7 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
ourUpdatedObservations.clear();
ourContentTypes.clear();
}
@Test
public void testRestHookSubscriptionInvalidCriteria() throws Exception {
String payload = "application/xml";
@ -81,11 +87,10 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
}
}
private Subscription createSubscription(String theCriteria, String thePayload, String theEndpoint) {
Subscription subscription = new Subscription();
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
subscription.setCriteria(theCriteria);
Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
@ -96,6 +101,7 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
subscription.setId(methodOutcome.getId().getIdPart());
mySubscriptionIds.add(methodOutcome.getId());
return subscription;
}
@ -117,7 +123,7 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
return observation;
}
@Test
public void testRestHookSubscriptionApplicationFhirJson() throws Exception {
String payload = "application/fhir+json";
@ -137,7 +143,7 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
assertEquals(0, ourUpdatedObservations.size());
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
}
@Test
public void testRestHookSubscriptionApplicationJson() throws Exception {
String payload = "application/json";
@ -156,28 +162,27 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
assertEquals(1, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
Assert.assertNotNull(subscriptionTemp);
subscriptionTemp.setCriteria(criteria1);
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
Observation observation2 = sendObservation(code, "SNOMED-CT");
// Should see two subscription notifications
// Should see one subscription notification
Thread.sleep(500);
assertEquals(3, ourCreatedObservations.size());
assertEquals(2, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
ourClient.delete().resourceById(new IdDt("Subscription", subscription2.getId())).execute();
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
@ -190,7 +195,7 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
// Should see no subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
@ -204,7 +209,7 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(1, ourUpdatedObservations.size());
Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
@ -232,7 +237,6 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
}
@Test
public void testRestHookSubscriptionApplicationXml() throws Exception {
String payload = "application/xml";
@ -251,28 +255,27 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
assertEquals(1, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
Assert.assertNotNull(subscriptionTemp);
subscriptionTemp.setCriteria(criteria1);
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
Observation observation2 = sendObservation(code, "SNOMED-CT");
// Should see two subscription notifications
// Should see one subscription notification
Thread.sleep(500);
assertEquals(3, ourCreatedObservations.size());
assertEquals(ourCreatedObservations.toString(), 2, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
ourClient.delete().resourceById(new IdDt("Subscription", subscription2.getId())).execute();
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
@ -285,7 +288,7 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
// Should see no subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(0, ourUpdatedObservations.size());
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
@ -299,17 +302,17 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
// Should see only one subscription notification
Thread.sleep(500);
assertEquals(4, ourCreatedObservations.size());
assertEquals(3, ourCreatedObservations.size());
assertEquals(1, ourUpdatedObservations.size());
Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
Assert.assertFalse(observation1.getId().isEmpty());
Assert.assertFalse(observation2.getId().isEmpty());
}
@BeforeClass
public static void startListenerServer() throws Exception {
ourListenerPort = RandomServerPortProvider.findFreePort();
ourListenerPort = PortUtil.findFreePort();
ourListenerRestServer = new RestfulServer(FhirContext.forR4());
ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context";

View File

@ -0,0 +1,232 @@
package ca.uhn.fhir.jpa.z;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.util.BundleUtil;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.io.*;
import java.net.URL;
import java.util.Collection;
public class ResourceMinimizerMojo {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceMinimizerMojo.class);
private String fhirVersion;
private long myByteCount;
private FhirContext myCtx;
private int myFileCount;
private File targetDirectory;
public void execute() throws Exception {
ourLog.info("Starting resource minimizer");
if (myCtx != null) {
// nothing
} else if ("DSTU2".equals(fhirVersion)) {
myCtx = FhirContext.forDstu2();
} else if ("HL7ORG_DSTU2".equals(fhirVersion)) {
myCtx = FhirContext.forDstu2Hl7Org();
} else if ("DSTU2_1".equals(fhirVersion)) {
myCtx = FhirContext.forDstu2_1();
} else if ("DSTU3".equals(fhirVersion)) {
myCtx = FhirContext.forDstu3();
} else if ("R4".equals(fhirVersion)) {
myCtx = FhirContext.forR4();
} else {
throw new Exception("Unknown version: " + fhirVersion);
}
ourLog.info("Looking for files in directory: {}", targetDirectory.getAbsolutePath());
Collection<File> files = FileUtils.listFiles(targetDirectory, new String[]{"xml", "json"}, true);
for (File nextFile : files) {
ourLog.debug("Checking file: {}", nextFile);
String inputString;
try {
inputString = IOUtils.toString(new FileInputStream(nextFile), "UTF-8");
} catch (IOException e) {
throw new Exception("Failed to read file: " + nextFile, e);
}
IParser parser = EncodingEnum.detectEncoding(inputString).newParser(myCtx);
IBaseResource input = parser.parseResource(inputString);
if (input instanceof IResource) {
((IResource) input).getText().getDiv().setValueAsString((String) null);
((IResource) input).getText().getStatus().setValueAsString((String) null);
if (input instanceof Bundle) {
for (Entry nextEntry : ((Bundle) input).getEntry()) {
if (nextEntry.getResource() != null) {
nextEntry.getResource().getText().getDiv().setValueAsString((String) null);
nextEntry.getResource().getText().getStatus().setValueAsString((String) null);
}
}
}
} else {
minimizeResource((IBaseResource) input);
}
String outputString = parser.setPrettyPrint(true).encodeResourceToString(input);
StringBuilder b = new StringBuilder();
for (String nextLine : outputString.split("\\n")) {
int i;
for (i = 0; i < nextLine.length(); i++) {
if (nextLine.charAt(i) != ' ') {
break;
}
}
b.append(StringUtils.leftPad("", i / 3, ' '));
b.append(nextLine.substring(i));
b.append("\n");
}
outputString = b.toString();
if (!inputString.equals(outputString)) {
ourLog.info("Trimming contents of resource: {} - From {} to {}", nextFile, FileUtils.byteCountToDisplaySize(inputString.length()), FileUtils.byteCountToDisplaySize(outputString.length()));
myByteCount += (inputString.length() - outputString.length());
myFileCount++;
try {
String f = nextFile.getAbsolutePath();
Writer w = new OutputStreamWriter(new FileOutputStream(f, false), "UTF-8");
w = new BufferedWriter(w);
w.append(outputString);
w.close();
} catch (IOException e) {
throw new Exception("Failed to write " + nextFile, e);
}
}
}
}
private void minimizeResource(IBaseResource theInput) {
if (theInput instanceof IBaseBundle) {
for (IBaseResource next : BundleUtil.toListOfResources(myCtx, (IBaseBundle) theInput)) {
minimizeResource(next);
}
}
BaseRuntimeElementCompositeDefinition<?> element = (BaseRuntimeElementCompositeDefinition) myCtx.getElementDefinition(theInput.getClass());
BaseRuntimeChildDefinition textElement = element.getChildByName("text");
if (textElement != null) {
textElement.getMutator().setValue(theInput, null);
}
}
public long getByteCount() {
return myByteCount;
}
public int getFileCount() {
return myFileCount;
}
public static void main(String[] args) throws Exception {
FhirContext ctxDstu2 = FhirContext.forDstu2();
// FhirContext ctxDstu2_1 = FhirContext.forDstu2_1();
FhirContext ctxDstu3 = FhirContext.forDstu3();
FhirContext ctxR4 = FhirContext.forR4();
LoggerContext loggerContext = ((ch.qos.logback.classic.Logger) ourLog).getLoggerContext();
URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(loggerContext);
System.out.println(mainURL);
// or even
ourLog.info("Logback used '{}' as the configuration file.", mainURL);
int fileCount = 0;
long byteCount = 0;
ResourceMinimizerMojo m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu2;
m.targetDirectory = new File("./hapi-tinder-plugin/src/main/resources/vs/dstu2");
m.fhirVersion = "DSTU2";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu2;
m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu2/src/main/resources/org/hl7/fhir/instance/model/valueset");
m.fhirVersion = "DSTU2";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu2;
m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu2/src/main/resources/org/hl7/fhir/instance/model/profile");
m.fhirVersion = "DSTU2";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu3;
m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/profile");
m.fhirVersion = "DSTU3";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu3;
m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/valueset");
m.fhirVersion = "DSTU3";
m.execute();
byteCount += m.getByteCount();
// m = new ResourceMinimizerMojo();
// m.myCtx = ctxDstu2_1;
// m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu2.1/src/main/resources/org/hl7/fhir/dstu2016may/model/profile");
// m.fhirVersion = "DSTU2_1";
// m.execute();
// byteCount += m.getByteCount();
// fileCount += m.getFileCount();
//
// m = new ResourceMinimizerMojo();
// m.myCtx = ctxDstu2_1;
// m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu2.1/src/main/resources/org/hl7/fhir/dstu2016may/model/valueset");
// m.fhirVersion = "DSTU2_1";
// m.execute();
// byteCount += m.getByteCount();
// fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxR4;
m.targetDirectory = new File("./hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/profile");
m.fhirVersion = "R4";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxR4;
m.targetDirectory = new File("./hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/valueset");
m.fhirVersion = "R4";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
ourLog.info("Trimmed {} files", fileCount);
ourLog.info("Trimmed {} bytes", FileUtils.byteCountToDisplaySize(byteCount));
}
}

View File

@ -24,6 +24,7 @@ import java.util.*;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -98,7 +99,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setPublisher(myPublisher);
retVal.setDate(conformanceDate());
retVal.setFhirVersion("0.0.82-3059"); // TODO: pull from model
retVal.setFhirVersion(FhirVersionEnum.DSTU1.getFhirVersionString());
retVal.setAcceptUnknown(false); // TODO: make this configurable - this is a fairly big effort since the parser needs to be modified to actually allow it
retVal.getImplementation().setDescription(myServerConfiguration.getImplementationDescription());

View File

@ -84,7 +84,38 @@ public class IdDtTest {
assertEquals("foo/_history/2", id.withVersion("2").getValue());
}
@Test
public void testBaseUrlFoo1() {
IdDt id = new IdDt("http://my.org/foo");
assertEquals("http://my.org/foo", id.getValueAsString());
assertEquals(null, id.getIdPart());
assertEquals("foo", id.toUnqualified().getValueAsString());
assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals("foo", id.getResourceType());
assertEquals("http://my.org", id.getBaseUrl());
assertEquals("Patient", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("http://my.org/foo//_history/2", id.withVersion("2").getValue());
}
@Test
public void testBaseUrlFoo2() {
IdDt id = new IdDt("http://my.org/a/b/c/foo");
assertEquals("http://my.org/a/b/c/foo", id.getValueAsString());
assertEquals("foo", id.getIdPart());
assertEquals("c/foo", id.toUnqualified().getValueAsString());
assertEquals("c/foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals("c", id.getResourceType());
assertEquals("http://my.org/a/b", id.getBaseUrl());
assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("http://my.org/a/b/c/foo/_history/2", id.withVersion("2").getValue());
}
@Test
public void testDetectIsIdPartValid() {
assertTrue(new IdDt("0").isIdPartValid());

View File

@ -358,11 +358,16 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
b.append(myResourceType);
}
if (b.length() > 0) {
if (b.length() > 0 && isNotBlank(myUnqualifiedId)) {
b.append('/');
}
if (isNotBlank(myUnqualifiedId)) {
b.append(myUnqualifiedId);
} else if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');
}
b.append(myUnqualifiedId);
if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');
b.append("_history");
@ -554,7 +559,21 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
if (typeIndex == -1) {
myResourceType = theValue.substring(0, idIndex);
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
if (typeIndex > 0 && '/' == theValue.charAt(typeIndex - 1)) {
typeIndex = theValue.indexOf('/', typeIndex + 1);
}
if (typeIndex >= idIndex) {
// e.g. http://example.org/foo
// 'foo' was the id but we're making that the resource type. Nullify the id part because we don't have an id.
// Also set null value to the super.setValue() and enable myHaveComponentParts so it forces getValue() to properly
// recreate the url
myResourceType = myUnqualifiedId;
myUnqualifiedId = null;
super.setValue(null);
myHaveComponentParts = true;
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
}
if (typeIndex > 4) {
myBaseUrl = theValue.substring(0, typeIndex);

View File

@ -92,6 +92,37 @@ public class IdTypeDstu2_1Test {
assertEquals("foo/_history/2", id.withVersion("2").getValue());
}
@Test
public void testBaseUrlFoo1() {
IdType id = new IdType("http://my.org/foo");
assertEquals("http://my.org/foo", id.getValueAsString());
assertEquals(null, id.getIdPart());
assertEquals("foo", id.toUnqualified().getValueAsString());
assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals("foo", id.getResourceType());
assertEquals("http://my.org", id.getBaseUrl());
assertEquals("Patient", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("http://my.org/foo//_history/2", id.withVersion("2").getValue());
}
@Test
public void testBaseUrlFoo2() {
IdType id = new IdType("http://my.org/a/b/c/foo");
assertEquals("http://my.org/a/b/c/foo", id.getValueAsString());
assertEquals("foo", id.getIdPart());
assertEquals("c/foo", id.toUnqualified().getValueAsString());
assertEquals("c/foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals("c", id.getResourceType());
assertEquals("http://my.org/a/b", id.getBaseUrl());
assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("http://my.org/a/b/c/foo/_history/2", id.withVersion("2").getValue());
}
@Test
public void testDetectLocal() {

View File

@ -1873,6 +1873,26 @@ public class JsonParserDstu2_1Test {
Assert.assertThat(message, containsString("contained"));
}
@Test
public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
String refVal = "http://my.org/FooBar";
Patient fhirPat = new Patient();
fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal));
IParser parser = ourCtx.newJsonParser();
String output = parser.encodeResourceToString(fhirPat);
System.out.println("output: " + output);
// Deserialize then check that valueReference value is still correct
fhirPat = parser.parseResource(Patient.class, output);
List<Extension> extlst = fhirPat.getExtensionsByUrl("x1");
Assert.assertEquals(1, extlst.size());
Assert.assertEquals(refVal, ((Reference) extlst.get(0).getValue()).getReference());
}
@AfterClass

View File

@ -1,21 +1,22 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.parser.FooMessageHeaderWithExplicitField.FooMessageSourceComponent;
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
import ca.uhn.fhir.parser.PatientWithCustomCompositeExtension.FooParentExtension;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.util.TestUtil;
import com.google.common.collect.Sets;
import org.apache.commons.io.IOUtils;
import org.hamcrest.collection.IsEmptyCollection;
import org.hamcrest.core.StringContains;
import org.hamcrest.text.StringContainsInOrder;
import org.hl7.fhir.dstu2016may.model.*;
import org.hl7.fhir.dstu2016may.model.Address.AddressUse;
import org.hl7.fhir.dstu2016may.model.*;
import org.hl7.fhir.dstu2016may.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu2016may.model.Bundle.BundleType;
import org.hl7.fhir.dstu2016may.model.ContactPoint.ContactPointSystem;
@ -32,20 +33,20 @@ import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.xmlunit.builder.DiffBuilder;
import org.xmlunit.builder.Input;
import org.xmlunit.diff.*;
import org.xmlunit.diff.ComparisonControllers;
import org.xmlunit.diff.DefaultNodeMatcher;
import org.xmlunit.diff.Diff;
import org.xmlunit.diff.ElementSelectors;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.parser.FooMessageHeaderWithExplicitField.FooMessageSourceComponent;
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
import ca.uhn.fhir.parser.PatientWithCustomCompositeExtension.FooParentExtension;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.util.TestUtil;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
public class XmlParserDstu2_1Test {
private static FhirContext ourCtx = FhirContext.forDstu2_1();
@ -2662,6 +2663,26 @@ public class XmlParserDstu2_1Test {
}
@Test
public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
String refVal = "http://my.org/FooBar";
Patient fhirPat = new Patient();
fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal));
IParser parser = ourCtx.newXmlParser();
String output = parser.encodeResourceToString(fhirPat);
System.out.println("output: " + output);
// Deserialize then check that valueReference value is still correct
fhirPat = parser.parseResource(Patient.class, output);
List<Extension> extlst = fhirPat.getExtensionsByUrl("x1");
Assert.assertEquals(1, extlst.size());
Assert.assertEquals(refVal, ((Reference) extlst.get(0).getValue()).getReference());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -28,6 +28,7 @@ import java.util.Map.Entry;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -162,7 +163,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setPublisher(myPublisher);
retVal.setDate(conformanceDate());
retVal.setFhirVersion("1.0.2"); // TODO: pull from model
retVal.setFhirVersion(FhirVersionEnum.DSTU2.getFhirVersionString());
retVal.setAcceptUnknown(UnknownContentCodeEnum.UNKNOWN_EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser
// needs to be modified to actually allow it

View File

@ -136,7 +136,7 @@ public class BaseResourceReferenceDtTest {
new ResourceReferenceDt("http://foo/123123").loadResource(client);
fail();
} catch (DataFormatException e) {
assertEquals("Unknown resource name \"foo\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage());
assertEquals("Unknown resource name \"123123\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage());
}
try {

View File

@ -1,121 +1,206 @@
package ca.uhn.fhir.parser;
import static org.junit.Assert.assertEquals;
import org.junit.AfterClass;
import org.junit.Ignore;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.util.TestUtil;
import org.junit.AfterClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Created by Sébastien Rivière 12/04/2017
*/
public class ElementWithExtensionDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ca.uhn.fhir.parser.ElementWithExtensionDstu2Test.class);
private static FhirContext ctx = FhirContext.forDstu2();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ca.uhn.fhir.parser.ElementWithExtensionDstu2Test.class);
private final FhirContext ctx = FhirContext.forDstu2();
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
@Ignore
public void testExtensionOnPrimitiveExtensionJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
parser.setServerBaseUrl("http://foo");
final String json = parser.encodeResourceToString(patient);
@Test
public void testExtensionOnPrimitiveExtensionJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
final HumanNameDt name = patient.getNameFirstRep();
name.addFamily(new StringDt("family"));
name.getFamilyFirstRep().addUndeclaredExtension(new ExtensionDt(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")));
ourLog.info(json);
final StringDt stringExt = new StringDt();
stringExt.setValue("myStringExt");
stringExt.addUndeclaredExtension(new ExtensionDt(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")));
final ExtensionDt ext = new ExtensionDt();
ext.setValue(stringExt);
ext.setUrl("/myExt");
patient.addUndeclaredExtension(ext);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getPetName().getUndeclaredExtensions().size());
}
@Test
public void testExtensionOnPrimitiveExtensionXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
parser.setServerBaseUrl("http://foo");
final String xml = parser.encodeResourceToString(patient);
patient.setPetName(new StringDt("myPet"));
patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
ourLog.info(xml);
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getPetName().getUndeclaredExtensions().size());
}
ourLog.info(json);
@Test
@Ignore
public void testExtensionOnIDDatatypeJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
parser.setServerBaseUrl("http://foo");
final String json = parser.encodeResourceToString(patient);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getName().get(0).getFamilyFirstRep().getUndeclaredExtensions().size());
assertEquals(1, ((StringDt) patient.getUndeclaredExtensionsByUrl("/myExt").get(0).getValue()).getUndeclaredExtensions().size());
assertEquals(1, patient.getPetName().getUndeclaredExtensions().size());
}
ourLog.info(json);
@Test
public void testExtensionOnPrimitiveExtensionWithNullValueJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getId().getUndeclaredExtensions().size());
}
ourLog.info(json);
@Test
@Ignore
public void testExtensionOnIDDatatypeXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
parser.setServerBaseUrl("http://foo");
final String xml = parser.encodeResourceToString(patient);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getPetName().getUndeclaredExtensions().size());
}
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getId().getUndeclaredExtensions().size());
}
@Test
public void testExtensionOnPrimitiveExtensionXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
@Test
@Ignore
public void testExtensionOnIDDatatypeExtensionJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
parser.setServerBaseUrl("http://foo");
final String json = parser.encodeResourceToString(patient);
final HumanNameDt name = patient.getNameFirstRep();
name.addFamily(new StringDt("family"));
name.getFamilyFirstRep().addUndeclaredExtension(new ExtensionDt(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")));
ourLog.info(json);
final StringDt stringExt = new StringDt();
stringExt.setValue("myStringExt");
stringExt.addUndeclaredExtension(new ExtensionDt(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK")));
final ExtensionDt ext = new ExtensionDt();
ext.setValue(stringExt);
ext.setUrl("/myExt");
patient.addUndeclaredExtension(ext);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size());
}
@Test
@Ignore
public void testExtensionOnIDDatatypeExtensionXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
parser.setServerBaseUrl("http://foo");
final String xml = parser.encodeResourceToString(patient);
patient.setPetName(new StringDt("myPet"));
patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size());
}
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getName().get(0).getFamilyFirstRep().getUndeclaredExtensions().size());
assertEquals(1, ((StringDt) patient.getUndeclaredExtensionsByUrl("/myExt").get(0).getValue()).getUndeclaredExtensions().size());
assertEquals(1, patient.getPetName().getUndeclaredExtensions().size());
}
@Test
public void testExtensionOnPrimitiveExtensionWithNullValueXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getPetName().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getPetName().getUndeclaredExtensions().size());
}
@Test
public void testExtensionOnIDDatatypeJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
ourLog.info(json);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getId().getUndeclaredExtensions().size());
}
@Test
public void testExtensionOnIDDatatypeXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getId().getUndeclaredExtensions().size());
}
@Test
public void testExtensionOnIDDatatypeExtensionJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.setCustomId(new IdDt("3"));
patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
ourLog.info(json);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size());
}
@Test
public void testExtensionOnIDDatatypeExtensionNullValueJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
ourLog.info(json);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size());
}
@Test
public void testExtensionOnIDDatatypeExtensionXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.setCustomId(new IdDt("4"));
patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size());
}
@Test
public void testExtensionOnIDDatatypeExtensionNullValueXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getCustomId().addUndeclaredExtension(false, "http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringDt("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getCustomId().getUndeclaredExtensions().size());
}
}

View File

@ -1948,4 +1948,24 @@ public class JsonParserDstu2Test {
assertEquals("myName", ((StringDt) newPatient.getUndeclaredExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
}
@Test
public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
String refVal = "http://my.org/FooBar";
Patient fhirPat = new Patient();
fhirPat.addUndeclaredExtension(false, "x1").setValue(new ResourceReferenceDt(refVal));
IParser parser = ourCtx.newJsonParser();
String output = parser.encodeResourceToString(fhirPat);
System.out.println("output: " + output);
// Deserialize then check that valueReference value is still correct
fhirPat = parser.parseResource(Patient.class, output);
List<ExtensionDt> extlst = fhirPat.getUndeclaredExtensionsByUrl("x1");
Assert.assertEquals(1, extlst.size());
Assert.assertEquals(refVal, ((ResourceReferenceDt) extlst.get(0).getValue()).getReference().getValue());
}
}

View File

@ -17,6 +17,7 @@ import org.hamcrest.text.StringContainsInOrder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@ -2790,6 +2791,26 @@ public class XmlParserDstu2Test {
}
@Test
public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
String refVal = "http://my.org/FooBar";
Patient fhirPat = new Patient();
fhirPat.addUndeclaredExtension(false, "x1").setValue(new ResourceReferenceDt(refVal));
IParser parser = ourCtx.newXmlParser();
String output = parser.encodeResourceToString(fhirPat);
System.out.println("output: " + output);
// Deserialize then check that valueReference value is still correct
fhirPat = parser.parseResource(Patient.class, output);
List<ExtensionDt> extlst = fhirPat.getUndeclaredExtensionsByUrl("x1");
Assert.assertEquals(1, extlst.size());
Assert.assertEquals(refVal, ((ResourceReferenceDt) extlst.get(0).getValue()).getReference().getValue());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -68,7 +68,7 @@ public class ClientServerValidationDstu2Test {
@Test
public void testClientUsesInterceptors() throws Exception {
Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0");
conf.setFhirVersion("1.0.2");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -110,7 +110,7 @@ public class ClientServerValidationDstu2Test {
@Test
public void testForceConformanceCheck() throws Exception {
Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0");
conf.setFhirVersion("1.0.2");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -254,6 +254,44 @@ public class ClientServerValidationDstu2Test {
verify(myHttpClient, times(4)).execute(Matchers.any(HttpUriRequest.class));
}
@Test
public void testServerReturnsAppropriateVersionForDstu2() throws Exception {
Conformance conf = new Conformance();
conf.setFhirVersion("1.0.2");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
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<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
if (myFirstResponse) {
myFirstResponse = false;
return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(new Patient())), Charset.forName("UTF-8"));
}
}
});
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
IGenericClient client = myCtx.newRestfulGenericClient("http://foo");
// don't load the conformance until the first time the client is actually used
assertTrue(myFirstResponse);
client.read(new UriDt("http://foo/Patient/123"));
assertFalse(myFirstResponse);
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123"));
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123"));
// Conformance only loaded once, then 3 reads
verify(myHttpClient, times(4)).execute(Matchers.any(HttpUriRequest.class));
}
@Test
public void testServerReturnsWrongVersionForDstu2() throws Exception {
Conformance conf = new Conformance();

View File

@ -351,11 +351,16 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
b.append(myResourceType);
}
if (b.length() > 0) {
if (b.length() > 0 && isNotBlank(myUnqualifiedId)) {
b.append('/');
}
if (isNotBlank(myUnqualifiedId)) {
b.append(myUnqualifiedId);
} else if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');
}
b.append(myUnqualifiedId);
if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');
b.append("_history");
@ -431,7 +436,7 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
@Override
public boolean isEmpty() {
return isBlank(getValue());
return super.isEmpty() && isBlank(getValue());
}
@Override
@ -547,7 +552,21 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
if (typeIndex == -1) {
myResourceType = theValue.substring(0, idIndex);
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
if (typeIndex > 0 && '/' == theValue.charAt(typeIndex - 1)) {
typeIndex = theValue.indexOf('/', typeIndex + 1);
}
if (typeIndex >= idIndex) {
// e.g. http://example.org/foo
// 'foo' was the id but we're making that the resource type. Nullify the id part because we don't have an id.
// Also set null value to the super.setValue() and enable myHaveComponentParts so it forces getValue() to properly
// recreate the url
myResourceType = myUnqualifiedId;
myUnqualifiedId = null;
super.setValue(null);
myHaveComponentParts = true;
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
}
if (typeIndex > 4) {
myBaseUrl = theValue.substring(0, typeIndex);

View File

@ -1,60 +0,0 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.DomainResource;
import org.hl7.fhir.dstu3.model.Narrative;
import org.hl7.fhir.dstu3.model.Resource;
import org.junit.AfterClass;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
public class MinimizeResources {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MinimizeResources.class);
private static FhirContext ourCtx = FhirContext.forDstu3();
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static void main(String[] args) throws Exception {
Collection<File> xml = FileUtils.listFiles(new File("../hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/profile"), new String[] {"xml"}, false);
for (File next : xml) {
ourLog.info("Checking: {}", next.getAbsoluteFile());
String inputFile = IOUtils.toString(new FileReader(next));
Bundle bundle = (Bundle) ourCtx.newXmlParser().parseResource(inputFile);
for (BundleEntryComponent nextEntry : bundle.getEntry()) {
Resource resource;
resource = nextEntry.getResource();
if (resource instanceof DomainResource) {
((DomainResource) resource).setText(new Narrative());
}
}
String output = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(bundle);
if (!output.equals(inputFile)) {
ourLog.info("Rewriting {}", next.getAbsolutePath());
FileWriter writer = new FileWriter(next, false);
writer.append(output);
writer.close();
}
}
}
}

View File

@ -92,6 +92,38 @@ public class IdTypeDstu3Test {
assertEquals("foo/_history/2", id.withVersion("2").getValue());
}
@Test
public void testBaseUrlFoo1() {
IdType id = new IdType("http://my.org/foo");
assertEquals("http://my.org/foo", id.getValueAsString());
assertEquals(null, id.getIdPart());
assertEquals("foo", id.toUnqualified().getValueAsString());
assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals("foo", id.getResourceType());
assertEquals("http://my.org", id.getBaseUrl());
assertEquals("Patient", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("http://my.org/foo//_history/2", id.withVersion("2").getValue());
}
@Test
public void testBaseUrlFoo2() {
IdType id = new IdType("http://my.org/a/b/c/foo");
assertEquals("http://my.org/a/b/c/foo", id.getValueAsString());
assertEquals("foo", id.getIdPart());
assertEquals("c/foo", id.toUnqualified().getValueAsString());
assertEquals("c/foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals("c", id.getResourceType());
assertEquals("http://my.org/a/b", id.getBaseUrl());
assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("http://my.org/a/b/c/foo/_history/2", id.withVersion("2").getValue());
}
@Test
public void testDetectLocal() {
IdType id;

View File

@ -1,116 +1,225 @@
package ca.uhn.fhir.parser;
import static org.junit.Assert.assertEquals;
import org.hl7.fhir.dstu3.model.StringType;
import org.junit.AfterClass;
import org.junit.Ignore;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.dstu3.model.*;
import org.junit.AfterClass;
import org.junit.Test;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
/**
* Created by Sébastien Rivière 12/04/2017
*/
public class ElementWithExtensionDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ca.uhn.fhir.parser.ElementWithExtensionDstu3Test.class);
private static final FhirContext ctx = FhirContext.forDstu3();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ca.uhn.fhir.parser.ElementWithExtensionDstu3Test.class);
private final FhirContext ctx = FhirContext.forDstu3();
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
@Ignore
public void testNullFlavorPrimitiveExtensionJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
@Test
public void testNullFlavorPrimitiveExtensionJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
final HumanName name = patient.getNameFirstRep();
name.setFamily("family");
name.getFamilyElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
ourLog.info(json);
patient.setPetName(new StringType("myPet"));
patient.getExtensionsByUrl("/petname");
patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getPetName().getExtension().size());
}
final StringType stringExt = new StringType();
stringExt.setValue("myStringExt");
stringExt.addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final Extension ext = new Extension();
ext.setValue(stringExt);
ext.setUrl("/myExt");
patient.addExtension(ext);
@Test
@Ignore
public void testNullFlavorPrimitiveExtensionXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
ourLog.info(xml);
ourLog.info(json);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getPetName().getExtension().size());
}
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getName().get(0).getFamilyElement().getExtension().size());
assertEquals(1, patient.getExtensionsByUrl("/myExt").get(0).getValue().getExtension().size());
assertEquals(1, patient.getPetName().getExtension().size());
}
@Test
@Ignore
public void testNullFlavorIDDatatypeJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getIdElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
@Test
public void testNullFlavorPrimitiveExtensionNullValueJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getExtensionsByUrl("/petname");
patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
ourLog.info(json);
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getIdElement().getExtension().size());
}
ourLog.info(json);
@Test
@Ignore
public void testNullFlavorIDDatatypeXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getIdElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getPetName().getExtension().size());
}
ourLog.info(xml);
@Test
public void testNullFlavorPrimitiveExtensionXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
final HumanName name = patient.getNameFirstRep();
name.setFamily("family");
name.getFamilyElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getIdElement().getExtension().size());
}
patient.setPetName(new StringType("myPet"));
patient.getExtensionsByUrl("/petname");
patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
@Test
@Ignore
public void testNullFlavorExtensionIDDatatypeJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getCustomId().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
final StringType stringExt = new StringType();
stringExt.setValue("myStringExt");
stringExt.addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final Extension ext = new Extension();
ext.setValue(stringExt);
ext.setUrl("/myExt");
patient.addExtension(ext);
ourLog.info(json);
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getCustomId().getExtension().size());
}
ourLog.info(xml);
@Test
@Ignore
public void testNullFlavorExtensionIDDatatypeXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getCustomId().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getName().get(0).getFamilyElement().getExtension().size());
assertEquals(1, patient.getExtensionsByUrl("/myExt").get(0).getValue().getExtension().size());
assertEquals(1, patient.getPetName().getExtension().size());
}
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getCustomId().getExtension().size());
}
@Test
public void testNullFlavorPrimitiveExtensionNullValueXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getExtensionsByUrl("/petname");
patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getPetName().getExtension().size());
}
@Test
public void testNullFlavorIDDatatypeJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getIdElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
ourLog.info(json);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getIdElement().getExtension().size());
}
@Test
public void testNullFlavorIDDatatypeXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getIdElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getIdElement().getExtension().size());
}
@Test
public void testNullFlavorExtensionIDDatatypeJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.setCustomId(new IdType(("4")));
patient.getCustomId().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ctx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
ourLog.info(json);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getCustomId().getExtension().size());
}
@Test
public void testNullFlavorExtensionIDDatatypeXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
final IParser parser = ctx.newXmlParser().setPrettyPrint(true);
patient.setCustomId(new IdType(("4")));
patient.getCustomId().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getCustomId().getExtension().size());
}
@Test
public void testExtensionOnResourceIdXml(){
Patient p = new Patient();
p.setActive(true);
p.getIdElement().setValue("123");
p.getIdElement().addExtension().setUrl("http://foo").setValue(new StringType("FOO"));
IParser parser = ctx.newXmlParser();
String encoded = parser.encodeResourceToString(p);
assertThat(encoded, containsString("http://foo"));
assertThat(encoded, containsString("FOO"));
p = (Patient) parser.parseResource(encoded);
assertEquals("Patient/123", p.getId());
Extension ex = p.getIdElement().getExtension().get(0);
assertEquals("http://foo", ex.getUrl());
assertEquals("FOO", ex.getValueAsPrimitive().getValueAsString());
}
@Test
public void testExtensionOnResourceIdJson(){
Patient p = new Patient();
p.setActive(true);
p.getIdElement().setValue("123");
p.getIdElement().addExtension().setUrl("http://foo").setValue(new StringType("FOO"));
IParser parser = ctx.newJsonParser();
String encoded = parser.encodeResourceToString(p);
assertThat(encoded, containsString("http://foo"));
assertThat(encoded, containsString("FOO"));
p = (Patient) parser.parseResource(encoded);
assertEquals("Patient/123", p.getId());
Extension ex = p.getIdElement().getExtension().get(0);
assertEquals("http://foo", ex.getUrl());
assertEquals("FOO", ex.getValueAsPrimitive().getValueAsString());
}
}

View File

@ -2414,6 +2414,26 @@ public class JsonParserDstu3Test {
assertTrue(result.isSuccessful());
}
@Test
public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
String refVal = "http://my.org/FooBar";
Patient fhirPat = new Patient();
fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal));
IParser parser = ourCtx.newJsonParser();
String output = parser.encodeResourceToString(fhirPat);
System.out.println("output: " + output);
// Deserialize then check that valueReference value is still correct
fhirPat = parser.parseResource(Patient.class, output);
List<Extension> extlst = fhirPat.getExtensionsByUrl("x1");
Assert.assertEquals(1, extlst.size());
Assert.assertEquals(refVal, ((Reference) extlst.get(0).getValue()).getReference());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -11,42 +11,42 @@ import org.hl7.fhir.dstu3.model.StringType;
@ResourceDef()
public class MyPatientWithCustomUrlExtension extends Patient {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;
@Child(name = "petName")
@Extension(url = "/petname", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The name of the patient's favourite pet")
private StringType myPetName;
@Child(name = "petName")
@Extension(url = "/petname", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The name of the patient's favourite pet")
private StringType myPetName;
@Child(name = "customid")
@Extension(url = "/customid", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The customid of the patient's ")
private IdType myCustomId;
@Child(name = "customid")
@Extension(url = "/customid", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The customid of the patient's ")
private IdType myCustomId;
public StringType getPetName() {
if (myPetName == null) {
myPetName = new StringType();
public StringType getPetName() {
if (myPetName == null) {
myPetName = new StringType();
}
return myPetName;
}
return myPetName;
}
public void setPetName(final StringType thePetName) {
myPetName = thePetName;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && myPetName.isEmpty();
}
public IdType getCustomId() {
if (myCustomId == null) {
myCustomId = new IdType();
public void setPetName(final StringType thePetName) {
myPetName = thePetName;
}
return myCustomId;
}
public void setCustomId(final IdType myCustomId) {
this.myCustomId = myCustomId;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && getCustomId().isEmpty() && getPetName().isEmpty();
}
public IdType getCustomId() {
if (myCustomId == null) {
myCustomId = new IdType();
}
return myCustomId;
}
public void setCustomId(final IdType myCustomId) {
this.myCustomId = myCustomId;
}
}

View File

@ -3334,6 +3334,26 @@ public class XmlParserDstu3Test {
}
@Test
public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
String refVal = "http://my.org/FooBar";
Patient fhirPat = new Patient();
fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal));
IParser parser = ourCtx.newXmlParser();
String output = parser.encodeResourceToString(fhirPat);
System.out.println("output: " + output);
// Deserialize then check that valueReference value is still correct
fhirPat = parser.parseResource(Patient.class, output);
List<Extension> extlst = fhirPat.getExtensionsByUrl("x1");
Assert.assertEquals(1, extlst.size());
Assert.assertEquals(refVal, ((Reference) extlst.get(0).getValue()).getReference());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();

View File

@ -30,6 +30,7 @@ import java.util.jar.Manifest;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.*;
import org.hl7.fhir.instance.model.Conformance.*;
@ -158,7 +159,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setPublisher(myPublisher);
retVal.setDate(conformanceDate());
retVal.setFhirVersion("1.0.2"); // TODO: pull from model
retVal.setFhirVersion(FhirVersionEnum.DSTU2_HL7ORG.getFhirVersionString());
retVal.setAcceptUnknown(UnknownContentCode.EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser
// needs to be modified to actually allow it

View File

@ -380,11 +380,16 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
b.append(myResourceType);
}
if (b.length() > 0) {
if (b.length() > 0 && isNotBlank(myUnqualifiedId)) {
b.append('/');
}
if (isNotBlank(myUnqualifiedId)) {
b.append(myUnqualifiedId);
} else if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');
}
b.append(myUnqualifiedId);
if (isNotBlank(myUnqualifiedVersionId)) {
b.append('/');
b.append("_history");
@ -571,7 +576,21 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
if (typeIndex == -1) {
myResourceType = theValue.substring(0, idIndex);
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
if (typeIndex > 0 && '/' == theValue.charAt(typeIndex - 1)) {
typeIndex = theValue.indexOf('/', typeIndex + 1);
}
if (typeIndex >= idIndex) {
// e.g. http://example.org/foo
// 'foo' was the id but we're making that the resource type. Nullify the id part because we don't have an id.
// Also set null value to the super.setValue() and enable myHaveComponentParts so it forces getValue() to properly
// recreate the url
myResourceType = myUnqualifiedId;
myUnqualifiedId = null;
super.setValue(null);
myHaveComponentParts = true;
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
}
if (typeIndex > 4) {
myBaseUrl = theValue.substring(0, typeIndex);

View File

@ -83,7 +83,55 @@ public class IdTypeTest {
assertEquals("Patient/cid:Patient-72/_history/1", id2.getIdPart());
}
@Test
public void testNormal() {
IdType id = new IdType("foo");
assertEquals("foo", id.getValueAsString());
assertEquals("foo", id.getIdPart());
assertEquals("foo", id.toUnqualified().getValueAsString());
assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals(null, id.getResourceType());
assertEquals(null, id.getBaseUrl());
assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("foo/_history/2", id.withVersion("2").getValue());
}
@Test
public void testBaseUrlFoo1() {
IdType id = new IdType("http://my.org/foo");
assertEquals("http://my.org/foo", id.getValueAsString());
assertEquals(null, id.getIdPart());
assertEquals("foo", id.toUnqualified().getValueAsString());
assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals("foo", id.getResourceType());
assertEquals("http://my.org", id.getBaseUrl());
assertEquals("Patient", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("http://my.org/foo//_history/2", id.withVersion("2").getValue());
}
@Test
public void testBaseUrlFoo2() {
IdType id = new IdType("http://my.org/a/b/c/foo");
assertEquals("http://my.org/a/b/c/foo", id.getValueAsString());
assertEquals("foo", id.getIdPart());
assertEquals("c/foo", id.toUnqualified().getValueAsString());
assertEquals("c/foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals("c", id.getResourceType());
assertEquals("http://my.org/a/b", id.getBaseUrl());
assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("http://my.org/a/b/c/foo/_history/2", id.withVersion("2").getValue());
}
@Test
public void testDetermineBase() {

View File

@ -1,13 +1,13 @@
package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.util.TestUtil;
import net.sf.json.JSON;
import net.sf.json.JSONSerializer;
import org.apache.commons.io.IOUtils;
import org.hamcrest.core.IsNot;
import org.hamcrest.core.StringContains;
@ -22,19 +22,23 @@ import org.hl7.fhir.instance.model.Narrative.NarrativeStatus;
import org.hl7.fhir.instance.model.Patient.ContactComponent;
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
import org.hl7.fhir.instance.model.ValueSet.ValueSetCodeSystemComponent;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.INarrative;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.*;
import org.xml.sax.SAXException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.util.TestUtil;
import net.sf.json.JSON;
import net.sf.json.JSONSerializer;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class JsonParserHl7OrgDstu2Test {
private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org();
@ -1274,6 +1278,26 @@ public class JsonParserHl7OrgDstu2Test {
}
@Test
public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
String refVal = "http://my.org/FooBar";
Patient fhirPat = new Patient();
fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal));
IParser parser = ourCtx.newJsonParser();
String output = parser.encodeResourceToString(fhirPat);
System.out.println("output: " + output);
// Deserialize then check that valueReference value is still correct
fhirPat = parser.parseResource(Patient.class, output);
List<Extension> extlst = fhirPat.getExtension();
Assert.assertEquals(1, extlst.size());
Assert.assertEquals(refVal, ((Reference) extlst.get(0).getValue()).getReference());
}
@ResourceDef(name = "Patient")
public static class MyPatientWithOneDeclaredAddressExtension extends Patient {

View File

@ -21,6 +21,24 @@ import org.hl7.fhir.instance.model.Identifier.IdentifierUse;
import org.hl7.fhir.instance.model.Narrative.NarrativeStatus;
import org.hl7.fhir.instance.model.api.*;
import org.junit.*;
import org.hl7.fhir.instance.model.Observation;
import org.hl7.fhir.instance.model.Organization;
import org.hl7.fhir.instance.model.Patient;
import org.hl7.fhir.instance.model.PrimitiveType;
import org.hl7.fhir.instance.model.Reference;
import org.hl7.fhir.instance.model.Resource;
import org.hl7.fhir.instance.model.SimpleQuantity;
import org.hl7.fhir.instance.model.Specimen;
import org.hl7.fhir.instance.model.StringType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.INarrative;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.junit.After;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.xml.sax.SAXException;
import org.xmlunit.builder.DiffBuilder;
import org.xmlunit.builder.Input;
@ -1738,6 +1756,26 @@ public class XmlParserHl7OrgDstu2Test {
}
@Test
public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() {
String refVal = "http://my.org/FooBar";
Patient fhirPat = new Patient();
fhirPat.addExtension().setUrl("x1").setValue(new Reference(refVal));
IParser parser = ourCtx.newXmlParser();
String output = parser.encodeResourceToString(fhirPat);
System.out.println("output: " + output);
// Deserialize then check that valueReference value is still correct
fhirPat = parser.parseResource(Patient.class, output);
List<Extension> extlst = fhirPat.getExtension();
Assert.assertEquals(1, extlst.size());
Assert.assertEquals(refVal, ((Reference) extlst.get(0).getValue()).getReference());
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu2Hl7Org();

View File

@ -57,46 +57,9 @@ public class ClientServerValidationTestHl7OrgDstu2 {
}
@Test
public void testServerReturnsAppropriateVersionForDstu2_040() throws Exception {
public void testServerReturnsAppropriateVersionForDstu2() throws Exception {
Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
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<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
if (myFirstResponse) {
myFirstResponse=false;
return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(new Patient())), Charset.forName("UTF-8"));
}
}});
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE);
IGenericClient client = myCtx.newRestfulGenericClient("http://foo");
// don't load the conformance until the first time the client is actually used
assertTrue(myFirstResponse);
client.read(new UriDt("http://foo/Patient/123"));
assertFalse(myFirstResponse);
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123"));
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123"));
// Conformance only loaded once, then 3 reads
verify(myHttpClient, times(4)).execute(Matchers.any(HttpUriRequest.class));
}
@Test
public void testServerReturnsAppropriateVersionForDstu2_050() throws Exception {
Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0");
conf.setFhirVersion("1.0.2");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -133,7 +96,7 @@ public class ClientServerValidationTestHl7OrgDstu2 {
@Test
public void testServerReturnsWrongVersionForDstu2() throws Exception {
Conformance conf = new Conformance();
conf.setFhirVersion("0.80");
conf.setFhirVersion("0.0.82");
String msg = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -150,7 +113,7 @@ public class ClientServerValidationTestHl7OrgDstu2 {
fail();
} catch (FhirClientInappropriateForServerException e) {
String out = e.toString();
String want = "The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"0.80\" which corresponds to DSTU1, but this client is configured to use DSTU2_HL7ORG (via the FhirContext)";
String want = "The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"0.0.82\" which corresponds to DSTU1, but this client is configured to use DSTU2_HL7ORG (via the FhirContext)";
ourLog.info(out);
ourLog.info(want);
assertThat(out, containsString(want));

View File

@ -1,138 +0,0 @@
package org.hl7.fhir.r4.model.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* 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%
*/
/**
* Base interface for ID datatype.
*
* <p>
* <b>Concrete Implementations:</b> This interface is often returned and/or accepted by methods in HAPI's API
* where either {@link ca.uhn.fhir.model.primitive.IdDt} (the HAPI structure ID type) or
* <code>org.hl7.fhir.instance.model.IdType</code> (the RI structure ID type) will be used, depending on
* which version of the strctures your application is using.
* </p>
*/
public interface IIdType {
void applyTo(IBaseResource theResource);
/**
* Returns the server base URL if this ID contains one. For example, the base URL is
* the 'http://example.com/fhir' in the following ID: <code>http://example.com/fhir/Patient/123/_history/55</code>
*/
String getBaseUrl();
/**
* Returns only the logical ID part of this ID. For example, given the ID
* "http://example,.com/fhir/Patient/123/_history/456", this method would
* return "123".
*/
String getIdPart();
/**
* Returns the ID part of this ID (e.g. in the ID http://example.com/Patient/123/_history/456 this would be the
* part "123") parsed as a {@link Long}.
*
* @throws NumberFormatException If the value can't be parsed as a long
*/
Long getIdPartAsLong();
String getResourceType();
/**
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to get just the ID portion.
*
* @see #getIdPart()
*/
String getValue();
String getVersionIdPart();
/**
* Returns the version ID part of this ID (e.g. in the ID http://example.com/Patient/123/_history/456 this would be the
* part "456") parsed as a {@link Long}.
*
* @throws NumberFormatException If the value can't be parsed as a long
*/
Long getVersionIdPartAsLong();
boolean hasBaseUrl();
/**
* Returns <code>true</code> if this ID contains an actual ID part. For example, the ID part is
* the '123' in the following ID: <code>http://example.com/fhir/Patient/123/_history/55</code>
*/
boolean hasIdPart();
boolean hasResourceType();
boolean hasVersionIdPart();
/**
* Returns <code>true</code> if this ID contains an absolute URL (in other words, a URL starting with "http://" or "https://"
*/
boolean isAbsolute();
boolean isEmpty();
/**
* Returns <code>true</code> if the {@link #getIdPart() ID part of this object} is valid according to the FHIR rules for valid IDs.
* <p>
* The FHIR specification states:
* <code>Any combination of upper or lower case ASCII letters ('A'..'Z', and 'a'..'z', numerals ('0'..'9'), '-' and '.', with a length limit of 64 characters. (This might be an integer, an un-prefixed OID, UUID or any other identifier pattern that meets these constraints.) regex: [A-Za-z0-9\-\.]{1,64}</code>
* </p>
*/
boolean isIdPartValid();
/**
* Returns <code>true</code> if the {@link #getIdPart() ID part of this object} contains
* only numbers
*/
boolean isIdPartValidLong();
/**
* Returns <code>true</code> if the ID is a local reference (in other words, it begins with the '#' character)
*/
boolean isLocal();
/**
* Returns <code>true</code> if the {@link #getVersionIdPart() version ID part of this object} contains
* only numbers
*/
boolean isVersionIdPartValidLong();
IIdType setValue(String theString);
IIdType toUnqualified();
IIdType toUnqualifiedVersionless();
IIdType toVersionless();
IIdType withResourceType(String theResName);
IIdType withServerBase(String theServerBase, String theResourceName);
IIdType withVersion(String theVersion);
}

View File

@ -0,0 +1,224 @@
package ca.uhn.fhir.parser;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.r4.model.*;
import org.junit.AfterClass;
import org.junit.Test;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.*;
/**
* Created by Sébastien Rivière 12/04/2017
*/
public class ElementWithExtensionR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElementWithExtensionR4Test.class);
private static FhirContext ourCtx = FhirContext.forR4();
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void testNullFlavorPrimitiveExtensionJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
final HumanName name = patient.getNameFirstRep();
name.setFamily("family");
name.getFamilyElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
patient.setPetName(new StringType("myPet"));
patient.getExtensionsByUrl("/petname");
patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final StringType stringExt = new StringType();
stringExt.setValue("myStringExt");
stringExt.addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final Extension ext = new Extension();
ext.setValue(stringExt);
ext.setUrl("/myExt");
patient.addExtension(ext);
final IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
ourLog.info(json);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getName().get(0).getFamilyElement().getExtension().size());
assertEquals(1, patient.getExtensionsByUrl("/myExt").get(0).getValue().getExtension().size());
assertEquals(1, patient.getPetName().getExtension().size());
}
@Test
public void testNullFlavorPrimitiveExtensionNullValueJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getExtensionsByUrl("/petname");
patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
ourLog.info(json);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getPetName().getExtension().size());
}
@Test
public void testNullFlavorPrimitiveExtensionXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
final HumanName name = patient.getNameFirstRep();
name.setFamily("family");
name.getFamilyElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
patient.setPetName(new StringType("myPet"));
patient.getExtensionsByUrl("/petname");
patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final StringType stringExt = new StringType();
stringExt.setValue("myStringExt");
stringExt.addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final Extension ext = new Extension();
ext.setValue(stringExt);
ext.setUrl("/myExt");
patient.addExtension(ext);
final IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getName().get(0).getFamilyElement().getExtension().size());
assertEquals(1, patient.getExtensionsByUrl("/myExt").get(0).getValue().getExtension().size());
assertEquals(1, patient.getPetName().getExtension().size());
}
@Test
public void testNullFlavorPrimitiveExtensionNullValueXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getExtensionsByUrl("/petname");
patient.getPetName().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getPetName().getExtension().size());
}
@Test
public void testNullFlavorIDDatatypeJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getIdElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
ourLog.info(json);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getIdElement().getExtension().size());
}
@Test
public void testNullFlavorIDDatatypeXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.getIdElement().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getIdElement().getExtension().size());
}
@Test
public void testNullFlavorExtensionIDDatatypeJson() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
patient.setCustomId(new IdType(("4")));
patient.getCustomId().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
final String json = parser.encodeResourceToString(patient);
ourLog.info(json);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, json);
assertEquals(1, patient.getCustomId().getExtension().size());
}
@Test
public void testNullFlavorExtensionIDDatatypeXml() throws Exception {
MyPatientWithCustomUrlExtension patient = new MyPatientWithCustomUrlExtension();
patient.setId("1");
final IParser parser = ourCtx.newXmlParser().setPrettyPrint(true);
patient.setCustomId(new IdType(("4")));
patient.getCustomId().addExtension("http://hl7.org/fhir/StructureDefinition/iso21090-nullFlavor", new StringType("UNK"));
final String xml = parser.encodeResourceToString(patient);
ourLog.info(xml);
patient = parser.parseResource(MyPatientWithCustomUrlExtension.class, xml);
assertEquals(1, patient.getCustomId().getExtension().size());
}
@Test
public void testExtensionOnResourceIdXml(){
Patient p = new Patient();
p.setActive(true);
p.getIdElement().setValue("123");
p.getIdElement().addExtension().setUrl("http://foo").setValue(new StringType("FOO"));
IParser parser = ourCtx.newXmlParser();
String encoded = parser.encodeResourceToString(p);
assertThat(encoded, containsString("http://foo"));
assertThat(encoded, containsString("FOO"));
p = (Patient) parser.parseResource(encoded);
assertEquals("Patient/123", p.getId());
Extension ex = p.getIdElement().getExtension().get(0);
assertEquals("http://foo", ex.getUrl());
assertEquals("FOO", ex.getValueAsPrimitive().getValueAsString());
}
@Test
public void testExtensionOnResourceIdJson(){
Patient p = new Patient();
p.setActive(true);
p.getIdElement().setValue("123");
p.getIdElement().addExtension().setUrl("http://foo").setValue(new StringType("FOO"));
IParser parser = ourCtx.newJsonParser();
String encoded = parser.encodeResourceToString(p);
assertThat(encoded, containsString("http://foo"));
assertThat(encoded, containsString("FOO"));
p = (Patient) parser.parseResource(encoded);
assertEquals("Patient/123", p.getId());
Extension ex = p.getIdElement().getExtension().get(0);
assertEquals("http://foo", ex.getUrl());
assertEquals("FOO", ex.getValueAsPrimitive().getValueAsString());
}
}

View File

@ -0,0 +1,52 @@
package ca.uhn.fhir.parser;
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 org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
@ResourceDef()
public class MyPatientWithCustomUrlExtension extends Patient {
private static final long serialVersionUID = 1L;
@Child(name = "petName")
@Extension(url = "/petname", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The name of the patient's favourite pet")
private StringType myPetName;
@Child(name = "customid")
@Extension(url = "/customid", definedLocally = false, isModifier = false)
@Description(shortDefinition = "The customid of the patient's ")
private IdType myCustomId;
public StringType getPetName() {
if (myPetName == null) {
myPetName = new StringType();
}
return myPetName;
}
public void setPetName(final StringType thePetName) {
myPetName = thePetName;
}
@Override
public boolean isEmpty() {
return super.isEmpty() && getCustomId().isEmpty() && getPetName().isEmpty();
}
public IdType getCustomId() {
if (myCustomId == null) {
myCustomId = new IdType();
}
return myCustomId;
}
public void setCustomId(final IdType myCustomId) {
this.myCustomId = myCustomId;
}
}

View File

@ -243,7 +243,7 @@ public class ClientR4Test {
CapabilityStatement cs = new CapabilityStatement();
cs.getPublisherElement().setValue("Health Intersections");
String msg = ourCtx.newXmlParser().encodeResourceToString(cs);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
@ -389,11 +389,11 @@ public class ClientR4Test {
String msg = getPatient();
ourLog.info(msg);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
Header[] headers = new Header[] {
Header[] headers = new Header[] {
new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333")
};
@ -1138,7 +1138,7 @@ public class ClientR4Test {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setDiagnostics("ALL GOOD");
String resp = ourCtx.newJsonParser().encodeResourceToString(oo);
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:foo").setValue("123");
@ -1161,12 +1161,12 @@ public class ClientR4Test {
assertNull(response.getResource());
}
@Test
public void testVRead() throws Exception {
//@formatter:off
String msg = "<Patient xmlns=\"http://hl7.org/fhir\">"
String msg = "<Patient xmlns=\"http://hl7.org/fhir\">"
+ "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal: 444333333 </div></text>"
+ "<identifier><label value=\"SSN\" /><system value=\"http://orionhealth.com/mrn\" /><value value=\"PRP1660\" /></identifier>"
+ "<name><use value=\"official\" /><family value=\"Cardinal\" /><given value=\"John\" /></name>"
@ -1216,7 +1216,7 @@ public class ClientR4Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
private static String getPatientFeedWithOneResult() {
static String getPatientFeedWithOneResult() {
return getPatientFeedWithOneResult(ourCtx);
}

View File

@ -87,6 +87,7 @@ public class ClientServerValidationDstu1Test {
}
@Test
@Ignore
public void testServerReturnsWrongVersionDstu() throws Exception {
CapabilityStatement conf = new CapabilityStatement();
conf.setFhirVersion("0.4.0");

View File

@ -108,7 +108,31 @@ public class GenericClientR4Test {
return capt;
}
@Test
@Test
public void testSearchWithNoExplicitBundleReturnType() throws Exception {
String msg = ClientR4Test.getPatientFeedWithOneResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
// httpResponse = new BasicHttpResponse(statusline, catalog, locale)
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
IGenericClient client = ourCtx.newRestfulGenericClient("http://foo");
Bundle response = (Bundle) client.search().forResource(Patient.class).execute();
assertEquals("http://foo/Patient", capt.getValue().getURI().toString());
Patient patient = (Patient) response.getEntry().get(0).getResource();
assertEquals("PRP1660", patient.getIdentifier().get(0).getValueElement().getValue());
}
@Test
public void testAcceptHeaderWithEncodingSpecified() throws Exception {
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";

View File

@ -0,0 +1,336 @@
package org.hl7.fhir.r4.model;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.util.TestUtil;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.math.BigDecimal;
import static org.junit.Assert.*;
public class IdTypeR4Test {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdTypeR4Test.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void testBaseUrlFoo1() {
IdType id = new IdType("http://my.org/foo");
assertEquals("http://my.org/foo", id.getValueAsString());
assertEquals(null, id.getIdPart());
assertEquals("foo", id.toUnqualified().getValueAsString());
assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals("foo", id.getResourceType());
assertEquals("http://my.org", id.getBaseUrl());
assertEquals("Patient", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("http://my.org/foo//_history/2", id.withVersion("2").getValue());
}
@Test
public void testBaseUrlFoo2() {
IdType id = new IdType("http://my.org/a/b/c/foo");
assertEquals("http://my.org/a/b/c/foo", id.getValueAsString());
assertEquals("foo", id.getIdPart());
assertEquals("c/foo", id.toUnqualified().getValueAsString());
assertEquals("c/foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals("c", id.getResourceType());
assertEquals("http://my.org/a/b", id.getBaseUrl());
assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("http://my.org/a/b/c/foo/_history/2", id.withVersion("2").getValue());
}
@Test
public void testUuid() {
IdType id = new IdType("urn:uuid:1234-5678");
assertEquals("urn:uuid:1234-5678", id.getValueAsString());
assertEquals("urn:uuid:1234-5678", id.getIdPart());
assertEquals("urn:uuid:1234-5678", id.toUnqualified().getValueAsString());
assertEquals("urn:uuid:1234-5678", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals(null, id.getResourceType());
assertEquals(null, id.getBaseUrl());
assertEquals("urn:uuid:1234-5678", id.withResourceType("Patient").getValue());
assertEquals("urn:uuid:1234-5678", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("urn:uuid:1234-5678", id.withVersion("2").getValue());
}
@Test
public void testOid() {
IdType id = new IdType("urn:oid:1.2.3.4");
assertEquals("urn:oid:1.2.3.4", id.getValueAsString());
assertEquals("urn:oid:1.2.3.4", id.getIdPart());
assertEquals("urn:oid:1.2.3.4", id.toUnqualified().getValueAsString());
assertEquals("urn:oid:1.2.3.4", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals(null, id.getResourceType());
assertEquals(null, id.getBaseUrl());
assertEquals("urn:oid:1.2.3.4", id.withResourceType("Patient").getValue());
assertEquals("urn:oid:1.2.3.4", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("urn:oid:1.2.3.4", id.withVersion("2").getValue());
}
@Test
public void testLocal() {
IdType id = new IdType("#foo");
assertEquals("#foo", id.getValueAsString());
assertEquals("#foo", id.getIdPart());
assertEquals("#foo", id.toUnqualified().getValueAsString());
assertEquals("#foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals(null, id.getResourceType());
assertEquals(null, id.getBaseUrl());
assertEquals("#foo", id.withResourceType("Patient").getValue());
assertEquals("#foo", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("#foo", id.withVersion("2").getValue());
}
@Test
public void testNormal() {
IdType id = new IdType("foo");
assertEquals("foo", id.getValueAsString());
assertEquals("foo", id.getIdPart());
assertEquals("foo", id.toUnqualified().getValueAsString());
assertEquals("foo", id.toUnqualifiedVersionless().getValueAsString());
assertEquals(null, id.getVersionIdPart());
assertEquals(null, id.getResourceType());
assertEquals(null, id.getBaseUrl());
assertEquals("Patient/foo", id.withResourceType("Patient").getValue());
assertEquals("http://foo/Patient/foo", id.withServerBase("http://foo", "Patient").getValue());
assertEquals("foo/_history/2", id.withVersion("2").getValue());
}
@Test
public void testDetectLocal() {
IdType id;
id = new IdType("#123");
assertEquals("#123", id.getValue());
assertTrue(id.isLocal());
id = new IdType("#Medication/499059CE-CDD4-48BC-9014-528A35D15CED/_history/1");
assertEquals("#Medication/499059CE-CDD4-48BC-9014-528A35D15CED/_history/1", id.getValue());
assertTrue(id.isLocal());
id = new IdType("http://example.com/Patient/33#123");
assertEquals("http://example.com/Patient/33#123", id.getValue());
assertFalse(id.isLocal());
}
@Test
public void testConstructorsWithNullArguments() {
IdType id = new IdType(null, null, null);
assertEquals(null, id.getValue());
}
@Test
public void testDetectLocalBase() {
assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getValue());
assertEquals(null, new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl());
assertEquals("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("urn:uuid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart());
assertEquals("cid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getValue());
assertEquals(null, new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl());
assertEquals("cid:180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("cid:180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart());
assertEquals("#180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getValue());
assertEquals(null, new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getBaseUrl());
assertEquals("#180f219f-97a8-486d-99d9-ed631fe4fc57", new IdType("#180f219f-97a8-486d-99d9-ed631fe4fc57").getIdPart());
}
/**
* See #67
*/
@Test
public void testComplicatedLocal() {
IdType id = new IdType("#Patient/cid:Patient-72/_history/1");
assertTrue(id.isLocal());
assertEquals(null, id.getBaseUrl());
assertNull(id.getResourceType());
assertNull(id.getVersionIdPart());
assertEquals("#Patient/cid:Patient-72/_history/1", id.getIdPart());
IdType id2 = new IdType("#Patient/cid:Patient-72/_history/1");
assertEquals(id, id2);
id2 = id2.toUnqualified();
assertTrue(id2.isLocal());
assertNull(id2.getBaseUrl());
assertNull(id2.getResourceType());
assertNull(id2.getVersionIdPart());
assertEquals("#Patient/cid:Patient-72/_history/1", id2.getIdPart());
}
@Test
public void testDetermineBase() {
IdType rr;
rr = new IdType("http://foo/fhir/Organization/123");
assertEquals("http://foo/fhir", rr.getBaseUrl());
rr = new IdType("http://foo/fhir/Organization/123/_history/123");
assertEquals("http://foo/fhir", rr.getBaseUrl());
rr = new IdType("Organization/123/_history/123");
assertEquals(null, rr.getBaseUrl());
}
@Test
public void testParseValueAbsolute() {
Patient patient = new Patient();
IdType rr = new IdType();
rr.setValue("http://foo/fhir/Organization/123");
patient.setManagingOrganization(new Reference(rr));
Patient actual = parseAndEncode(patient);
Reference ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getReferenceElement().getResourceType());
assertEquals("123", ref.getReferenceElement().getIdPart());
}
@Test
public void testBigDecimalIds() {
IdType id = new IdType(new BigDecimal("123"));
assertEquals(id.getIdPartAsBigDecimal(), new BigDecimal("123"));
}
@Test
public void testParseValueAbsoluteWithVersion() {
Patient patient = new Patient();
IdType rr = new IdType();
rr.setValue("http://foo/fhir/Organization/123/_history/999");
patient.setManagingOrganization(new Reference(rr));
Patient actual = parseAndEncode(patient);
Reference ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getReferenceElement().getResourceType());
assertEquals("123", ref.getReferenceElement().getIdPart());
assertEquals(null, ref.getReferenceElement().getVersionIdPart());
}
@Test
public void testViewMethods() {
IdType i = new IdType("http://foo/fhir/Organization/123/_history/999");
assertEquals("Organization/123/_history/999", i.toUnqualified().getValue());
assertEquals("http://foo/fhir/Organization/123", i.toVersionless().getValue());
assertEquals("Organization/123", i.toUnqualifiedVersionless().getValue());
}
@Test
public void testParseValueWithVersion() {
Patient patient = new Patient();
IdType rr = new IdType();
rr.setValue("/123/_history/999");
patient.setManagingOrganization(new Reference(rr));
Patient actual = parseAndEncode(patient);
Reference ref = actual.getManagingOrganization();
assertEquals(null, ref.getReferenceElement().getResourceType());
assertEquals("123", ref.getReferenceElement().getIdPart());
assertEquals(null, ref.getReferenceElement().getVersionIdPart());
}
@Test
public void testParseValueMissingType1() {
Patient patient = new Patient();
IdType rr = new IdType();
rr.setValue("/123");
patient.setManagingOrganization(new Reference(rr));
Patient actual = parseAndEncode(patient);
Reference ref = actual.getManagingOrganization();
assertEquals(null, ref.getReferenceElement().getResourceType());
assertEquals("123", ref.getReferenceElement().getIdPart());
}
@Test
public void testParseValueMissingType2() {
Patient patient = new Patient();
IdType rr = new IdType();
rr.setValue("123");
patient.setManagingOrganization(new Reference(rr));
Patient actual = parseAndEncode(patient);
Reference ref = actual.getManagingOrganization();
assertEquals(null, ref.getReferenceElement().getResourceType());
assertEquals("123", ref.getReferenceElement().getIdPart());
}
@Test
public void testParseValueRelative1() {
Patient patient = new Patient();
IdType rr = new IdType();
rr.setValue("Organization/123");
patient.setManagingOrganization(new Reference(rr));
Patient actual = parseAndEncode(patient);
Reference ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getReferenceElement().getResourceType());
assertEquals("123", ref.getReferenceElement().getIdPart());
}
@Test
public void testEncodeParts() {
IdType id = new IdType("http://foo", "Patient", "123", "456");
assertEquals("http://foo/Patient/123/_history/456", id.getValue());
assertEquals("http://foo/Patient/123/_history/9", id.withVersion("9").getValue());
}
@Test
public void testParseValueRelative2() {
Patient patient = new Patient();
IdType rr = new IdType();
rr.setValue("/Organization/123");
patient.setManagingOrganization(new Reference(rr));
Patient actual = parseAndEncode(patient);
Reference ref = actual.getManagingOrganization();
assertEquals("Organization", ref.getReferenceElement().getResourceType());
assertEquals("123", ref.getReferenceElement().getIdPart());
}
private Patient parseAndEncode(Patient patient) {
String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
ourLog.info("\n" + encoded);
return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forR4();
}
}

View File

@ -70,14 +70,88 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
)
);
} else if (theSearchParamType == 'token') {
var tokenQualifiers = new Array();
tokenQualifiers.push(new Object());
tokenQualifiers[0].name='Matches';
tokenQualifiers[0].value='';
tokenQualifiers.push(new Object());
tokenQualifiers[1].name='Text';
tokenQualifiers[1].value=':text';
tokenQualifiers[1].description='The search parameter is processed as a string that searches text associated with the code/value.';
tokenQualifiers.push(new Object());
tokenQualifiers[2].name='Not';
tokenQualifiers[2].value=':not';
tokenQualifiers[2].description='Reverse the code matching described in the paragraph above. Note that this includes resources that have no value for the parameter.';
tokenQualifiers.push(new Object());
tokenQualifiers[3].name='Above';
tokenQualifiers[3].value=':above';
tokenQualifiers[3].description='The search parameter is a concept with the form [system]|[code], and the search parameter tests whether the coding in a resource subsumes the specified search code. For example, the search concept has an is-a relationship with the coding in the resource, and this includes the coding itself.';
tokenQualifiers.push(new Object());
tokenQualifiers[4].name='Below';
tokenQualifiers[4].value=':below';
tokenQualifiers[4].description='The search parameter is a concept with the form [system]|[code], and the search parameter tests whether the coding in a resource is subsumed by the specified search code. For example, the coding in the resource has an is-a relationship with the search concept, and this includes the coding itself.';
tokenQualifiers.push(new Object());
tokenQualifiers[5].name='In';
tokenQualifiers[5].value=':in';
tokenQualifiers[5].description='The search parameter is a URI (relative or absolute) that identifies a value set, and the search parameter tests whether the coding is in the specified value set. The reference may be literal (to an address where the value set can be found) or logical (a reference to ValueSet.url). If the server can treat the reference as a literal URL, it does, else it tries to match known logical ValueSet.url values.';
tokenQualifiers.push(new Object());
tokenQualifiers[6].name='Not-in';
tokenQualifiers[6].value=':not-in';
tokenQualifiers[6].description='The search parameter is a URI (relative or absolute) that identifies a value set, and the search parameter tests whether the coding is not in the specified value set.';
var tokenQualifierInput = $('<input />', { id: 'param.' + theRowNum + '.qualifier', type: 'hidden' });
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<div />', { 'class': 'col-sm-3' }).append(
$('<input />', { id: 'param.' + theRowNum + '.0', placeholder: 'system/namespace', type: 'text', 'class': 'form-control' })
),
$('<div />', { 'class': 'col-sm-3' }).append(
$('<input />', { id: 'param.' + theRowNum + '.1', placeholder: 'value', type: 'text', 'class': 'form-control' })
tokenQualifierInput
);
function clickTokenFunction(value, name){
return function(){
tokenQualifierInput.val(value);
tokenQualifierLabel.text(name);
}
};
var tokenQualifierLabel = $('<span>' + tokenQualifiers[0].name + '</span>');
var tokenQualifierDropdown = $('<ul />', {'class':'dropdown-menu', role:'menu'});
for (var i = 0; i < tokenQualifiers.length; i++) {
var qualName = tokenQualifiers[i].name;
var nextValue = tokenQualifiers[i].value;
var nextLink = $('<a>' + tokenQualifiers[i].name+'</a>');
tokenQualifierDropdown.append($('<li />').append(nextLink));
nextLink.click(clickTokenFunction(nextValue, qualName));
}
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<div />', { 'class': 'col-sm-6' }).append(
$('<div />', { 'class':'input-group'}).append(
$('<div />', {'class':'input-group-btn'}).append(
$('<button />', {'class':'btn btn-default dropdown-toggle', 'data-toggle':'dropdown'}).append(
tokenQualifierLabel,
$('<span class="caret" style="margin-left: 5px;"></span>')
),
tokenQualifierDropdown
),
$('<div />', { 'class':'input-group-addon', 'style':'padding:6px;'} ).append(
$('<span>System</span>')
),
$('<input />', { type:'text', 'class':'form-control', id: 'param.' + theRowNum + '.0', placeholder: "(opt)" }),
$('<div />', { 'class':'input-group-addon', 'style':'padding:6px;'} ).append(
$('<span>Code</span>')
),
$('<input />', { type:'text', 'class':'form-control', id: 'param.' + theRowNum + '.1', placeholder: "(opt)" })
)
)
);
} else if (theSearchParamType == 'string') {
var placeholderText = 'value';
var qualifiers = new Array();
@ -93,17 +167,23 @@ function addSearchControls(theConformance, theSearchParamType, theSearchParamNam
qualifierInput
);
var matchesLabel = $('<span>' + qualifiers[0].name + '</span>');
var qualifierDropdown = $('<ul />', {'class':'dropdown-menu', role:'menu'});
function clickFunction(value, name){
return function(){
qualifierInput.val(value);
matchesLabel.text(name);
}
};
for (var i = 0; i < qualifiers.length; i++) {
var nextLink = $('<a>' + qualifiers[i].name+'</a>');
var qualName = qualifiers[i].name;
var nextValue = qualifiers[i].value;
qualifierDropdown.append($('<li />').append(nextLink));
nextLink.click(function(){
qualifierInput.val(nextValue);
matchesLabel.text(qualName);
});
nextLink.click(clickFunction(nextValue, qualName));
}
$('#search-param-rowopts-' + theContainerRowNum).append(

View File

@ -65,8 +65,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">
@ -99,8 +99,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">

View File

@ -64,8 +64,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">
@ -97,8 +97,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">

View File

@ -64,8 +64,8 @@
<max value="1"/>
</base>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="[^\s]+([\s][^\s]+)*"/>
@ -100,8 +100,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="[^\s]+([\s][^\s]+)*"/>

View File

@ -64,8 +64,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="-?[0-9]{4}(-(0[1-9]|1[0-2])(-(0[0-9]|[1-2][0-9]|3[0-1]))?)?"/>
@ -100,8 +100,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="-?[0-9]{4}(-(0[1-9]|1[0-2])(-(0[0-9]|[1-2][0-9]|3[0-1]))?)?"/>

View File

@ -64,8 +64,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="-?[0-9]{4}(-(0[1-9]|1[0-2])(-(0[0-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))?)?)?"/>
@ -100,8 +100,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="-?[0-9]{4}(-(0[1-9]|1[0-2])(-(0[0-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))?)?)?"/>

View File

@ -65,8 +65,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="-?([0]|([1-9][0-9]*))(\.[0-9]+)?"/>
@ -102,8 +102,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="-?([0]|([1-9][0-9]*))(\.[0-9]+)?"/>

View File

@ -65,8 +65,8 @@
<max value="1"/>
</base>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="[A-Za-z0-9\-\.]{1,64}"/>
@ -102,8 +102,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="[A-Za-z0-9\-\.]{1,64}"/>

View File

@ -65,8 +65,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">
@ -99,8 +99,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">

View File

@ -65,8 +65,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="-?([0]|([1-9][0-9]*))"/>
@ -104,8 +104,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="-?([0]|([1-9][0-9]*))"/>

View File

@ -65,8 +65,8 @@
<max value="1"/>
</base>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">
@ -99,8 +99,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">

View File

@ -65,8 +65,8 @@
<max value="1"/>
</base>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="urn:oid:(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*"/>
@ -102,8 +102,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="urn:oid:(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*"/>

View File

@ -64,8 +64,8 @@
<max value="1"/>
</base>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="[1-9][0-9]*"/>
@ -100,8 +100,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="[1-9][0-9]*"/>

View File

@ -65,8 +65,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">
@ -100,8 +100,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">

View File

@ -64,8 +64,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?"/>
@ -100,8 +100,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?"/>

View File

@ -64,8 +64,8 @@
<max value="1"/>
</base>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="[0]|([1-9][0-9]*)"/>
@ -100,8 +100,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="[0]|([1-9][0-9]*)"/>

View File

@ -65,8 +65,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">
@ -99,8 +99,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<code>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type">

View File

@ -65,8 +65,8 @@
<max value="1"/>
</base>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"/>
@ -102,8 +102,8 @@
<min value="0"/>
<max value="1"/>
<!-- Note: primitive values do not have an assigned type
e.g. this is compiler magic
XML and JSON types provided by extension -->
e.g. this is compiler magic
XML and JSON types provided by extension -->
<type>
<extension url="http://hl7.org/fhir/StructureDefinition/structuredefinition-regex">
<valueString value="urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"/>

View File

@ -56749,14 +56749,14 @@
<contained>
<ConceptMap xmlns="http://hl7.org/fhir">
<id value="map"/>
<name value="NHIN / V3 Mapping"/>
<status value="draft"/>
<sourceReference>
<reference value="http://hl7.org/fhir/ValueSet/nhin-purposeofuse"/>
</sourceReference>
<targetReference>
<reference value="http://hl7.org/fhir/ValueSet/v3-PurposeOfUse"/>
</targetReference>
<name value="NHIN / V3 Mapping"/>
<status value="draft"/>
<group>
<source value="http://healthit.gov/nhin/purposeofuse"/>
<target value="http://hl7.org/fhir/v3/ActReason"/>

View File

@ -4,6 +4,10 @@ import java.io.*;
import java.net.URL;
import java.util.Collection;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.util.BundleUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@ -11,6 +15,7 @@ import org.apache.maven.plugin.*;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
@ -51,6 +56,8 @@ public class ResourceMinimizerMojo extends AbstractMojo {
myCtx = FhirContext.forDstu2_1();
} else if ("DSTU3".equals(fhirVersion)) {
myCtx = FhirContext.forDstu3();
} else if ("R4".equals(fhirVersion)) {
myCtx = FhirContext.forR4();
} else {
throw new MojoFailureException("Unknown version: " + fhirVersion);
}
@ -82,30 +89,8 @@ public class ResourceMinimizerMojo extends AbstractMojo {
}
}
}
} else if (input instanceof org.hl7.fhir.dstu3.model.Bundle) {
for (org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent nextEntry : ((org.hl7.fhir.dstu3.model.Bundle) input).getEntry()) {
if (nextEntry.getResource() instanceof org.hl7.fhir.dstu3.model.DomainResource) {
((org.hl7.fhir.dstu3.model.DomainResource) nextEntry.getResource()).getText().getDiv().setValueAsString((String) null);
((org.hl7.fhir.dstu3.model.DomainResource) nextEntry.getResource()).getText().getStatusElement().setValueAsString((String) null);
}
}
} else if (input instanceof org.hl7.fhir.dstu2016may.model.Bundle) {
for (org.hl7.fhir.dstu2016may.model.Bundle.BundleEntryComponent nextEntry : ((org.hl7.fhir.dstu2016may.model.Bundle) input).getEntry()) {
if (nextEntry.getResource() instanceof org.hl7.fhir.dstu2016may.model.DomainResource) {
((org.hl7.fhir.dstu2016may.model.DomainResource) nextEntry.getResource()).getText().getDiv().setValueAsString((String) null);
((org.hl7.fhir.dstu2016may.model.DomainResource) nextEntry.getResource()).getText().getStatusElement().setValueAsString((String) null);
}
}
} else if (input instanceof org.hl7.fhir.dstu3.model.DomainResource) {
try {
((org.hl7.fhir.dstu3.model.DomainResource) input).getText().setDivAsString(null);
((org.hl7.fhir.dstu3.model.DomainResource) input).getText().getStatusElement().setValueAsString(null);
} catch (Exception e) {
ourLog.error("Failed to clear narrative", e);
}
} else {
ourLog.info("Ignoring type: " + input.getClass());
continue;
minimizeResource((IBaseResource)input);
}
String outputString = parser.setPrettyPrint(true).encodeResourceToString(input);
@ -143,6 +128,20 @@ public class ResourceMinimizerMojo extends AbstractMojo {
}
}
private void minimizeResource(IBaseResource theInput) {
if (theInput instanceof IBaseBundle) {
for (IBaseResource next : BundleUtil.toListOfResources(myCtx, (IBaseBundle) theInput)) {
minimizeResource(next);
}
}
BaseRuntimeElementCompositeDefinition<?> element = (BaseRuntimeElementCompositeDefinition) myCtx.getElementDefinition(theInput.getClass());
BaseRuntimeChildDefinition textElement = element.getChildByName("text");
if (textElement != null) {
textElement.getMutator().setValue(theInput, null);
}
}
public long getByteCount() {
return myByteCount;
}
@ -153,9 +152,10 @@ public class ResourceMinimizerMojo extends AbstractMojo {
public static void main(String[] args) throws Exception {
FhirContext ctxDstu2 = FhirContext.forDstu2();
// FhirContext ctxDstu2_1 = FhirContext.forDstu2_1();
// FhirContext ctxDstu3 = FhirContext.forDstu3();
FhirContext ctxDstu2_1 = FhirContext.forDstu2_1();
FhirContext ctxDstu3 = FhirContext.forDstu3();
FhirContext ctxR4 = FhirContext.forR4();
LoggerContext loggerContext = ((ch.qos.logback.classic.Logger) ourLog).getLoggerContext();
URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(loggerContext);
System.out.println(mainURL);
@ -166,59 +166,76 @@ public class ResourceMinimizerMojo extends AbstractMojo {
long byteCount = 0;
ResourceMinimizerMojo m = new ResourceMinimizerMojo();
// m.myCtx = ctxDstu2;
// m.targetDirectory = new File("../hapi-tinder-plugin/src/main/resources/vs/dstu2");
// m.fhirVersion = "DSTU2";
// m.execute();
// byteCount += m.getByteCount();
// fileCount += m.getFileCount();
//
// m = new ResourceMinimizerMojo();
// m.myCtx = ctxDstu2;
// m.targetDirectory = new File("../hapi-fhir-validation-resources-dstu2/src/main/resources/org/hl7/fhir/instance/model/valueset");
// m.fhirVersion = "DSTU2";
// m.execute();
// byteCount += m.getByteCount();
// fileCount += m.getFileCount();
//
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu2;
m.targetDirectory = new File("../hapi-fhir-validation-resources-dstu2/src/main/resources/org/hl7/fhir/instance/model/profile");
m.targetDirectory = new File("./hapi-tinder-plugin/src/main/resources/vs/dstu2");
m.fhirVersion = "DSTU2";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
// m = new ResourceMinimizerMojo();
// m.myCtx = ctxDstu3;
// m.targetDirectory = new File("../hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/profile");
// m.fhirVersion = "DSTU3";
// m.execute();
// byteCount += m.getByteCount();
// fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu2;
m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu2/src/main/resources/org/hl7/fhir/instance/model/valueset");
m.fhirVersion = "DSTU2";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
// m = new ResourceMinimizerMojo();
// m.myCtx = ctxDstu3;
// m.targetDirectory = new File("../hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/valueset");
// m.fhirVersion = "DSTU3";
// m.execute();
// byteCount += m.getByteCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu2;
m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu2/src/main/resources/org/hl7/fhir/instance/model/profile");
m.fhirVersion = "DSTU2";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
// m = new ResourceMinimizerMojo();
// m.myCtx = ctxDstu2_1;
// m.targetDirectory = new File("../hapi-fhir-validation-resources-dstu2.1/src/main/resources/org/hl7/fhir/dstu2016may/model/profile");
// m.fhirVersion = "DSTU2_1";
// m.execute();
// byteCount += m.getByteCount();
// fileCount += m.getFileCount();
//
// m = new ResourceMinimizerMojo();
// m.myCtx = ctxDstu2_1;
// m.targetDirectory = new File("../hapi-fhir-validation-resources-dstu2.1/src/main/resources/org/hl7/fhir/dstu2016may/model/valueset");
// m.fhirVersion = "DSTU2_1";
// m.execute();
// byteCount += m.getByteCount();
// fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu3;
m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/profile");
m.fhirVersion = "DSTU3";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu3;
m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu3/src/main/resources/org/hl7/fhir/dstu3/model/valueset");
m.fhirVersion = "DSTU3";
m.execute();
byteCount += m.getByteCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu2_1;
m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu2.1/src/main/resources/org/hl7/fhir/dstu2016may/model/profile");
m.fhirVersion = "DSTU2_1";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu2_1;
m.targetDirectory = new File("./hapi-fhir-validation-resources-dstu2.1/src/main/resources/org/hl7/fhir/dstu2016may/model/valueset");
m.fhirVersion = "DSTU2_1";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu2_1;
m.targetDirectory = new File("./hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/profile");
m.fhirVersion = "R4";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
m = new ResourceMinimizerMojo();
m.myCtx = ctxDstu2_1;
m.targetDirectory = new File("./hapi-fhir-validation-resources-r4/src/main/resources/org/hl7/fhir/r4/model/valueset");
m.fhirVersion = "R4";
m.execute();
byteCount += m.getByteCount();
fileCount += m.getFileCount();
ourLog.info("Trimmed {} files", fileCount);
ourLog.info("Trimmed {} bytes", FileUtils.byteCountToDisplaySize(byteCount));

12
pom.xml
View File

@ -346,6 +346,18 @@
<id>SarenCurrie</id>
<name>Saren Currie</name>
</developer>
<developer>
<id>dconlan</id>
<name>dconlan</name>
</developer>
<developer>
<id>psbrandt</id>
<name>Pascal Brandt</name>
</developer>
<developer>
<id>InfiniteLoop90</id>
<name>Clayton Bodendein</name>
</developer>
</developers>
<licenses>

View File

@ -269,6 +269,33 @@
correctly validated if the element contained only a profiled extension. Thanks
to Sébastien Rivière for the pull request!
</action>
<action type="add" issue="701">
Testing UI now has a dropdown for modifiers on token search. Thanks
to GitHub user @dconlan for the pull request!
</action>
<action type="add" issue="688">
When parsing an incomplete ID with the form <![CDATA[<code>http://my.org/Foo</code>]]> into
IdDt and IdType objects, the Foo portion will now be treated as the resource type.
Previously my.org was treated as the resource type and Foo was treated as the ID. Thanks
to GitHub user @CarthageKing for the pull request!
</action>
<action type="fix" issue="695">
Extensions on ID datatypes were not parsed or serialized correctly. Thanks to
Stephen Rivière for the pull request!
</action>
<action type="fix" issue="710">
Fix a bug in REST Hook Subscription interceptors which prevented subscriptions
from being activated. Thanks to Jeff Chung for the pull request!
</action>
<action type="fix" issue="708">
Fix broken links in usage pattern diagram on website. Thanks to
Pascal Brandt for the pull request!
</action>
<action type="fix" issue="706">
Fix incorrect FHIR Version Strings that were being outputted and verified in the
client for some versions of FHIR. Thanks to Clayton Bodendein for the
pull request!
</action>
</release>
<release version="2.5" date="2017-06-08">
<action type="fix">