Merge remote-tracking branch 'origin/master' into issue-2445-support-patient-level-export

This commit is contained in:
Tadgh 2021-03-10 10:33:24 -05:00
commit b7398cb6f2
51 changed files with 66284 additions and 595 deletions

View File

@ -30,7 +30,6 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
import ca.uhn.fhir.context.RuntimeChildContainedResources;
import ca.uhn.fhir.context.RuntimeChildDirectResource;
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IIdentifiableElement;
@ -39,16 +38,15 @@ import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.path.EncodeContextPath;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.base.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseCoding;
@ -58,7 +56,6 @@ import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IDomainResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
@ -77,7 +74,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -102,7 +98,7 @@ public abstract class BaseParser implements IParser {
private static final Set<String> notEncodeForContainedResource = new HashSet<>(Arrays.asList("security", "versionId", "lastUpdated"));
private ContainedResources myContainedResources;
private FhirTerser.ContainedResources myContainedResources;
private boolean myEncodeElementsAppliesToChildResourcesOnly;
private FhirContext myContext;
private List<EncodeContextPath> myDontEncodeElements;
@ -118,7 +114,6 @@ public abstract class BaseParser implements IParser {
private boolean mySummaryMode;
private boolean mySuppressNarratives;
private Set<String> myDontStripVersionsFromReferencesAtPaths;
/**
* Constructor
*/
@ -127,6 +122,10 @@ public abstract class BaseParser implements IParser {
myErrorHandler = theParserErrorHandler;
}
protected FhirContext getContext() {
return myContext;
}
List<EncodeContextPath> getDontEncodeElements() {
return myDontEncodeElements;
}
@ -213,156 +212,6 @@ public abstract class BaseParser implements IParser {
});
}
private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, IBaseResource theTarget) {
List<IBaseReference> allReferences = getAllBaseReferences(theResource);
for (IBaseReference next : allReferences) {
IBaseResource resource = next.getResource();
if (resource == null && next.getReferenceElement().isLocal()) {
if (theContained.hasExistingIdToContainedResource()) {
IBaseResource potentialTarget = theContained.getExistingIdToContainedResource().remove(next.getReferenceElement().getValue());
if (potentialTarget != null) {
theContained.addContained(next.getReferenceElement(), potentialTarget);
containResourcesForEncoding(theContained, potentialTarget, theTarget);
}
}
}
}
for (IBaseReference next : allReferences) {
IBaseResource resource = next.getResource();
if (resource != null) {
if (resource.getIdElement().isEmpty() || resource.getIdElement().isLocal()) {
if (theContained.getResourceId(resource) != null) {
// Prevent infinite recursion if there are circular loops in the contained resources
continue;
}
theContained.addContained(resource);
if (resource.getIdElement().isLocal() && theContained.hasExistingIdToContainedResource()) {
theContained.getExistingIdToContainedResource().remove(resource.getIdElement().getValue());
}
} else {
continue;
}
containResourcesForEncoding(theContained, resource, theTarget);
}
}
}
protected void containResourcesForEncoding(IBaseResource theResource) {
ContainedResources contained = new ContainedResources();
if (theResource instanceof IResource) {
List<? extends IResource> containedResources = ((IResource) theResource).getContained().getContainedResources();
for (IResource next : containedResources) {
String nextId = next.getId().getValue();
if (StringUtils.isNotBlank(nextId)) {
if (!nextId.startsWith("#")) {
nextId = '#' + nextId;
}
contained.getExistingIdToContainedResource().put(nextId, next);
}
}
} else if (theResource instanceof IDomainResource) {
List<? extends IAnyResource> containedResources = ((IDomainResource) theResource).getContained();
for (IAnyResource next : containedResources) {
String nextId = next.getIdElement().getValue();
if (StringUtils.isNotBlank(nextId)) {
if (!nextId.startsWith("#")) {
nextId = '#' + nextId;
}
contained.getExistingIdToContainedResource().put(nextId, next);
}
}
}
containResourcesForEncoding(contained, theResource, theResource);
contained.assignIdsToContainedResources();
myContainedResources = contained;
}
protected List<IBaseReference> getAllBaseReferences(IBaseResource theResource) {
final ArrayList<IBaseReference> retVal = new ArrayList<IBaseReference>();
findBaseReferences(retVal, theResource, myContext.getResourceDefinition(theResource));
return retVal;
}
/**
* A customised traversal of the tree to find the 'top level' base references. Nested references are found via the recursive traversal
* of contained resources.
*/
protected void findBaseReferences(List<IBaseReference> allElements, IBase theElement, BaseRuntimeElementDefinition<?> theDefinition) {
if (theElement instanceof IBaseReference) {
allElements.add((IBaseReference) theElement);
}
BaseRuntimeElementDefinition<?> def = theDefinition;
if (def.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) {
def = myContext.getElementDefinition(theElement.getClass());
}
switch (def.getChildType()) {
case ID_DATATYPE:
case PRIMITIVE_XHTML_HL7ORG:
case PRIMITIVE_XHTML:
case PRIMITIVE_DATATYPE:
// These are primitive types
break;
case RESOURCE:
case RESOURCE_BLOCK:
case COMPOSITE_DATATYPE: {
BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) def;
for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
List<?> values = nextChild.getAccessor().getValues(theElement);
if (values != null) {
for (Object nextValueObject : values) {
IBase nextValue;
try {
nextValue = (IBase) nextValueObject;
} catch (ClassCastException e) {
String s = "Found instance of " + nextValueObject.getClass() + " - Did you set a field value to the incorrect type? Expected " + IBase.class.getName();
throw new ClassCastException(s);
}
if (nextValue == null) {
continue;
}
if (nextValue.isEmpty()) {
continue;
}
BaseRuntimeElementDefinition<?> childElementDef;
childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
if (childElementDef == null) {
childElementDef = myContext.getElementDefinition(nextValue.getClass());
}
if (nextChild instanceof RuntimeChildDirectResource) {
// Don't descend into embedded resources
if (nextValue instanceof IBaseReference) {
allElements.add((IBaseReference) nextValue);
}
} else {
findBaseReferences(allElements, nextValue, childElementDef);
}
}
}
}
break;
}
case CONTAINED_RESOURCES:
// skip contained resources when looking for resources to contain
break;
case CONTAINED_RESOURCE_LIST:
case EXTENSION_DECLARED:
case UNDECL_EXT: {
throw new IllegalStateException("state should not happen: " + def.getChildType());
}
}
}
private String determineReferenceText(IBaseReference theRef, CompositeChildElement theCompositeChildElement) {
IIdType ref = theRef.getReferenceElement();
@ -529,10 +378,14 @@ public abstract class BaseParser implements IParser {
return elementId;
}
ContainedResources getContainedResources() {
FhirTerser.ContainedResources getContainedResources() {
return myContainedResources;
}
void setContainedResources(FhirTerser.ContainedResources theContainedResources) {
myContainedResources = theContainedResources;
}
@Override
public Set<String> getDontStripVersionsFromReferencesAtPaths() {
return myDontStripVersionsFromReferencesAtPaths;
@ -1359,126 +1212,6 @@ public abstract class BaseParser implements IParser {
}
static class ContainedResources {
private long myNextContainedId = 1;
private List<IBaseResource> myResourceList;
private IdentityHashMap<IBaseResource, IIdType> myResourceToIdMap;
private Map<String, IBaseResource> myExistingIdToContainedResourceMap;
public Map<String, IBaseResource> getExistingIdToContainedResource() {
if (myExistingIdToContainedResourceMap == null) {
myExistingIdToContainedResourceMap = new HashMap<>();
}
return myExistingIdToContainedResourceMap;
}
public void addContained(IBaseResource theResource) {
if (getResourceToIdMap().containsKey(theResource)) {
return;
}
IIdType newId;
if (theResource.getIdElement().isLocal()) {
newId = theResource.getIdElement();
} else {
newId = null;
}
getResourceToIdMap().put(theResource, newId);
getResourceList().add(theResource);
}
public void addContained(IIdType theId, IBaseResource theResource) {
if (!getResourceToIdMap().containsKey(theResource)) {
getResourceToIdMap().put(theResource, theId);
getResourceList().add(theResource);
}
}
public List<IBaseResource> getContainedResources() {
if (getResourceToIdMap() == null) {
return Collections.emptyList();
}
return getResourceList();
}
public IIdType getResourceId(IBaseResource theNext) {
if (getResourceToIdMap() == null) {
return null;
}
return getResourceToIdMap().get(theNext);
}
private List<IBaseResource> getResourceList() {
if (myResourceList == null) {
myResourceList = new ArrayList<>();
}
return myResourceList;
}
private IdentityHashMap<IBaseResource, IIdType> getResourceToIdMap() {
if (myResourceToIdMap == null) {
myResourceToIdMap = new IdentityHashMap<>();
}
return myResourceToIdMap;
}
public boolean isEmpty() {
if (myResourceToIdMap == null) {
return true;
}
return myResourceToIdMap.isEmpty();
}
public boolean hasExistingIdToContainedResource() {
return myExistingIdToContainedResourceMap != null;
}
public void assignIdsToContainedResources() {
if (getResourceList() != null) {
/*
* The idea with the code block below:
*
* We want to preserve any IDs that were user-assigned, so that if it's really
* important to someone that their contained resource have the ID of #FOO
* or #1 we will keep that.
*
* For any contained resources where no ID was assigned by the user, we
* want to manually create an ID but make sure we don't reuse an existing ID.
*/
Set<String> ids = new HashSet<>();
// Gather any user assigned IDs
for (IBaseResource nextResource : getResourceList()) {
if (getResourceToIdMap().get(nextResource) != null) {
ids.add(getResourceToIdMap().get(nextResource).getValue());
}
}
// Automatically assign IDs to the rest
for (IBaseResource nextResource : getResourceList()) {
while (getResourceToIdMap().get(nextResource) == null) {
String nextCandidate = "#" + myNextContainedId;
myNextContainedId++;
if (!ids.add(nextCandidate)) {
continue;
}
getResourceToIdMap().put(nextResource, new IdDt(nextCandidate));
}
}
}
}
}
protected static <T> List<T> extractMetadataListNotNull(IResource resource, ResourceMetadataKeyEnum<List<T>> key) {
List<? extends T> securityLabels = key.get(resource);
if (securityLabels == null) {

View File

@ -45,6 +45,7 @@ import ca.uhn.fhir.parser.json.JsonLikeWriter;
import ca.uhn.fhir.parser.json.jackson.JacksonStructure;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.util.ElementUtil;
import ca.uhn.fhir.util.FhirTerser;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.text.WordUtils;
@ -74,7 +75,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParser.HeldExtension.class);
private FhirContext myContext;
private boolean myPrettyPrint;
/**
@ -85,7 +85,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
*/
public JsonParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
super(theContext, theParserErrorHandler);
myContext = theContext;
}
private boolean addToHeldComments(int valueIdx, List<String> theCommentsToAdd, ArrayList<ArrayList<String>> theListToAddTo) {
@ -177,7 +176,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
theEventWriter.init();
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(theResource);
encodeResourceToJsonStreamWriter(resDef, theResource, theEventWriter, null, false, theEncodeContext);
theEventWriter.flush();
}
@ -209,7 +208,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
String resourceType = resourceTypeObj.getAsString();
ParserState<? extends IBaseResource> state = ParserState.getPreResourceInstance(this, theResourceType, myContext, true, getErrorHandler());
ParserState<? extends IBaseResource> state = ParserState.getPreResourceInstance(this, theResourceType, getContext(), true, getErrorHandler());
state.enteringNewElement(null, resourceType);
parseChildren(object, state);
@ -355,7 +354,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
case RESOURCE:
IBaseResource resource = (IBaseResource) theNextValue;
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
RuntimeResourceDefinition def = getContext().getResourceDefinition(resource);
theEncodeContext.pushPath(def.getName(), true);
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, theContainedResource, theEncodeContext);
@ -387,14 +386,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
if (nextChildElem.getDef().getElementName().equals("extension") || nextChildElem.getDef().getElementName().equals("modifierExtension")
|| nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
if (!haveWrittenExtensions) {
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, myContext.getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem, theParent, theEncodeContext, theContainedResource);
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, getContext().getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem, theParent, theEncodeContext, theContainedResource);
haveWrittenExtensions = true;
}
continue;
}
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrativeGenerator gen = myContext.getNarrativeGenerator();
INarrativeGenerator gen = getContext().getNarrativeGenerator();
if (gen != null) {
INarrative narr;
if (theResource instanceof IResource) {
@ -405,7 +404,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
narr = null;
}
if (narr != null && narr.isEmpty()) {
gen.populateResourceNarrative(myContext, theResource);
gen.populateResourceNarrative(getContext(), theResource);
if (!narr.isEmpty()) {
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
String childName = nextChild.getChildNameByDatatype(child.getDatatype());
@ -619,13 +618,13 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
Validate.notNull(theResource, "theResource can not be null");
Validate.notNull(theJsonLikeWriter, "theJsonLikeWriter can not be null");
if (theResource.getStructureFhirVersionEnum() != myContext.getVersion().getVersion()) {
if (theResource.getStructureFhirVersionEnum() != getContext().getVersion().getVersion()) {
throw new IllegalArgumentException(
"This parser is for FHIR version " + myContext.getVersion().getVersion() + " - Can not encode a structure for version " + theResource.getStructureFhirVersionEnum());
"This parser is for FHIR version " + getContext().getVersion().getVersion() + " - Can not encode a structure for version " + theResource.getStructureFhirVersionEnum());
}
EncodeContext encodeContext = new EncodeContext();
String resourceName = myContext.getResourceType(theResource);
String resourceName = getContext().getResourceType(theResource);
encodeContext.pushPath(resourceName, true);
doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter, encodeContext);
}
@ -660,10 +659,10 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
if (!theContainedResource) {
super.containResourcesForEncoding(theResource);
setContainedResources(getContext().newTerser().containResources(theResource));
}
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(theResource);
if (theObjectNameOrNull == null) {
theEventWriter.beginObject();
@ -888,8 +887,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
private boolean isEncodeExtension(CompositeChildElement theParent, EncodeContext theEncodeContext, boolean theContainedResource, IBase theElement) {
// theEncodeContext.pushPath("extension", false);
BaseRuntimeElementDefinition<?> runtimeElementDefinition = myContext.getElementDefinition(theElement.getClass());
BaseRuntimeElementDefinition<?> runtimeElementDefinition = getContext().getElementDefinition(theElement.getClass());
boolean retVal = true;
if (runtimeElementDefinition instanceof BaseRuntimeElementCompositeDefinition) {
BaseRuntimeElementCompositeDefinition definition = (BaseRuntimeElementCompositeDefinition) runtimeElementDefinition;
@ -897,7 +895,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
CompositeChildElement c = new CompositeChildElement(theParent, childDef, theEncodeContext);
retVal = c.shouldBeEncoded(theContainedResource);
}
// theEncodeContext.popPath();
return retVal;
}
@ -917,37 +914,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
return object.getAsArray();
}
// private JsonObject parse(Reader theReader) {
//
// PushbackReader pbr = new PushbackReader(theReader);
// JsonObject object;
// try {
// while(true) {
// int nextInt;
// nextInt = pbr.read();
// if (nextInt == -1) {
// throw new DataFormatException("Did not find any content to parse");
// }
// if (nextInt == '{') {
// pbr.unread('{');
// break;
// }
// if (Character.isWhitespace(nextInt)) {
// continue;
// }
// throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char)nextInt + "' (must be '{')");
// }
//
// Gson gson = newGson();
//
// object = gson.fromJson(pbr, JsonObject.class);
// } catch (Exception e) {
// throw new DataFormatException("Failed to parse JSON content, error was: " + e.getMessage(), e);
// }
//
// return object;
// }
private void parseAlternates(JsonLikeValue theAlternateVal, ParserState<?> theState, String theElementName, String theAlternateName) {
if (theAlternateVal == null || theAlternateVal.isNull()) {
return;
@ -1234,13 +1200,13 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
* the correct FHIR version
*/
if (theResourceType != null) {
myContext.getResourceDefinition(theResourceType);
getContext().getResourceDefinition(theResourceType);
}
// Actually do the parse
T retVal = doParseResource(theResourceType, theJsonLikeStructure);
RuntimeResourceDefinition def = myContext.getResourceDefinition(retVal);
RuntimeResourceDefinition def = getContext().getResourceDefinition(retVal);
if ("Bundle".equals(def.getName())) {
BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
@ -1553,10 +1519,10 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
*/
value = preProcessValues(myDef, theResource, Collections.singletonList(value), myChildElem, theEncodeContext).get(0);
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
RuntimeChildUndeclaredExtensionDefinition extDef = getContext().getRuntimeChildUndeclaredExtensionDefinition();
String childName = extDef.getChildNameByDatatype(value.getClass());
if (childName == null) {
childName = "value" + WordUtils.capitalize(myContext.getElementDefinition(value.getClass()).getName());
childName = "value" + WordUtils.capitalize(getContext().getElementDefinition(value.getClass()).getName());
}
BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
if (childDef == null) {

View File

@ -1149,7 +1149,7 @@ class ParserState<T> {
for (IBaseReference nextRef : myLocalReferences) {
String ref = nextRef.getReferenceElement().getValue();
if (isNotBlank(ref)) {
if (ref.startsWith("#")) {
if (ref.startsWith("#") && ref.length() > 1) {
IBaseResource target = myContainedResources.get(ref);
if (target != null) {
ourLog.debug("Resource contains local ref {}", ref);
@ -1187,12 +1187,6 @@ class ParserState<T> {
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
}
// @Override
// public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
// super.enteringNewElement(theNamespaceUri, theLocalPart);
// populateTarget();
// }
@Override
protected void populateTarget() {
weaveContainedResources();

View File

@ -73,7 +73,6 @@ public class RDFParser extends BaseParser {
public static final String MODIFIER_EXTENSION = "modifierExtension";
private final Map<Class, String> classToFhirTypeMap = new HashMap<>();
private final FhirContext context;
private final Lang lang;
/**
@ -84,7 +83,6 @@ public class RDFParser extends BaseParser {
*/
public RDFParser(final FhirContext context, final IParserErrorHandler parserErrorHandler, final Lang lang) {
super(context, parserErrorHandler);
this.context = context;
this.lang = lang;
}
@ -148,13 +146,13 @@ public class RDFParser extends BaseParser {
final EncodeContext encodeContext,
final boolean rootResource, Resource parentResource) {
RuntimeResourceDefinition resDef = this.context.getResourceDefinition(resource);
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(resource);
if (resDef == null) {
throw new ConfigurationException("Unknown resource type: " + resource.getClass());
}
if (!containedResource) {
super.containResourcesForEncoding(resource);
setContainedResources(getContext().newTerser().containResources(resource));
}
if (!(resource instanceof IAnyResource)) {
@ -323,7 +321,7 @@ public class RDFParser extends BaseParser {
if (hasExtension.getExtension() != null && hasExtension.getExtension().size() > 0) {
int i = 0;
for (IBaseExtension extension : hasExtension.getExtension()) {
RuntimeResourceDefinition resDef = this.context.getResourceDefinition(resource);
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(resource);
Resource extensionResource = rdfModel.createResource();
extensionResource.addProperty(rdfModel.createProperty(FHIR_NS+FHIR_INDEX), rdfModel.createTypedLiteral(i, XSDDatatype.XSDinteger));
valueResource.addProperty(rdfModel.createProperty(FHIR_NS + ELEMENT_EXTENSION), extensionResource);
@ -375,7 +373,7 @@ public class RDFParser extends BaseParser {
}
case RESOURCE: {
IBaseResource baseResource = (IBaseResource) element;
String resourceName = this.context.getResourceType(baseResource);
String resourceName = getContext().getResourceType(baseResource);
if (!super.shouldEncodeResource(resourceName)) {
break;
}
@ -486,7 +484,7 @@ public class RDFParser extends BaseParser {
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrativeGenerator gen = this.context.getNarrativeGenerator();
INarrativeGenerator gen = getContext().getNarrativeGenerator();
if (gen != null) {
INarrative narrative;
if (resource instanceof IResource) {
@ -498,7 +496,7 @@ public class RDFParser extends BaseParser {
}
assert narrative != null;
if (narrative.isEmpty()) {
gen.populateResourceNarrative(this.context, resource);
gen.populateResourceNarrative(getContext(), resource);
}
else {
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
@ -619,7 +617,7 @@ public class RDFParser extends BaseParser {
private <T extends IBaseResource> T parseResource(Class<T> resourceType, Model rdfModel) {
// jsonMode of true is passed in so that the xhtml parser state behaves as expected
// Push PreResourceState
ParserState<T> parserState = ParserState.getPreResourceInstance(this, resourceType, context, true, getErrorHandler());
ParserState<T> parserState = ParserState.getPreResourceInstance(this, resourceType, getContext(), true, getErrorHandler());
return parseRootResource(rdfModel, parserState, resourceType);
}
@ -646,7 +644,7 @@ public class RDFParser extends BaseParser {
fhirTypeString = resourceType.getSimpleName();
}
RuntimeResourceDefinition definition = this.context.getResourceDefinition(fhirTypeString);
RuntimeResourceDefinition definition = getContext().getResourceDefinition(fhirTypeString);
fhirResourceType = definition.getName();
parseResource(parserState, fhirResourceType, rootResource);
@ -777,7 +775,7 @@ public class RDFParser extends BaseParser {
String propertyUri = statement.getPredicate().getURI();
if (propertyUri.contains("Extension.value")) {
extensionValueType = propertyUri.replace(FHIR_NS + "Extension.", "");
BaseRuntimeElementDefinition<?> target = context.getRuntimeChildUndeclaredExtensionDefinition().getChildByName(extensionValueType);
BaseRuntimeElementDefinition<?> target = getContext().getRuntimeChildUndeclaredExtensionDefinition().getChildByName(extensionValueType);
if (target.getChildType().equals(ID_DATATYPE) || target.getChildType().equals(PRIMITIVE_DATATYPE)) {
extensionValueResource = statement.getObject().asResource().getProperty(resource.getModel().createProperty(FHIR_NS+VALUE)).getObject().asLiteral();
} else {

View File

@ -56,9 +56,6 @@ public class XmlParser extends BaseParser {
static final String FHIR_NS = "http://hl7.org/fhir";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParser.class);
// private static final Set<String> RESOURCE_NAMESPACES;
private FhirContext myContext;
private boolean myPrettyPrint;
/**
@ -69,7 +66,6 @@ public class XmlParser extends BaseParser {
*/
public XmlParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
super(theContext, theParserErrorHandler);
myContext = theContext;
}
private XMLEventReader createStreamReader(Reader theReader) {
@ -295,7 +291,7 @@ public class XmlParser extends BaseParser {
}
case RESOURCE: {
IBaseResource resource = (IBaseResource) theElement;
String resourceName = myContext.getResourceType(resource);
String resourceName = getContext().getResourceType(resource);
if (!super.shouldEncodeResource(resourceName)) {
break;
}
@ -354,9 +350,9 @@ public class XmlParser extends BaseParser {
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
Optional<IBase> narr = nextChild.getAccessor().getFirstValueOrNull(theElement);
INarrativeGenerator gen = myContext.getNarrativeGenerator();
INarrativeGenerator gen = getContext().getNarrativeGenerator();
if (gen != null && narr.isPresent() == false) {
gen.populateResourceNarrative(myContext, theResource);
gen.populateResourceNarrative(getContext(), theResource);
}
narr = nextChild.getAccessor().getFirstValueOrNull(theElement);
@ -490,13 +486,13 @@ public class XmlParser extends BaseParser {
}
private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theContainedResource, IIdType theResourceId, EncodeContext theEncodeContext) throws XMLStreamException {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(theResource);
if (resDef == null) {
throw new ConfigurationException("Unknown resource type: " + theResource.getClass());
}
if (!theContainedResource) {
super.containResourcesForEncoding(theResource);
setContainedResources(getContext().newTerser().containResources(theResource));
}
theEventWriter.writeStartElement(resDef.getName());
@ -615,11 +611,11 @@ public class XmlParser extends BaseParser {
if (next.getValue() != null) {
IBaseDatatype value = next.getValue();
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
RuntimeChildUndeclaredExtensionDefinition extDef = getContext().getRuntimeChildUndeclaredExtensionDefinition();
String childName = extDef.getChildNameByDatatype(value.getClass());
BaseRuntimeElementDefinition<?> childDef;
if (childName == null) {
childDef = myContext.getElementDefinition(value.getClass());
childDef = getContext().getElementDefinition(value.getClass());
if (childDef == null) {
throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName());
}
@ -749,7 +745,7 @@ public class XmlParser extends BaseParser {
}
private <T extends IBaseResource> T parseResource(Class<T> theResourceType, XMLEventReader theStreamReader) {
ParserState<T> parserState = ParserState.getPreResourceInstance(this, theResourceType, myContext, false, getErrorHandler());
ParserState<T> parserState = ParserState.getPreResourceInstance(this, theResourceType, getContext(), false, getErrorHandler());
return doXmlLoop(theStreamReader, parserState);
}

View File

@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.api;
/*
* #%L
* HAPI FHIR Search Parameters
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
* %%

View File

@ -13,14 +13,15 @@ import ca.uhn.fhir.context.RuntimeExtensionDtDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IIdentifiableElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseElement;
@ -29,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IDomainResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
@ -36,10 +38,14 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -47,6 +53,7 @@ import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.substring;
/*
* #%L
@ -71,7 +78,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirTerser {
private static final Pattern COMPARTMENT_MATCHER_PATH = Pattern.compile("([a-zA-Z.]+)\\.where\\(resolve\\(\\) is ([a-zA-Z]+)\\)");
private FhirContext myContext;
private static final EnumSet<OptionsEnum> EMPTY_OPTION_SET = EnumSet.noneOf(OptionsEnum.class);
private static final String USER_DATA_KEY_CONTAIN_RESOURCES_COMPLETED = FhirTerser.class.getName() + "_CONTAIN_RESOURCES_COMPLETED";
private final FhirContext myContext;
public FhirTerser(FhirContext theContext) {
super();
@ -312,7 +321,6 @@ public class FhirTerser {
return Optional.ofNullable(getSingleValueOrNull(theTarget, thePath, theWantedType));
}
private <T extends IBase> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, IBase theCurrentObj, List<String> theSubList, Class<T> theWantedClass) {
return getValues(theCurrentDef, theCurrentObj, theSubList, theWantedClass, false, false);
}
@ -1058,4 +1066,251 @@ public class FhirTerser {
});
}
private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, EnumSet<OptionsEnum> theOptions) {
List<IBaseReference> allReferences = getAllPopulatedChildElementsOfType(theResource, IBaseReference.class);
for (IBaseReference next : allReferences) {
IBaseResource resource = next.getResource();
if (resource == null && next.getReferenceElement().isLocal()) {
if (theContained.hasExistingIdToContainedResource()) {
IBaseResource potentialTarget = theContained.getExistingIdToContainedResource().remove(next.getReferenceElement().getValue());
if (potentialTarget != null) {
theContained.addContained(next.getReferenceElement(), potentialTarget);
containResourcesForEncoding(theContained, potentialTarget, theOptions);
}
}
}
}
for (IBaseReference next : allReferences) {
IBaseResource resource = next.getResource();
if (resource != null) {
if (resource.getIdElement().isEmpty() || resource.getIdElement().isLocal()) {
if (theContained.getResourceId(resource) != null) {
// Prevent infinite recursion if there are circular loops in the contained resources
continue;
}
IIdType id = theContained.addContained(resource);
if (theOptions.contains(OptionsEnum.MODIFY_RESOURCE)) {
getContainedResourceList(theResource).add(resource);
next.setReference(id.getValue());
}
if (resource.getIdElement().isLocal() && theContained.hasExistingIdToContainedResource()) {
theContained.getExistingIdToContainedResource().remove(resource.getIdElement().getValue());
}
} else {
continue;
}
}
}
}
/**
* Iterate through the whole resource and identify any contained resources. Optionally this method
* can also assign IDs and modify references where the resource link has been specified but not the
* reference text.
*
* @since 5.4.0
*/
public ContainedResources containResources(IBaseResource theResource, OptionsEnum... theOptions) {
EnumSet<OptionsEnum> options = toOptionSet(theOptions);
if (options.contains(OptionsEnum.STORE_AND_REUSE_RESULTS)) {
Object cachedValue = theResource.getUserData(USER_DATA_KEY_CONTAIN_RESOURCES_COMPLETED);
if (cachedValue != null) {
return (ContainedResources) cachedValue;
}
}
ContainedResources contained = new ContainedResources();
List<? extends IBaseResource> containedResources = getContainedResourceList(theResource);
for (IBaseResource next : containedResources) {
String nextId = next.getIdElement().getValue();
if (StringUtils.isNotBlank(nextId)) {
if (!nextId.startsWith("#")) {
nextId = '#' + nextId;
}
next.getIdElement().setValue(nextId);
}
contained.addContained(next);
}
containResourcesForEncoding(contained, theResource, options);
if (options.contains(OptionsEnum.STORE_AND_REUSE_RESULTS)) {
theResource.setUserData(USER_DATA_KEY_CONTAIN_RESOURCES_COMPLETED, contained);
}
return contained;
}
private EnumSet<OptionsEnum> toOptionSet(OptionsEnum[] theOptions) {
EnumSet<OptionsEnum> options;
if (theOptions == null || theOptions.length == 0) {
options = EMPTY_OPTION_SET;
} else {
options = EnumSet.of(theOptions[0], theOptions);
}
return options;
}
@SuppressWarnings("unchecked")
private <T extends IBaseResource> List<T> getContainedResourceList(T theResource) {
List<T> containedResources = Collections.emptyList();
if (theResource instanceof IResource) {
containedResources = (List<T>) ((IResource) theResource).getContained().getContainedResources();
} else if (theResource instanceof IDomainResource) {
containedResources = (List<T>) ((IDomainResource) theResource).getContained();
}
return containedResources;
}
public enum OptionsEnum {
/**
* Should we modify the resource in the case that contained resource IDs are assigned
* during a {@link #containResources(IBaseResource, OptionsEnum...)} pass.
*/
MODIFY_RESOURCE,
/**
* Store the results of the operation in the resource metadata and reuse them if
* subsequent calls are made.
*/
STORE_AND_REUSE_RESULTS
}
public static class ContainedResources {
private long myNextContainedId = 1;
private List<IBaseResource> myResourceList;
private IdentityHashMap<IBaseResource, IIdType> myResourceToIdMap;
private Map<String, IBaseResource> myExistingIdToContainedResourceMap;
public Map<String, IBaseResource> getExistingIdToContainedResource() {
if (myExistingIdToContainedResourceMap == null) {
myExistingIdToContainedResourceMap = new HashMap<>();
}
return myExistingIdToContainedResourceMap;
}
public IIdType addContained(IBaseResource theResource) {
IIdType existing = getResourceToIdMap().get(theResource);
if (existing != null) {
return existing;
}
IIdType newId = theResource.getIdElement();
if (isBlank(newId.getValue())) {
newId.setValue("#" + myNextContainedId++);
} else {
// Avoid auto-assigned contained IDs colliding with pre-existing ones
String idPart = newId.getValue();
if (substring(idPart, 0, 1).equals("#")) {
idPart = idPart.substring(1);
if (StringUtils.isNumeric(idPart)) {
myNextContainedId = Long.parseLong(idPart) + 1;
}
}
}
getResourceToIdMap().put(theResource, newId);
getOrCreateResourceList().add(theResource);
return newId;
}
public void addContained(IIdType theId, IBaseResource theResource) {
if (!getResourceToIdMap().containsKey(theResource)) {
getResourceToIdMap().put(theResource, theId);
getOrCreateResourceList().add(theResource);
}
}
public List<IBaseResource> getContainedResources() {
if (getResourceToIdMap() == null) {
return Collections.emptyList();
}
return getOrCreateResourceList();
}
public IIdType getResourceId(IBaseResource theNext) {
if (getResourceToIdMap() == null) {
return null;
}
return getResourceToIdMap().get(theNext);
}
private List<IBaseResource> getOrCreateResourceList() {
if (myResourceList == null) {
myResourceList = new ArrayList<>();
}
return myResourceList;
}
private IdentityHashMap<IBaseResource, IIdType> getResourceToIdMap() {
if (myResourceToIdMap == null) {
myResourceToIdMap = new IdentityHashMap<>();
}
return myResourceToIdMap;
}
public boolean isEmpty() {
if (myResourceToIdMap == null) {
return true;
}
return myResourceToIdMap.isEmpty();
}
public boolean hasExistingIdToContainedResource() {
return myExistingIdToContainedResourceMap != null;
}
public void assignIdsToContainedResources() {
if (!getContainedResources().isEmpty()) {
/*
* The idea with the code block below:
*
* We want to preserve any IDs that were user-assigned, so that if it's really
* important to someone that their contained resource have the ID of #FOO
* or #1 we will keep that.
*
* For any contained resources where no ID was assigned by the user, we
* want to manually create an ID but make sure we don't reuse an existing ID.
*/
Set<String> ids = new HashSet<>();
// Gather any user assigned IDs
for (IBaseResource nextResource : getContainedResources()) {
if (getResourceToIdMap().get(nextResource) != null) {
ids.add(getResourceToIdMap().get(nextResource).getValue());
}
}
// Automatically assign IDs to the rest
for (IBaseResource nextResource : getContainedResources()) {
while (getResourceToIdMap().get(nextResource) == null) {
String nextCandidate = "#" + myNextContainedId;
myNextContainedId++;
if (!ids.add(nextCandidate)) {
continue;
}
getResourceToIdMap().put(nextResource, new IdDt(nextCandidate));
}
}
}
}
}
}

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 2462
title: "References from a contained resource to the containing resource are now possible in the JPA server."

View File

@ -0,0 +1,6 @@
---
type: perf
issue: 2462
title: "Several optimizations have been made to the JPA server transaction processor that should result in
improved performance, particularly when processing large transaction Bundles, such as transactions
containing many entries, or transactions containing very large entries."

View File

@ -0,0 +1,4 @@
---
type: perf
issue: 2462
title: "The HAPI FHIR parser will now preserve the order of contained resources across round-trip parse/serialize passes."

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.jpa.bulk.api;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.Date;

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.jpa.bulk.job;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.jpa.bulk.job;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
* %%
* 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%
*/
public class GroupBulkExportJobParametersBuilder extends BulkExportJobParametersBuilder {
public GroupBulkExportJobParametersBuilder setGroupId(String theGroupId) {
this.addString(BulkExportJobConfig.GROUP_ID_PARAMETER, theGroupId);

View File

@ -11,7 +11,6 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IDao;
@ -65,7 +64,6 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
@ -237,6 +235,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
private IPartitionLookupSvc myPartitionLookupSvc;
@Autowired
private MemoryCacheService myMemoryCacheService;
@Autowired(required = false)
private IFulltextSearchSvc myFulltextSearchSvc;
@VisibleForTesting
public void setSearchParamPresenceSvc(ISearchParamPresenceSvc theSearchParamPresenceSvc) {
mySearchParamPresenceSvc = theSearchParamPresenceSvc;
}
@Override
protected IInterceptorBroadcaster getInterceptorBroadcaster() {
@ -492,41 +497,37 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
/**
* Returns true if the resource has changed (either the contents or the tags)
*/
protected EncodedResource populateResourceIntoEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, boolean theUpdateHash) {
protected EncodedResource populateResourceIntoEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, boolean thePerformIndexing) {
if (theEntity.getResourceType() == null) {
theEntity.setResourceType(toResourceName(theResource));
}
if (theResource != null) {
List<BaseResourceReferenceDt> refs = myContext.newTerser().getAllPopulatedChildElementsOfType(theResource, BaseResourceReferenceDt.class);
for (BaseResourceReferenceDt nextRef : refs) {
if (nextRef.getReference().isEmpty() == false) {
if (nextRef.getReference().hasVersionIdPart()) {
nextRef.setReference(nextRef.getReference().toUnqualifiedVersionless());
}
}
}
}
byte[] bytes;
ResourceEncodingEnum encoding;
boolean changed = false;
if (theEntity.getDeleted() == null) {
encoding = myConfig.getResourceEncoding();
Set<String> excludeElements = ResourceMetaParams.EXCLUDE_ELEMENTS_IN_ENCODED;
theEntity.setFhirVersion(myContext.getVersion().getVersion());
if (thePerformIndexing) {
bytes = encodeResource(theResource, encoding, excludeElements, myContext);
encoding = myConfig.getResourceEncoding();
Set<String> excludeElements = ResourceMetaParams.EXCLUDE_ELEMENTS_IN_ENCODED;
theEntity.setFhirVersion(myContext.getVersion().getVersion());
bytes = encodeResource(theResource, encoding, excludeElements, myContext);
if (theUpdateHash) {
HashFunction sha256 = Hashing.sha256();
String hashSha256 = sha256.hashBytes(bytes).toString();
if (hashSha256.equals(theEntity.getHashSha256()) == false) {
changed = true;
}
theEntity.setHashSha256(hashSha256);
} else {
encoding = null;
bytes = null;
}
Set<ResourceTag> allDefs = new HashSet<>();
@ -543,11 +544,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
String profile = def.getResourceProfile("");
if (isNotBlank(profile)) {
TagDefinition profileDef = getTagOrNull(TagTypeEnum.PROFILE, NS_JPA_PROFILE, profile, null);
if (def != null) {
ResourceTag tag = theEntity.addTag(profileDef);
allDefs.add(tag);
theEntity.setHasTags(true);
}
ResourceTag tag = theEntity.addTag(profileDef);
allDefs.add(tag);
theEntity.setHasTags(true);
}
}
@ -580,7 +580,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
encoding = ResourceEncodingEnum.DEL;
}
if (changed == false) {
if (thePerformIndexing && changed == false) {
if (theEntity.getId() == null) {
changed = true;
} else {
@ -988,6 +988,26 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
return updateEntity(theRequest, null, entity, updateTime, true, true, theTransactionDetails, false, true);
}
@VisibleForTesting
public void setEntityManager(EntityManager theEntityManager) {
myEntityManager = theEntityManager;
}
@VisibleForTesting
public void setSearchParamWithInlineReferencesExtractor(SearchParamWithInlineReferencesExtractor theSearchParamWithInlineReferencesExtractor) {
mySearchParamWithInlineReferencesExtractor = theSearchParamWithInlineReferencesExtractor;
}
@VisibleForTesting
public void setResourceHistoryTableDao(IResourceHistoryTableDao theResourceHistoryTableDao) {
myResourceHistoryTableDao = theResourceHistoryTableDao;
}
@VisibleForTesting
public void setDaoSearchParamSynchronizer(DaoSearchParamSynchronizer theDaoSearchParamSynchronizer) {
myDaoSearchParamSynchronizer = theDaoSearchParamSynchronizer;
}
@SuppressWarnings("unchecked")
@Override
public ResourceTable updateEntity(RequestDetails theRequest, final IBaseResource theResource, IBasePersistedResource
@ -1059,7 +1079,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
newParams.populateResourceTableSearchParamsPresentFlags(entity);
entity.setIndexStatus(INDEX_STATUS_INDEXED);
}
populateFullTextFields(myContext, theResource, entity);
if (myFulltextSearchSvc != null && !myFulltextSearchSvc.isDisabled()) {
populateFullTextFields(myContext, theResource, entity);
}
} else {
changed = populateResourceIntoEntity(theRequest, theResource, entity, false);
@ -1071,7 +1095,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
}
if (!changed.isChanged() && !theForceUpdate && myConfig.isSuppressUpdatesWithNoChange()) {
if (thePerformIndexing && changed != null && !changed.isChanged() && !theForceUpdate && myConfig.isSuppressUpdatesWithNoChange()) {
ourLog.debug("Resource {} has not changed", entity.getIdDt().toUnqualified().getValue());
if (theResource != null) {
updateResourceMetadata(entity, theResource);
@ -1323,7 +1347,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
}
}
private void validateChildReferences(IBase theElement, String thePath) {
private void validateChildReferenceTargetTypes(IBase theElement, String thePath) {
if (theElement == null) {
return;
}
@ -1343,7 +1367,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
String newPath = thePath + "." + nextChildDef.getElementName();
for (IBase nextChild : values) {
validateChildReferences(nextChild, newPath);
validateChildReferenceTargetTypes(nextChild, newPath);
}
if (nextChildDef instanceof RuntimeChildResourceDefinition) {
@ -1426,8 +1450,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
throw new UnprocessableEntityException("Resource contains the 'subsetted' tag, and must not be stored as it may contain a subset of available data");
}
String resName = getContext().getResourceType(theResource);
validateChildReferences(theResource, resName);
if (getConfig().isEnforceReferenceTargetTypes()) {
String resName = getContext().getResourceType(theResource);
validateChildReferenceTargetTypes(theResource, resName);
}
validateMetaCount(totalMetaCount);

View File

@ -97,6 +97,7 @@ import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ValidationOptions;
import ca.uhn.fhir.validation.ValidationResult;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.Validate;
import org.apache.commons.text.WordUtils;
import org.hl7.fhir.instance.model.api.IBaseCoding;
@ -222,11 +223,21 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return create(theResource, theIfNoneExist, true, new TransactionDetails(), theRequestDetails);
}
@VisibleForTesting
public void setTransactionService(HapiTransactionService theTransactionService) {
myTransactionService = theTransactionService;
}
@Override
public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, @Nonnull TransactionDetails theTransactionDetails, RequestDetails theRequestDetails) {
return myTransactionService.execute(theRequestDetails, tx -> doCreateForPost(theResource, theIfNoneExist, thePerformIndexing, theTransactionDetails, theRequestDetails));
}
@VisibleForTesting
public void setRequestPartitionHelperService(IRequestPartitionHelperSvc theRequestPartitionHelperService) {
myRequestPartitionHelperService = theRequestPartitionHelperService;
}
/**
* Called for FHIR create (POST) operations
*/
@ -1709,6 +1720,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
}
@VisibleForTesting
public void setDaoConfig(DaoConfig theDaoConfig) {
myDaoConfig = theDaoConfig;
}
private static class IdChecker implements IValidatorModule {
private final ValidationModeEnum myMode;

View File

@ -54,6 +54,7 @@ import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.OperationOutcomeUtil;
import ca.uhn.fhir.util.ResourceReferenceInfo;
import com.google.common.annotations.VisibleForTesting;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseReference;
@ -89,6 +90,11 @@ public abstract class BaseStorageDao {
@Autowired
protected ModelConfig myModelConfig;
@VisibleForTesting
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
mySearchParamRegistry = theSearchParamRegistry;
}
/**
* May be overridden by subclasses to validate resources prior to storage
*
@ -176,7 +182,7 @@ public abstract class BaseStorageDao {
/**
* Handle {@link ModelConfig#getAutoVersionReferenceAtPaths() auto-populate-versions}
*
* <p>
* We only do this if thePerformIndexing is true because if it's false, that means
* we're in a FHIR transaction during the first phase of write operation processing,
* meaning that the versions of other resources may not have need updated yet. For example
@ -184,7 +190,7 @@ public abstract class BaseStorageDao {
* is also being updated in the same transaction, during the first "no index" phase,
* the Patient will not yet have its version number incremented, so it would be wrong
* to use that value. During the second phase it is correct.
*
* <p>
* Also note that {@link BaseTransactionProcessor} also has code to do auto-versioning
* and it is the one that takes care of the placeholder IDs. Look for the other caller of
* {@link #extractReferencesToAutoVersion(FhirContext, ModelConfig, IBaseResource)}

View File

@ -69,6 +69,7 @@ import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.ResourceReferenceInfo;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import org.apache.commons.lang3.Validate;
@ -278,6 +279,16 @@ public abstract class BaseTransactionProcessor {
}
}
@VisibleForTesting
public void setVersionAdapter(ITransactionProcessorVersionAdapter theVersionAdapter) {
myVersionAdapter = theVersionAdapter;
}
@VisibleForTesting
public void setTxManager(PlatformTransactionManager theTxManager) {
myTxManager = theTxManager;
}
private IBaseBundle batch(final RequestDetails theRequestDetails, IBaseBundle theRequest) {
ourLog.info("Beginning batch with {} resources", myVersionAdapter.getEntries(theRequest).size());
long start = System.currentTimeMillis();
@ -335,6 +346,11 @@ public abstract class BaseTransactionProcessor {
return resp;
}
@VisibleForTesting
public void setHapiTransactionService(HapiTransactionService theHapiTransactionService) {
myHapiTransactionService = theHapiTransactionService;
}
private IBaseBundle processTransaction(final RequestDetails theRequestDetails, final IBaseBundle theRequest, final String theActionName) {
validateDependencies();
@ -549,6 +565,10 @@ public abstract class BaseTransactionProcessor {
return myContext.getVersion().newIdType().setValue(theValue);
}
@VisibleForTesting
public void setModelConfig(ModelConfig theModelConfig) {
myModelConfig = theModelConfig;
}
private Map<IBase, IBasePersistedResource> doTransactionWriteOperations(final RequestDetails theRequest, String theActionName, TransactionDetails theTransactionDetails, Set<IIdType> theAllIds,
Map<IIdType, IIdType> theIdSubstitutions, Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome, IBaseBundle theResponse, IdentityHashMap<IBase, Integer> theOriginalRequestOrder, List<IBase> theEntries, StopWatch theTransactionStopWatch) {
@ -612,16 +632,16 @@ public abstract class BaseTransactionProcessor {
String existingUuid = keyToUuid.get(key);
for (IBase nextEntry : theEntries) {
IBaseResource nextResource = myVersionAdapter.getResource(nextEntry);
for (ResourceReferenceInfo nextReference : myContext.newTerser().getAllResourceReferences(nextResource)) {
for (IBaseReference nextReference : myContext.newTerser().getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class)) {
// We're interested in any references directly to the placeholder ID, but also
// references that have a resource target that has the placeholder ID.
String nextReferenceId = nextReference.getResourceReference().getReferenceElement().getValue();
if (isBlank(nextReferenceId) && nextReference.getResourceReference().getResource() != null) {
nextReferenceId = nextReference.getResourceReference().getResource().getIdElement().getValue();
String nextReferenceId = nextReference.getReferenceElement().getValue();
if (isBlank(nextReferenceId) && nextReference.getResource() != null) {
nextReferenceId = nextReference.getResource().getIdElement().getValue();
}
if (entryUrl.equals(nextReferenceId)) {
nextReference.getResourceReference().setReference(existingUuid);
nextReference.getResourceReference().setResource(null);
nextReference.setReference(existingUuid);
nextReference.setResource(null);
}
}
}
@ -923,7 +943,6 @@ public abstract class BaseTransactionProcessor {
IIdType newId = theIdSubstitutions.get(nextId);
ourLog.debug(" * Replacing resource ref {} with {}", nextId, newId);
if (referencesToVersion.contains(resourceReference)) {
DaoMethodOutcome outcome = theIdToPersistedOutcome.get(newId);
resourceReference.setReference(newId.getValue());
} else {
resourceReference.setReference(newId.toVersionless().getValue());
@ -1042,6 +1061,11 @@ public abstract class BaseTransactionProcessor {
return newIdType(theToResourceName, theIdPart, null);
}
@VisibleForTesting
public void setDaoRegistry(DaoRegistry theDaoRegistry) {
myDaoRegistry = theDaoRegistry;
}
private IFhirResourceDao getDaoOrThrowException(Class<? extends IBaseResource> theClass) {
IFhirResourceDao<? extends IBaseResource> dao = myDaoRegistry.getResourceDaoOrNull(theClass);
if (dao == null) {
@ -1113,6 +1137,11 @@ public abstract class BaseTransactionProcessor {
return null;
}
@VisibleForTesting
public void setDaoConfig(DaoConfig theDaoConfig) {
myDaoConfig = theDaoConfig;
}
public interface ITransactionProcessorVersionAdapter<BUNDLE extends IBaseBundle, BUNDLEENTRY extends IBase> {
void setResponseStatus(BUNDLEENTRY theBundleEntry, String theStatus);

View File

@ -62,9 +62,16 @@ public class TransactionProcessor extends BaseTransactionProcessor {
@Override
protected void flushSession(Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome) {
try {
int insertionCount;
int updateCount;
SessionImpl session = myEntityManager.unwrap(SessionImpl.class);
int insertionCount = session.getActionQueue().numberOfInsertions();
int updateCount = session.getActionQueue().numberOfUpdates();
if (session != null) {
insertionCount = session.getActionQueue().numberOfInsertions();
updateCount = session.getActionQueue().numberOfUpdates();
} else {
insertionCount = -1;
updateCount = -1;
}
StopWatch sw = new StopWatch();
myEntityManager.flush();

View File

@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
import ca.uhn.fhir.jpa.util.AddRemoveCount;
import com.google.common.annotations.VisibleForTesting;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -68,6 +69,11 @@ public class DaoSearchParamSynchronizer {
return retVal;
}
@VisibleForTesting
public void setEntityManager(EntityManager theEntityManager) {
myEntityManager = theEntityManager;
}
private <T extends BaseResourceIndex> void synchronize(ResourceTable theEntity, AddRemoveCount theAddRemoveCount, Collection<T> theNewParams, Collection<T> theExistingParams) {
Collection<T> newParams = theNewParams;
for (T next : newParams) {

View File

@ -47,6 +47,7 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.annotations.VisibleForTesting;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -95,6 +96,21 @@ public class SearchParamWithInlineReferencesExtractor {
@Autowired
private PartitionSettings myPartitionSettings;
@VisibleForTesting
public void setPartitionSettings(PartitionSettings thePartitionSettings) {
myPartitionSettings = thePartitionSettings;
}
@VisibleForTesting
public void setSearchParamExtractorService(SearchParamExtractorService theSearchParamExtractorService) {
mySearchParamExtractorService = theSearchParamExtractorService;
}
@VisibleForTesting
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
mySearchParamRegistry = theSearchParamRegistry;
}
public void populateFromResource(ResourceIndexedSearchParams theParams, TransactionDetails theTransactionDetails, ResourceTable theEntity, IBaseResource theResource, ResourceIndexedSearchParams theExistingParams, RequestDetails theRequest) {
extractInlineReferences(theResource, theRequest);
@ -214,6 +230,16 @@ public class SearchParamWithInlineReferencesExtractor {
}
@VisibleForTesting
public void setDaoConfig(DaoConfig theDaoConfig) {
myDaoConfig = theDaoConfig;
}
@VisibleForTesting
public void setContext(FhirContext theContext) {
myContext = theContext;
}
/**
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the
* matching resource.
@ -276,6 +302,11 @@ public class SearchParamWithInlineReferencesExtractor {
}
}
@VisibleForTesting
public void setDaoSearchParamSynchronizer(DaoSearchParamSynchronizer theDaoSearchParamSynchronizer) {
myDaoSearchParamSynchronizer = theDaoSearchParamSynchronizer;
}
public void storeCompositeStringUniques(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams existingParams) {
// Store composite string uniques

View File

@ -27,9 +27,9 @@ import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import com.google.common.annotations.VisibleForTesting;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Meta;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
@ -42,11 +42,14 @@ import java.util.List;
public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoR4.class);
@Autowired
private TransactionProcessor myTransactionProcessor;
@VisibleForTesting
public void setTransactionProcessorForUnitTest(TransactionProcessor theTransactionProcessor) {
myTransactionProcessor = theTransactionProcessor;
}
@Override
@PostConstruct
public void start() {

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -47,6 +48,16 @@ public class HapiTransactionService {
private PlatformTransactionManager myTransactionManager;
private TransactionTemplate myTxTemplate;
@VisibleForTesting
public void setInterceptorBroadcaster(IInterceptorBroadcaster theInterceptorBroadcaster) {
myInterceptorBroadcaster = theInterceptorBroadcaster;
}
@VisibleForTesting
public void setTransactionManager(PlatformTransactionManager theTransactionManager) {
myTransactionManager = theTransactionManager;
}
@PostConstruct
public void start() {
myTxTemplate = new TransactionTemplate(myTransactionManager);

View File

@ -26,10 +26,15 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
import ca.uhn.fhir.jpa.util.AddRemoveCount;
import com.google.common.annotations.VisibleForTesting;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@Service
@ -40,10 +45,14 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
@Autowired
private PartitionSettings myPartitionSettings;
@Autowired
private DaoConfig myDaoConfig;
@VisibleForTesting
public void setDaoConfig(DaoConfig theDaoConfig) {
myDaoConfig = theDaoConfig;
}
@Override
public AddRemoveCount updatePresence(ResourceTable theResource, Map<String, Boolean> theParamNameToPresence) {
AddRemoveCount retVal = new AddRemoveCount();

View File

@ -5,8 +5,6 @@ import ca.uhn.fhir.jpa.batch.api.IBatchJobSubmitter;
import ca.uhn.fhir.jpa.batch.svc.BatchJobSubmitterImpl;
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl;
import ca.uhn.fhir.jpa.bulk.svc.BulkExportDaoSvc;
import ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer;
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
@ -16,10 +14,6 @@ import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings;
import org.hibernate.search.backend.lucene.cfg.LuceneIndexSettings;
import org.hibernate.search.engine.cfg.BackendSettings;
import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

View File

@ -1095,14 +1095,18 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
.stream()
.filter(t -> t.getParamName().equals("medicationadministration-ingredient-medication"))
.collect(Collectors.toList());
ourLog.info("Tokens: {}", tokens);
ourLog.info("Tokens:\n * {}", tokens.stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
assertEquals(1, tokens.size(), tokens.toString());
assertEquals(false, tokens.get(0).isMissing());
});
SearchParameterMap map = new SearchParameterMap();
SearchParameterMap map = SearchParameterMap.newSynchronous();
map.add("medicationadministration-ingredient-medication", new TokenParam("system","code"));
assertEquals(1, myMedicationAdministrationDao.search(map).size().intValue());
myCaptureQueriesListener.clear();
IBundleProvider search = myMedicationAdministrationDao.search(map);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(1, search.sizeOrThrowNpe());
}

View File

@ -25,6 +25,7 @@ import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.test.annotation.DirtiesContext;
import java.util.ArrayList;
import java.util.Collections;
@ -45,6 +46,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@SuppressWarnings({"unchecked", "deprecation", "Duplicates"})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class FhirResourceDaoR4ConcurrentWriteTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ConcurrentWriteTest.class);

View File

@ -32,6 +32,9 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ContainedTest.class);
@ -64,6 +67,8 @@ public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdObs));
runInTransaction(()->{
ourLog.info("String indexes:\n * {}", myResourceIndexedSearchParamStringDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
Long i = myEntityManager
.createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'subject.family' AND s.myResourceType = 'Observation'", Long.class)
.getSingleResult();

View File

@ -306,13 +306,17 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
o.getMeta().addTag("http://foo", "bar", "FOOBAR");
p.getManagingOrganization().setResource(o);
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
ourLog.info("Input: {}", encoded);
assertThat(encoded, containsString("#1"));
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
p = myPatientDao.read(id);
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
ourLog.info("Output: {}", encoded);
assertThat(encoded, containsString("#1"));
Organization org = (Organization) p.getManagingOrganization().getResource();
assertEquals("#1", org.getId());

View File

@ -787,7 +787,8 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
// Lookup the two existing IDs to make sure they are legit
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
myCaptureQueriesListener.logInsertQueriesForCurrentThread();
myCaptureQueriesListener.logUpdateQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());

View File

@ -94,6 +94,7 @@ import org.hl7.fhir.r4.model.OperationOutcome.IssueType;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Period;
import org.hl7.fhir.r4.model.Provenance;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.Quantity.QuantityComparator;
import org.hl7.fhir.r4.model.Questionnaire;
@ -295,6 +296,39 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
});
}
@Test
public void testStoreReferenceFromContainedToContainer() {
Patient patient = new Patient();
patient.setActive(true);
Provenance provenance = new Provenance();
provenance.setId("#1");
provenance.addTarget().setReference("#");
patient.getContained().add(provenance);
Observation observation = new Observation();
observation.setId("#2");
observation.getSubject().setReference("#");
patient.getContained().add(observation);
IIdType id = myPatientDao.create(patient).getId();
patient = myPatientDao.read(id);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
assertEquals(2, patient.getContained().size());
provenance = (Provenance) patient.getContained().get(0);
assertEquals("#1", provenance.getId());
assertEquals("#", provenance.getTargetFirstRep().getReference());
observation = (Observation) patient.getContained().get(1);
assertEquals("#2", observation.getId());
assertEquals("#", observation.getSubject().getReference());
}
@Test
public void testTermConceptReindexingDoesntDuplicateData() {
myDaoConfig.setSchedulingDisabled(true);

View File

@ -26,6 +26,7 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
@AfterEach
public void afterEach() {
myFhirCtx.getParserOptions().setStripVersionsFromReferences(true);
myFhirCtx.getParserOptions().getDontStripVersionsFromReferencesAtPaths().clear();
myDaoConfig.setDeleteEnabled(new DaoConfig().isDeleteEnabled());
myModelConfig.setRespectVersionsForSearchIncludes(new ModelConfig().isRespectVersionsForSearchIncludes());
myModelConfig.setAutoVersionReferenceAtPaths(new ModelConfig().getAutoVersionReferenceAtPaths());
@ -457,6 +458,10 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
observation.getSubject().setReference(patientId.withVersion("1").getValue());
IIdType observationId = myObservationDao.create(observation).getId().toUnqualified();
// Read the observation back
observation = myObservationDao.read(observationId);
assertEquals(patientId.toVersionless().getValue(), observation.getSubject().getReference());
// Search - Non Synchronous for *
{
IBundleProvider outcome = myObservationDao.search(SearchParameterMap.newSynchronous().addInclude(IBaseResource.INCLUDE_ALL));

View File

@ -168,12 +168,11 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
myValidationSupport = wac.getBean(IValidationSupport.class);
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class);
confProvider.setSearchParamRegistry(ourSearchParamRegistry);
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myFhirCtx.getRestfulClientFactory().setSocketTimeout(20000);
myFhirCtx.getRestfulClientFactory().setSocketTimeout(400000);
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();

View File

@ -15,6 +15,7 @@ import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.in;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.hamcrest.Matchers.matchesPattern;
@ -47,7 +48,9 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.util.BundleBuilder;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
@ -849,6 +852,72 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
}
@Test
public void testCreateAndReadBackResourceWithContainedReferenceToContainer() {
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
String input = "{\n" +
" \"resourceType\": \"Organization\",\n" +
" \"id\": \"1\",\n" +
" \"meta\": {\n" +
" \"tag\": [\n" +
" {\n" +
" \"system\": \"https://blah.org/deployment\",\n" +
" \"code\": \"e69414dd-b5c2-462d-bcfd-9d04d6b16596\",\n" +
" \"display\": \"DEPLOYMENT\"\n" +
" },\n" +
" {\n" +
" \"system\": \"https://blah.org/region\",\n" +
" \"code\": \"b47d7a5b-b159-4bed-a8f8-3258e6603adb\",\n" +
" \"display\": \"REGION\"\n" +
" },\n" +
" {\n" +
" \"system\": \"https://blah.org/provider\",\n" +
" \"code\": \"28c30004-0333-40cf-9e7f-3f9e080930bd\",\n" +
" \"display\": \"PROVIDER\"\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"contained\": [\n" +
" {\n" +
" \"resourceType\": \"Location\",\n" +
" \"id\": \"2\",\n" +
" \"position\": {\n" +
" \"longitude\": 51.443238301454289,\n" +
" \"latitude\": 7.34196905697293\n" +
" },\n" +
" \"managingOrganization\": {\n" +
" \"reference\": \"#\"\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"type\": [\n" +
" {\n" +
" \"coding\": [\n" +
" {\n" +
" \"system\": \"https://blah.org/fmc/OrganizationType\",\n" +
" \"code\": \"CLINIC\",\n" +
" \"display\": \"Clinic\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" ],\n" +
" \"name\": \"testOrg\"\n" +
"}";
Organization org = myFhirCtx.newJsonParser().parseResource(Organization.class, input);
IIdType id = myOrganizationDao.create(org).getId();
org = myOrganizationDao.read(id);
String output = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(org);
ourLog.info(output);
Location loc = (Location) org.getContained().get(0);
assertEquals("#", loc.getManagingOrganization().getReference());
}
@Test
public void testCountParam() {
List<IBaseResource> resources = new ArrayList<>();
@ -857,7 +926,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
org.setName("rpr4_testCountParam_01");
resources.add(org);
}
myClient.transaction().withResources(resources).prettyPrint().encodedXml().execute();
List<IBaseResource> outcome = myClient.transaction().withResources(resources).prettyPrint().encodedXml().execute();
runInTransaction(()->{
assertEquals(100, myResourceTableDao.count());
});
Bundle found = myClient
.search()

View File

@ -0,0 +1,830 @@
package ca.uhn.fhir.jpa.stresstest;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.executor.InterceptorService;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.cache.IResourceChangeListener;
import ca.uhn.fhir.jpa.cache.IResourceVersionSvc;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCache;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheFactory;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl;
import ca.uhn.fhir.jpa.cache.ResourceVersionMap;
import ca.uhn.fhir.jpa.dao.JpaResourceDao;
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
import ca.uhn.fhir.jpa.dao.index.DaoSearchParamSynchronizer;
import ca.uhn.fhir.jpa.dao.index.SearchParamWithInlineReferencesExtractor;
import ca.uhn.fhir.jpa.dao.r4.FhirSystemDaoR4;
import ca.uhn.fhir.jpa.dao.r4.TransactionProcessorVersionAdapterR4;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.ClasspathUtil;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.validation.IInstanceValidatorModule;
import com.google.common.collect.Lists;
import org.hamcrest.Matchers;
import org.hibernate.Session;
import org.hibernate.internal.SessionImpl;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.ExplanationOfBenefit;
import org.junit.Ignore;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.SimpleTransactionStatus;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.metamodel.Metamodel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class GiantTransactionPerfTest {
private static final Logger ourLog = LoggerFactory.getLogger(GiantTransactionPerfTest.class);
private final FhirContext myCtx = FhirContext.forCached(FhirVersionEnum.R4);
private FhirSystemDaoR4 mySystemDao;
private IInterceptorBroadcaster myInterceptorSvc;
private TransactionProcessor myTransactionProcessor;
private PlatformTransactionManager myTransactionManager;
private MockEntityManager myEntityManager;
private DaoConfig myDaoConfig;
private HapiTransactionService myHapiTransactionService;
private DaoRegistry myDaoRegistry;
private JpaResourceDao<ExplanationOfBenefit> myEobDao;
@Mock(answer = Answers.CALLS_REAL_METHODS)
private ApplicationContext myAppCtx;
@Mock
private IInstanceValidatorModule myInstanceValidatorSvc;
private SearchParamWithInlineReferencesExtractor mySearchParamWithInlineReferencesExtractor;
private PartitionSettings myPartitionSettings;
private SearchParamExtractorService mySearchParamExtractorSvc;
private SearchParamExtractorR4 mySearchParamExtractor;
private SearchParamRegistryImpl mySearchParamRegistry;
private ResourceChangeListenerRegistryImpl myResourceChangeListenerRegistry;
private InMemoryResourceMatcher myInMemoryResourceMatcher;
@Mock
private ResourceChangeListenerCacheFactory myResourceChangeListenerCacheFactory;
private ResourceChangeListenerCacheRefresherImpl myResourceChangeListenerCacheRefresher;
private MockResourceVersionSvc myResourceVersionSvc;
private MockResourceHistoryTableDao myResourceHistoryTableDao;
private SearchParamPresenceSvcImpl mySearchParamPresenceSvc;
private DaoSearchParamSynchronizer myDaoSearchParamSynchronizer;
@AfterEach
public void afterEach() {
myDaoConfig.setEnforceReferenceTargetTypes(new DaoConfig().isEnforceReferenceTargetTypes());
myDaoConfig.setAllowInlineMatchUrlReferences(new DaoConfig().isAllowInlineMatchUrlReferences());
}
@BeforeEach
public void beforeEach() {
myDaoConfig = new DaoConfig();
mySearchParamPresenceSvc = new SearchParamPresenceSvcImpl();
mySearchParamPresenceSvc.setDaoConfig(myDaoConfig);
myTransactionManager = new MockTransactionManager();
myResourceHistoryTableDao = new MockResourceHistoryTableDao();
myEntityManager = new MockEntityManager();
myInterceptorSvc = new InterceptorService();
myDaoRegistry = new DaoRegistry(myCtx);
myPartitionSettings = new PartitionSettings();
myHapiTransactionService = new HapiTransactionService();
myHapiTransactionService.setTransactionManager(myTransactionManager);
myHapiTransactionService.setInterceptorBroadcaster(myInterceptorSvc);
myHapiTransactionService.start();
myTransactionProcessor = new TransactionProcessor();
myTransactionProcessor.setContext(myCtx);
myTransactionProcessor.setDao(mySystemDao);
myTransactionProcessor.setTxManager(myTransactionManager);
myTransactionProcessor.setEntityManagerForUnitTest(myEntityManager);
myTransactionProcessor.setVersionAdapter(new TransactionProcessorVersionAdapterR4());
myTransactionProcessor.setDaoConfig(myDaoConfig);
myTransactionProcessor.setModelConfig(myDaoConfig.getModelConfig());
myTransactionProcessor.setHapiTransactionService(myHapiTransactionService);
myTransactionProcessor.setDaoRegistry(myDaoRegistry);
myTransactionProcessor.start();
mySystemDao = new FhirSystemDaoR4();
mySystemDao.setTransactionProcessorForUnitTest(myTransactionProcessor);
mySystemDao.start();
when(myAppCtx.getBean(eq(IInstanceValidatorModule.class))).thenReturn(myInstanceValidatorSvc);
myInMemoryResourceMatcher = new InMemoryResourceMatcher();
myResourceVersionSvc = new MockResourceVersionSvc();
myResourceChangeListenerCacheRefresher = new ResourceChangeListenerCacheRefresherImpl();
myResourceChangeListenerCacheRefresher.setSchedulerService(new MockSchedulerSvc());
myResourceChangeListenerCacheRefresher.setResourceVersionSvc(myResourceVersionSvc);
when(myResourceChangeListenerCacheFactory.create(any(), any(), any(), anyLong())).thenAnswer(t -> {
String resourceName = t.getArgument(0, String.class);
SearchParameterMap searchParameterMap = t.getArgument(1, SearchParameterMap.class);
IResourceChangeListener changeListener = t.getArgument(2, IResourceChangeListener.class);
long refreshInterval = t.getArgument(3, Long.class);
ResourceChangeListenerCache retVal = new ResourceChangeListenerCache(resourceName, changeListener, searchParameterMap, refreshInterval);
retVal.setResourceChangeListenerCacheRefresher(myResourceChangeListenerCacheRefresher);
return retVal;
});
myResourceChangeListenerRegistry = new ResourceChangeListenerRegistryImpl();
myResourceChangeListenerRegistry.setFhirContext(myCtx);
myResourceChangeListenerRegistry.setInMemoryResourceMatcher(myInMemoryResourceMatcher);
myResourceChangeListenerRegistry.setResourceChangeListenerCacheFactory(myResourceChangeListenerCacheFactory);
myResourceChangeListenerCacheRefresher.setResourceChangeListenerRegistry(myResourceChangeListenerRegistry);
mySearchParamRegistry = new SearchParamRegistryImpl();
mySearchParamRegistry.setResourceChangeListenerRegistry(myResourceChangeListenerRegistry);
mySearchParamRegistry.setFhirContext(myCtx);
mySearchParamRegistry.setModelConfig(myDaoConfig.getModelConfig());
mySearchParamRegistry.registerListener();
mySearchParamExtractor = new SearchParamExtractorR4();
mySearchParamExtractor.setContext(myCtx);
mySearchParamExtractor.setSearchParamRegistry(mySearchParamRegistry);
mySearchParamExtractor.setPartitionSettings(myPartitionSettings);
mySearchParamExtractor.setModelConfig(myDaoConfig.getModelConfig());
mySearchParamExtractor.start();
mySearchParamExtractorSvc = new SearchParamExtractorService();
mySearchParamExtractorSvc.setContext(myCtx);
mySearchParamExtractorSvc.setSearchParamExtractor(mySearchParamExtractor);
mySearchParamExtractorSvc.setModelConfig(myDaoConfig.getModelConfig());
myDaoSearchParamSynchronizer = new DaoSearchParamSynchronizer();
myDaoSearchParamSynchronizer.setEntityManager(myEntityManager);
mySearchParamWithInlineReferencesExtractor = new SearchParamWithInlineReferencesExtractor();
mySearchParamWithInlineReferencesExtractor.setDaoConfig(myDaoConfig);
mySearchParamWithInlineReferencesExtractor.setContext(myCtx);
mySearchParamWithInlineReferencesExtractor.setPartitionSettings(myPartitionSettings);
mySearchParamWithInlineReferencesExtractor.setSearchParamExtractorService(mySearchParamExtractorSvc);
mySearchParamWithInlineReferencesExtractor.setSearchParamRegistry(mySearchParamRegistry);
mySearchParamWithInlineReferencesExtractor.setDaoSearchParamSynchronizer(myDaoSearchParamSynchronizer);
myEobDao = new JpaResourceDao<>();
myEobDao.setContext(myCtx);
myEobDao.setConfig(myDaoConfig);
myEobDao.setResourceType(ExplanationOfBenefit.class);
myEobDao.setApplicationContext(myAppCtx);
myEobDao.setTransactionService(myHapiTransactionService);
myEobDao.setDaoConfig(myDaoConfig);
myEobDao.setRequestPartitionHelperService(new MockRequestPartitionHelperSvc());
myEobDao.setEntityManager(myEntityManager);
myEobDao.setSearchParamWithInlineReferencesExtractor(mySearchParamWithInlineReferencesExtractor);
myEobDao.setResourceHistoryTableDao(myResourceHistoryTableDao);
myEobDao.setSearchParamRegistry(mySearchParamRegistry);
myEobDao.setSearchParamPresenceSvc(mySearchParamPresenceSvc);
myEobDao.setDaoSearchParamSynchronizer(myDaoSearchParamSynchronizer);
myEobDao.start();
myDaoRegistry.setResourceDaos(Lists.newArrayList(myEobDao));
}
@Test
public void testTransaction() {
Bundle input = ClasspathUtil.loadResource(myCtx, Bundle.class, "/r4/large-transaction.json");
while (input.getEntry().size() > 1) {
input.getEntry().remove(1);
}
ServletRequestDetails requestDetails = new ServletRequestDetails(myInterceptorSvc);
requestDetails.setServletRequest(new MockServletRequest());
mySystemDao.transaction(requestDetails, input);
assertThat(myEntityManager.myPersistCount.stream().map(t -> t.getClass().getSimpleName()).collect(Collectors.toList()), Matchers.contains("ResourceTable"));
assertThat(myEntityManager.myMergeCount.stream().map(t -> t.getClass().getSimpleName()).collect(Collectors.toList()), Matchers.containsInAnyOrder("ResourceTable", "ResourceIndexedSearchParamToken", "ResourceIndexedSearchParamToken"));
assertEquals(1, myEntityManager.myFlushCount);
assertEquals(1, myResourceVersionSvc.myGetVersionMap);
assertEquals(1, myResourceHistoryTableDao.mySaveCount);
}
@Test
@Disabled
public void testTransactionStressTest() {
myDaoConfig.setEnforceReferenceTargetTypes(false);
myDaoConfig.setAllowInlineMatchUrlReferences(false);
Bundle input = ClasspathUtil.loadResource(myCtx, Bundle.class, "/r4/large-transaction.json");
ServletRequestDetails requestDetails = new ServletRequestDetails(myInterceptorSvc);
requestDetails.setServletRequest(new MockServletRequest());
// Pre-warmup
ourLog.info("Warming up...");
for (int i = 0; i < 10; i++) {
mySystemDao.transaction(requestDetails, input);
}
ourLog.info("Done warming up");
StopWatch sw = new StopWatch();
for (int i = 1; i < 10000; i++) {
mySystemDao.transaction(requestDetails, input);
if (i % 5 == 0) {
ourLog.info("Processed {} - {}/second", i, sw.formatThroughput(i, TimeUnit.SECONDS));
}
myEntityManager.clearCounts();
}
assertThat(myEntityManager.myPersistCount.stream().map(t -> t.getClass().getSimpleName()).collect(Collectors.toList()), Matchers.contains("ResourceTable"));
assertThat(myEntityManager.myMergeCount.stream().map(t -> t.getClass().getSimpleName()).collect(Collectors.toList()), Matchers.containsInAnyOrder("ResourceTable", "ResourceIndexedSearchParamToken", "ResourceIndexedSearchParamToken"));
assertEquals(1, myEntityManager.myFlushCount);
assertEquals(1, myResourceVersionSvc.myGetVersionMap);
assertEquals(1, myResourceHistoryTableDao.mySaveCount);
}
private class MockResourceVersionSvc implements IResourceVersionSvc {
private int myGetVersionMap;
@Nonnull
@Override
public ResourceVersionMap getVersionMap(String theResourceName, SearchParameterMap theSearchParamMap) {
myGetVersionMap++;
return ResourceVersionMap.fromResources(Lists.newArrayList());
}
}
private class MockResourceHistoryTableDao implements IResourceHistoryTableDao {
private int mySaveCount;
@Override
public ResourceHistoryTable findForIdAndVersionAndFetchProvenance(long theId, long theVersion) {
throw new UnsupportedOperationException();
}
@Override
public Slice<Long> findForResourceId(Pageable thePage, Long theId, Long theDontWantVersion) {
throw new UnsupportedOperationException();
}
@Override
public Slice<Long> findIdsOfPreviousVersionsOfResourceId(Pageable thePage, Long theResourceId) {
throw new UnsupportedOperationException();
}
@Override
public Slice<Long> findIdsOfPreviousVersionsOfResources(Pageable thePage, String theResourceName) {
throw new UnsupportedOperationException();
}
@Override
public Slice<Long> findIdsOfPreviousVersionsOfResources(Pageable thePage) {
throw new UnsupportedOperationException();
}
@Override
public void updateVersion(long theId, long theOldVersion, long theNewVersion) {
throw new UnsupportedOperationException();
}
@Override
public void deleteByPid(Long theId) {
throw new UnsupportedOperationException();
}
@Override
public List<ResourceHistoryTable> findAll() {
throw new UnsupportedOperationException();
}
@Override
public List<ResourceHistoryTable> findAll(Sort sort) {
throw new UnsupportedOperationException();
}
@Override
public Page<ResourceHistoryTable> findAll(Pageable pageable) {
throw new UnsupportedOperationException();
}
@Override
public List<ResourceHistoryTable> findAllById(Iterable<Long> ids) {
throw new UnsupportedOperationException();
}
@Override
public long count() {
throw new UnsupportedOperationException();
}
@Override
public void deleteById(Long theLong) {
throw new UnsupportedOperationException();
}
@Override
public void delete(ResourceHistoryTable entity) {
throw new UnsupportedOperationException();
}
@Override
public void deleteAll(Iterable<? extends ResourceHistoryTable> entities) {
throw new UnsupportedOperationException();
}
@Override
public void deleteAll() {
throw new UnsupportedOperationException();
}
@Override
public <S extends ResourceHistoryTable> S save(S entity) {
mySaveCount++;
return entity;
}
@Override
public <S extends ResourceHistoryTable> List<S> saveAll(Iterable<S> entities) {
throw new UnsupportedOperationException();
}
@Override
public Optional<ResourceHistoryTable> findById(Long theLong) {
throw new UnsupportedOperationException();
}
@Override
public boolean existsById(Long theLong) {
throw new UnsupportedOperationException();
}
@Override
public void flush() {
throw new UnsupportedOperationException();
}
@Override
public <S extends ResourceHistoryTable> S saveAndFlush(S entity) {
throw new UnsupportedOperationException();
}
@Override
public void deleteInBatch(Iterable<ResourceHistoryTable> entities) {
throw new UnsupportedOperationException();
}
@Override
public void deleteAllInBatch() {
throw new UnsupportedOperationException();
}
@Override
public ResourceHistoryTable getOne(Long theLong) {
throw new UnsupportedOperationException();
}
@Override
public <S extends ResourceHistoryTable> Optional<S> findOne(Example<S> example) {
return Optional.empty();
}
@Override
public <S extends ResourceHistoryTable> List<S> findAll(Example<S> example) {
throw new UnsupportedOperationException();
}
@Override
public <S extends ResourceHistoryTable> List<S> findAll(Example<S> example, Sort sort) {
throw new UnsupportedOperationException();
}
@Override
public <S extends ResourceHistoryTable> Page<S> findAll(Example<S> example, Pageable pageable) {
throw new UnsupportedOperationException();
}
@Override
public <S extends ResourceHistoryTable> long count(Example<S> example) {
throw new UnsupportedOperationException();
}
@Override
public <S extends ResourceHistoryTable> boolean exists(Example<S> example) {
throw new UnsupportedOperationException();
}
}
private class MockEntityManager implements EntityManager {
private List<Object> myPersistCount = new ArrayList<>();
private List<Object> myMergeCount = new ArrayList<>();
private long ourNextId = 0L;
private int myFlushCount;
@Override
public void persist(Object entity) {
myPersistCount.add(entity);
if (entity instanceof ResourceTable) {
((ResourceTable) entity).setId(ourNextId++);
}
}
@Override
public <T> T merge(T entity) {
myMergeCount.add(entity);
return entity;
}
@Override
public void remove(Object entity) {
throw new UnsupportedOperationException();
}
@Override
public <T> T find(Class<T> entityClass, Object primaryKey) {
throw new UnsupportedOperationException();
}
@Override
public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties) {
throw new UnsupportedOperationException();
}
@Override
public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode) {
throw new UnsupportedOperationException();
}
@Override
public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties) {
throw new UnsupportedOperationException();
}
@Override
public <T> T getReference(Class<T> entityClass, Object primaryKey) {
throw new UnsupportedOperationException();
}
@Override
public void flush() {
myFlushCount++;
}
@Override
public FlushModeType getFlushMode() {
throw new UnsupportedOperationException();
}
@Override
public void setFlushMode(FlushModeType flushMode) {
throw new UnsupportedOperationException();
}
@Override
public void lock(Object entity, LockModeType lockMode) {
throw new UnsupportedOperationException();
}
@Override
public void lock(Object entity, LockModeType lockMode, Map<String, Object> properties) {
throw new UnsupportedOperationException();
}
@Override
public void refresh(Object entity) {
throw new UnsupportedOperationException();
}
@Override
public void refresh(Object entity, Map<String, Object> properties) {
throw new UnsupportedOperationException();
}
@Override
public void refresh(Object entity, LockModeType lockMode) {
throw new UnsupportedOperationException();
}
@Override
public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public void detach(Object entity) {
throw new UnsupportedOperationException();
}
@Override
public boolean contains(Object entity) {
throw new UnsupportedOperationException();
}
@Override
public LockModeType getLockMode(Object entity) {
throw new UnsupportedOperationException();
}
@Override
public void setProperty(String propertyName, Object value) {
throw new UnsupportedOperationException();
}
@Override
public Map<String, Object> getProperties() {
throw new UnsupportedOperationException();
}
@Override
public Query createQuery(String qlString) {
throw new UnsupportedOperationException();
}
@Override
public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) {
throw new UnsupportedOperationException();
}
@Override
public Query createQuery(CriteriaUpdate updateQuery) {
throw new UnsupportedOperationException();
}
@Override
public Query createQuery(CriteriaDelete deleteQuery) {
throw new UnsupportedOperationException();
}
@Override
public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass) {
throw new UnsupportedOperationException();
}
@Override
public Query createNamedQuery(String name) {
throw new UnsupportedOperationException();
}
@Override
public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass) {
throw new UnsupportedOperationException();
}
@Override
public Query createNativeQuery(String sqlString) {
throw new UnsupportedOperationException();
}
@Override
public Query createNativeQuery(String sqlString, Class resultClass) {
throw new UnsupportedOperationException();
}
@Override
public Query createNativeQuery(String sqlString, String resultSetMapping) {
throw new UnsupportedOperationException();
}
@Override
public StoredProcedureQuery createNamedStoredProcedureQuery(String name) {
throw new UnsupportedOperationException();
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
throw new UnsupportedOperationException();
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) {
throw new UnsupportedOperationException();
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) {
throw new UnsupportedOperationException();
}
@Override
public void joinTransaction() {
throw new UnsupportedOperationException();
}
@Override
public boolean isJoinedToTransaction() {
throw new UnsupportedOperationException();
}
@Override
public <T> T unwrap(Class<T> cls) {
if (cls.equals(SessionImpl.class)) {
return null;
}
throw new UnsupportedOperationException();
}
@Override
public Object getDelegate() {
throw new UnsupportedOperationException();
}
@Override
public void close() {
throw new UnsupportedOperationException();
}
@Override
public boolean isOpen() {
throw new UnsupportedOperationException();
}
@Override
public EntityTransaction getTransaction() {
throw new UnsupportedOperationException();
}
@Override
public EntityManagerFactory getEntityManagerFactory() {
throw new UnsupportedOperationException();
}
@Override
public CriteriaBuilder getCriteriaBuilder() {
throw new UnsupportedOperationException();
}
@Override
public Metamodel getMetamodel() {
throw new UnsupportedOperationException();
}
@Override
public <T> EntityGraph<T> createEntityGraph(Class<T> rootType) {
throw new UnsupportedOperationException();
}
@Override
public EntityGraph<?> createEntityGraph(String graphName) {
throw new UnsupportedOperationException();
}
@Override
public EntityGraph<?> getEntityGraph(String graphName) {
throw new UnsupportedOperationException();
}
@Override
public <T> List<EntityGraph<? super T>> getEntityGraphs(Class<T> entityClass) {
throw new UnsupportedOperationException();
}
public void clearCounts() {
myMergeCount.clear();
myPersistCount.clear();
}
}
private static class MockSchedulerSvc implements ISchedulerService {
@Override
public void purgeAllScheduledJobsForUnitTest() {
throw new UnsupportedOperationException();
}
@Override
public void logStatusForUnitTest() {
throw new UnsupportedOperationException();
}
@Override
public void scheduleLocalJob(long theIntervalMillis, ScheduledJobDefinition theJobDefinition) {
throw new UnsupportedOperationException();
}
@Override
public void scheduleClusteredJob(long theIntervalMillis, ScheduledJobDefinition theJobDefinition) {
throw new UnsupportedOperationException();
}
@Override
public Set<JobKey> getLocalJobKeysForUnitTest() {
throw new UnsupportedOperationException();
}
@Override
public Set<JobKey> getClusteredJobKeysForUnitTest() {
throw new UnsupportedOperationException();
}
@Override
public boolean isStopping() {
return false;
}
}
private static class MockServletRequest extends MockHttpServletRequest {
}
private static class MockRequestPartitionHelperSvc implements ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc {
@Nonnull
@Override
public RequestPartitionId determineReadPartitionForRequest(@Nullable RequestDetails theRequest, String theResourceType) {
return RequestPartitionId.defaultPartition();
}
@Nonnull
@Override
public RequestPartitionId determineCreatePartitionForRequest(@Nullable RequestDetails theRequest, @Nonnull IBaseResource theResource, @Nonnull String theResourceType) {
return RequestPartitionId.defaultPartition();
}
}
private static class MockTransactionManager implements PlatformTransactionManager {
@Nonnull
@Override
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
return new SimpleTransactionStatus();
}
@Override
public void commit(@Nonnull TransactionStatus status) throws TransactionException {
}
@Override
public void rollback(@Nonnull TransactionStatus status) throws TransactionException {
}
}
}

View File

@ -27,7 +27,7 @@
<appender-ref ref="STDOUT" />
</logger>
j
<logger name="org.hibernate.event.internal.DefaultPersistEventListener" additivity="true" level="trace">
<logger name="org.hibernate.event.internal.DefaultPersistEventListener" additivity="true" level="info">
<appender-ref ref="STDOUT" />
</logger>

File diff suppressed because it is too large Load Diff

View File

@ -98,7 +98,6 @@ public class ModelConfig {
private Set<String> myAutoVersionReferenceAtPaths = Collections.emptySet();
private Map<String, Set<String>> myTypeToAutoVersionReferenceAtPaths = Collections.emptyMap();
private boolean myRespectVersionsForSearchIncludes;
private boolean myIndexOnContainedResources = false;
/**
@ -731,8 +730,7 @@ public class ModelConfig {
public void setRespectVersionsForSearchIncludes(boolean theRespectVersionsForSearchIncludes) {
myRespectVersionsForSearchIncludes = theRespectVersionsForSearchIncludes;
}
/**
* Should indexing and searching on contained resources be enabled on this server.
* This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.

View File

@ -257,6 +257,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
b.append("hashIdentity", myHashIdentity);
b.append("hashSystem", myHashSystem);
b.append("hashValue", myHashValue);
b.append("hashSysAndValue", myHashSystemAndValue);
return b.build();
}

View File

@ -134,6 +134,11 @@ public class ResourceChangeListenerCache implements IResourceChangeListenerCache
return retval;
}
@VisibleForTesting
public void setResourceChangeListenerCacheRefresher(IResourceChangeListenerCacheRefresher theResourceChangeListenerCacheRefresher) {
myResourceChangeListenerCacheRefresher = theResourceChangeListenerCacheRefresher;
}
private ResourceChangeResult refreshCacheAndNotifyListenersWithRetry() {
Retrier<ResourceChangeResult> refreshCacheRetrier = new Retrier<>(() -> {
synchronized (this) {

View File

@ -103,6 +103,21 @@ public class ResourceChangeListenerCacheRefresherImpl implements IResourceChange
return retval;
}
@VisibleForTesting
public void setSchedulerService(ISchedulerService theSchedulerService) {
mySchedulerService = theSchedulerService;
}
@VisibleForTesting
public void setResourceChangeListenerRegistry(ResourceChangeListenerRegistryImpl theResourceChangeListenerRegistry) {
myResourceChangeListenerRegistry = theResourceChangeListenerRegistry;
}
@VisibleForTesting
public void setResourceVersionSvc(IResourceVersionSvc theResourceVersionSvc) {
myResourceVersionSvc = theResourceVersionSvc;
}
@Override
public ResourceChangeResult refreshCacheAndNotifyListener(IResourceChangeListenerCache theCache) {
ResourceChangeResult retVal = new ResourceChangeResult();

View File

@ -47,28 +47,42 @@ import java.util.concurrent.ConcurrentLinkedQueue;
@Component
public class ResourceChangeListenerRegistryImpl implements IResourceChangeListenerRegistry {
private static final Logger ourLog = LoggerFactory.getLogger(ResourceChangeListenerRegistryImpl.class);
private final Queue<ResourceChangeListenerCache> myListenerEntries = new ConcurrentLinkedQueue<>();
@Autowired
ResourceChangeListenerCacheFactory myResourceChangeListenerCacheFactory;
@Autowired
private FhirContext myFhirContext;
@Autowired
private InMemoryResourceMatcher myInMemoryResourceMatcher;
@Autowired
ResourceChangeListenerCacheFactory myResourceChangeListenerCacheFactory;
private final Queue<ResourceChangeListenerCache> myListenerEntries = new ConcurrentLinkedQueue<>();
@VisibleForTesting
public void setResourceChangeListenerCacheFactory(ResourceChangeListenerCacheFactory theResourceChangeListenerCacheFactory) {
myResourceChangeListenerCacheFactory = theResourceChangeListenerCacheFactory;
}
@VisibleForTesting
public void setFhirContext(FhirContext theFhirContext) {
myFhirContext = theFhirContext;
}
@VisibleForTesting
public void setInMemoryResourceMatcher(InMemoryResourceMatcher theInMemoryResourceMatcher) {
myInMemoryResourceMatcher = theInMemoryResourceMatcher;
}
/**
* Register a listener in order to be notified whenever a resource matching the provided SearchParameterMap
* changes in any way. If the change happened on the same jvm process where this registry resides, then the listener will be called
* within {@link ResourceChangeListenerCacheRefresherImpl#LOCAL_REFRESH_INTERVAL_MS} of the change happening. If the change happened
* on a different jvm process, then the listener will be called within theRemoteRefreshIntervalMs.
* @param theResourceName the type of the resource the listener should be notified about (e.g. "Subscription" or "SearchParameter")
* @param theSearchParameterMap the listener will only be notified of changes to resources that match this map
* @param theResourceChangeListener the listener that will be called whenever resource changes are detected
*
* @param theResourceName the type of the resource the listener should be notified about (e.g. "Subscription" or "SearchParameter")
* @param theSearchParameterMap the listener will only be notified of changes to resources that match this map
* @param theResourceChangeListener the listener that will be called whenever resource changes are detected
* @param theRemoteRefreshIntervalMs the number of milliseconds between checking the database for changed resources that match the search parameter map
* @throws ca.uhn.fhir.parser.DataFormatException if theResourceName is not a valid resource type in our FhirContext
* @throws IllegalArgumentException if theSearchParamMap cannot be evaluated in-memory
* @return RegisteredResourceChangeListener that stores the resource id cache, and the next refresh time
* @throws ca.uhn.fhir.parser.DataFormatException if theResourceName is not a valid resource type in our FhirContext
* @throws IllegalArgumentException if theSearchParamMap cannot be evaluated in-memory
*/
@Override
public IResourceChangeListenerCache registerResourceResourceChangeListener(String theResourceName, SearchParameterMap theSearchParameterMap, IResourceChangeListener theResourceChangeListener, long theRemoteRefreshIntervalMs) {

View File

@ -45,8 +45,10 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.StringUtil;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@ -81,6 +83,7 @@ import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim;
@ -532,6 +535,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
protected abstract IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath);
@VisibleForTesting
public void setContext(FhirContext theContext) {
myContext = theContext;
}
protected FhirContext getContext() {
return myContext;
}
@ -540,6 +548,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
return myModelConfig;
}
@VisibleForTesting
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
mySearchParamRegistry = theSearchParamRegistry;
}
private Collection<RuntimeSearchParam> getSearchParams(IBaseResource theResource) {
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
Collection<RuntimeSearchParam> retVal = mySearchParamRegistry.getActiveSearchParams(def.getName()).values();
@ -644,6 +657,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
}
@VisibleForTesting
public void setModelConfig(ModelConfig theModelConfig) {
myModelConfig = theModelConfig;
}
protected boolean shouldIndexTextComponentOfToken(RuntimeSearchParam theSearchParam) {
return tokenTextIndexingEnabledForSearchParam(myModelConfig, theSearchParam);
}
@ -938,6 +956,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
SearchParamSet<T> retVal = new SearchParamSet<>();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
cleanUpContainedResourceReferences(theResource, theSearchParamType, searchParams);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != theSearchParamType) {
continue;
@ -948,6 +969,31 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
return retVal;
}
/**
* HAPI FHIR Reference objects (e.g. {@link org.hl7.fhir.r4.model.Reference}) can hold references either by text
* (e.g. "#3") or by resource (e.g. "new Reference(patientInstance)"). The FHIRPath evaluator only understands the
* first way, so if there is any chance of the FHIRPath evaluator needing to descend across references, we
* have to assign values to those references before indexing.
*
* Doing this cleanup isn't hugely expensive, but it's not completely free either so we only do it
* if we think there's actually a chance
*/
private void cleanUpContainedResourceReferences(IBaseResource theResource, RestSearchParameterTypeEnum theSearchParamType, Collection<RuntimeSearchParam> searchParams) {
boolean havePathWithResolveExpression = myModelConfig.isIndexOnContainedResources();
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != theSearchParamType) {
continue;
}
if (defaultString(nextSpDef.getPath()).contains("resolve")) {
havePathWithResolveExpression = true;
break;
}
}
if (havePathWithResolveExpression) {
myContext.newTerser().containResources(theResource, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
}
}
private <T> void extractSearchParam(RuntimeSearchParam theSearchParameterDef, IBaseResource theResource, IExtractor<T> theExtractor, SearchParamSet<T> theSetToPopulate, boolean theWantLocalReferences) {
String nextPathUnsplit = theSearchParameterDef.getPath();
if (isBlank(nextPathUnsplit)) {
@ -1020,6 +1066,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
}
@VisibleForTesting
public void setPartitionSettings(PartitionSettings thePartitionSettings) {
myPartitionSettings = thePartitionSettings;
}
private ResourceIndexedSearchParamToken createTokenIndexIfNotBlank(String theResourceType, RuntimeSearchParam theSearchParam, String theSystem, String theValue) {
String system = theSystem;
String value = theValue;

View File

@ -74,10 +74,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class SearchParamExtractorService {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorService.class);
@Autowired
private ISearchParamExtractor mySearchParamExtractor;
@Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
@Autowired
@ -91,22 +89,25 @@ public class SearchParamExtractorService {
@Autowired(required = false)
private IResourceLinkResolver myResourceLinkResolver;
@VisibleForTesting
public void setSearchParamExtractor(ISearchParamExtractor theSearchParamExtractor) {
mySearchParamExtractor = theSearchParamExtractor;
}
/**
* This method is responsible for scanning a resource for all of the search parameter instances. I.e. for all search parameters defined for
* a given resource type, it extracts the associated indexes and populates {@literal theParams}.
*/
public void extractFromResource(RequestPartitionId theRequestPartitionId, RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference) {
IBaseResource resource = normalizeResource(theResource);
// All search parameter types except Reference
ResourceIndexedSearchParams normalParams = new ResourceIndexedSearchParams();
extractSearchIndexParameters(theRequestDetails, normalParams, resource, theEntity);
extractSearchIndexParameters(theRequestDetails, normalParams, theResource, theEntity);
mergeParams(normalParams, theParams);
if (myModelConfig.isIndexOnContainedResources()) {
ResourceIndexedSearchParams containedParams = new ResourceIndexedSearchParams();
extractSearchIndexParametersForContainedResources(theRequestDetails, containedParams, resource, theEntity);
extractSearchIndexParametersForContainedResources(theRequestDetails, containedParams, theResource, theEntity);
mergeParams(containedParams, theParams);
}
@ -114,11 +115,16 @@ public class SearchParamExtractorService {
populateResourceTables(theParams, theEntity);
// Reference search parameters
extractResourceLinks(theRequestPartitionId, theParams, theEntity, resource, theTransactionDetails, theFailOnInvalidReference, theRequestDetails);
extractResourceLinks(theRequestPartitionId, theParams, theEntity, theResource, theTransactionDetails, theFailOnInvalidReference, theRequestDetails);
theParams.setUpdatedTime(theTransactionDetails.getTransactionDate());
}
@VisibleForTesting
public void setModelConfig(ModelConfig theModelConfig) {
myModelConfig = theModelConfig;
}
private void extractSearchIndexParametersForContainedResources(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, ResourceTable theEntity) {
FhirTerser terser = myContext.newTerser();
@ -182,7 +188,7 @@ public class SearchParamExtractorService {
}
private void extractSearchIndexParameters(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, ResourceTable theEntity) {
// Strings
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamString> strings = extractSearchParamStrings(theResource);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, strings);
@ -203,7 +209,7 @@ public class SearchParamExtractorService {
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantitiesNormalized);
theParams.myQuantityNormalizedParams.addAll(quantitiesNormalized);
}
// Dates
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamDate> dates = extractSearchParamDates(theResource);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, dates);
@ -247,19 +253,9 @@ public class SearchParamExtractorService {
populateResourceTable(theParams.myCoordsParams, theEntity);
}
/**
* This is a bit hacky, but if someone has manually populated a resource (ie. my working directly with the model
* as opposed to by parsing a serialized instance) it's possible that they have put in contained resources
* using {@link IBaseReference#setResource(IBaseResource)}, and those contained resources have not yet
* ended up in the Resource.contained array, meaning that FHIRPath expressions won't be able to find them.
*
* As a result, we to a serialize-and-parse to normalize the object. This really only affects people who
* are calling the JPA DAOs directly, but there are a few of those...
*/
private IBaseResource normalizeResource(IBaseResource theResource) {
IParser parser = myContext.newJsonParser().setPrettyPrint(false);
theResource = parser.parseResource(parser.encodeResourceToString(theResource));
return theResource;
@VisibleForTesting
public void setContext(FhirContext theContext) {
myContext = theContext;
}
private void extractResourceLinks(RequestPartitionId theRequestPartitionId, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference, RequestDetails theRequest) {
@ -291,6 +287,10 @@ public class SearchParamExtractorService {
nextId = nextReference.getResource().getIdElement();
}
if (myContext.getParserOptions().isStripVersionsFromReferences() && !myContext.getParserOptions().getDontStripVersionsFromReferencesAtPaths().contains(thePathAndRef.getPath()) && nextId.hasVersionIdPart()) {
nextId = nextId.toVersionless();
}
theParams.myPopulatedResourceLinkParameters.add(thePathAndRef.getSearchParamName());
boolean canonical = thePathAndRef.isCanonical();
@ -431,24 +431,6 @@ public class SearchParamExtractorService {
return ResourceLink.forLocalReference(nextPathAndRef.getPath(), theEntity, targetResourceType, targetResourcePid, targetResourceIdPart, theUpdateTime, targetVersion);
}
static void handleWarnings(RequestDetails theRequestDetails, IInterceptorBroadcaster theInterceptorBroadcaster, ISearchParamExtractor.SearchParamSet<?> theSearchParamSet) {
if (theSearchParamSet.getWarnings().isEmpty()) {
return;
}
// If extraction generated any warnings, broadcast an error
for (String next : theSearchParamSet.getWarnings()) {
StorageProcessingMessage messageHolder = new StorageProcessingMessage();
messageHolder.setMessage(next);
HookParams params = new HookParams()
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(StorageProcessingMessage.class, messageHolder);
JpaInterceptorBroadcaster.doCallHooks(theInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params);
}
}
private void populateResourceTable(Collection<? extends BaseResourceIndexedSearchParam> theParams, ResourceTable theResourceTable) {
for (BaseResourceIndexedSearchParam next : theParams) {
if (next.getResourcePid() == null) {
@ -498,5 +480,22 @@ public class SearchParamExtractorService {
public List<String> extractParamValuesAsStrings(RuntimeSearchParam theActiveSearchParam, IBaseResource theResource) {
return mySearchParamExtractor.extractParamValuesAsStrings(theActiveSearchParam, theResource);
}
static void handleWarnings(RequestDetails theRequestDetails, IInterceptorBroadcaster theInterceptorBroadcaster, ISearchParamExtractor.SearchParamSet<?> theSearchParamSet) {
if (theSearchParamSet.getWarnings().isEmpty()) {
return;
}
// If extraction generated any warnings, broadcast an error
for (String next : theSearchParamSet.getWarnings()) {
StorageProcessingMessage messageHolder = new StorageProcessingMessage();
messageHolder.setMessage(next);
HookParams params = new HookParams()
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
.add(StorageProcessingMessage.class, messageHolder);
JpaInterceptorBroadcaster.doCallHooks(theInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params);
}
}
}

View File

@ -148,6 +148,11 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry, IResourceC
ourLog.debug("Refreshed search parameter cache in {}ms", sw.getMillis());
}
@VisibleForTesting
public void setFhirContext(FhirContext theFhirContext) {
myFhirContext = theFhirContext;
}
private ReadOnlySearchParamCache getBuiltInSearchParams() {
if (myBuiltInSearchParams == null) {
myBuiltInSearchParams = ReadOnlySearchParamCache.fromFhirContext(myFhirContext);
@ -162,6 +167,11 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry, IResourceC
}
}
@VisibleForTesting
public void setModelConfig(ModelConfig theModelConfig) {
myModelConfig = theModelConfig;
}
private long overrideBuiltinSearchParamsWithActiveJpaSearchParams(RuntimeSearchParamCache theSearchParamCache, Collection<IBaseResource> theSearchParams) {
if (!myModelConfig.isDefaultSearchParamsCanBeOverridden() || theSearchParams == null) {
return 0;
@ -228,6 +238,11 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry, IResourceC
return myResourceChangeListenerCache.refreshCacheIfNecessary();
}
@VisibleForTesting
public void setResourceChangeListenerRegistry(IResourceChangeListenerRegistry theResourceChangeListenerRegistry) {
myResourceChangeListenerRegistry = theResourceChangeListenerRegistry;
}
@PostConstruct
public void registerListener() {
myResourceChangeListenerCache = myResourceChangeListenerRegistry.registerResourceResourceChangeListener("SearchParameter", SearchParameterMap.newSynchronous(), this, REFRESH_INTERVAL);

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.server.method;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;

View File

@ -1090,6 +1090,7 @@ public class XmlParserDstu3Test {
* See #103
*/
@Test
@Disabled
public void testEncodeAndReEncodeContainedJson() {
Composition comp = new Composition();
comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section0_Allergy0")));
@ -1115,6 +1116,7 @@ public class XmlParserDstu3Test {
* See #103
*/
@Test
@Disabled
public void testEncodeAndReEncodeContainedXml() {
Composition comp = new Composition();
comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section0_Allergy0")));

View File

@ -2,10 +2,7 @@ package ca.uhn.fhir.parser;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.test.BaseTest;
import ca.uhn.fhir.util.StopWatch;
@ -22,7 +19,6 @@ import org.hl7.fhir.r4.model.Composition;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.Device;
import org.hl7.fhir.r4.model.DocumentReference;
import org.hl7.fhir.r4.model.Dosage;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Medication;
@ -39,7 +35,6 @@ import org.hl7.fhir.r4.model.QuestionnaireResponse;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Type;
import org.hl7.fhir.r4.model.UuidType;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Disabled;
@ -473,8 +468,8 @@ public class JsonParserR4Test extends BaseTest {
ourLog.info(encoded);
mr = ourCtx.newJsonParser().parseResource(MedicationRequest.class, encoded);
assertEquals("#2", mr.getContained().get(0).getId());
assertEquals("#1", mr.getContained().get(1).getId());
assertEquals("#1", mr.getContained().get(0).getId());
assertEquals("#2", mr.getContained().get(1).getId());
}
@ -929,6 +924,50 @@ public class JsonParserR4Test extends BaseTest {
}
/**
* Ensure that a contained bundle doesn't cause a crash
*/
@Test
public void testParseAndEncodePreservesContainedResourceOrder() {
String auditEvent = "{\n" +
" \"resourceType\": \"AuditEvent\",\n" +
" \"contained\": [ {\n" +
" \"resourceType\": \"Observation\",\n" +
" \"id\": \"A\",\n" +
" \"identifier\": [ {\n" +
" \"value\": \"A\"\n" +
" } ]\n" +
" }, {\n" +
" \"resourceType\": \"Observation\",\n" +
" \"id\": \"B\",\n" +
" \"identifier\": [ {\n" +
" \"value\": \"B\"\n" +
" } ]\n" +
" } ],\n" +
" \"entity\": [ {\n" +
" \"what\": {\n" +
" \"reference\": \"#B\"\n" +
" }\n" +
" }, {\n" +
" \"what\": {\n" +
" \"reference\": \"#A\"\n" +
" }\n" +
" } ]\n" +
"}";
ourLog.info("Input: {}", auditEvent);
AuditEvent ae = ourCtx.newJsonParser().parseResource(AuditEvent.class, auditEvent);
assertEquals("#A", ae.getContained().get(0).getId());
assertEquals("#B", ae.getContained().get(1).getId());
assertEquals("#B", ae.getEntity().get(0).getWhat().getReference());
assertEquals("#A", ae.getEntity().get(1).getWhat().getReference());
String serialized = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(ae);
assertEquals(auditEvent, serialized);
}
@DatatypeDef(
name = "UnknownPrimitiveType"
)

View File

@ -44,6 +44,55 @@ public class XmlParserR4Test extends BaseTest {
return c;
}
/**
* Ensure that a contained bundle doesn't cause a crash
*/
@Test
public void testParseAndEncodePreservesContainedResourceOrder() {
String auditEvent = "<AuditEvent xmlns=\"http://hl7.org/fhir\">\n" +
" <contained>\n" +
" <Observation xmlns=\"http://hl7.org/fhir\">\n" +
" <id value=\"A\"/>\n" +
" <identifier>\n" +
" <value value=\"A\"/>\n" +
" </identifier>\n" +
" </Observation>\n" +
" </contained>\n" +
" <contained>\n" +
" <Observation xmlns=\"http://hl7.org/fhir\">\n" +
" <id value=\"B\"/>\n" +
" <identifier>\n" +
" <value value=\"B\"/>\n" +
" </identifier>\n" +
" </Observation>\n" +
" </contained>\n" +
" <entity>\n" +
" <what>\n" +
" <reference value=\"#B\"/>\n" +
" </what>\n" +
" </entity>\n" +
" <entity>\n" +
" <what>\n" +
" <reference value=\"#A\"/>\n" +
" </what>\n" +
" </entity>\n" +
"</AuditEvent>";
ourLog.info("Input: {}", auditEvent);
AuditEvent ae = ourCtx.newXmlParser().parseResource(AuditEvent.class, auditEvent);
assertEquals("#A", ae.getContained().get(0).getId());
assertEquals("#B", ae.getContained().get(1).getId());
assertEquals("#B", ae.getEntity().get(0).getWhat().getReference());
assertEquals("#A", ae.getEntity().get(1).getWhat().getReference());
String serialized = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(ae);
assertEquals(auditEvent, serialized);
}
/**
* See #402 section.text is overwritten by composition.text
*/

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.util;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.parser.DataFormatException;
import org.hamcrest.Matchers;
@ -14,12 +15,14 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.DocumentReference;
import org.hl7.fhir.r4.model.Element;
import org.hl7.fhir.r4.model.Enumeration;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.MarkdownType;
import org.hl7.fhir.r4.model.Medication;
import org.hl7.fhir.r4.model.MedicationAdministration;
import org.hl7.fhir.r4.model.MedicationRequest;
import org.hl7.fhir.r4.model.Money;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Organization;
@ -29,8 +32,10 @@ import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.PrimitiveType;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.ResourceType;
import org.hl7.fhir.r4.model.SimpleQuantity;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Substance;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
@ -62,14 +67,74 @@ import static org.mockito.Mockito.when;
public class FhirTerserR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(FhirTerserR4Test.class);
private static FhirContext ourCtx = FhirContext.forR4();
private FhirContext myCtx = FhirContext.forCached(FhirVersionEnum.R4);
@Test
public void testContainResourcesWithModify() {
MedicationRequest mr = new MedicationRequest();
mr.setMedication(new Reference(new Medication().setStatus(Medication.MedicationStatus.ACTIVE)));
mr.getRequester().setResource(new Practitioner().setActive(true));
FhirTerser.ContainedResources contained = myCtx.newTerser().containResources(mr, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
assertEquals("#1", mr.getContained().get(0).getId());
assertEquals("#2", mr.getContained().get(1).getId());
assertEquals(ResourceType.Medication, mr.getContained().get(0).getResourceType());
assertEquals(ResourceType.Practitioner, mr.getContained().get(1).getResourceType());
assertEquals("#1", mr.getMedicationReference().getReference());
assertEquals("#2", mr.getRequester().getReference());
FhirTerser.ContainedResources secondPass = myCtx.newTerser().containResources(mr, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
assertSame(contained, secondPass);
}
@Test
public void testContainedResourcesWithModify_DoubleLink() {
Substance ingredient = new Substance();
ingredient.getCode().addCoding().setSystem("system").setCode("code");
Medication medication = new Medication();
medication.addIngredient().setItem(new Reference(ingredient));
MedicationAdministration medAdmin = new MedicationAdministration();
medAdmin.setMedication(new Reference(medication));
myCtx.newTerser().containResources(medAdmin, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
assertEquals("#1", medAdmin.getContained().get(0).getId());
assertEquals("#2", medAdmin.getContained().get(1).getId());
assertEquals(ResourceType.Medication, medAdmin.getContained().get(0).getResourceType());
assertEquals(ResourceType.Substance, medAdmin.getContained().get(1).getResourceType());
assertEquals("#1", medAdmin.getMedicationReference().getReference());
assertEquals("#2", ((Medication)(medAdmin.getContained().get(0))).getIngredientFirstRep().getItemReference().getReference());
}
@Test
public void testContainResourcesWithModify_DontTouchEmbeddedResources() {
MedicationRequest mr = new MedicationRequest();
mr.setMedication(new Reference(new Medication().setStatus(Medication.MedicationStatus.ACTIVE)));
mr.getRequester().setResource(new Practitioner().setActive(true));
Bundle bundle = new Bundle();
bundle.addEntry().setResource(mr);
myCtx.newTerser().containResources(bundle, FhirTerser.OptionsEnum.MODIFY_RESOURCE);
assertEquals(0, mr.getContained().size());
assertEquals(null, mr.getRequester().getReference());
assertEquals( null, mr.getMedicationReference().getReference());
}
@Test
public void testGetValuesCreateEnumeration_SetsEnumFactory() {
Patient patient = new Patient();
Enumeration<?> enumeration = (Enumeration<?>) ourCtx.newTerser().getValues(patient, "Patient.gender", Enumeration.class, true).get(0);
Enumeration<?> enumeration = (Enumeration<?>) myCtx.newTerser().getValues(patient, "Patient.gender", Enumeration.class, true).get(0);
assertNotNull(enumeration.getEnumFactory());
}
@ -98,9 +163,9 @@ public class FhirTerserR4Test {
.setUrl("Observation/obs")
.setMethod(Bundle.HTTPVerb.PUT);
ourCtx.newTerser().clear(input);
myCtx.newTerser().clear(input);
String output = ourCtx.newJsonParser().encodeResourceToString(input);
String output = myCtx.newJsonParser().encodeResourceToString(input);
ourLog.info(output);
assertTrue(input.isEmpty());
assertEquals("{\"resourceType\":\"Bundle\"}", output);
@ -132,7 +197,7 @@ public class FhirTerserR4Test {
.setMethod(Bundle.HTTPVerb.PUT);
Bundle ionputClone = new Bundle();
ourCtx.newTerser().cloneInto(input, ionputClone, false);
myCtx.newTerser().cloneInto(input, ionputClone, false);
}
@Test
@ -141,7 +206,7 @@ public class FhirTerserR4Test {
source.setCode("CODE");
SimpleQuantity target = new SimpleQuantity();
ourCtx.newTerser().cloneInto(source, target, true);
myCtx.newTerser().cloneInto(source, target, true);
assertEquals("CODE", target.getCode());
}
@ -153,12 +218,12 @@ public class FhirTerserR4Test {
source.setUnit("UNIT");
Identifier target = new Identifier();
ourCtx.newTerser().cloneInto(source, target, true);
myCtx.newTerser().cloneInto(source, target, true);
assertEquals("SYSTEM", target.getSystem());
try {
ourCtx.newTerser().cloneInto(source, target, false);
myCtx.newTerser().cloneInto(source, target, false);
fail();
} catch (DataFormatException e) {
// good
@ -175,7 +240,7 @@ public class FhirTerserR4Test {
patient.addExtension(new Extension("http://example.com", new StringType("FOO")));
Patient target = new Patient();
ourCtx.newTerser().cloneInto(patient, target, false);
myCtx.newTerser().cloneInto(patient, target, false);
List<Extension> exts = target.getExtensionsByUrl("http://example.com");
assertEquals(1, exts.size());
@ -189,7 +254,7 @@ public class FhirTerserR4Test {
DocumentReference target = new DocumentReference();
ourCtx.newTerser().cloneInto(source, target, true);
myCtx.newTerser().cloneInto(source, target, true);
assertEquals(1, target.getContentFirstRep().getAttachment().getDataElement().getExtension().size());
assertEquals("http://foo", target.getContentFirstRep().getAttachment().getDataElement().getExtension().get(0).getUrl());
@ -204,7 +269,7 @@ public class FhirTerserR4Test {
patient.addExtension((Extension) new Extension().setUrl("http://foo").addExtension(ext));
Patient target = new Patient();
ourCtx.newTerser().cloneInto(patient, target, false);
myCtx.newTerser().cloneInto(patient, target, false);
List<Extension> exts = target.getExtensionsByUrl("http://foo");
assertEquals(1, exts.size());
@ -218,7 +283,7 @@ public class FhirTerserR4Test {
patient.setGender(Enumerations.AdministrativeGender.MALE);
Patient target = new Patient();
ourCtx.newTerser().cloneInto(patient, target, false);
myCtx.newTerser().cloneInto(patient, target, false);
assertEquals("http://hl7.org/fhir/administrative-gender", target.getGenderElement().getSystem());
}
@ -229,7 +294,7 @@ public class FhirTerserR4Test {
source.setId("STRING_ID");
MarkdownType target = new MarkdownType();
ourCtx.newTerser().cloneInto(source, target, true);
myCtx.newTerser().cloneInto(source, target, true);
assertEquals("STR", target.getValueAsString());
assertEquals("STRING_ID", target.getId());
@ -241,11 +306,11 @@ public class FhirTerserR4Test {
StringType source = new StringType("STR");
Money target = new Money();
ourCtx.newTerser().cloneInto(source, target, true);
myCtx.newTerser().cloneInto(source, target, true);
assertTrue(target.isEmpty());
try {
ourCtx.newTerser().cloneInto(source, target, false);
myCtx.newTerser().cloneInto(source, target, false);
fail();
} catch (DataFormatException e) {
// good
@ -263,7 +328,7 @@ public class FhirTerserR4Test {
obs.addNote().setText("COMMENTS");
Observation target = new Observation();
ourCtx.newTerser().cloneInto(obs, target, false);
myCtx.newTerser().cloneInto(obs, target, false);
assertEquals("AAA", ((StringType) obs.getValue()).getValue());
assertEquals("COMMENTS", obs.getNote().get(0).getText());
@ -278,7 +343,7 @@ public class FhirTerserR4Test {
obs.addNote().setText("COMMENTS");
Observation target = new Observation();
ourCtx.newTerser().cloneInto(obs, target, false);
myCtx.newTerser().cloneInto(obs, target, false);
assertEquals("http://foo/base/Observation/_history/123", target.getId());
}
@ -292,7 +357,7 @@ public class FhirTerserR4Test {
obs.setValue(string);
Observation target = new Observation();
ourCtx.newTerser().cloneInto(obs, target, false);
myCtx.newTerser().cloneInto(obs, target, false);
assertEquals("BBB", target.getValueStringType().getId());
}
@ -307,7 +372,7 @@ public class FhirTerserR4Test {
o.getNameElement().setValue("ORGANIZATION");
p.getContained().add(o);
FhirTerser t = ourCtx.newTerser();
FhirTerser t = myCtx.newTerser();
List<StringType> strings = t.getAllPopulatedChildElementsOfType(p, StringType.class);
assertThat(toStrings(strings), containsInAnyOrder("PATIENT", "ORGANIZATION"));
@ -323,7 +388,7 @@ public class FhirTerserR4Test {
b.addEntry().setResource(p);
b.addLink().setRelation("BUNDLE");
FhirTerser t = ourCtx.newTerser();
FhirTerser t = myCtx.newTerser();
List<StringType> strings = t.getAllPopulatedChildElementsOfType(b, StringType.class);
assertEquals(1, strings.size());
@ -342,7 +407,7 @@ public class FhirTerserR4Test {
Extension ext = new Extension("urn:foo", ref);
p.addExtension(ext);
FhirTerser t = ourCtx.newTerser();
FhirTerser t = myCtx.newTerser();
List<IBaseReference> refs = t.getAllPopulatedChildElementsOfType(p, IBaseReference.class);
assertEquals(1, refs.size());
assertSame(ref, refs.get(0));
@ -367,29 +432,29 @@ public class FhirTerserR4Test {
.setUrl("http://acme.org/childExtension")
.setValue(new StringType("nestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof PrimitiveType);
assertTrue(values.get(0) instanceof BooleanType);
assertTrue(((BooleanType) values.get(0)).booleanValue());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -426,7 +491,7 @@ public class FhirTerserR4Test {
innerPatient1.setActive(true);
outerBundle.addEntry().setResource(innerPatient1);
FhirTerser t = ourCtx.newTerser();
FhirTerser t = myCtx.newTerser();
Collection<IBaseResource> resources;
@ -452,7 +517,7 @@ public class FhirTerserR4Test {
patient.getContained().add(practitioner1);
patient.addGeneralPractitioner().setReference("#practitioner1");
FhirTerser t = ourCtx.newTerser();
FhirTerser t = myCtx.newTerser();
Collection<IBaseResource> resources;
@ -489,9 +554,9 @@ public class FhirTerserR4Test {
.setUrl("http://acme.org/childExtension")
.setValue(new StringType("nestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof PrimitiveType);
assertTrue(values.get(0) instanceof BooleanType);
@ -499,15 +564,15 @@ public class FhirTerserR4Test {
((BooleanType) values.get(0)).setValue(Boolean.FALSE);
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
values = ourCtx.newTerser().getValues(p, "Patient.active");
values = myCtx.newTerser().getValues(p, "Patient.active");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof PrimitiveType);
assertTrue(values.get(0) instanceof BooleanType);
assertFalse(((BooleanType) values.get(0)).booleanValue());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -516,16 +581,16 @@ public class FhirTerserR4Test {
((Extension) values.get(0)).setValue(new StringType("modifiedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
assertEquals("modifiedValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -534,16 +599,16 @@ public class FhirTerserR4Test {
((Extension) values.get(0)).setValue(new StringType("modifiedModifierValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
assertEquals("modifiedModifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -552,9 +617,9 @@ public class FhirTerserR4Test {
((Extension) values.get(0)).setValue(new StringType("modifiedNestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -591,9 +656,9 @@ public class FhirTerserR4Test {
.setUrl("http://acme.org/childExtension")
.setValue(new StringType("nestedValue2"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
assertEquals(2, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -604,7 +669,7 @@ public class FhirTerserR4Test {
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
assertEquals("value2", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
assertEquals(2, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -615,7 +680,7 @@ public class FhirTerserR4Test {
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
assertEquals("modifierValue2", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
assertEquals(2, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -646,26 +711,26 @@ public class FhirTerserR4Test {
.setUrl("http://acme.org/childExtension")
.setValue(new StringType("nestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
List<PrimitiveType> values = ourCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
List<PrimitiveType> values = myCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof BooleanType);
assertTrue(((BooleanType) values.get(0)).booleanValue());
List<Extension> extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
List<Extension> extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
assertEquals(1, extValues.size());
assertTrue(extValues.get(0).getValue() instanceof StringType);
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
assertEquals("value", ((StringType) (extValues.get(0).getValue())).getValueAsString());
extValues = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
extValues = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
assertEquals(1, extValues.size());
assertTrue(extValues.get(0).getValue() instanceof StringType);
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
assertEquals("modifierValue", ((StringType) (extValues.get(0).getValue())).getValueAsString());
extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
assertEquals(1, extValues.size());
assertTrue(extValues.get(0).getValue() instanceof StringType);
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
@ -691,23 +756,23 @@ public class FhirTerserR4Test {
.setUrl("http://acme.org/childExtension")
.setValue(new StringType("nestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
List<PrimitiveType> values = ourCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
List<PrimitiveType> values = myCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof BooleanType);
assertTrue(((BooleanType) values.get(0)).booleanValue());
((BooleanType) values.get(0)).setValue(Boolean.FALSE);
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
values = ourCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
values = myCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof BooleanType);
assertFalse(((BooleanType) values.get(0)).booleanValue());
List<Extension> extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
List<Extension> extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
assertEquals(1, extValues.size());
assertTrue(extValues.get(0).getValue() instanceof StringType);
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
@ -715,15 +780,15 @@ public class FhirTerserR4Test {
extValues.get(0).setValue(new StringType("modifiedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
assertEquals(1, extValues.size());
assertTrue(extValues.get(0).getValue() instanceof StringType);
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
assertEquals("modifiedValue", ((StringType) (extValues.get(0).getValue())).getValueAsString());
extValues = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
extValues = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
assertEquals(1, extValues.size());
assertTrue(extValues.get(0).getValue() instanceof StringType);
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
@ -731,15 +796,15 @@ public class FhirTerserR4Test {
extValues.get(0).setValue(new StringType("modifiedModifierValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
extValues = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
extValues = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
assertEquals(1, extValues.size());
assertTrue(extValues.get(0).getValue() instanceof StringType);
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
assertEquals("modifiedModifierValue", ((StringType) (extValues.get(0).getValue())).getValueAsString());
extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
assertEquals(1, extValues.size());
assertTrue(extValues.get(0).getValue() instanceof StringType);
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
@ -747,9 +812,9 @@ public class FhirTerserR4Test {
extValues.get(0).setValue(new StringType("modifiedNestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
assertEquals(1, extValues.size());
assertTrue(extValues.get(0).getValue() instanceof StringType);
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
@ -760,24 +825,24 @@ public class FhirTerserR4Test {
public void testGetValuesWithWantedClassAndTheCreate() {
Patient p = new Patient();
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
List<PrimitiveType> values = ourCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class, true);
List<PrimitiveType> values = myCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class, true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof BooleanType);
assertNull(((BooleanType) values.get(0)).getValue());
List<Extension> extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class, true);
List<Extension> extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class, true);
assertEquals(1, extValues.size());
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
assertNull(extValues.get(0).getValue());
extValues = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class, true);
extValues = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class, true);
assertEquals(1, extValues.size());
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
assertNull(extValues.get(0).getValue());
extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class, true);
extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class, true);
assertEquals(1, extValues.size());
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
assertNull(extValues.get(0).getValue());
@ -802,29 +867,29 @@ public class FhirTerserR4Test {
.setUrl("http://acme.org/childExtension")
.setValue(new StringType("nestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof PrimitiveType);
assertTrue(values.get(0) instanceof BooleanType);
assertTrue(((BooleanType) values.get(0)).booleanValue());
// No change.
values = ourCtx.newTerser().getValues(p, "Patient.active", false, true);
values = myCtx.newTerser().getValues(p, "Patient.active", false, true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof PrimitiveType);
assertTrue(values.get(0) instanceof BooleanType);
assertTrue(((BooleanType) values.get(0)).booleanValue());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", false, true);
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", false, true);
assertEquals(2, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -837,21 +902,21 @@ public class FhirTerserR4Test {
((Extension) values.get(1)).setValue(new StringType("addedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
assertTrue(values.get(1) instanceof IBaseExtension);
assertTrue(values.get(1) instanceof Extension);
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
assertEquals("addedValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", false, true);
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", false, true);
assertEquals(2, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -864,23 +929,23 @@ public class FhirTerserR4Test {
((Extension) values.get(1)).setValue(new StringType("addedModifierValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
assertTrue(values.get(1) instanceof IBaseExtension);
assertTrue(values.get(1) instanceof Extension);
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
assertEquals("addedModifierValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/childExtension", ((Extension) values.get(0)).getUrl());
assertEquals("nestedValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", false, true);
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", false, true);
assertEquals(2, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -893,7 +958,7 @@ public class FhirTerserR4Test {
((Extension) values.get(1)).setValue(new StringType("addedNestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
assertTrue(values.get(1) instanceof IBaseExtension);
assertTrue(values.get(1) instanceof Extension);
@ -905,27 +970,27 @@ public class FhirTerserR4Test {
public void testGetValuesWithTheCreate() {
Patient p = new Patient();
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active", true);
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active", true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof PrimitiveType);
assertTrue(values.get(0) instanceof BooleanType);
assertNull(((BooleanType) values.get(0)).getValue());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true);
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
assertNull(((Extension) values.get(0)).getValue());
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true);
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
assertNull(((Extension) values.get(0)).getValue());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true);
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -952,29 +1017,29 @@ public class FhirTerserR4Test {
.setUrl("http://acme.org/childExtension")
.setValue(new StringType("nestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof PrimitiveType);
assertTrue(values.get(0) instanceof BooleanType);
assertTrue(((BooleanType) values.get(0)).booleanValue());
// No change.
values = ourCtx.newTerser().getValues(p, "Patient.active", true, true);
values = myCtx.newTerser().getValues(p, "Patient.active", true, true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof PrimitiveType);
assertTrue(values.get(0) instanceof BooleanType);
assertTrue(((BooleanType) values.get(0)).booleanValue());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true, true);
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true, true);
assertEquals(2, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -987,21 +1052,21 @@ public class FhirTerserR4Test {
((Extension) values.get(1)).setValue(new StringType("addedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
assertTrue(values.get(1) instanceof IBaseExtension);
assertTrue(values.get(1) instanceof Extension);
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
assertEquals("addedValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true, true);
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true, true);
assertEquals(2, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -1014,21 +1079,21 @@ public class FhirTerserR4Test {
((Extension) values.get(1)).setValue(new StringType("addedModifierValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
assertTrue(values.get(1) instanceof IBaseExtension);
assertTrue(values.get(1) instanceof Extension);
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
assertEquals("addedModifierValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/childExtension", ((Extension) values.get(0)).getUrl());
assertEquals("nestedValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true, true);
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true, true);
assertEquals(2, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -1041,7 +1106,7 @@ public class FhirTerserR4Test {
((Extension) values.get(1)).setValue(new StringType("addedNestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
assertTrue(values.get(1) instanceof IBaseExtension);
assertTrue(values.get(1) instanceof Extension);
@ -1068,29 +1133,29 @@ public class FhirTerserR4Test {
.setUrl("http://acme.org/childExtension")
.setValue(new StringType("nestedValue"));
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active", true);
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active", true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof PrimitiveType);
assertTrue(values.get(0) instanceof BooleanType);
assertTrue(((BooleanType) values.get(0)).booleanValue());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true);
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true);
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true);
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true);
assertEquals(1, values.size());
assertTrue(values.get(0) instanceof IBaseExtension);
assertTrue(values.get(0) instanceof Extension);
@ -1107,7 +1172,7 @@ public class FhirTerserR4Test {
vs.getExpansion().setIdentifier("http://foo");
Set<String> strings = new HashSet<>();
ourCtx.newTerser().visit(vs, new IModelVisitor() {
myCtx.newTerser().visit(vs, new IModelVisitor() {
@Override
public void acceptElement(IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
if (theElement instanceof IPrimitiveType) {
@ -1118,7 +1183,7 @@ public class FhirTerserR4Test {
assertThat(strings, Matchers.contains("http://foo"));
strings.clear();
ourCtx.newTerser().visit(vs, new IModelVisitor2() {
myCtx.newTerser().visit(vs, new IModelVisitor2() {
@Override
public boolean acceptElement(IBase theElement, List<IBase> theContainingElementPath, List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath) {
if (theElement instanceof IPrimitiveType) {
@ -1148,7 +1213,7 @@ public class FhirTerserR4Test {
Patient p = new Patient();
p.addLink().getTypeElement().setValue(LinkType.REFER);
ourCtx.newTerser().visit(p, visitor);
myCtx.newTerser().visit(p, visitor);
assertEquals(3, element.getAllValues().size());
assertSame(p, element.getAllValues().get(0));
@ -1172,7 +1237,7 @@ public class FhirTerserR4Test {
p.addAddress().addLine("Line2");
p.addName().setFamily("Line3");
FhirTerser t = ourCtx.newTerser();
FhirTerser t = myCtx.newTerser();
List<StringType> strings = t.getAllPopulatedChildElementsOfType(p, StringType.class);
assertEquals(3, strings.size());
@ -1192,7 +1257,7 @@ public class FhirTerserR4Test {
Observation obs = new Observation();
obs.setValue(new Quantity(123L));
FhirTerser t = ourCtx.newTerser();
FhirTerser t = myCtx.newTerser();
// As string
{
@ -1243,8 +1308,8 @@ public class FhirTerserR4Test {
"</Observation>";
//@formatter:on
Observation parsed = ourCtx.newXmlParser().parseResource(Observation.class, msg);
FhirTerser t = ourCtx.newTerser();
Observation parsed = myCtx.newXmlParser().parseResource(Observation.class, msg);
FhirTerser t = myCtx.newTerser();
List<Reference> elems = t.getAllPopulatedChildElementsOfType(parsed, Reference.class);
assertEquals(2, elems.size());

View File

@ -984,7 +984,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.7</version>
<version>1.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>