Merge branch 'master' into ng_validation_s13n_interceptors
This commit is contained in:
commit
8c77882da0
|
@ -30,7 +30,6 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
|
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeChildContainedResources;
|
import ca.uhn.fhir.context.RuntimeChildContainedResources;
|
||||||
import ca.uhn.fhir.context.RuntimeChildDirectResource;
|
|
||||||
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
|
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.model.api.IIdentifiableElement;
|
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.ResourceMetadataKeyEnum;
|
||||||
import ca.uhn.fhir.model.api.Tag;
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
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.parser.path.EncodeContextPath;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.util.BundleUtil;
|
import ca.uhn.fhir.util.BundleUtil;
|
||||||
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
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.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
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.IBaseMetaType;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
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.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
@ -77,7 +74,6 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.IdentityHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
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 static final Set<String> notEncodeForContainedResource = new HashSet<>(Arrays.asList("security", "versionId", "lastUpdated"));
|
||||||
|
|
||||||
private ContainedResources myContainedResources;
|
private FhirTerser.ContainedResources myContainedResources;
|
||||||
private boolean myEncodeElementsAppliesToChildResourcesOnly;
|
private boolean myEncodeElementsAppliesToChildResourcesOnly;
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
private List<EncodeContextPath> myDontEncodeElements;
|
private List<EncodeContextPath> myDontEncodeElements;
|
||||||
|
@ -118,7 +114,6 @@ public abstract class BaseParser implements IParser {
|
||||||
private boolean mySummaryMode;
|
private boolean mySummaryMode;
|
||||||
private boolean mySuppressNarratives;
|
private boolean mySuppressNarratives;
|
||||||
private Set<String> myDontStripVersionsFromReferencesAtPaths;
|
private Set<String> myDontStripVersionsFromReferencesAtPaths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -127,6 +122,10 @@ public abstract class BaseParser implements IParser {
|
||||||
myErrorHandler = theParserErrorHandler;
|
myErrorHandler = theParserErrorHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected FhirContext getContext() {
|
||||||
|
return myContext;
|
||||||
|
}
|
||||||
|
|
||||||
List<EncodeContextPath> getDontEncodeElements() {
|
List<EncodeContextPath> getDontEncodeElements() {
|
||||||
return myDontEncodeElements;
|
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) {
|
private String determineReferenceText(IBaseReference theRef, CompositeChildElement theCompositeChildElement) {
|
||||||
IIdType ref = theRef.getReferenceElement();
|
IIdType ref = theRef.getReferenceElement();
|
||||||
|
@ -529,10 +378,14 @@ public abstract class BaseParser implements IParser {
|
||||||
return elementId;
|
return elementId;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainedResources getContainedResources() {
|
FhirTerser.ContainedResources getContainedResources() {
|
||||||
return myContainedResources;
|
return myContainedResources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setContainedResources(FhirTerser.ContainedResources theContainedResources) {
|
||||||
|
myContainedResources = theContainedResources;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getDontStripVersionsFromReferencesAtPaths() {
|
public Set<String> getDontStripVersionsFromReferencesAtPaths() {
|
||||||
return myDontStripVersionsFromReferencesAtPaths;
|
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) {
|
protected static <T> List<T> extractMetadataListNotNull(IResource resource, ResourceMetadataKeyEnum<List<T>> key) {
|
||||||
List<? extends T> securityLabels = key.get(resource);
|
List<? extends T> securityLabels = key.get(resource);
|
||||||
if (securityLabels == null) {
|
if (securityLabels == null) {
|
||||||
|
|
|
@ -45,6 +45,7 @@ import ca.uhn.fhir.parser.json.JsonLikeWriter;
|
||||||
import ca.uhn.fhir.parser.json.jackson.JacksonStructure;
|
import ca.uhn.fhir.parser.json.jackson.JacksonStructure;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
import ca.uhn.fhir.util.ElementUtil;
|
||||||
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.text.WordUtils;
|
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 static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParser.HeldExtension.class);
|
||||||
|
|
||||||
private FhirContext myContext;
|
|
||||||
private boolean myPrettyPrint;
|
private boolean myPrettyPrint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,7 +85,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
*/
|
*/
|
||||||
public JsonParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
|
public JsonParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
|
||||||
super(theContext, theParserErrorHandler);
|
super(theContext, theParserErrorHandler);
|
||||||
myContext = theContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean addToHeldComments(int valueIdx, List<String> theCommentsToAdd, ArrayList<ArrayList<String>> theListToAddTo) {
|
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();
|
theEventWriter.init();
|
||||||
|
|
||||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
|
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(theResource);
|
||||||
encodeResourceToJsonStreamWriter(resDef, theResource, theEventWriter, null, false, theEncodeContext);
|
encodeResourceToJsonStreamWriter(resDef, theResource, theEventWriter, null, false, theEncodeContext);
|
||||||
theEventWriter.flush();
|
theEventWriter.flush();
|
||||||
}
|
}
|
||||||
|
@ -209,7 +208,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
|
|
||||||
String resourceType = resourceTypeObj.getAsString();
|
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);
|
state.enteringNewElement(null, resourceType);
|
||||||
|
|
||||||
parseChildren(object, state);
|
parseChildren(object, state);
|
||||||
|
@ -355,7 +354,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
case RESOURCE:
|
case RESOURCE:
|
||||||
IBaseResource resource = (IBaseResource) theNextValue;
|
IBaseResource resource = (IBaseResource) theNextValue;
|
||||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
|
RuntimeResourceDefinition def = getContext().getResourceDefinition(resource);
|
||||||
|
|
||||||
theEncodeContext.pushPath(def.getName(), true);
|
theEncodeContext.pushPath(def.getName(), true);
|
||||||
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, theContainedResource, theEncodeContext);
|
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")
|
if (nextChildElem.getDef().getElementName().equals("extension") || nextChildElem.getDef().getElementName().equals("modifierExtension")
|
||||||
|| nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
|
|| nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
|
||||||
if (!haveWrittenExtensions) {
|
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;
|
haveWrittenExtensions = true;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||||
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
INarrativeGenerator gen = getContext().getNarrativeGenerator();
|
||||||
if (gen != null) {
|
if (gen != null) {
|
||||||
INarrative narr;
|
INarrative narr;
|
||||||
if (theResource instanceof IResource) {
|
if (theResource instanceof IResource) {
|
||||||
|
@ -405,7 +404,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
narr = null;
|
narr = null;
|
||||||
}
|
}
|
||||||
if (narr != null && narr.isEmpty()) {
|
if (narr != null && narr.isEmpty()) {
|
||||||
gen.populateResourceNarrative(myContext, theResource);
|
gen.populateResourceNarrative(getContext(), theResource);
|
||||||
if (!narr.isEmpty()) {
|
if (!narr.isEmpty()) {
|
||||||
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
|
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
|
||||||
String childName = nextChild.getChildNameByDatatype(child.getDatatype());
|
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(theResource, "theResource can not be null");
|
||||||
Validate.notNull(theJsonLikeWriter, "theJsonLikeWriter 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(
|
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();
|
EncodeContext encodeContext = new EncodeContext();
|
||||||
String resourceName = myContext.getResourceType(theResource);
|
String resourceName = getContext().getResourceType(theResource);
|
||||||
encodeContext.pushPath(resourceName, true);
|
encodeContext.pushPath(resourceName, true);
|
||||||
doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter, encodeContext);
|
doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter, encodeContext);
|
||||||
}
|
}
|
||||||
|
@ -660,10 +659,10 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!theContainedResource) {
|
if (!theContainedResource) {
|
||||||
super.containResourcesForEncoding(theResource);
|
setContainedResources(getContext().newTerser().containResources(theResource));
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
|
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(theResource);
|
||||||
|
|
||||||
if (theObjectNameOrNull == null) {
|
if (theObjectNameOrNull == null) {
|
||||||
theEventWriter.beginObject();
|
theEventWriter.beginObject();
|
||||||
|
@ -888,8 +887,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEncodeExtension(CompositeChildElement theParent, EncodeContext theEncodeContext, boolean theContainedResource, IBase theElement) {
|
private boolean isEncodeExtension(CompositeChildElement theParent, EncodeContext theEncodeContext, boolean theContainedResource, IBase theElement) {
|
||||||
// theEncodeContext.pushPath("extension", false);
|
BaseRuntimeElementDefinition<?> runtimeElementDefinition = getContext().getElementDefinition(theElement.getClass());
|
||||||
BaseRuntimeElementDefinition<?> runtimeElementDefinition = myContext.getElementDefinition(theElement.getClass());
|
|
||||||
boolean retVal = true;
|
boolean retVal = true;
|
||||||
if (runtimeElementDefinition instanceof BaseRuntimeElementCompositeDefinition) {
|
if (runtimeElementDefinition instanceof BaseRuntimeElementCompositeDefinition) {
|
||||||
BaseRuntimeElementCompositeDefinition definition = (BaseRuntimeElementCompositeDefinition) runtimeElementDefinition;
|
BaseRuntimeElementCompositeDefinition definition = (BaseRuntimeElementCompositeDefinition) runtimeElementDefinition;
|
||||||
|
@ -897,7 +895,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
CompositeChildElement c = new CompositeChildElement(theParent, childDef, theEncodeContext);
|
CompositeChildElement c = new CompositeChildElement(theParent, childDef, theEncodeContext);
|
||||||
retVal = c.shouldBeEncoded(theContainedResource);
|
retVal = c.shouldBeEncoded(theContainedResource);
|
||||||
}
|
}
|
||||||
// theEncodeContext.popPath();
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -917,37 +914,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
return object.getAsArray();
|
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) {
|
private void parseAlternates(JsonLikeValue theAlternateVal, ParserState<?> theState, String theElementName, String theAlternateName) {
|
||||||
if (theAlternateVal == null || theAlternateVal.isNull()) {
|
if (theAlternateVal == null || theAlternateVal.isNull()) {
|
||||||
return;
|
return;
|
||||||
|
@ -1234,13 +1200,13 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
||||||
* the correct FHIR version
|
* the correct FHIR version
|
||||||
*/
|
*/
|
||||||
if (theResourceType != null) {
|
if (theResourceType != null) {
|
||||||
myContext.getResourceDefinition(theResourceType);
|
getContext().getResourceDefinition(theResourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually do the parse
|
// Actually do the parse
|
||||||
T retVal = doParseResource(theResourceType, theJsonLikeStructure);
|
T retVal = doParseResource(theResourceType, theJsonLikeStructure);
|
||||||
|
|
||||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(retVal);
|
RuntimeResourceDefinition def = getContext().getResourceDefinition(retVal);
|
||||||
if ("Bundle".equals(def.getName())) {
|
if ("Bundle".equals(def.getName())) {
|
||||||
|
|
||||||
BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
|
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);
|
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());
|
String childName = extDef.getChildNameByDatatype(value.getClass());
|
||||||
if (childName == null) {
|
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());
|
BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
|
||||||
if (childDef == null) {
|
if (childDef == null) {
|
||||||
|
|
|
@ -1149,7 +1149,7 @@ class ParserState<T> {
|
||||||
for (IBaseReference nextRef : myLocalReferences) {
|
for (IBaseReference nextRef : myLocalReferences) {
|
||||||
String ref = nextRef.getReferenceElement().getValue();
|
String ref = nextRef.getReferenceElement().getValue();
|
||||||
if (isNotBlank(ref)) {
|
if (isNotBlank(ref)) {
|
||||||
if (ref.startsWith("#")) {
|
if (ref.startsWith("#") && ref.length() > 1) {
|
||||||
IBaseResource target = myContainedResources.get(ref);
|
IBaseResource target = myContainedResources.get(ref);
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
ourLog.debug("Resource contains local ref {}", ref);
|
ourLog.debug("Resource contains local ref {}", ref);
|
||||||
|
@ -1187,12 +1187,6 @@ class ParserState<T> {
|
||||||
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
|
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
|
||||||
// public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
|
|
||||||
// super.enteringNewElement(theNamespaceUri, theLocalPart);
|
|
||||||
// populateTarget();
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void populateTarget() {
|
protected void populateTarget() {
|
||||||
weaveContainedResources();
|
weaveContainedResources();
|
||||||
|
|
|
@ -73,7 +73,6 @@ public class RDFParser extends BaseParser {
|
||||||
public static final String MODIFIER_EXTENSION = "modifierExtension";
|
public static final String MODIFIER_EXTENSION = "modifierExtension";
|
||||||
private final Map<Class, String> classToFhirTypeMap = new HashMap<>();
|
private final Map<Class, String> classToFhirTypeMap = new HashMap<>();
|
||||||
|
|
||||||
private final FhirContext context;
|
|
||||||
private final Lang lang;
|
private final Lang lang;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,7 +83,6 @@ public class RDFParser extends BaseParser {
|
||||||
*/
|
*/
|
||||||
public RDFParser(final FhirContext context, final IParserErrorHandler parserErrorHandler, final Lang lang) {
|
public RDFParser(final FhirContext context, final IParserErrorHandler parserErrorHandler, final Lang lang) {
|
||||||
super(context, parserErrorHandler);
|
super(context, parserErrorHandler);
|
||||||
this.context = context;
|
|
||||||
this.lang = lang;
|
this.lang = lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,13 +146,13 @@ public class RDFParser extends BaseParser {
|
||||||
final EncodeContext encodeContext,
|
final EncodeContext encodeContext,
|
||||||
final boolean rootResource, Resource parentResource) {
|
final boolean rootResource, Resource parentResource) {
|
||||||
|
|
||||||
RuntimeResourceDefinition resDef = this.context.getResourceDefinition(resource);
|
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(resource);
|
||||||
if (resDef == null) {
|
if (resDef == null) {
|
||||||
throw new ConfigurationException("Unknown resource type: " + resource.getClass());
|
throw new ConfigurationException("Unknown resource type: " + resource.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!containedResource) {
|
if (!containedResource) {
|
||||||
super.containResourcesForEncoding(resource);
|
setContainedResources(getContext().newTerser().containResources(resource));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(resource instanceof IAnyResource)) {
|
if (!(resource instanceof IAnyResource)) {
|
||||||
|
@ -323,7 +321,7 @@ public class RDFParser extends BaseParser {
|
||||||
if (hasExtension.getExtension() != null && hasExtension.getExtension().size() > 0) {
|
if (hasExtension.getExtension() != null && hasExtension.getExtension().size() > 0) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (IBaseExtension extension : hasExtension.getExtension()) {
|
for (IBaseExtension extension : hasExtension.getExtension()) {
|
||||||
RuntimeResourceDefinition resDef = this.context.getResourceDefinition(resource);
|
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(resource);
|
||||||
Resource extensionResource = rdfModel.createResource();
|
Resource extensionResource = rdfModel.createResource();
|
||||||
extensionResource.addProperty(rdfModel.createProperty(FHIR_NS+FHIR_INDEX), rdfModel.createTypedLiteral(i, XSDDatatype.XSDinteger));
|
extensionResource.addProperty(rdfModel.createProperty(FHIR_NS+FHIR_INDEX), rdfModel.createTypedLiteral(i, XSDDatatype.XSDinteger));
|
||||||
valueResource.addProperty(rdfModel.createProperty(FHIR_NS + ELEMENT_EXTENSION), extensionResource);
|
valueResource.addProperty(rdfModel.createProperty(FHIR_NS + ELEMENT_EXTENSION), extensionResource);
|
||||||
|
@ -375,7 +373,7 @@ public class RDFParser extends BaseParser {
|
||||||
}
|
}
|
||||||
case RESOURCE: {
|
case RESOURCE: {
|
||||||
IBaseResource baseResource = (IBaseResource) element;
|
IBaseResource baseResource = (IBaseResource) element;
|
||||||
String resourceName = this.context.getResourceType(baseResource);
|
String resourceName = getContext().getResourceType(baseResource);
|
||||||
if (!super.shouldEncodeResource(resourceName)) {
|
if (!super.shouldEncodeResource(resourceName)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -486,7 +484,7 @@ public class RDFParser extends BaseParser {
|
||||||
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
|
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
|
||||||
|
|
||||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||||
INarrativeGenerator gen = this.context.getNarrativeGenerator();
|
INarrativeGenerator gen = getContext().getNarrativeGenerator();
|
||||||
if (gen != null) {
|
if (gen != null) {
|
||||||
INarrative narrative;
|
INarrative narrative;
|
||||||
if (resource instanceof IResource) {
|
if (resource instanceof IResource) {
|
||||||
|
@ -498,7 +496,7 @@ public class RDFParser extends BaseParser {
|
||||||
}
|
}
|
||||||
assert narrative != null;
|
assert narrative != null;
|
||||||
if (narrative.isEmpty()) {
|
if (narrative.isEmpty()) {
|
||||||
gen.populateResourceNarrative(this.context, resource);
|
gen.populateResourceNarrative(getContext(), resource);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
|
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
|
||||||
|
@ -619,7 +617,7 @@ public class RDFParser extends BaseParser {
|
||||||
private <T extends IBaseResource> T parseResource(Class<T> resourceType, Model rdfModel) {
|
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
|
// jsonMode of true is passed in so that the xhtml parser state behaves as expected
|
||||||
// Push PreResourceState
|
// 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);
|
return parseRootResource(rdfModel, parserState, resourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,7 +644,7 @@ public class RDFParser extends BaseParser {
|
||||||
fhirTypeString = resourceType.getSimpleName();
|
fhirTypeString = resourceType.getSimpleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeResourceDefinition definition = this.context.getResourceDefinition(fhirTypeString);
|
RuntimeResourceDefinition definition = getContext().getResourceDefinition(fhirTypeString);
|
||||||
fhirResourceType = definition.getName();
|
fhirResourceType = definition.getName();
|
||||||
|
|
||||||
parseResource(parserState, fhirResourceType, rootResource);
|
parseResource(parserState, fhirResourceType, rootResource);
|
||||||
|
@ -777,7 +775,7 @@ public class RDFParser extends BaseParser {
|
||||||
String propertyUri = statement.getPredicate().getURI();
|
String propertyUri = statement.getPredicate().getURI();
|
||||||
if (propertyUri.contains("Extension.value")) {
|
if (propertyUri.contains("Extension.value")) {
|
||||||
extensionValueType = propertyUri.replace(FHIR_NS + "Extension.", "");
|
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)) {
|
if (target.getChildType().equals(ID_DATATYPE) || target.getChildType().equals(PRIMITIVE_DATATYPE)) {
|
||||||
extensionValueResource = statement.getObject().asResource().getProperty(resource.getModel().createProperty(FHIR_NS+VALUE)).getObject().asLiteral();
|
extensionValueResource = statement.getObject().asResource().getProperty(resource.getModel().createProperty(FHIR_NS+VALUE)).getObject().asLiteral();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -56,9 +56,6 @@ public class XmlParser extends BaseParser {
|
||||||
|
|
||||||
static final String FHIR_NS = "http://hl7.org/fhir";
|
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 org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParser.class);
|
||||||
|
|
||||||
// private static final Set<String> RESOURCE_NAMESPACES;
|
|
||||||
private FhirContext myContext;
|
|
||||||
private boolean myPrettyPrint;
|
private boolean myPrettyPrint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,7 +66,6 @@ public class XmlParser extends BaseParser {
|
||||||
*/
|
*/
|
||||||
public XmlParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
|
public XmlParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
|
||||||
super(theContext, theParserErrorHandler);
|
super(theContext, theParserErrorHandler);
|
||||||
myContext = theContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private XMLEventReader createStreamReader(Reader theReader) {
|
private XMLEventReader createStreamReader(Reader theReader) {
|
||||||
|
@ -295,7 +291,7 @@ public class XmlParser extends BaseParser {
|
||||||
}
|
}
|
||||||
case RESOURCE: {
|
case RESOURCE: {
|
||||||
IBaseResource resource = (IBaseResource) theElement;
|
IBaseResource resource = (IBaseResource) theElement;
|
||||||
String resourceName = myContext.getResourceType(resource);
|
String resourceName = getContext().getResourceType(resource);
|
||||||
if (!super.shouldEncodeResource(resourceName)) {
|
if (!super.shouldEncodeResource(resourceName)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -354,9 +350,9 @@ public class XmlParser extends BaseParser {
|
||||||
|
|
||||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||||
Optional<IBase> narr = nextChild.getAccessor().getFirstValueOrNull(theElement);
|
Optional<IBase> narr = nextChild.getAccessor().getFirstValueOrNull(theElement);
|
||||||
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
INarrativeGenerator gen = getContext().getNarrativeGenerator();
|
||||||
if (gen != null && narr.isPresent() == false) {
|
if (gen != null && narr.isPresent() == false) {
|
||||||
gen.populateResourceNarrative(myContext, theResource);
|
gen.populateResourceNarrative(getContext(), theResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
narr = nextChild.getAccessor().getFirstValueOrNull(theElement);
|
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 {
|
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) {
|
if (resDef == null) {
|
||||||
throw new ConfigurationException("Unknown resource type: " + theResource.getClass());
|
throw new ConfigurationException("Unknown resource type: " + theResource.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!theContainedResource) {
|
if (!theContainedResource) {
|
||||||
super.containResourcesForEncoding(theResource);
|
setContainedResources(getContext().newTerser().containResources(theResource));
|
||||||
}
|
}
|
||||||
|
|
||||||
theEventWriter.writeStartElement(resDef.getName());
|
theEventWriter.writeStartElement(resDef.getName());
|
||||||
|
@ -615,11 +611,11 @@ public class XmlParser extends BaseParser {
|
||||||
|
|
||||||
if (next.getValue() != null) {
|
if (next.getValue() != null) {
|
||||||
IBaseDatatype value = next.getValue();
|
IBaseDatatype value = next.getValue();
|
||||||
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
|
RuntimeChildUndeclaredExtensionDefinition extDef = getContext().getRuntimeChildUndeclaredExtensionDefinition();
|
||||||
String childName = extDef.getChildNameByDatatype(value.getClass());
|
String childName = extDef.getChildNameByDatatype(value.getClass());
|
||||||
BaseRuntimeElementDefinition<?> childDef;
|
BaseRuntimeElementDefinition<?> childDef;
|
||||||
if (childName == null) {
|
if (childName == null) {
|
||||||
childDef = myContext.getElementDefinition(value.getClass());
|
childDef = getContext().getElementDefinition(value.getClass());
|
||||||
if (childDef == null) {
|
if (childDef == null) {
|
||||||
throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName());
|
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) {
|
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);
|
return doXmlLoop(theStreamReader, parserState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,15 @@ import ca.uhn.fhir.context.RuntimeExtensionDtDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
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.IIdentifiableElement;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
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.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseElement;
|
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.IBaseHasModifierExtensions;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
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.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
@ -36,10 +38,14 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
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.defaultString;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.substring;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
|
@ -71,7 +78,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
public class FhirTerser {
|
public class FhirTerser {
|
||||||
|
|
||||||
private static final Pattern COMPARTMENT_MATCHER_PATH = Pattern.compile("([a-zA-Z.]+)\\.where\\(resolve\\(\\) is ([a-zA-Z]+)\\)");
|
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) {
|
public FhirTerser(FhirContext theContext) {
|
||||||
super();
|
super();
|
||||||
|
@ -312,7 +321,6 @@ public class FhirTerser {
|
||||||
return Optional.ofNullable(getSingleValueOrNull(theTarget, thePath, theWantedType));
|
return Optional.ofNullable(getSingleValueOrNull(theTarget, thePath, theWantedType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private <T extends IBase> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, IBase theCurrentObj, List<String> theSubList, Class<T> theWantedClass) {
|
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);
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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."
|
|
@ -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."
|
|
@ -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."
|
|
@ -608,6 +608,11 @@ public class DaoConfig {
|
||||||
* <code>_sort</code> parameter on searches): If the server is configured
|
* <code>_sort</code> parameter on searches): If the server is configured
|
||||||
* to not index missing field.
|
* to not index missing field.
|
||||||
* </p>
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* The following index may need to be added into the indexed tables such as <code>HFJ_SPIDX_TOKEN</code>
|
||||||
|
* to improve the search performance while <code>:missing</code> is enabled.
|
||||||
|
* <code>RES_TYPE, SP_NAME, SP_MISSING</code>
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void setIndexMissingFields(IndexEnabledEnum theIndexMissingFields) {
|
public void setIndexMissingFields(IndexEnabledEnum theIndexMissingFields) {
|
||||||
Validate.notNull(theIndexMissingFields, "theIndexMissingFields must not be null");
|
Validate.notNull(theIndexMissingFields, "theIndexMissingFields must not be null");
|
||||||
|
|
|
@ -151,6 +151,7 @@ import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -200,6 +201,29 @@ public abstract class BaseConfig {
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoRegistry myDaoRegistry;
|
private DaoRegistry myDaoRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses may override this method to provide settings such as search coordinator pool sizes.
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
public void initSettings() {}
|
||||||
|
|
||||||
|
private Integer searchCoordCorePoolSize = 20;
|
||||||
|
private Integer searchCoordMaxPoolSize = 100;
|
||||||
|
private Integer searchCoordQueueCapacity = 200;
|
||||||
|
|
||||||
|
public void setSearchCoordCorePoolSize(Integer searchCoordCorePoolSize) {
|
||||||
|
this.searchCoordCorePoolSize = searchCoordCorePoolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchCoordMaxPoolSize(Integer searchCoordMaxPoolSize) {
|
||||||
|
this.searchCoordMaxPoolSize = searchCoordMaxPoolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSearchCoordQueueCapacity(Integer searchCoordQueueCapacity) {
|
||||||
|
this.searchCoordQueueCapacity = searchCoordQueueCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public BatchConfigurer batchConfigurer() {
|
public BatchConfigurer batchConfigurer() {
|
||||||
return new NonPersistedBatchConfigurer();
|
return new NonPersistedBatchConfigurer();
|
||||||
|
@ -316,6 +340,9 @@ public abstract class BaseConfig {
|
||||||
public ThreadPoolTaskExecutor searchCoordinatorThreadFactory() {
|
public ThreadPoolTaskExecutor searchCoordinatorThreadFactory() {
|
||||||
final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
|
final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
|
||||||
threadPoolTaskExecutor.setThreadNamePrefix("search_coord_");
|
threadPoolTaskExecutor.setThreadNamePrefix("search_coord_");
|
||||||
|
threadPoolTaskExecutor.setCorePoolSize(searchCoordCorePoolSize);
|
||||||
|
threadPoolTaskExecutor.setMaxPoolSize(searchCoordMaxPoolSize);
|
||||||
|
threadPoolTaskExecutor.setQueueCapacity(searchCoordQueueCapacity);
|
||||||
threadPoolTaskExecutor.initialize();
|
threadPoolTaskExecutor.initialize();
|
||||||
return threadPoolTaskExecutor;
|
return threadPoolTaskExecutor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.interceptor.api.HookParams;
|
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
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.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IDao;
|
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.Tag;
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
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.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
||||||
|
@ -237,6 +235,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
private IPartitionLookupSvc myPartitionLookupSvc;
|
private IPartitionLookupSvc myPartitionLookupSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
private MemoryCacheService myMemoryCacheService;
|
private MemoryCacheService myMemoryCacheService;
|
||||||
|
@Autowired(required = false)
|
||||||
|
private IFulltextSearchSvc myFulltextSearchSvc;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setSearchParamPresenceSvc(ISearchParamPresenceSvc theSearchParamPresenceSvc) {
|
||||||
|
mySearchParamPresenceSvc = theSearchParamPresenceSvc;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IInterceptorBroadcaster getInterceptorBroadcaster() {
|
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)
|
* 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) {
|
if (theEntity.getResourceType() == null) {
|
||||||
theEntity.setResourceType(toResourceName(theResource));
|
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;
|
byte[] bytes;
|
||||||
ResourceEncodingEnum encoding;
|
ResourceEncodingEnum encoding;
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
|
|
||||||
if (theEntity.getDeleted() == null) {
|
if (theEntity.getDeleted() == null) {
|
||||||
|
|
||||||
encoding = myConfig.getResourceEncoding();
|
if (thePerformIndexing) {
|
||||||
Set<String> excludeElements = ResourceMetaParams.EXCLUDE_ELEMENTS_IN_ENCODED;
|
|
||||||
theEntity.setFhirVersion(myContext.getVersion().getVersion());
|
|
||||||
|
|
||||||
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();
|
HashFunction sha256 = Hashing.sha256();
|
||||||
String hashSha256 = sha256.hashBytes(bytes).toString();
|
String hashSha256 = sha256.hashBytes(bytes).toString();
|
||||||
if (hashSha256.equals(theEntity.getHashSha256()) == false) {
|
if (hashSha256.equals(theEntity.getHashSha256()) == false) {
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
theEntity.setHashSha256(hashSha256);
|
theEntity.setHashSha256(hashSha256);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
encoding = null;
|
||||||
|
bytes = null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<ResourceTag> allDefs = new HashSet<>();
|
Set<ResourceTag> allDefs = new HashSet<>();
|
||||||
|
@ -543,11 +544,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
String profile = def.getResourceProfile("");
|
String profile = def.getResourceProfile("");
|
||||||
if (isNotBlank(profile)) {
|
if (isNotBlank(profile)) {
|
||||||
TagDefinition profileDef = getTagOrNull(TagTypeEnum.PROFILE, NS_JPA_PROFILE, profile, null);
|
TagDefinition profileDef = getTagOrNull(TagTypeEnum.PROFILE, NS_JPA_PROFILE, profile, null);
|
||||||
if (def != null) {
|
|
||||||
ResourceTag tag = theEntity.addTag(profileDef);
|
ResourceTag tag = theEntity.addTag(profileDef);
|
||||||
allDefs.add(tag);
|
allDefs.add(tag);
|
||||||
theEntity.setHasTags(true);
|
theEntity.setHasTags(true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,7 +580,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
encoding = ResourceEncodingEnum.DEL;
|
encoding = ResourceEncodingEnum.DEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed == false) {
|
if (thePerformIndexing && changed == false) {
|
||||||
if (theEntity.getId() == null) {
|
if (theEntity.getId() == null) {
|
||||||
changed = true;
|
changed = true;
|
||||||
} else {
|
} 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);
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public ResourceTable updateEntity(RequestDetails theRequest, final IBaseResource theResource, IBasePersistedResource
|
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);
|
newParams.populateResourceTableSearchParamsPresentFlags(entity);
|
||||||
entity.setIndexStatus(INDEX_STATUS_INDEXED);
|
entity.setIndexStatus(INDEX_STATUS_INDEXED);
|
||||||
}
|
}
|
||||||
populateFullTextFields(myContext, theResource, entity);
|
|
||||||
|
if (myFulltextSearchSvc != null && !myFulltextSearchSvc.isDisabled()) {
|
||||||
|
populateFullTextFields(myContext, theResource, entity);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
changed = populateResourceIntoEntity(theRequest, theResource, entity, false);
|
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());
|
ourLog.debug("Resource {} has not changed", entity.getIdDt().toUnqualified().getValue());
|
||||||
if (theResource != null) {
|
if (theResource != null) {
|
||||||
updateResourceMetadata(entity, theResource);
|
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) {
|
if (theElement == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1343,7 +1367,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
String newPath = thePath + "." + nextChildDef.getElementName();
|
String newPath = thePath + "." + nextChildDef.getElementName();
|
||||||
|
|
||||||
for (IBase nextChild : values) {
|
for (IBase nextChild : values) {
|
||||||
validateChildReferences(nextChild, newPath);
|
validateChildReferenceTargetTypes(nextChild, newPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextChildDef instanceof RuntimeChildResourceDefinition) {
|
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");
|
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);
|
if (getConfig().isEnforceReferenceTargetTypes()) {
|
||||||
validateChildReferences(theResource, resName);
|
String resName = getContext().getResourceType(theResource);
|
||||||
|
validateChildReferenceTargetTypes(theResource, resName);
|
||||||
|
}
|
||||||
|
|
||||||
validateMetaCount(totalMetaCount);
|
validateMetaCount(totalMetaCount);
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,7 @@ import ca.uhn.fhir.validation.IValidationContext;
|
||||||
import ca.uhn.fhir.validation.IValidatorModule;
|
import ca.uhn.fhir.validation.IValidatorModule;
|
||||||
import ca.uhn.fhir.validation.ValidationOptions;
|
import ca.uhn.fhir.validation.ValidationOptions;
|
||||||
import ca.uhn.fhir.validation.ValidationResult;
|
import ca.uhn.fhir.validation.ValidationResult;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.text.WordUtils;
|
import org.apache.commons.text.WordUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
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);
|
return create(theResource, theIfNoneExist, true, new TransactionDetails(), theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setTransactionService(HapiTransactionService theTransactionService) {
|
||||||
|
myTransactionService = theTransactionService;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, @Nonnull TransactionDetails theTransactionDetails, RequestDetails theRequestDetails) {
|
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));
|
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
|
* 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 static class IdChecker implements IValidatorModule {
|
||||||
|
|
||||||
private final ValidationModeEnum myMode;
|
private final ValidationModeEnum myMode;
|
||||||
|
|
|
@ -54,6 +54,7 @@ import ca.uhn.fhir.util.BundleUtil;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
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.IBaseBundle;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
|
@ -89,6 +90,11 @@ public abstract class BaseStorageDao {
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ModelConfig myModelConfig;
|
protected ModelConfig myModelConfig;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
|
||||||
|
mySearchParamRegistry = theSearchParamRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* May be overridden by subclasses to validate resources prior to storage
|
* 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}
|
* Handle {@link ModelConfig#getAutoVersionReferenceAtPaths() auto-populate-versions}
|
||||||
*
|
* <p>
|
||||||
* We only do this if thePerformIndexing is true because if it's false, that means
|
* 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,
|
* 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
|
* 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,
|
* 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
|
* 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.
|
* to use that value. During the second phase it is correct.
|
||||||
*
|
* <p>
|
||||||
* Also note that {@link BaseTransactionProcessor} also has code to do auto-versioning
|
* 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
|
* and it is the one that takes care of the placeholder IDs. Look for the other caller of
|
||||||
* {@link #extractReferencesToAutoVersion(FhirContext, ModelConfig, IBaseResource)}
|
* {@link #extractReferencesToAutoVersion(FhirContext, ModelConfig, IBaseResource)}
|
||||||
|
|
|
@ -69,6 +69,7 @@ import ca.uhn.fhir.util.FhirTerser;
|
||||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
import org.apache.commons.lang3.Validate;
|
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) {
|
private IBaseBundle batch(final RequestDetails theRequestDetails, IBaseBundle theRequest) {
|
||||||
ourLog.info("Beginning batch with {} resources", myVersionAdapter.getEntries(theRequest).size());
|
ourLog.info("Beginning batch with {} resources", myVersionAdapter.getEntries(theRequest).size());
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
|
@ -335,6 +346,11 @@ public abstract class BaseTransactionProcessor {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setHapiTransactionService(HapiTransactionService theHapiTransactionService) {
|
||||||
|
myHapiTransactionService = theHapiTransactionService;
|
||||||
|
}
|
||||||
|
|
||||||
private IBaseBundle processTransaction(final RequestDetails theRequestDetails, final IBaseBundle theRequest, final String theActionName) {
|
private IBaseBundle processTransaction(final RequestDetails theRequestDetails, final IBaseBundle theRequest, final String theActionName) {
|
||||||
validateDependencies();
|
validateDependencies();
|
||||||
|
|
||||||
|
@ -549,6 +565,10 @@ public abstract class BaseTransactionProcessor {
|
||||||
return myContext.getVersion().newIdType().setValue(theValue);
|
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,
|
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) {
|
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);
|
String existingUuid = keyToUuid.get(key);
|
||||||
for (IBase nextEntry : theEntries) {
|
for (IBase nextEntry : theEntries) {
|
||||||
IBaseResource nextResource = myVersionAdapter.getResource(nextEntry);
|
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
|
// We're interested in any references directly to the placeholder ID, but also
|
||||||
// references that have a resource target that has the placeholder ID.
|
// references that have a resource target that has the placeholder ID.
|
||||||
String nextReferenceId = nextReference.getResourceReference().getReferenceElement().getValue();
|
String nextReferenceId = nextReference.getReferenceElement().getValue();
|
||||||
if (isBlank(nextReferenceId) && nextReference.getResourceReference().getResource() != null) {
|
if (isBlank(nextReferenceId) && nextReference.getResource() != null) {
|
||||||
nextReferenceId = nextReference.getResourceReference().getResource().getIdElement().getValue();
|
nextReferenceId = nextReference.getResource().getIdElement().getValue();
|
||||||
}
|
}
|
||||||
if (entryUrl.equals(nextReferenceId)) {
|
if (entryUrl.equals(nextReferenceId)) {
|
||||||
nextReference.getResourceReference().setReference(existingUuid);
|
nextReference.setReference(existingUuid);
|
||||||
nextReference.getResourceReference().setResource(null);
|
nextReference.setResource(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -923,7 +943,6 @@ public abstract class BaseTransactionProcessor {
|
||||||
IIdType newId = theIdSubstitutions.get(nextId);
|
IIdType newId = theIdSubstitutions.get(nextId);
|
||||||
ourLog.debug(" * Replacing resource ref {} with {}", nextId, newId);
|
ourLog.debug(" * Replacing resource ref {} with {}", nextId, newId);
|
||||||
if (referencesToVersion.contains(resourceReference)) {
|
if (referencesToVersion.contains(resourceReference)) {
|
||||||
DaoMethodOutcome outcome = theIdToPersistedOutcome.get(newId);
|
|
||||||
resourceReference.setReference(newId.getValue());
|
resourceReference.setReference(newId.getValue());
|
||||||
} else {
|
} else {
|
||||||
resourceReference.setReference(newId.toVersionless().getValue());
|
resourceReference.setReference(newId.toVersionless().getValue());
|
||||||
|
@ -1042,6 +1061,11 @@ public abstract class BaseTransactionProcessor {
|
||||||
return newIdType(theToResourceName, theIdPart, null);
|
return newIdType(theToResourceName, theIdPart, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setDaoRegistry(DaoRegistry theDaoRegistry) {
|
||||||
|
myDaoRegistry = theDaoRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
private IFhirResourceDao getDaoOrThrowException(Class<? extends IBaseResource> theClass) {
|
private IFhirResourceDao getDaoOrThrowException(Class<? extends IBaseResource> theClass) {
|
||||||
IFhirResourceDao<? extends IBaseResource> dao = myDaoRegistry.getResourceDaoOrNull(theClass);
|
IFhirResourceDao<? extends IBaseResource> dao = myDaoRegistry.getResourceDaoOrNull(theClass);
|
||||||
if (dao == null) {
|
if (dao == null) {
|
||||||
|
@ -1113,6 +1137,11 @@ public abstract class BaseTransactionProcessor {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setDaoConfig(DaoConfig theDaoConfig) {
|
||||||
|
myDaoConfig = theDaoConfig;
|
||||||
|
}
|
||||||
|
|
||||||
public interface ITransactionProcessorVersionAdapter<BUNDLE extends IBaseBundle, BUNDLEENTRY extends IBase> {
|
public interface ITransactionProcessorVersionAdapter<BUNDLE extends IBaseBundle, BUNDLEENTRY extends IBase> {
|
||||||
|
|
||||||
void setResponseStatus(BUNDLEENTRY theBundleEntry, String theStatus);
|
void setResponseStatus(BUNDLEENTRY theBundleEntry, String theStatus);
|
||||||
|
|
|
@ -62,9 +62,16 @@ public class TransactionProcessor extends BaseTransactionProcessor {
|
||||||
@Override
|
@Override
|
||||||
protected void flushSession(Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome) {
|
protected void flushSession(Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome) {
|
||||||
try {
|
try {
|
||||||
|
int insertionCount;
|
||||||
|
int updateCount;
|
||||||
SessionImpl session = myEntityManager.unwrap(SessionImpl.class);
|
SessionImpl session = myEntityManager.unwrap(SessionImpl.class);
|
||||||
int insertionCount = session.getActionQueue().numberOfInsertions();
|
if (session != null) {
|
||||||
int updateCount = session.getActionQueue().numberOfUpdates();
|
insertionCount = session.getActionQueue().numberOfInsertions();
|
||||||
|
updateCount = session.getActionQueue().numberOfUpdates();
|
||||||
|
} else {
|
||||||
|
insertionCount = -1;
|
||||||
|
updateCount = -1;
|
||||||
|
}
|
||||||
|
|
||||||
StopWatch sw = new StopWatch();
|
StopWatch sw = new StopWatch();
|
||||||
myEntityManager.flush();
|
myEntityManager.flush();
|
||||||
|
|
|
@ -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.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||||
import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -68,6 +69,11 @@ public class DaoSearchParamSynchronizer {
|
||||||
return retVal;
|
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) {
|
private <T extends BaseResourceIndex> void synchronize(ResourceTable theEntity, AddRemoveCount theAddRemoveCount, Collection<T> theNewParams, Collection<T> theExistingParams) {
|
||||||
Collection<T> newParams = theNewParams;
|
Collection<T> newParams = theNewParams;
|
||||||
for (T next : newParams) {
|
for (T next : newParams) {
|
||||||
|
|
|
@ -47,6 +47,7 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
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.IBaseReference;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
@ -95,6 +96,21 @@ public class SearchParamWithInlineReferencesExtractor {
|
||||||
@Autowired
|
@Autowired
|
||||||
private PartitionSettings myPartitionSettings;
|
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) {
|
public void populateFromResource(ResourceIndexedSearchParams theParams, TransactionDetails theTransactionDetails, ResourceTable theEntity, IBaseResource theResource, ResourceIndexedSearchParams theExistingParams, RequestDetails theRequest) {
|
||||||
extractInlineReferences(theResource, 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
|
* 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.
|
* 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) {
|
public void storeCompositeStringUniques(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams existingParams) {
|
||||||
|
|
||||||
// Store composite string uniques
|
// Store composite string uniques
|
||||||
|
|
|
@ -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.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
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.instance.model.api.IBaseBundle;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
|
||||||
import org.hl7.fhir.r4.model.Meta;
|
import org.hl7.fhir.r4.model.Meta;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
@ -42,11 +42,14 @@ import java.util.List;
|
||||||
|
|
||||||
public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoR4.class);
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TransactionProcessor myTransactionProcessor;
|
private TransactionProcessor myTransactionProcessor;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setTransactionProcessorForUnitTest(TransactionProcessor theTransactionProcessor) {
|
||||||
|
myTransactionProcessor = theTransactionProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void start() {
|
public void start() {
|
||||||
|
|
|
@ -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.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -47,6 +48,16 @@ public class HapiTransactionService {
|
||||||
private PlatformTransactionManager myTransactionManager;
|
private PlatformTransactionManager myTransactionManager;
|
||||||
private TransactionTemplate myTxTemplate;
|
private TransactionTemplate myTxTemplate;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setInterceptorBroadcaster(IInterceptorBroadcaster theInterceptorBroadcaster) {
|
||||||
|
myInterceptorBroadcaster = theInterceptorBroadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setTransactionManager(PlatformTransactionManager theTransactionManager) {
|
||||||
|
myTransactionManager = theTransactionManager;
|
||||||
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void start() {
|
public void start() {
|
||||||
myTxTemplate = new TransactionTemplate(myTransactionManager);
|
myTxTemplate = new TransactionTemplate(myTransactionManager);
|
||||||
|
|
|
@ -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.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
||||||
import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
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;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@ -40,10 +45,14 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PartitionSettings myPartitionSettings;
|
private PartitionSettings myPartitionSettings;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoConfig myDaoConfig;
|
private DaoConfig myDaoConfig;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setDaoConfig(DaoConfig theDaoConfig) {
|
||||||
|
myDaoConfig = theDaoConfig;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AddRemoveCount updatePresence(ResourceTable theResource, Map<String, Boolean> theParamNameToPresence) {
|
public AddRemoveCount updatePresence(ResourceTable theResource, Map<String, Boolean> theParamNameToPresence) {
|
||||||
AddRemoveCount retVal = new AddRemoveCount();
|
AddRemoveCount retVal = new AddRemoveCount();
|
||||||
|
|
|
@ -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.batch.svc.BatchJobSubmitterImpl;
|
||||||
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
|
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
|
||||||
import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl;
|
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.CircularQueueCaptureQueriesListener;
|
||||||
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
|
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
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 net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import org.hibernate.dialect.H2Dialect;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
|
@ -1095,14 +1095,18 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
.stream()
|
.stream()
|
||||||
.filter(t -> t.getParamName().equals("medicationadministration-ingredient-medication"))
|
.filter(t -> t.getParamName().equals("medicationadministration-ingredient-medication"))
|
||||||
.collect(Collectors.toList());
|
.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(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"));
|
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());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.hl7.fhir.r4.model.StringType;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -45,6 +46,7 @@ import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "deprecation", "Duplicates"})
|
@SuppressWarnings({"unchecked", "deprecation", "Duplicates"})
|
||||||
|
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||||
public class FhirResourceDaoR4ConcurrentWriteTest extends BaseJpaR4Test {
|
public class FhirResourceDaoR4ConcurrentWriteTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ConcurrentWriteTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ConcurrentWriteTest.class);
|
||||||
|
|
|
@ -32,6 +32,9 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
|
public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ContainedTest.class);
|
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));
|
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdObs));
|
||||||
|
|
||||||
runInTransaction(()->{
|
runInTransaction(()->{
|
||||||
|
ourLog.info("String indexes:\n * {}", myResourceIndexedSearchParamStringDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||||
|
|
||||||
Long i = myEntityManager
|
Long i = myEntityManager
|
||||||
.createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'subject.family' AND s.myResourceType = 'Observation'", Long.class)
|
.createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'subject.family' AND s.myResourceType = 'Observation'", Long.class)
|
||||||
.getSingleResult();
|
.getSingleResult();
|
||||||
|
|
|
@ -306,13 +306,17 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
|
||||||
o.getMeta().addTag("http://foo", "bar", "FOOBAR");
|
o.getMeta().addTag("http://foo", "bar", "FOOBAR");
|
||||||
p.getManagingOrganization().setResource(o);
|
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();
|
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
p = myPatientDao.read(id);
|
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();
|
Organization org = (Organization) p.getManagingOrganization().getResource();
|
||||||
assertEquals("#1", org.getId());
|
assertEquals("#1", org.getId());
|
||||||
|
|
|
@ -787,7 +787,8 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
||||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
|
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||||
|
|
||||||
// Lookup the two existing IDs to make sure they are legit
|
// Lookup the two existing IDs to make sure they are legit
|
||||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
myCaptureQueriesListener.logInsertQueriesForCurrentThread();
|
||||||
|
myCaptureQueriesListener.logUpdateQueriesForCurrentThread();
|
||||||
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
|
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
|
||||||
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
|
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
|
||||||
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
|
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
|
||||||
|
|
|
@ -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.Organization;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.Period;
|
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;
|
||||||
import org.hl7.fhir.r4.model.Quantity.QuantityComparator;
|
import org.hl7.fhir.r4.model.Quantity.QuantityComparator;
|
||||||
import org.hl7.fhir.r4.model.Questionnaire;
|
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
|
@Test
|
||||||
public void testTermConceptReindexingDoesntDuplicateData() {
|
public void testTermConceptReindexingDoesntDuplicateData() {
|
||||||
myDaoConfig.setSchedulingDisabled(true);
|
myDaoConfig.setSchedulingDisabled(true);
|
||||||
|
|
|
@ -26,6 +26,7 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void afterEach() {
|
public void afterEach() {
|
||||||
myFhirCtx.getParserOptions().setStripVersionsFromReferences(true);
|
myFhirCtx.getParserOptions().setStripVersionsFromReferences(true);
|
||||||
|
myFhirCtx.getParserOptions().getDontStripVersionsFromReferencesAtPaths().clear();
|
||||||
myDaoConfig.setDeleteEnabled(new DaoConfig().isDeleteEnabled());
|
myDaoConfig.setDeleteEnabled(new DaoConfig().isDeleteEnabled());
|
||||||
myModelConfig.setRespectVersionsForSearchIncludes(new ModelConfig().isRespectVersionsForSearchIncludes());
|
myModelConfig.setRespectVersionsForSearchIncludes(new ModelConfig().isRespectVersionsForSearchIncludes());
|
||||||
myModelConfig.setAutoVersionReferenceAtPaths(new ModelConfig().getAutoVersionReferenceAtPaths());
|
myModelConfig.setAutoVersionReferenceAtPaths(new ModelConfig().getAutoVersionReferenceAtPaths());
|
||||||
|
@ -457,6 +458,10 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
|
||||||
observation.getSubject().setReference(patientId.withVersion("1").getValue());
|
observation.getSubject().setReference(patientId.withVersion("1").getValue());
|
||||||
IIdType observationId = myObservationDao.create(observation).getId().toUnqualified();
|
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 *
|
// Search - Non Synchronous for *
|
||||||
{
|
{
|
||||||
IBundleProvider outcome = myObservationDao.search(SearchParameterMap.newSynchronous().addInclude(IBaseResource.INCLUDE_ALL));
|
IBundleProvider outcome = myObservationDao.search(SearchParameterMap.newSynchronous().addInclude(IBaseResource.INCLUDE_ALL));
|
||||||
|
|
|
@ -168,12 +168,11 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
|
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
|
||||||
myValidationSupport = wac.getBean(IValidationSupport.class);
|
myValidationSupport = wac.getBean(IValidationSupport.class);
|
||||||
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
||||||
SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class);
|
|
||||||
|
|
||||||
confProvider.setSearchParamRegistry(ourSearchParamRegistry);
|
confProvider.setSearchParamRegistry(ourSearchParamRegistry);
|
||||||
|
|
||||||
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(20000);
|
myFhirCtx.getRestfulClientFactory().setSocketTimeout(400000);
|
||||||
|
|
||||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
|
|
@ -15,6 +15,7 @@ import static org.hamcrest.Matchers.greaterThan;
|
||||||
import static org.hamcrest.Matchers.hasItem;
|
import static org.hamcrest.Matchers.hasItem;
|
||||||
import static org.hamcrest.Matchers.hasItems;
|
import static org.hamcrest.Matchers.hasItems;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.hamcrest.Matchers.in;
|
||||||
import static org.hamcrest.Matchers.lessThan;
|
import static org.hamcrest.Matchers.lessThan;
|
||||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||||
import static org.hamcrest.Matchers.matchesPattern;
|
import static org.hamcrest.Matchers.matchesPattern;
|
||||||
|
@ -47,7 +48,9 @@ import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.stream.Collectors;
|
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.jpa.model.entity.NormalizedQuantitySearchLevel;
|
||||||
|
import ca.uhn.fhir.util.BundleBuilder;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
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
|
@Test
|
||||||
public void testCountParam() {
|
public void testCountParam() {
|
||||||
List<IBaseResource> resources = new ArrayList<>();
|
List<IBaseResource> resources = new ArrayList<>();
|
||||||
|
@ -857,7 +926,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
org.setName("rpr4_testCountParam_01");
|
org.setName("rpr4_testCountParam_01");
|
||||||
resources.add(org);
|
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
|
Bundle found = myClient
|
||||||
.search()
|
.search()
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</logger>
|
</logger>
|
||||||
j
|
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" />
|
<appender-ref ref="STDOUT" />
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -72,6 +72,20 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||||
init510(); // 20200516 - 20201028
|
init510(); // 20200516 - 20201028
|
||||||
init520(); // 20201029 -
|
init520(); // 20201029 -
|
||||||
init530();
|
init530();
|
||||||
|
init540(); // 20210218 -
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init540() {
|
||||||
|
|
||||||
|
Builder version = forVersion(VersionEnum.V5_4_0);
|
||||||
|
|
||||||
|
//-- add index on HFJ_SPIDX_DATE
|
||||||
|
version.onTable("HFJ_SPIDX_DATE").addIndex("20210309.1", "IDX_SP_DATE_HASH_HIGH")
|
||||||
|
.unique(false).withColumns("HASH_IDENTITY", "SP_VALUE_HIGH");
|
||||||
|
|
||||||
|
//-- add index on HFJ_FORCED_ID
|
||||||
|
version.onTable("HFJ_FORCED_ID").addIndex("20210309.2", "IDX_FORCEID_FID")
|
||||||
|
.unique(false).withColumns("FORCED_ID");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init530() {
|
private void init530() {
|
||||||
|
@ -126,6 +140,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||||
// HFJ_RES_LINK
|
// HFJ_RES_LINK
|
||||||
version.onTable("HFJ_RES_LINK")
|
version.onTable("HFJ_RES_LINK")
|
||||||
.addColumn("20210126.1", "TARGET_RESOURCE_VERSION").nullable().type(ColumnTypeEnum.LONG);
|
.addColumn("20210126.1", "TARGET_RESOURCE_VERSION").nullable().type(ColumnTypeEnum.LONG);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void init520() {
|
protected void init520() {
|
||||||
|
|
|
@ -31,6 +31,7 @@ import javax.persistence.ForeignKey;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Index;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.OneToOne;
|
import javax.persistence.OneToOne;
|
||||||
import javax.persistence.SequenceGenerator;
|
import javax.persistence.SequenceGenerator;
|
||||||
|
@ -48,6 +49,7 @@ import javax.persistence.UniqueConstraint;
|
||||||
* - IDX_FORCEDID_TYPE_RESID
|
* - IDX_FORCEDID_TYPE_RESID
|
||||||
* so don't reuse these names
|
* so don't reuse these names
|
||||||
*/
|
*/
|
||||||
|
@Index(name = "IDX_FORCEID_FID", columnList = "FORCED_ID")
|
||||||
})
|
})
|
||||||
public class ForcedId extends BasePartitionable {
|
public class ForcedId extends BasePartitionable {
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,6 @@ public class ModelConfig {
|
||||||
private Set<String> myAutoVersionReferenceAtPaths = Collections.emptySet();
|
private Set<String> myAutoVersionReferenceAtPaths = Collections.emptySet();
|
||||||
private Map<String, Set<String>> myTypeToAutoVersionReferenceAtPaths = Collections.emptyMap();
|
private Map<String, Set<String>> myTypeToAutoVersionReferenceAtPaths = Collections.emptyMap();
|
||||||
private boolean myRespectVersionsForSearchIncludes;
|
private boolean myRespectVersionsForSearchIncludes;
|
||||||
|
|
||||||
private boolean myIndexOnContainedResources = false;
|
private boolean myIndexOnContainedResources = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -732,7 +731,6 @@ public class ModelConfig {
|
||||||
myRespectVersionsForSearchIncludes = theRespectVersionsForSearchIncludes;
|
myRespectVersionsForSearchIncludes = theRespectVersionsForSearchIncludes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should indexing and searching on contained resources be enabled on this server.
|
* 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>.
|
* This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
|
||||||
|
|
|
@ -55,6 +55,7 @@ import java.util.Date;
|
||||||
// We previously had an index called IDX_SP_DATE - Dont reuse
|
// We previously had an index called IDX_SP_DATE - Dont reuse
|
||||||
@Index(name = "IDX_SP_DATE_HASH", columnList = "HASH_IDENTITY,SP_VALUE_LOW,SP_VALUE_HIGH"),
|
@Index(name = "IDX_SP_DATE_HASH", columnList = "HASH_IDENTITY,SP_VALUE_LOW,SP_VALUE_HIGH"),
|
||||||
@Index(name = "IDX_SP_DATE_HASH_LOW", columnList = "HASH_IDENTITY,SP_VALUE_LOW"),
|
@Index(name = "IDX_SP_DATE_HASH_LOW", columnList = "HASH_IDENTITY,SP_VALUE_LOW"),
|
||||||
|
@Index(name = "IDX_SP_DATE_HASH_HIGH", columnList = "HASH_IDENTITY,SP_VALUE_HIGH"),
|
||||||
@Index(name = "IDX_SP_DATE_ORD_HASH", columnList = "HASH_IDENTITY,SP_VALUE_LOW_DATE_ORDINAL,SP_VALUE_HIGH_DATE_ORDINAL"),
|
@Index(name = "IDX_SP_DATE_ORD_HASH", columnList = "HASH_IDENTITY,SP_VALUE_LOW_DATE_ORDINAL,SP_VALUE_HIGH_DATE_ORDINAL"),
|
||||||
@Index(name = "IDX_SP_DATE_ORD_HASH_LOW", columnList = "HASH_IDENTITY,SP_VALUE_LOW_DATE_ORDINAL"),
|
@Index(name = "IDX_SP_DATE_ORD_HASH_LOW", columnList = "HASH_IDENTITY,SP_VALUE_LOW_DATE_ORDINAL"),
|
||||||
@Index(name = "IDX_SP_DATE_RESID", columnList = "RES_ID"),
|
@Index(name = "IDX_SP_DATE_RESID", columnList = "RES_ID"),
|
||||||
|
|
|
@ -257,6 +257,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
b.append("hashIdentity", myHashIdentity);
|
b.append("hashIdentity", myHashIdentity);
|
||||||
b.append("hashSystem", myHashSystem);
|
b.append("hashSystem", myHashSystem);
|
||||||
b.append("hashValue", myHashValue);
|
b.append("hashValue", myHashValue);
|
||||||
|
b.append("hashSysAndValue", myHashSystemAndValue);
|
||||||
return b.build();
|
return b.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,11 @@ public class ResourceChangeListenerCache implements IResourceChangeListenerCache
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setResourceChangeListenerCacheRefresher(IResourceChangeListenerCacheRefresher theResourceChangeListenerCacheRefresher) {
|
||||||
|
myResourceChangeListenerCacheRefresher = theResourceChangeListenerCacheRefresher;
|
||||||
|
}
|
||||||
|
|
||||||
private ResourceChangeResult refreshCacheAndNotifyListenersWithRetry() {
|
private ResourceChangeResult refreshCacheAndNotifyListenersWithRetry() {
|
||||||
Retrier<ResourceChangeResult> refreshCacheRetrier = new Retrier<>(() -> {
|
Retrier<ResourceChangeResult> refreshCacheRetrier = new Retrier<>(() -> {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
|
|
@ -103,6 +103,21 @@ public class ResourceChangeListenerCacheRefresherImpl implements IResourceChange
|
||||||
return retval;
|
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
|
@Override
|
||||||
public ResourceChangeResult refreshCacheAndNotifyListener(IResourceChangeListenerCache theCache) {
|
public ResourceChangeResult refreshCacheAndNotifyListener(IResourceChangeListenerCache theCache) {
|
||||||
ResourceChangeResult retVal = new ResourceChangeResult();
|
ResourceChangeResult retVal = new ResourceChangeResult();
|
||||||
|
|
|
@ -47,28 +47,42 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
@Component
|
@Component
|
||||||
public class ResourceChangeListenerRegistryImpl implements IResourceChangeListenerRegistry {
|
public class ResourceChangeListenerRegistryImpl implements IResourceChangeListenerRegistry {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(ResourceChangeListenerRegistryImpl.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(ResourceChangeListenerRegistryImpl.class);
|
||||||
|
private final Queue<ResourceChangeListenerCache> myListenerEntries = new ConcurrentLinkedQueue<>();
|
||||||
|
@Autowired
|
||||||
|
ResourceChangeListenerCacheFactory myResourceChangeListenerCacheFactory;
|
||||||
@Autowired
|
@Autowired
|
||||||
private FhirContext myFhirContext;
|
private FhirContext myFhirContext;
|
||||||
@Autowired
|
@Autowired
|
||||||
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
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
|
* 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
|
* 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
|
* 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.
|
* 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 theResourceName the type of the resource the listener should be notified about (e.g. "Subscription" or "SearchParameter")
|
||||||
* @param theResourceChangeListener the listener that will be called whenever resource changes are detected
|
* @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
|
* @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
|
* @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
|
@Override
|
||||||
public IResourceChangeListenerCache registerResourceResourceChangeListener(String theResourceName, SearchParameterMap theSearchParameterMap, IResourceChangeListener theResourceChangeListener, long theRemoteRefreshIntervalMs) {
|
public IResourceChangeListenerCache registerResourceResourceChangeListener(String theResourceName, SearchParameterMap theSearchParameterMap, IResourceChangeListener theResourceChangeListener, long theRemoteRefreshIntervalMs) {
|
||||||
|
|
|
@ -45,8 +45,10 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
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.HapiExtensions;
|
||||||
import ca.uhn.fhir.util.StringUtil;
|
import ca.uhn.fhir.util.StringUtil;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -81,6 +83,7 @@ import java.util.TreeSet;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
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.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.trim;
|
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);
|
protected abstract IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath);
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setContext(FhirContext theContext) {
|
||||||
|
myContext = theContext;
|
||||||
|
}
|
||||||
|
|
||||||
protected FhirContext getContext() {
|
protected FhirContext getContext() {
|
||||||
return myContext;
|
return myContext;
|
||||||
}
|
}
|
||||||
|
@ -540,6 +548,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
||||||
return myModelConfig;
|
return myModelConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
|
||||||
|
mySearchParamRegistry = theSearchParamRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
private Collection<RuntimeSearchParam> getSearchParams(IBaseResource theResource) {
|
private Collection<RuntimeSearchParam> getSearchParams(IBaseResource theResource) {
|
||||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||||
Collection<RuntimeSearchParam> retVal = mySearchParamRegistry.getActiveSearchParams(def.getName()).values();
|
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) {
|
protected boolean shouldIndexTextComponentOfToken(RuntimeSearchParam theSearchParam) {
|
||||||
return tokenTextIndexingEnabledForSearchParam(myModelConfig, theSearchParam);
|
return tokenTextIndexingEnabledForSearchParam(myModelConfig, theSearchParam);
|
||||||
}
|
}
|
||||||
|
@ -938,6 +956,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
||||||
SearchParamSet<T> retVal = new SearchParamSet<>();
|
SearchParamSet<T> retVal = new SearchParamSet<>();
|
||||||
|
|
||||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||||
|
|
||||||
|
cleanUpContainedResourceReferences(theResource, theSearchParamType, searchParams);
|
||||||
|
|
||||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||||
if (nextSpDef.getParamType() != theSearchParamType) {
|
if (nextSpDef.getParamType() != theSearchParamType) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -948,6 +969,31 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
||||||
return retVal;
|
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) {
|
private <T> void extractSearchParam(RuntimeSearchParam theSearchParameterDef, IBaseResource theResource, IExtractor<T> theExtractor, SearchParamSet<T> theSetToPopulate, boolean theWantLocalReferences) {
|
||||||
String nextPathUnsplit = theSearchParameterDef.getPath();
|
String nextPathUnsplit = theSearchParameterDef.getPath();
|
||||||
if (isBlank(nextPathUnsplit)) {
|
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) {
|
private ResourceIndexedSearchParamToken createTokenIndexIfNotBlank(String theResourceType, RuntimeSearchParam theSearchParam, String theSystem, String theValue) {
|
||||||
String system = theSystem;
|
String system = theSystem;
|
||||||
String value = theValue;
|
String value = theValue;
|
||||||
|
|
|
@ -74,10 +74,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class SearchParamExtractorService {
|
public class SearchParamExtractorService {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorService.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorService.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
private ISearchParamExtractor mySearchParamExtractor;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -91,22 +89,25 @@ public class SearchParamExtractorService {
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private IResourceLinkResolver myResourceLinkResolver;
|
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
|
* 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}.
|
* 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) {
|
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
|
// All search parameter types except Reference
|
||||||
ResourceIndexedSearchParams normalParams = new ResourceIndexedSearchParams();
|
ResourceIndexedSearchParams normalParams = new ResourceIndexedSearchParams();
|
||||||
extractSearchIndexParameters(theRequestDetails, normalParams, resource, theEntity);
|
extractSearchIndexParameters(theRequestDetails, normalParams, theResource, theEntity);
|
||||||
mergeParams(normalParams, theParams);
|
mergeParams(normalParams, theParams);
|
||||||
|
|
||||||
if (myModelConfig.isIndexOnContainedResources()) {
|
if (myModelConfig.isIndexOnContainedResources()) {
|
||||||
ResourceIndexedSearchParams containedParams = new ResourceIndexedSearchParams();
|
ResourceIndexedSearchParams containedParams = new ResourceIndexedSearchParams();
|
||||||
extractSearchIndexParametersForContainedResources(theRequestDetails, containedParams, resource, theEntity);
|
extractSearchIndexParametersForContainedResources(theRequestDetails, containedParams, theResource, theEntity);
|
||||||
mergeParams(containedParams, theParams);
|
mergeParams(containedParams, theParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,11 +115,16 @@ public class SearchParamExtractorService {
|
||||||
populateResourceTables(theParams, theEntity);
|
populateResourceTables(theParams, theEntity);
|
||||||
|
|
||||||
// Reference search parameters
|
// Reference search parameters
|
||||||
extractResourceLinks(theRequestPartitionId, theParams, theEntity, resource, theTransactionDetails, theFailOnInvalidReference, theRequestDetails);
|
extractResourceLinks(theRequestPartitionId, theParams, theEntity, theResource, theTransactionDetails, theFailOnInvalidReference, theRequestDetails);
|
||||||
|
|
||||||
theParams.setUpdatedTime(theTransactionDetails.getTransactionDate());
|
theParams.setUpdatedTime(theTransactionDetails.getTransactionDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setModelConfig(ModelConfig theModelConfig) {
|
||||||
|
myModelConfig = theModelConfig;
|
||||||
|
}
|
||||||
|
|
||||||
private void extractSearchIndexParametersForContainedResources(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, ResourceTable theEntity) {
|
private void extractSearchIndexParametersForContainedResources(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, ResourceTable theEntity) {
|
||||||
|
|
||||||
FhirTerser terser = myContext.newTerser();
|
FhirTerser terser = myContext.newTerser();
|
||||||
|
@ -247,19 +253,9 @@ public class SearchParamExtractorService {
|
||||||
populateResourceTable(theParams.myCoordsParams, theEntity);
|
populateResourceTable(theParams.myCoordsParams, theEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@VisibleForTesting
|
||||||
* This is a bit hacky, but if someone has manually populated a resource (ie. my working directly with the model
|
public void setContext(FhirContext theContext) {
|
||||||
* as opposed to by parsing a serialized instance) it's possible that they have put in contained resources
|
myContext = theContext;
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void extractResourceLinks(RequestPartitionId theRequestPartitionId, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference, RequestDetails theRequest) {
|
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();
|
nextId = nextReference.getResource().getIdElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (myContext.getParserOptions().isStripVersionsFromReferences() && !myContext.getParserOptions().getDontStripVersionsFromReferencesAtPaths().contains(thePathAndRef.getPath()) && nextId.hasVersionIdPart()) {
|
||||||
|
nextId = nextId.toVersionless();
|
||||||
|
}
|
||||||
|
|
||||||
theParams.myPopulatedResourceLinkParameters.add(thePathAndRef.getSearchParamName());
|
theParams.myPopulatedResourceLinkParameters.add(thePathAndRef.getSearchParamName());
|
||||||
|
|
||||||
boolean canonical = thePathAndRef.isCanonical();
|
boolean canonical = thePathAndRef.isCanonical();
|
||||||
|
@ -431,24 +431,6 @@ public class SearchParamExtractorService {
|
||||||
return ResourceLink.forLocalReference(nextPathAndRef.getPath(), theEntity, targetResourceType, targetResourcePid, targetResourceIdPart, theUpdateTime, targetVersion);
|
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) {
|
private void populateResourceTable(Collection<? extends BaseResourceIndexedSearchParam> theParams, ResourceTable theResourceTable) {
|
||||||
for (BaseResourceIndexedSearchParam next : theParams) {
|
for (BaseResourceIndexedSearchParam next : theParams) {
|
||||||
if (next.getResourcePid() == null) {
|
if (next.getResourcePid() == null) {
|
||||||
|
@ -498,5 +480,22 @@ public class SearchParamExtractorService {
|
||||||
public List<String> extractParamValuesAsStrings(RuntimeSearchParam theActiveSearchParam, IBaseResource theResource) {
|
public List<String> extractParamValuesAsStrings(RuntimeSearchParam theActiveSearchParam, IBaseResource theResource) {
|
||||||
return mySearchParamExtractor.extractParamValuesAsStrings(theActiveSearchParam, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,11 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry, IResourceC
|
||||||
ourLog.debug("Refreshed search parameter cache in {}ms", sw.getMillis());
|
ourLog.debug("Refreshed search parameter cache in {}ms", sw.getMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setFhirContext(FhirContext theFhirContext) {
|
||||||
|
myFhirContext = theFhirContext;
|
||||||
|
}
|
||||||
|
|
||||||
private ReadOnlySearchParamCache getBuiltInSearchParams() {
|
private ReadOnlySearchParamCache getBuiltInSearchParams() {
|
||||||
if (myBuiltInSearchParams == null) {
|
if (myBuiltInSearchParams == null) {
|
||||||
myBuiltInSearchParams = ReadOnlySearchParamCache.fromFhirContext(myFhirContext);
|
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) {
|
private long overrideBuiltinSearchParamsWithActiveJpaSearchParams(RuntimeSearchParamCache theSearchParamCache, Collection<IBaseResource> theSearchParams) {
|
||||||
if (!myModelConfig.isDefaultSearchParamsCanBeOverridden() || theSearchParams == null) {
|
if (!myModelConfig.isDefaultSearchParamsCanBeOverridden() || theSearchParams == null) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -228,6 +238,11 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry, IResourceC
|
||||||
return myResourceChangeListenerCache.refreshCacheIfNecessary();
|
return myResourceChangeListenerCache.refreshCacheIfNecessary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setResourceChangeListenerRegistry(IResourceChangeListenerRegistry theResourceChangeListenerRegistry) {
|
||||||
|
myResourceChangeListenerRegistry = theResourceChangeListenerRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void registerListener() {
|
public void registerListener() {
|
||||||
myResourceChangeListenerCache = myResourceChangeListenerRegistry.registerResourceResourceChangeListener("SearchParameter", SearchParameterMap.newSynchronous(), this, REFRESH_INTERVAL);
|
myResourceChangeListenerCache = myResourceChangeListenerRegistry.registerResourceResourceChangeListener("SearchParameter", SearchParameterMap.newSynchronous(), this, REFRESH_INTERVAL);
|
||||||
|
|
|
@ -1090,6 +1090,7 @@ public class XmlParserDstu3Test {
|
||||||
* See #103
|
* See #103
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@Disabled
|
||||||
public void testEncodeAndReEncodeContainedJson() {
|
public void testEncodeAndReEncodeContainedJson() {
|
||||||
Composition comp = new Composition();
|
Composition comp = new Composition();
|
||||||
comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section0_Allergy0")));
|
comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section0_Allergy0")));
|
||||||
|
@ -1115,6 +1116,7 @@ public class XmlParserDstu3Test {
|
||||||
* See #103
|
* See #103
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@Disabled
|
||||||
public void testEncodeAndReEncodeContainedXml() {
|
public void testEncodeAndReEncodeContainedXml() {
|
||||||
Composition comp = new Composition();
|
Composition comp = new Composition();
|
||||||
comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section0_Allergy0")));
|
comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section0_Allergy0")));
|
||||||
|
|
|
@ -2,10 +2,7 @@ package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
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.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.rest.api.Constants;
|
||||||
import ca.uhn.fhir.test.BaseTest;
|
import ca.uhn.fhir.test.BaseTest;
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
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.DateTimeType;
|
||||||
import org.hl7.fhir.r4.model.Device;
|
import org.hl7.fhir.r4.model.Device;
|
||||||
import org.hl7.fhir.r4.model.DocumentReference;
|
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.Encounter;
|
||||||
import org.hl7.fhir.r4.model.Extension;
|
import org.hl7.fhir.r4.model.Extension;
|
||||||
import org.hl7.fhir.r4.model.Medication;
|
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.Reference;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
import org.hl7.fhir.r4.model.Type;
|
import org.hl7.fhir.r4.model.Type;
|
||||||
import org.hl7.fhir.r4.model.UuidType;
|
|
||||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
@ -473,8 +468,8 @@ public class JsonParserR4Test extends BaseTest {
|
||||||
ourLog.info(encoded);
|
ourLog.info(encoded);
|
||||||
mr = ourCtx.newJsonParser().parseResource(MedicationRequest.class, encoded);
|
mr = ourCtx.newJsonParser().parseResource(MedicationRequest.class, encoded);
|
||||||
|
|
||||||
assertEquals("#2", mr.getContained().get(0).getId());
|
assertEquals("#1", mr.getContained().get(0).getId());
|
||||||
assertEquals("#1", mr.getContained().get(1).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(
|
@DatatypeDef(
|
||||||
name = "UnknownPrimitiveType"
|
name = "UnknownPrimitiveType"
|
||||||
)
|
)
|
||||||
|
|
|
@ -44,6 +44,55 @@ public class XmlParserR4Test extends BaseTest {
|
||||||
return c;
|
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
|
* See #402 section.text is overwritten by composition.text
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.util;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.model.api.annotation.Block;
|
import ca.uhn.fhir.model.api.annotation.Block;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import org.hamcrest.Matchers;
|
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.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.DocumentReference;
|
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.Enumeration;
|
||||||
import org.hl7.fhir.r4.model.Enumerations;
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
import org.hl7.fhir.r4.model.Extension;
|
import org.hl7.fhir.r4.model.Extension;
|
||||||
import org.hl7.fhir.r4.model.Identifier;
|
import org.hl7.fhir.r4.model.Identifier;
|
||||||
import org.hl7.fhir.r4.model.MarkdownType;
|
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.Money;
|
||||||
import org.hl7.fhir.r4.model.Observation;
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
import org.hl7.fhir.r4.model.Organization;
|
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.PrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.Quantity;
|
import org.hl7.fhir.r4.model.Quantity;
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
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.SimpleQuantity;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.hl7.fhir.r4.model.Substance;
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -62,14 +67,74 @@ import static org.mockito.Mockito.when;
|
||||||
public class FhirTerserR4Test {
|
public class FhirTerserR4Test {
|
||||||
|
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(FhirTerserR4Test.class);
|
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
|
@Test
|
||||||
public void testGetValuesCreateEnumeration_SetsEnumFactory() {
|
public void testGetValuesCreateEnumeration_SetsEnumFactory() {
|
||||||
|
|
||||||
Patient patient = new Patient();
|
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());
|
assertNotNull(enumeration.getEnumFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,9 +163,9 @@ public class FhirTerserR4Test {
|
||||||
.setUrl("Observation/obs")
|
.setUrl("Observation/obs")
|
||||||
.setMethod(Bundle.HTTPVerb.PUT);
|
.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);
|
ourLog.info(output);
|
||||||
assertTrue(input.isEmpty());
|
assertTrue(input.isEmpty());
|
||||||
assertEquals("{\"resourceType\":\"Bundle\"}", output);
|
assertEquals("{\"resourceType\":\"Bundle\"}", output);
|
||||||
|
@ -132,7 +197,7 @@ public class FhirTerserR4Test {
|
||||||
.setMethod(Bundle.HTTPVerb.PUT);
|
.setMethod(Bundle.HTTPVerb.PUT);
|
||||||
|
|
||||||
Bundle ionputClone = new Bundle();
|
Bundle ionputClone = new Bundle();
|
||||||
ourCtx.newTerser().cloneInto(input, ionputClone, false);
|
myCtx.newTerser().cloneInto(input, ionputClone, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -141,7 +206,7 @@ public class FhirTerserR4Test {
|
||||||
source.setCode("CODE");
|
source.setCode("CODE");
|
||||||
SimpleQuantity target = new SimpleQuantity();
|
SimpleQuantity target = new SimpleQuantity();
|
||||||
|
|
||||||
ourCtx.newTerser().cloneInto(source, target, true);
|
myCtx.newTerser().cloneInto(source, target, true);
|
||||||
|
|
||||||
assertEquals("CODE", target.getCode());
|
assertEquals("CODE", target.getCode());
|
||||||
}
|
}
|
||||||
|
@ -153,12 +218,12 @@ public class FhirTerserR4Test {
|
||||||
source.setUnit("UNIT");
|
source.setUnit("UNIT");
|
||||||
Identifier target = new Identifier();
|
Identifier target = new Identifier();
|
||||||
|
|
||||||
ourCtx.newTerser().cloneInto(source, target, true);
|
myCtx.newTerser().cloneInto(source, target, true);
|
||||||
|
|
||||||
assertEquals("SYSTEM", target.getSystem());
|
assertEquals("SYSTEM", target.getSystem());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ourCtx.newTerser().cloneInto(source, target, false);
|
myCtx.newTerser().cloneInto(source, target, false);
|
||||||
fail();
|
fail();
|
||||||
} catch (DataFormatException e) {
|
} catch (DataFormatException e) {
|
||||||
// good
|
// good
|
||||||
|
@ -175,7 +240,7 @@ public class FhirTerserR4Test {
|
||||||
patient.addExtension(new Extension("http://example.com", new StringType("FOO")));
|
patient.addExtension(new Extension("http://example.com", new StringType("FOO")));
|
||||||
|
|
||||||
Patient target = new Patient();
|
Patient target = new Patient();
|
||||||
ourCtx.newTerser().cloneInto(patient, target, false);
|
myCtx.newTerser().cloneInto(patient, target, false);
|
||||||
|
|
||||||
List<Extension> exts = target.getExtensionsByUrl("http://example.com");
|
List<Extension> exts = target.getExtensionsByUrl("http://example.com");
|
||||||
assertEquals(1, exts.size());
|
assertEquals(1, exts.size());
|
||||||
|
@ -189,7 +254,7 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
DocumentReference target = new DocumentReference();
|
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(1, target.getContentFirstRep().getAttachment().getDataElement().getExtension().size());
|
||||||
assertEquals("http://foo", target.getContentFirstRep().getAttachment().getDataElement().getExtension().get(0).getUrl());
|
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.addExtension((Extension) new Extension().setUrl("http://foo").addExtension(ext));
|
||||||
|
|
||||||
Patient target = new Patient();
|
Patient target = new Patient();
|
||||||
ourCtx.newTerser().cloneInto(patient, target, false);
|
myCtx.newTerser().cloneInto(patient, target, false);
|
||||||
|
|
||||||
List<Extension> exts = target.getExtensionsByUrl("http://foo");
|
List<Extension> exts = target.getExtensionsByUrl("http://foo");
|
||||||
assertEquals(1, exts.size());
|
assertEquals(1, exts.size());
|
||||||
|
@ -218,7 +283,7 @@ public class FhirTerserR4Test {
|
||||||
patient.setGender(Enumerations.AdministrativeGender.MALE);
|
patient.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
|
||||||
Patient target = new Patient();
|
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());
|
assertEquals("http://hl7.org/fhir/administrative-gender", target.getGenderElement().getSystem());
|
||||||
}
|
}
|
||||||
|
@ -229,7 +294,7 @@ public class FhirTerserR4Test {
|
||||||
source.setId("STRING_ID");
|
source.setId("STRING_ID");
|
||||||
MarkdownType target = new MarkdownType();
|
MarkdownType target = new MarkdownType();
|
||||||
|
|
||||||
ourCtx.newTerser().cloneInto(source, target, true);
|
myCtx.newTerser().cloneInto(source, target, true);
|
||||||
|
|
||||||
assertEquals("STR", target.getValueAsString());
|
assertEquals("STR", target.getValueAsString());
|
||||||
assertEquals("STRING_ID", target.getId());
|
assertEquals("STRING_ID", target.getId());
|
||||||
|
@ -241,11 +306,11 @@ public class FhirTerserR4Test {
|
||||||
StringType source = new StringType("STR");
|
StringType source = new StringType("STR");
|
||||||
Money target = new Money();
|
Money target = new Money();
|
||||||
|
|
||||||
ourCtx.newTerser().cloneInto(source, target, true);
|
myCtx.newTerser().cloneInto(source, target, true);
|
||||||
assertTrue(target.isEmpty());
|
assertTrue(target.isEmpty());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ourCtx.newTerser().cloneInto(source, target, false);
|
myCtx.newTerser().cloneInto(source, target, false);
|
||||||
fail();
|
fail();
|
||||||
} catch (DataFormatException e) {
|
} catch (DataFormatException e) {
|
||||||
// good
|
// good
|
||||||
|
@ -263,7 +328,7 @@ public class FhirTerserR4Test {
|
||||||
obs.addNote().setText("COMMENTS");
|
obs.addNote().setText("COMMENTS");
|
||||||
|
|
||||||
Observation target = new Observation();
|
Observation target = new Observation();
|
||||||
ourCtx.newTerser().cloneInto(obs, target, false);
|
myCtx.newTerser().cloneInto(obs, target, false);
|
||||||
|
|
||||||
assertEquals("AAA", ((StringType) obs.getValue()).getValue());
|
assertEquals("AAA", ((StringType) obs.getValue()).getValue());
|
||||||
assertEquals("COMMENTS", obs.getNote().get(0).getText());
|
assertEquals("COMMENTS", obs.getNote().get(0).getText());
|
||||||
|
@ -278,7 +343,7 @@ public class FhirTerserR4Test {
|
||||||
obs.addNote().setText("COMMENTS");
|
obs.addNote().setText("COMMENTS");
|
||||||
|
|
||||||
Observation target = new Observation();
|
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());
|
assertEquals("http://foo/base/Observation/_history/123", target.getId());
|
||||||
}
|
}
|
||||||
|
@ -292,7 +357,7 @@ public class FhirTerserR4Test {
|
||||||
obs.setValue(string);
|
obs.setValue(string);
|
||||||
|
|
||||||
Observation target = new Observation();
|
Observation target = new Observation();
|
||||||
ourCtx.newTerser().cloneInto(obs, target, false);
|
myCtx.newTerser().cloneInto(obs, target, false);
|
||||||
|
|
||||||
assertEquals("BBB", target.getValueStringType().getId());
|
assertEquals("BBB", target.getValueStringType().getId());
|
||||||
}
|
}
|
||||||
|
@ -307,7 +372,7 @@ public class FhirTerserR4Test {
|
||||||
o.getNameElement().setValue("ORGANIZATION");
|
o.getNameElement().setValue("ORGANIZATION");
|
||||||
p.getContained().add(o);
|
p.getContained().add(o);
|
||||||
|
|
||||||
FhirTerser t = ourCtx.newTerser();
|
FhirTerser t = myCtx.newTerser();
|
||||||
List<StringType> strings = t.getAllPopulatedChildElementsOfType(p, StringType.class);
|
List<StringType> strings = t.getAllPopulatedChildElementsOfType(p, StringType.class);
|
||||||
|
|
||||||
assertThat(toStrings(strings), containsInAnyOrder("PATIENT", "ORGANIZATION"));
|
assertThat(toStrings(strings), containsInAnyOrder("PATIENT", "ORGANIZATION"));
|
||||||
|
@ -323,7 +388,7 @@ public class FhirTerserR4Test {
|
||||||
b.addEntry().setResource(p);
|
b.addEntry().setResource(p);
|
||||||
b.addLink().setRelation("BUNDLE");
|
b.addLink().setRelation("BUNDLE");
|
||||||
|
|
||||||
FhirTerser t = ourCtx.newTerser();
|
FhirTerser t = myCtx.newTerser();
|
||||||
List<StringType> strings = t.getAllPopulatedChildElementsOfType(b, StringType.class);
|
List<StringType> strings = t.getAllPopulatedChildElementsOfType(b, StringType.class);
|
||||||
|
|
||||||
assertEquals(1, strings.size());
|
assertEquals(1, strings.size());
|
||||||
|
@ -342,7 +407,7 @@ public class FhirTerserR4Test {
|
||||||
Extension ext = new Extension("urn:foo", ref);
|
Extension ext = new Extension("urn:foo", ref);
|
||||||
p.addExtension(ext);
|
p.addExtension(ext);
|
||||||
|
|
||||||
FhirTerser t = ourCtx.newTerser();
|
FhirTerser t = myCtx.newTerser();
|
||||||
List<IBaseReference> refs = t.getAllPopulatedChildElementsOfType(p, IBaseReference.class);
|
List<IBaseReference> refs = t.getAllPopulatedChildElementsOfType(p, IBaseReference.class);
|
||||||
assertEquals(1, refs.size());
|
assertEquals(1, refs.size());
|
||||||
assertSame(ref, refs.get(0));
|
assertSame(ref, refs.get(0));
|
||||||
|
@ -367,29 +432,29 @@ public class FhirTerserR4Test {
|
||||||
.setUrl("http://acme.org/childExtension")
|
.setUrl("http://acme.org/childExtension")
|
||||||
.setValue(new StringType("nestedValue"));
|
.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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -426,7 +491,7 @@ public class FhirTerserR4Test {
|
||||||
innerPatient1.setActive(true);
|
innerPatient1.setActive(true);
|
||||||
outerBundle.addEntry().setResource(innerPatient1);
|
outerBundle.addEntry().setResource(innerPatient1);
|
||||||
|
|
||||||
FhirTerser t = ourCtx.newTerser();
|
FhirTerser t = myCtx.newTerser();
|
||||||
|
|
||||||
Collection<IBaseResource> resources;
|
Collection<IBaseResource> resources;
|
||||||
|
|
||||||
|
@ -452,7 +517,7 @@ public class FhirTerserR4Test {
|
||||||
patient.getContained().add(practitioner1);
|
patient.getContained().add(practitioner1);
|
||||||
patient.addGeneralPractitioner().setReference("#practitioner1");
|
patient.addGeneralPractitioner().setReference("#practitioner1");
|
||||||
|
|
||||||
FhirTerser t = ourCtx.newTerser();
|
FhirTerser t = myCtx.newTerser();
|
||||||
|
|
||||||
Collection<IBaseResource> resources;
|
Collection<IBaseResource> resources;
|
||||||
|
|
||||||
|
@ -489,9 +554,9 @@ public class FhirTerserR4Test {
|
||||||
.setUrl("http://acme.org/childExtension")
|
.setUrl("http://acme.org/childExtension")
|
||||||
.setValue(new StringType("nestedValue"));
|
.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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
|
@ -499,15 +564,15 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
((BooleanType) values.get(0)).setValue(Boolean.FALSE);
|
((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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertFalse(((BooleanType) values.get(0)).booleanValue());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -516,16 +581,16 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
((Extension) values.get(0)).setValue(new StringType("modifiedValue"));
|
((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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("modifiedValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -534,16 +599,16 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
((Extension) values.get(0)).setValue(new StringType("modifiedModifierValue"));
|
((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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("modifiedModifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -552,9 +617,9 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
((Extension) values.get(0)).setValue(new StringType("modifiedNestedValue"));
|
((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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -591,9 +656,9 @@ public class FhirTerserR4Test {
|
||||||
.setUrl("http://acme.org/childExtension")
|
.setUrl("http://acme.org/childExtension")
|
||||||
.setValue(new StringType("nestedValue2"));
|
.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());
|
assertEquals(2, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -604,7 +669,7 @@ public class FhirTerserR4Test {
|
||||||
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
|
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
|
||||||
assertEquals("value2", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
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());
|
assertEquals(2, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -615,7 +680,7 @@ public class FhirTerserR4Test {
|
||||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
|
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
|
||||||
assertEquals("modifierValue2", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
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());
|
assertEquals(2, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -646,26 +711,26 @@ public class FhirTerserR4Test {
|
||||||
.setUrl("http://acme.org/childExtension")
|
.setUrl("http://acme.org/childExtension")
|
||||||
.setValue(new StringType("nestedValue"));
|
.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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
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());
|
assertEquals(1, extValues.size());
|
||||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||||
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
||||||
assertEquals("value", ((StringType) (extValues.get(0).getValue())).getValueAsString());
|
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());
|
assertEquals(1, extValues.size());
|
||||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||||
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
||||||
assertEquals("modifierValue", ((StringType) (extValues.get(0).getValue())).getValueAsString());
|
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());
|
assertEquals(1, extValues.size());
|
||||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||||
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
||||||
|
@ -691,23 +756,23 @@ public class FhirTerserR4Test {
|
||||||
.setUrl("http://acme.org/childExtension")
|
.setUrl("http://acme.org/childExtension")
|
||||||
.setValue(new StringType("nestedValue"));
|
.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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||||
|
|
||||||
((BooleanType) values.get(0)).setValue(Boolean.FALSE);
|
((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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertFalse(((BooleanType) values.get(0)).booleanValue());
|
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());
|
assertEquals(1, extValues.size());
|
||||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||||
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
||||||
|
@ -715,15 +780,15 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
extValues.get(0).setValue(new StringType("modifiedValue"));
|
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());
|
assertEquals(1, extValues.size());
|
||||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||||
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
||||||
assertEquals("modifiedValue", ((StringType) (extValues.get(0).getValue())).getValueAsString());
|
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());
|
assertEquals(1, extValues.size());
|
||||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||||
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
||||||
|
@ -731,15 +796,15 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
extValues.get(0).setValue(new StringType("modifiedModifierValue"));
|
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());
|
assertEquals(1, extValues.size());
|
||||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||||
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
||||||
assertEquals("modifiedModifierValue", ((StringType) (extValues.get(0).getValue())).getValueAsString());
|
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());
|
assertEquals(1, extValues.size());
|
||||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||||
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
||||||
|
@ -747,9 +812,9 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
extValues.get(0).setValue(new StringType("modifiedNestedValue"));
|
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());
|
assertEquals(1, extValues.size());
|
||||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||||
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
||||||
|
@ -760,24 +825,24 @@ public class FhirTerserR4Test {
|
||||||
public void testGetValuesWithWantedClassAndTheCreate() {
|
public void testGetValuesWithWantedClassAndTheCreate() {
|
||||||
Patient p = new Patient();
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertNull(((BooleanType) values.get(0)).getValue());
|
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(1, extValues.size());
|
||||||
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
||||||
assertNull(extValues.get(0).getValue());
|
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(1, extValues.size());
|
||||||
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
||||||
assertNull(extValues.get(0).getValue());
|
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(1, extValues.size());
|
||||||
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
||||||
assertNull(extValues.get(0).getValue());
|
assertNull(extValues.get(0).getValue());
|
||||||
|
@ -802,29 +867,29 @@ public class FhirTerserR4Test {
|
||||||
.setUrl("http://acme.org/childExtension")
|
.setUrl("http://acme.org/childExtension")
|
||||||
.setValue(new StringType("nestedValue"));
|
.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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||||
|
|
||||||
// No change.
|
// No change.
|
||||||
values = ourCtx.newTerser().getValues(p, "Patient.active", false, true);
|
values = myCtx.newTerser().getValues(p, "Patient.active", false, true);
|
||||||
assertEquals(1, values.size());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(2, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -837,21 +902,21 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
((Extension) values.get(1)).setValue(new StringType("addedValue"));
|
((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 IBaseExtension);
|
||||||
assertTrue(values.get(1) instanceof Extension);
|
assertTrue(values.get(1) instanceof Extension);
|
||||||
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
|
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
|
||||||
assertEquals("addedValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(2, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -864,23 +929,23 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
((Extension) values.get(1)).setValue(new StringType("addedModifierValue"));
|
((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 IBaseExtension);
|
||||||
assertTrue(values.get(1) instanceof Extension);
|
assertTrue(values.get(1) instanceof Extension);
|
||||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
|
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
|
||||||
assertEquals("addedModifierValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/childExtension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/childExtension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("nestedValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(2, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -893,7 +958,7 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
((Extension) values.get(1)).setValue(new StringType("addedNestedValue"));
|
((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 IBaseExtension);
|
||||||
assertTrue(values.get(1) instanceof Extension);
|
assertTrue(values.get(1) instanceof Extension);
|
||||||
|
@ -905,27 +970,27 @@ public class FhirTerserR4Test {
|
||||||
public void testGetValuesWithTheCreate() {
|
public void testGetValuesWithTheCreate() {
|
||||||
Patient p = new Patient();
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertNull(((BooleanType) values.get(0)).getValue());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||||
assertNull(((Extension) values.get(0)).getValue());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||||
assertNull(((Extension) values.get(0)).getValue());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -952,29 +1017,29 @@ public class FhirTerserR4Test {
|
||||||
.setUrl("http://acme.org/childExtension")
|
.setUrl("http://acme.org/childExtension")
|
||||||
.setValue(new StringType("nestedValue"));
|
.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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||||
|
|
||||||
// No change.
|
// No change.
|
||||||
values = ourCtx.newTerser().getValues(p, "Patient.active", true, true);
|
values = myCtx.newTerser().getValues(p, "Patient.active", true, true);
|
||||||
assertEquals(1, values.size());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(2, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -987,21 +1052,21 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
((Extension) values.get(1)).setValue(new StringType("addedValue"));
|
((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 IBaseExtension);
|
||||||
assertTrue(values.get(1) instanceof Extension);
|
assertTrue(values.get(1) instanceof Extension);
|
||||||
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
|
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
|
||||||
assertEquals("addedValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(2, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -1014,21 +1079,21 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
((Extension) values.get(1)).setValue(new StringType("addedModifierValue"));
|
((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 IBaseExtension);
|
||||||
assertTrue(values.get(1) instanceof Extension);
|
assertTrue(values.get(1) instanceof Extension);
|
||||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
|
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
|
||||||
assertEquals("addedModifierValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/childExtension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/childExtension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("nestedValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(2, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -1041,7 +1106,7 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
((Extension) values.get(1)).setValue(new StringType("addedNestedValue"));
|
((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 IBaseExtension);
|
||||||
assertTrue(values.get(1) instanceof Extension);
|
assertTrue(values.get(1) instanceof Extension);
|
||||||
|
@ -1068,29 +1133,29 @@ public class FhirTerserR4Test {
|
||||||
.setUrl("http://acme.org/childExtension")
|
.setUrl("http://acme.org/childExtension")
|
||||||
.setValue(new StringType("nestedValue"));
|
.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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||||
assertTrue(values.get(0) instanceof BooleanType);
|
assertTrue(values.get(0) instanceof BooleanType);
|
||||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||||
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
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());
|
assertEquals(1, values.size());
|
||||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||||
assertTrue(values.get(0) instanceof Extension);
|
assertTrue(values.get(0) instanceof Extension);
|
||||||
|
@ -1107,7 +1172,7 @@ public class FhirTerserR4Test {
|
||||||
vs.getExpansion().setIdentifier("http://foo");
|
vs.getExpansion().setIdentifier("http://foo");
|
||||||
|
|
||||||
Set<String> strings = new HashSet<>();
|
Set<String> strings = new HashSet<>();
|
||||||
ourCtx.newTerser().visit(vs, new IModelVisitor() {
|
myCtx.newTerser().visit(vs, new IModelVisitor() {
|
||||||
@Override
|
@Override
|
||||||
public void acceptElement(IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
|
public void acceptElement(IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
|
||||||
if (theElement instanceof IPrimitiveType) {
|
if (theElement instanceof IPrimitiveType) {
|
||||||
|
@ -1118,7 +1183,7 @@ public class FhirTerserR4Test {
|
||||||
assertThat(strings, Matchers.contains("http://foo"));
|
assertThat(strings, Matchers.contains("http://foo"));
|
||||||
|
|
||||||
strings.clear();
|
strings.clear();
|
||||||
ourCtx.newTerser().visit(vs, new IModelVisitor2() {
|
myCtx.newTerser().visit(vs, new IModelVisitor2() {
|
||||||
@Override
|
@Override
|
||||||
public boolean acceptElement(IBase theElement, List<IBase> theContainingElementPath, List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath) {
|
public boolean acceptElement(IBase theElement, List<IBase> theContainingElementPath, List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath) {
|
||||||
if (theElement instanceof IPrimitiveType) {
|
if (theElement instanceof IPrimitiveType) {
|
||||||
|
@ -1148,7 +1213,7 @@ public class FhirTerserR4Test {
|
||||||
|
|
||||||
Patient p = new Patient();
|
Patient p = new Patient();
|
||||||
p.addLink().getTypeElement().setValue(LinkType.REFER);
|
p.addLink().getTypeElement().setValue(LinkType.REFER);
|
||||||
ourCtx.newTerser().visit(p, visitor);
|
myCtx.newTerser().visit(p, visitor);
|
||||||
|
|
||||||
assertEquals(3, element.getAllValues().size());
|
assertEquals(3, element.getAllValues().size());
|
||||||
assertSame(p, element.getAllValues().get(0));
|
assertSame(p, element.getAllValues().get(0));
|
||||||
|
@ -1172,7 +1237,7 @@ public class FhirTerserR4Test {
|
||||||
p.addAddress().addLine("Line2");
|
p.addAddress().addLine("Line2");
|
||||||
p.addName().setFamily("Line3");
|
p.addName().setFamily("Line3");
|
||||||
|
|
||||||
FhirTerser t = ourCtx.newTerser();
|
FhirTerser t = myCtx.newTerser();
|
||||||
List<StringType> strings = t.getAllPopulatedChildElementsOfType(p, StringType.class);
|
List<StringType> strings = t.getAllPopulatedChildElementsOfType(p, StringType.class);
|
||||||
|
|
||||||
assertEquals(3, strings.size());
|
assertEquals(3, strings.size());
|
||||||
|
@ -1192,7 +1257,7 @@ public class FhirTerserR4Test {
|
||||||
Observation obs = new Observation();
|
Observation obs = new Observation();
|
||||||
obs.setValue(new Quantity(123L));
|
obs.setValue(new Quantity(123L));
|
||||||
|
|
||||||
FhirTerser t = ourCtx.newTerser();
|
FhirTerser t = myCtx.newTerser();
|
||||||
|
|
||||||
// As string
|
// As string
|
||||||
{
|
{
|
||||||
|
@ -1243,8 +1308,8 @@ public class FhirTerserR4Test {
|
||||||
"</Observation>";
|
"</Observation>";
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|
||||||
Observation parsed = ourCtx.newXmlParser().parseResource(Observation.class, msg);
|
Observation parsed = myCtx.newXmlParser().parseResource(Observation.class, msg);
|
||||||
FhirTerser t = ourCtx.newTerser();
|
FhirTerser t = myCtx.newTerser();
|
||||||
|
|
||||||
List<Reference> elems = t.getAllPopulatedChildElementsOfType(parsed, Reference.class);
|
List<Reference> elems = t.getAllPopulatedChildElementsOfType(parsed, Reference.class);
|
||||||
assertEquals(2, elems.size());
|
assertEquals(2, elems.size());
|
||||||
|
|
2
pom.xml
2
pom.xml
|
@ -984,7 +984,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-csv</artifactId>
|
<artifactId>commons-csv</artifactId>
|
||||||
<version>1.7</version>
|
<version>1.8</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.aspectj</groupId>
|
<groupId>org.aspectj</groupId>
|
||||||
|
|
Loading…
Reference in New Issue