Merge remote-tracking branch 'origin/master' into issue-2445-support-patient-level-export
This commit is contained in:
commit
b7398cb6f2
|
@ -30,7 +30,6 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeChildContainedResources;
|
||||
import ca.uhn.fhir.context.RuntimeChildDirectResource;
|
||||
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.IIdentifiableElement;
|
||||
|
@ -39,16 +38,15 @@ import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
|||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.parser.path.EncodeContextPath;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
|
@ -58,7 +56,6 @@ import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
|
|||
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IDomainResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
|
@ -77,7 +74,6 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
@ -102,7 +98,7 @@ public abstract class BaseParser implements IParser {
|
|||
|
||||
private static final Set<String> notEncodeForContainedResource = new HashSet<>(Arrays.asList("security", "versionId", "lastUpdated"));
|
||||
|
||||
private ContainedResources myContainedResources;
|
||||
private FhirTerser.ContainedResources myContainedResources;
|
||||
private boolean myEncodeElementsAppliesToChildResourcesOnly;
|
||||
private FhirContext myContext;
|
||||
private List<EncodeContextPath> myDontEncodeElements;
|
||||
|
@ -118,7 +114,6 @@ public abstract class BaseParser implements IParser {
|
|||
private boolean mySummaryMode;
|
||||
private boolean mySuppressNarratives;
|
||||
private Set<String> myDontStripVersionsFromReferencesAtPaths;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -127,6 +122,10 @@ public abstract class BaseParser implements IParser {
|
|||
myErrorHandler = theParserErrorHandler;
|
||||
}
|
||||
|
||||
protected FhirContext getContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
||||
List<EncodeContextPath> getDontEncodeElements() {
|
||||
return myDontEncodeElements;
|
||||
}
|
||||
|
@ -213,156 +212,6 @@ public abstract class BaseParser implements IParser {
|
|||
});
|
||||
}
|
||||
|
||||
private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, IBaseResource theTarget) {
|
||||
List<IBaseReference> allReferences = getAllBaseReferences(theResource);
|
||||
for (IBaseReference next : allReferences) {
|
||||
IBaseResource resource = next.getResource();
|
||||
if (resource == null && next.getReferenceElement().isLocal()) {
|
||||
if (theContained.hasExistingIdToContainedResource()) {
|
||||
IBaseResource potentialTarget = theContained.getExistingIdToContainedResource().remove(next.getReferenceElement().getValue());
|
||||
if (potentialTarget != null) {
|
||||
theContained.addContained(next.getReferenceElement(), potentialTarget);
|
||||
containResourcesForEncoding(theContained, potentialTarget, theTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IBaseReference next : allReferences) {
|
||||
IBaseResource resource = next.getResource();
|
||||
if (resource != null) {
|
||||
if (resource.getIdElement().isEmpty() || resource.getIdElement().isLocal()) {
|
||||
if (theContained.getResourceId(resource) != null) {
|
||||
// Prevent infinite recursion if there are circular loops in the contained resources
|
||||
continue;
|
||||
}
|
||||
theContained.addContained(resource);
|
||||
if (resource.getIdElement().isLocal() && theContained.hasExistingIdToContainedResource()) {
|
||||
theContained.getExistingIdToContainedResource().remove(resource.getIdElement().getValue());
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
containResourcesForEncoding(theContained, resource, theTarget);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void containResourcesForEncoding(IBaseResource theResource) {
|
||||
ContainedResources contained = new ContainedResources();
|
||||
|
||||
if (theResource instanceof IResource) {
|
||||
List<? extends IResource> containedResources = ((IResource) theResource).getContained().getContainedResources();
|
||||
for (IResource next : containedResources) {
|
||||
String nextId = next.getId().getValue();
|
||||
if (StringUtils.isNotBlank(nextId)) {
|
||||
if (!nextId.startsWith("#")) {
|
||||
nextId = '#' + nextId;
|
||||
}
|
||||
contained.getExistingIdToContainedResource().put(nextId, next);
|
||||
}
|
||||
}
|
||||
} else if (theResource instanceof IDomainResource) {
|
||||
List<? extends IAnyResource> containedResources = ((IDomainResource) theResource).getContained();
|
||||
for (IAnyResource next : containedResources) {
|
||||
String nextId = next.getIdElement().getValue();
|
||||
if (StringUtils.isNotBlank(nextId)) {
|
||||
if (!nextId.startsWith("#")) {
|
||||
nextId = '#' + nextId;
|
||||
}
|
||||
contained.getExistingIdToContainedResource().put(nextId, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
containResourcesForEncoding(contained, theResource, theResource);
|
||||
contained.assignIdsToContainedResources();
|
||||
myContainedResources = contained;
|
||||
|
||||
}
|
||||
|
||||
protected List<IBaseReference> getAllBaseReferences(IBaseResource theResource) {
|
||||
final ArrayList<IBaseReference> retVal = new ArrayList<IBaseReference>();
|
||||
findBaseReferences(retVal, theResource, myContext.getResourceDefinition(theResource));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* A customised traversal of the tree to find the 'top level' base references. Nested references are found via the recursive traversal
|
||||
* of contained resources.
|
||||
*/
|
||||
protected void findBaseReferences(List<IBaseReference> allElements, IBase theElement, BaseRuntimeElementDefinition<?> theDefinition) {
|
||||
if (theElement instanceof IBaseReference) {
|
||||
allElements.add((IBaseReference) theElement);
|
||||
}
|
||||
|
||||
BaseRuntimeElementDefinition<?> def = theDefinition;
|
||||
if (def.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) {
|
||||
def = myContext.getElementDefinition(theElement.getClass());
|
||||
}
|
||||
|
||||
switch (def.getChildType()) {
|
||||
case ID_DATATYPE:
|
||||
case PRIMITIVE_XHTML_HL7ORG:
|
||||
case PRIMITIVE_XHTML:
|
||||
case PRIMITIVE_DATATYPE:
|
||||
// These are primitive types
|
||||
break;
|
||||
case RESOURCE:
|
||||
case RESOURCE_BLOCK:
|
||||
case COMPOSITE_DATATYPE: {
|
||||
BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) def;
|
||||
for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
|
||||
|
||||
List<?> values = nextChild.getAccessor().getValues(theElement);
|
||||
if (values != null) {
|
||||
for (Object nextValueObject : values) {
|
||||
IBase nextValue;
|
||||
try {
|
||||
nextValue = (IBase) nextValueObject;
|
||||
} catch (ClassCastException e) {
|
||||
String s = "Found instance of " + nextValueObject.getClass() + " - Did you set a field value to the incorrect type? Expected " + IBase.class.getName();
|
||||
throw new ClassCastException(s);
|
||||
}
|
||||
if (nextValue == null) {
|
||||
continue;
|
||||
}
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
BaseRuntimeElementDefinition<?> childElementDef;
|
||||
childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
|
||||
|
||||
if (childElementDef == null) {
|
||||
childElementDef = myContext.getElementDefinition(nextValue.getClass());
|
||||
}
|
||||
|
||||
if (nextChild instanceof RuntimeChildDirectResource) {
|
||||
// Don't descend into embedded resources
|
||||
if (nextValue instanceof IBaseReference) {
|
||||
allElements.add((IBaseReference) nextValue);
|
||||
}
|
||||
} else {
|
||||
findBaseReferences(allElements, nextValue, childElementDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONTAINED_RESOURCES:
|
||||
// skip contained resources when looking for resources to contain
|
||||
break;
|
||||
case CONTAINED_RESOURCE_LIST:
|
||||
case EXTENSION_DECLARED:
|
||||
case UNDECL_EXT: {
|
||||
throw new IllegalStateException("state should not happen: " + def.getChildType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String determineReferenceText(IBaseReference theRef, CompositeChildElement theCompositeChildElement) {
|
||||
IIdType ref = theRef.getReferenceElement();
|
||||
|
@ -529,10 +378,14 @@ public abstract class BaseParser implements IParser {
|
|||
return elementId;
|
||||
}
|
||||
|
||||
ContainedResources getContainedResources() {
|
||||
FhirTerser.ContainedResources getContainedResources() {
|
||||
return myContainedResources;
|
||||
}
|
||||
|
||||
void setContainedResources(FhirTerser.ContainedResources theContainedResources) {
|
||||
myContainedResources = theContainedResources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDontStripVersionsFromReferencesAtPaths() {
|
||||
return myDontStripVersionsFromReferencesAtPaths;
|
||||
|
@ -1359,126 +1212,6 @@ public abstract class BaseParser implements IParser {
|
|||
}
|
||||
|
||||
|
||||
static class ContainedResources {
|
||||
private long myNextContainedId = 1;
|
||||
|
||||
private List<IBaseResource> myResourceList;
|
||||
private IdentityHashMap<IBaseResource, IIdType> myResourceToIdMap;
|
||||
private Map<String, IBaseResource> myExistingIdToContainedResourceMap;
|
||||
|
||||
public Map<String, IBaseResource> getExistingIdToContainedResource() {
|
||||
if (myExistingIdToContainedResourceMap == null) {
|
||||
myExistingIdToContainedResourceMap = new HashMap<>();
|
||||
}
|
||||
return myExistingIdToContainedResourceMap;
|
||||
}
|
||||
|
||||
public void addContained(IBaseResource theResource) {
|
||||
if (getResourceToIdMap().containsKey(theResource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IIdType newId;
|
||||
if (theResource.getIdElement().isLocal()) {
|
||||
newId = theResource.getIdElement();
|
||||
} else {
|
||||
newId = null;
|
||||
}
|
||||
|
||||
getResourceToIdMap().put(theResource, newId);
|
||||
getResourceList().add(theResource);
|
||||
}
|
||||
|
||||
public void addContained(IIdType theId, IBaseResource theResource) {
|
||||
if (!getResourceToIdMap().containsKey(theResource)) {
|
||||
getResourceToIdMap().put(theResource, theId);
|
||||
getResourceList().add(theResource);
|
||||
}
|
||||
}
|
||||
|
||||
public List<IBaseResource> getContainedResources() {
|
||||
if (getResourceToIdMap() == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return getResourceList();
|
||||
}
|
||||
|
||||
public IIdType getResourceId(IBaseResource theNext) {
|
||||
if (getResourceToIdMap() == null) {
|
||||
return null;
|
||||
}
|
||||
return getResourceToIdMap().get(theNext);
|
||||
}
|
||||
|
||||
private List<IBaseResource> getResourceList() {
|
||||
if (myResourceList == null) {
|
||||
myResourceList = new ArrayList<>();
|
||||
}
|
||||
return myResourceList;
|
||||
}
|
||||
|
||||
private IdentityHashMap<IBaseResource, IIdType> getResourceToIdMap() {
|
||||
if (myResourceToIdMap == null) {
|
||||
myResourceToIdMap = new IdentityHashMap<>();
|
||||
}
|
||||
return myResourceToIdMap;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
if (myResourceToIdMap == null) {
|
||||
return true;
|
||||
}
|
||||
return myResourceToIdMap.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasExistingIdToContainedResource() {
|
||||
return myExistingIdToContainedResourceMap != null;
|
||||
}
|
||||
|
||||
public void assignIdsToContainedResources() {
|
||||
|
||||
if (getResourceList() != null) {
|
||||
|
||||
/*
|
||||
* The idea with the code block below:
|
||||
*
|
||||
* We want to preserve any IDs that were user-assigned, so that if it's really
|
||||
* important to someone that their contained resource have the ID of #FOO
|
||||
* or #1 we will keep that.
|
||||
*
|
||||
* For any contained resources where no ID was assigned by the user, we
|
||||
* want to manually create an ID but make sure we don't reuse an existing ID.
|
||||
*/
|
||||
|
||||
Set<String> ids = new HashSet<>();
|
||||
|
||||
// Gather any user assigned IDs
|
||||
for (IBaseResource nextResource : getResourceList()) {
|
||||
if (getResourceToIdMap().get(nextResource) != null) {
|
||||
ids.add(getResourceToIdMap().get(nextResource).getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically assign IDs to the rest
|
||||
for (IBaseResource nextResource : getResourceList()) {
|
||||
|
||||
while (getResourceToIdMap().get(nextResource) == null) {
|
||||
String nextCandidate = "#" + myNextContainedId;
|
||||
myNextContainedId++;
|
||||
if (!ids.add(nextCandidate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
getResourceToIdMap().put(nextResource, new IdDt(nextCandidate));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected static <T> List<T> extractMetadataListNotNull(IResource resource, ResourceMetadataKeyEnum<List<T>> key) {
|
||||
List<? extends T> securityLabels = key.get(resource);
|
||||
if (securityLabels == null) {
|
||||
|
|
|
@ -45,6 +45,7 @@ import ca.uhn.fhir.parser.json.JsonLikeWriter;
|
|||
import ca.uhn.fhir.parser.json.jackson.JacksonStructure;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.util.ElementUtil;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.text.WordUtils;
|
||||
|
@ -74,7 +75,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParser.HeldExtension.class);
|
||||
|
||||
private FhirContext myContext;
|
||||
private boolean myPrettyPrint;
|
||||
|
||||
/**
|
||||
|
@ -85,7 +85,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
*/
|
||||
public JsonParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
|
||||
super(theContext, theParserErrorHandler);
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
private boolean addToHeldComments(int valueIdx, List<String> theCommentsToAdd, ArrayList<ArrayList<String>> theListToAddTo) {
|
||||
|
@ -177,7 +176,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
}
|
||||
theEventWriter.init();
|
||||
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
|
||||
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(theResource);
|
||||
encodeResourceToJsonStreamWriter(resDef, theResource, theEventWriter, null, false, theEncodeContext);
|
||||
theEventWriter.flush();
|
||||
}
|
||||
|
@ -209,7 +208,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
|
||||
String resourceType = resourceTypeObj.getAsString();
|
||||
|
||||
ParserState<? extends IBaseResource> state = ParserState.getPreResourceInstance(this, theResourceType, myContext, true, getErrorHandler());
|
||||
ParserState<? extends IBaseResource> state = ParserState.getPreResourceInstance(this, theResourceType, getContext(), true, getErrorHandler());
|
||||
state.enteringNewElement(null, resourceType);
|
||||
|
||||
parseChildren(object, state);
|
||||
|
@ -355,7 +354,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
}
|
||||
case RESOURCE:
|
||||
IBaseResource resource = (IBaseResource) theNextValue;
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(resource);
|
||||
|
||||
theEncodeContext.pushPath(def.getName(), true);
|
||||
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, theContainedResource, theEncodeContext);
|
||||
|
@ -387,14 +386,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
if (nextChildElem.getDef().getElementName().equals("extension") || nextChildElem.getDef().getElementName().equals("modifierExtension")
|
||||
|| nextChild instanceof RuntimeChildDeclaredExtensionDefinition) {
|
||||
if (!haveWrittenExtensions) {
|
||||
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, myContext.getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem, theParent, theEncodeContext, theContainedResource);
|
||||
extractAndWriteExtensionsAsDirectChild(theElement, theEventWriter, getContext().getElementDefinition(theElement.getClass()), theResDef, theResource, nextChildElem, theParent, theEncodeContext, theContainedResource);
|
||||
haveWrittenExtensions = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
||||
INarrativeGenerator gen = getContext().getNarrativeGenerator();
|
||||
if (gen != null) {
|
||||
INarrative narr;
|
||||
if (theResource instanceof IResource) {
|
||||
|
@ -405,7 +404,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
narr = null;
|
||||
}
|
||||
if (narr != null && narr.isEmpty()) {
|
||||
gen.populateResourceNarrative(myContext, theResource);
|
||||
gen.populateResourceNarrative(getContext(), theResource);
|
||||
if (!narr.isEmpty()) {
|
||||
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
|
||||
String childName = nextChild.getChildNameByDatatype(child.getDatatype());
|
||||
|
@ -619,13 +618,13 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
Validate.notNull(theResource, "theResource can not be null");
|
||||
Validate.notNull(theJsonLikeWriter, "theJsonLikeWriter can not be null");
|
||||
|
||||
if (theResource.getStructureFhirVersionEnum() != myContext.getVersion().getVersion()) {
|
||||
if (theResource.getStructureFhirVersionEnum() != getContext().getVersion().getVersion()) {
|
||||
throw new IllegalArgumentException(
|
||||
"This parser is for FHIR version " + myContext.getVersion().getVersion() + " - Can not encode a structure for version " + theResource.getStructureFhirVersionEnum());
|
||||
"This parser is for FHIR version " + getContext().getVersion().getVersion() + " - Can not encode a structure for version " + theResource.getStructureFhirVersionEnum());
|
||||
}
|
||||
|
||||
EncodeContext encodeContext = new EncodeContext();
|
||||
String resourceName = myContext.getResourceType(theResource);
|
||||
String resourceName = getContext().getResourceType(theResource);
|
||||
encodeContext.pushPath(resourceName, true);
|
||||
doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter, encodeContext);
|
||||
}
|
||||
|
@ -660,10 +659,10 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
}
|
||||
|
||||
if (!theContainedResource) {
|
||||
super.containResourcesForEncoding(theResource);
|
||||
setContainedResources(getContext().newTerser().containResources(theResource));
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
|
||||
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(theResource);
|
||||
|
||||
if (theObjectNameOrNull == null) {
|
||||
theEventWriter.beginObject();
|
||||
|
@ -888,8 +887,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
}
|
||||
|
||||
private boolean isEncodeExtension(CompositeChildElement theParent, EncodeContext theEncodeContext, boolean theContainedResource, IBase theElement) {
|
||||
// theEncodeContext.pushPath("extension", false);
|
||||
BaseRuntimeElementDefinition<?> runtimeElementDefinition = myContext.getElementDefinition(theElement.getClass());
|
||||
BaseRuntimeElementDefinition<?> runtimeElementDefinition = getContext().getElementDefinition(theElement.getClass());
|
||||
boolean retVal = true;
|
||||
if (runtimeElementDefinition instanceof BaseRuntimeElementCompositeDefinition) {
|
||||
BaseRuntimeElementCompositeDefinition definition = (BaseRuntimeElementCompositeDefinition) runtimeElementDefinition;
|
||||
|
@ -897,7 +895,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
CompositeChildElement c = new CompositeChildElement(theParent, childDef, theEncodeContext);
|
||||
retVal = c.shouldBeEncoded(theContainedResource);
|
||||
}
|
||||
// theEncodeContext.popPath();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -917,37 +914,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
return object.getAsArray();
|
||||
}
|
||||
|
||||
// private JsonObject parse(Reader theReader) {
|
||||
//
|
||||
// PushbackReader pbr = new PushbackReader(theReader);
|
||||
// JsonObject object;
|
||||
// try {
|
||||
// while(true) {
|
||||
// int nextInt;
|
||||
// nextInt = pbr.read();
|
||||
// if (nextInt == -1) {
|
||||
// throw new DataFormatException("Did not find any content to parse");
|
||||
// }
|
||||
// if (nextInt == '{') {
|
||||
// pbr.unread('{');
|
||||
// break;
|
||||
// }
|
||||
// if (Character.isWhitespace(nextInt)) {
|
||||
// continue;
|
||||
// }
|
||||
// throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char)nextInt + "' (must be '{')");
|
||||
// }
|
||||
//
|
||||
// Gson gson = newGson();
|
||||
//
|
||||
// object = gson.fromJson(pbr, JsonObject.class);
|
||||
// } catch (Exception e) {
|
||||
// throw new DataFormatException("Failed to parse JSON content, error was: " + e.getMessage(), e);
|
||||
// }
|
||||
//
|
||||
// return object;
|
||||
// }
|
||||
|
||||
private void parseAlternates(JsonLikeValue theAlternateVal, ParserState<?> theState, String theElementName, String theAlternateName) {
|
||||
if (theAlternateVal == null || theAlternateVal.isNull()) {
|
||||
return;
|
||||
|
@ -1234,13 +1200,13 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
* the correct FHIR version
|
||||
*/
|
||||
if (theResourceType != null) {
|
||||
myContext.getResourceDefinition(theResourceType);
|
||||
getContext().getResourceDefinition(theResourceType);
|
||||
}
|
||||
|
||||
// Actually do the parse
|
||||
T retVal = doParseResource(theResourceType, theJsonLikeStructure);
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(retVal);
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(retVal);
|
||||
if ("Bundle".equals(def.getName())) {
|
||||
|
||||
BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
|
||||
|
@ -1553,10 +1519,10 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
*/
|
||||
value = preProcessValues(myDef, theResource, Collections.singletonList(value), myChildElem, theEncodeContext).get(0);
|
||||
|
||||
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
|
||||
RuntimeChildUndeclaredExtensionDefinition extDef = getContext().getRuntimeChildUndeclaredExtensionDefinition();
|
||||
String childName = extDef.getChildNameByDatatype(value.getClass());
|
||||
if (childName == null) {
|
||||
childName = "value" + WordUtils.capitalize(myContext.getElementDefinition(value.getClass()).getName());
|
||||
childName = "value" + WordUtils.capitalize(getContext().getElementDefinition(value.getClass()).getName());
|
||||
}
|
||||
BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
|
||||
if (childDef == null) {
|
||||
|
|
|
@ -1149,7 +1149,7 @@ class ParserState<T> {
|
|||
for (IBaseReference nextRef : myLocalReferences) {
|
||||
String ref = nextRef.getReferenceElement().getValue();
|
||||
if (isNotBlank(ref)) {
|
||||
if (ref.startsWith("#")) {
|
||||
if (ref.startsWith("#") && ref.length() > 1) {
|
||||
IBaseResource target = myContainedResources.get(ref);
|
||||
if (target != null) {
|
||||
ourLog.debug("Resource contains local ref {}", ref);
|
||||
|
@ -1187,12 +1187,6 @@ class ParserState<T> {
|
|||
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void enteringNewElement(String theNamespaceUri, String theLocalPart) throws DataFormatException {
|
||||
// super.enteringNewElement(theNamespaceUri, theLocalPart);
|
||||
// populateTarget();
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected void populateTarget() {
|
||||
weaveContainedResources();
|
||||
|
|
|
@ -73,7 +73,6 @@ public class RDFParser extends BaseParser {
|
|||
public static final String MODIFIER_EXTENSION = "modifierExtension";
|
||||
private final Map<Class, String> classToFhirTypeMap = new HashMap<>();
|
||||
|
||||
private final FhirContext context;
|
||||
private final Lang lang;
|
||||
|
||||
/**
|
||||
|
@ -84,7 +83,6 @@ public class RDFParser extends BaseParser {
|
|||
*/
|
||||
public RDFParser(final FhirContext context, final IParserErrorHandler parserErrorHandler, final Lang lang) {
|
||||
super(context, parserErrorHandler);
|
||||
this.context = context;
|
||||
this.lang = lang;
|
||||
}
|
||||
|
||||
|
@ -148,13 +146,13 @@ public class RDFParser extends BaseParser {
|
|||
final EncodeContext encodeContext,
|
||||
final boolean rootResource, Resource parentResource) {
|
||||
|
||||
RuntimeResourceDefinition resDef = this.context.getResourceDefinition(resource);
|
||||
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(resource);
|
||||
if (resDef == null) {
|
||||
throw new ConfigurationException("Unknown resource type: " + resource.getClass());
|
||||
}
|
||||
|
||||
if (!containedResource) {
|
||||
super.containResourcesForEncoding(resource);
|
||||
setContainedResources(getContext().newTerser().containResources(resource));
|
||||
}
|
||||
|
||||
if (!(resource instanceof IAnyResource)) {
|
||||
|
@ -323,7 +321,7 @@ public class RDFParser extends BaseParser {
|
|||
if (hasExtension.getExtension() != null && hasExtension.getExtension().size() > 0) {
|
||||
int i = 0;
|
||||
for (IBaseExtension extension : hasExtension.getExtension()) {
|
||||
RuntimeResourceDefinition resDef = this.context.getResourceDefinition(resource);
|
||||
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(resource);
|
||||
Resource extensionResource = rdfModel.createResource();
|
||||
extensionResource.addProperty(rdfModel.createProperty(FHIR_NS+FHIR_INDEX), rdfModel.createTypedLiteral(i, XSDDatatype.XSDinteger));
|
||||
valueResource.addProperty(rdfModel.createProperty(FHIR_NS + ELEMENT_EXTENSION), extensionResource);
|
||||
|
@ -375,7 +373,7 @@ public class RDFParser extends BaseParser {
|
|||
}
|
||||
case RESOURCE: {
|
||||
IBaseResource baseResource = (IBaseResource) element;
|
||||
String resourceName = this.context.getResourceType(baseResource);
|
||||
String resourceName = getContext().getResourceType(baseResource);
|
||||
if (!super.shouldEncodeResource(resourceName)) {
|
||||
break;
|
||||
}
|
||||
|
@ -486,7 +484,7 @@ public class RDFParser extends BaseParser {
|
|||
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
|
||||
|
||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||
INarrativeGenerator gen = this.context.getNarrativeGenerator();
|
||||
INarrativeGenerator gen = getContext().getNarrativeGenerator();
|
||||
if (gen != null) {
|
||||
INarrative narrative;
|
||||
if (resource instanceof IResource) {
|
||||
|
@ -498,7 +496,7 @@ public class RDFParser extends BaseParser {
|
|||
}
|
||||
assert narrative != null;
|
||||
if (narrative.isEmpty()) {
|
||||
gen.populateResourceNarrative(this.context, resource);
|
||||
gen.populateResourceNarrative(getContext(), resource);
|
||||
}
|
||||
else {
|
||||
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
|
||||
|
@ -619,7 +617,7 @@ public class RDFParser extends BaseParser {
|
|||
private <T extends IBaseResource> T parseResource(Class<T> resourceType, Model rdfModel) {
|
||||
// jsonMode of true is passed in so that the xhtml parser state behaves as expected
|
||||
// Push PreResourceState
|
||||
ParserState<T> parserState = ParserState.getPreResourceInstance(this, resourceType, context, true, getErrorHandler());
|
||||
ParserState<T> parserState = ParserState.getPreResourceInstance(this, resourceType, getContext(), true, getErrorHandler());
|
||||
return parseRootResource(rdfModel, parserState, resourceType);
|
||||
}
|
||||
|
||||
|
@ -646,7 +644,7 @@ public class RDFParser extends BaseParser {
|
|||
fhirTypeString = resourceType.getSimpleName();
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition definition = this.context.getResourceDefinition(fhirTypeString);
|
||||
RuntimeResourceDefinition definition = getContext().getResourceDefinition(fhirTypeString);
|
||||
fhirResourceType = definition.getName();
|
||||
|
||||
parseResource(parserState, fhirResourceType, rootResource);
|
||||
|
@ -777,7 +775,7 @@ public class RDFParser extends BaseParser {
|
|||
String propertyUri = statement.getPredicate().getURI();
|
||||
if (propertyUri.contains("Extension.value")) {
|
||||
extensionValueType = propertyUri.replace(FHIR_NS + "Extension.", "");
|
||||
BaseRuntimeElementDefinition<?> target = context.getRuntimeChildUndeclaredExtensionDefinition().getChildByName(extensionValueType);
|
||||
BaseRuntimeElementDefinition<?> target = getContext().getRuntimeChildUndeclaredExtensionDefinition().getChildByName(extensionValueType);
|
||||
if (target.getChildType().equals(ID_DATATYPE) || target.getChildType().equals(PRIMITIVE_DATATYPE)) {
|
||||
extensionValueResource = statement.getObject().asResource().getProperty(resource.getModel().createProperty(FHIR_NS+VALUE)).getObject().asLiteral();
|
||||
} else {
|
||||
|
|
|
@ -56,9 +56,6 @@ public class XmlParser extends BaseParser {
|
|||
|
||||
static final String FHIR_NS = "http://hl7.org/fhir";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParser.class);
|
||||
|
||||
// private static final Set<String> RESOURCE_NAMESPACES;
|
||||
private FhirContext myContext;
|
||||
private boolean myPrettyPrint;
|
||||
|
||||
/**
|
||||
|
@ -69,7 +66,6 @@ public class XmlParser extends BaseParser {
|
|||
*/
|
||||
public XmlParser(FhirContext theContext, IParserErrorHandler theParserErrorHandler) {
|
||||
super(theContext, theParserErrorHandler);
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
private XMLEventReader createStreamReader(Reader theReader) {
|
||||
|
@ -295,7 +291,7 @@ public class XmlParser extends BaseParser {
|
|||
}
|
||||
case RESOURCE: {
|
||||
IBaseResource resource = (IBaseResource) theElement;
|
||||
String resourceName = myContext.getResourceType(resource);
|
||||
String resourceName = getContext().getResourceType(resource);
|
||||
if (!super.shouldEncodeResource(resourceName)) {
|
||||
break;
|
||||
}
|
||||
|
@ -354,9 +350,9 @@ public class XmlParser extends BaseParser {
|
|||
|
||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||
Optional<IBase> narr = nextChild.getAccessor().getFirstValueOrNull(theElement);
|
||||
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
||||
INarrativeGenerator gen = getContext().getNarrativeGenerator();
|
||||
if (gen != null && narr.isPresent() == false) {
|
||||
gen.populateResourceNarrative(myContext, theResource);
|
||||
gen.populateResourceNarrative(getContext(), theResource);
|
||||
}
|
||||
|
||||
narr = nextChild.getAccessor().getFirstValueOrNull(theElement);
|
||||
|
@ -490,13 +486,13 @@ public class XmlParser extends BaseParser {
|
|||
}
|
||||
|
||||
private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theContainedResource, IIdType theResourceId, EncodeContext theEncodeContext) throws XMLStreamException {
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
|
||||
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(theResource);
|
||||
if (resDef == null) {
|
||||
throw new ConfigurationException("Unknown resource type: " + theResource.getClass());
|
||||
}
|
||||
|
||||
if (!theContainedResource) {
|
||||
super.containResourcesForEncoding(theResource);
|
||||
setContainedResources(getContext().newTerser().containResources(theResource));
|
||||
}
|
||||
|
||||
theEventWriter.writeStartElement(resDef.getName());
|
||||
|
@ -615,11 +611,11 @@ public class XmlParser extends BaseParser {
|
|||
|
||||
if (next.getValue() != null) {
|
||||
IBaseDatatype value = next.getValue();
|
||||
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
|
||||
RuntimeChildUndeclaredExtensionDefinition extDef = getContext().getRuntimeChildUndeclaredExtensionDefinition();
|
||||
String childName = extDef.getChildNameByDatatype(value.getClass());
|
||||
BaseRuntimeElementDefinition<?> childDef;
|
||||
if (childName == null) {
|
||||
childDef = myContext.getElementDefinition(value.getClass());
|
||||
childDef = getContext().getElementDefinition(value.getClass());
|
||||
if (childDef == null) {
|
||||
throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName());
|
||||
}
|
||||
|
@ -749,7 +745,7 @@ public class XmlParser extends BaseParser {
|
|||
}
|
||||
|
||||
private <T extends IBaseResource> T parseResource(Class<T> theResourceType, XMLEventReader theStreamReader) {
|
||||
ParserState<T> parserState = ParserState.getPreResourceInstance(this, theResourceType, myContext, false, getErrorHandler());
|
||||
ParserState<T> parserState = ParserState.getPreResourceInstance(this, theResourceType, getContext(), false, getErrorHandler());
|
||||
return doXmlLoop(theStreamReader, parserState);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.api;
|
|||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR Search Parameters
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
|
|
|
@ -13,14 +13,15 @@ import ca.uhn.fhir.context.RuntimeExtensionDtDefinition;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IElement;
|
||||
import ca.uhn.fhir.model.api.IIdentifiableElement;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
||||
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseElement;
|
||||
|
@ -29,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
|||
import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IDomainResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
|
@ -36,10 +38,14 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -47,6 +53,7 @@ import java.util.stream.Collectors;
|
|||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.substring;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -71,7 +78,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
public class FhirTerser {
|
||||
|
||||
private static final Pattern COMPARTMENT_MATCHER_PATH = Pattern.compile("([a-zA-Z.]+)\\.where\\(resolve\\(\\) is ([a-zA-Z]+)\\)");
|
||||
private FhirContext myContext;
|
||||
private static final EnumSet<OptionsEnum> EMPTY_OPTION_SET = EnumSet.noneOf(OptionsEnum.class);
|
||||
private static final String USER_DATA_KEY_CONTAIN_RESOURCES_COMPLETED = FhirTerser.class.getName() + "_CONTAIN_RESOURCES_COMPLETED";
|
||||
private final FhirContext myContext;
|
||||
|
||||
public FhirTerser(FhirContext theContext) {
|
||||
super();
|
||||
|
@ -312,7 +321,6 @@ public class FhirTerser {
|
|||
return Optional.ofNullable(getSingleValueOrNull(theTarget, thePath, theWantedType));
|
||||
}
|
||||
|
||||
|
||||
private <T extends IBase> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, IBase theCurrentObj, List<String> theSubList, Class<T> theWantedClass) {
|
||||
return getValues(theCurrentDef, theCurrentObj, theSubList, theWantedClass, false, false);
|
||||
}
|
||||
|
@ -1058,4 +1066,251 @@ public class FhirTerser {
|
|||
});
|
||||
}
|
||||
|
||||
private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, EnumSet<OptionsEnum> theOptions) {
|
||||
List<IBaseReference> allReferences = getAllPopulatedChildElementsOfType(theResource, IBaseReference.class);
|
||||
for (IBaseReference next : allReferences) {
|
||||
IBaseResource resource = next.getResource();
|
||||
if (resource == null && next.getReferenceElement().isLocal()) {
|
||||
if (theContained.hasExistingIdToContainedResource()) {
|
||||
IBaseResource potentialTarget = theContained.getExistingIdToContainedResource().remove(next.getReferenceElement().getValue());
|
||||
if (potentialTarget != null) {
|
||||
theContained.addContained(next.getReferenceElement(), potentialTarget);
|
||||
containResourcesForEncoding(theContained, potentialTarget, theOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IBaseReference next : allReferences) {
|
||||
IBaseResource resource = next.getResource();
|
||||
if (resource != null) {
|
||||
if (resource.getIdElement().isEmpty() || resource.getIdElement().isLocal()) {
|
||||
if (theContained.getResourceId(resource) != null) {
|
||||
// Prevent infinite recursion if there are circular loops in the contained resources
|
||||
continue;
|
||||
}
|
||||
IIdType id = theContained.addContained(resource);
|
||||
if (theOptions.contains(OptionsEnum.MODIFY_RESOURCE)) {
|
||||
getContainedResourceList(theResource).add(resource);
|
||||
next.setReference(id.getValue());
|
||||
}
|
||||
if (resource.getIdElement().isLocal() && theContained.hasExistingIdToContainedResource()) {
|
||||
theContained.getExistingIdToContainedResource().remove(resource.getIdElement().getValue());
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through the whole resource and identify any contained resources. Optionally this method
|
||||
* can also assign IDs and modify references where the resource link has been specified but not the
|
||||
* reference text.
|
||||
*
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public ContainedResources containResources(IBaseResource theResource, OptionsEnum... theOptions) {
|
||||
EnumSet<OptionsEnum> options = toOptionSet(theOptions);
|
||||
|
||||
if (options.contains(OptionsEnum.STORE_AND_REUSE_RESULTS)) {
|
||||
Object cachedValue = theResource.getUserData(USER_DATA_KEY_CONTAIN_RESOURCES_COMPLETED);
|
||||
if (cachedValue != null) {
|
||||
return (ContainedResources) cachedValue;
|
||||
}
|
||||
}
|
||||
|
||||
ContainedResources contained = new ContainedResources();
|
||||
|
||||
List<? extends IBaseResource> containedResources = getContainedResourceList(theResource);
|
||||
for (IBaseResource next : containedResources) {
|
||||
String nextId = next.getIdElement().getValue();
|
||||
if (StringUtils.isNotBlank(nextId)) {
|
||||
if (!nextId.startsWith("#")) {
|
||||
nextId = '#' + nextId;
|
||||
}
|
||||
next.getIdElement().setValue(nextId);
|
||||
}
|
||||
contained.addContained(next);
|
||||
}
|
||||
|
||||
containResourcesForEncoding(contained, theResource, options);
|
||||
|
||||
if (options.contains(OptionsEnum.STORE_AND_REUSE_RESULTS)) {
|
||||
theResource.setUserData(USER_DATA_KEY_CONTAIN_RESOURCES_COMPLETED, contained);
|
||||
}
|
||||
|
||||
return contained;
|
||||
}
|
||||
|
||||
private EnumSet<OptionsEnum> toOptionSet(OptionsEnum[] theOptions) {
|
||||
EnumSet<OptionsEnum> options;
|
||||
if (theOptions == null || theOptions.length == 0) {
|
||||
options = EMPTY_OPTION_SET;
|
||||
} else {
|
||||
options = EnumSet.of(theOptions[0], theOptions);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends IBaseResource> List<T> getContainedResourceList(T theResource) {
|
||||
List<T> containedResources = Collections.emptyList();
|
||||
if (theResource instanceof IResource) {
|
||||
containedResources = (List<T>) ((IResource) theResource).getContained().getContainedResources();
|
||||
} else if (theResource instanceof IDomainResource) {
|
||||
containedResources = (List<T>) ((IDomainResource) theResource).getContained();
|
||||
}
|
||||
return containedResources;
|
||||
}
|
||||
|
||||
|
||||
public enum OptionsEnum {
|
||||
|
||||
/**
|
||||
* Should we modify the resource in the case that contained resource IDs are assigned
|
||||
* during a {@link #containResources(IBaseResource, OptionsEnum...)} pass.
|
||||
*/
|
||||
MODIFY_RESOURCE,
|
||||
|
||||
/**
|
||||
* Store the results of the operation in the resource metadata and reuse them if
|
||||
* subsequent calls are made.
|
||||
*/
|
||||
STORE_AND_REUSE_RESULTS
|
||||
}
|
||||
|
||||
public static class ContainedResources {
|
||||
private long myNextContainedId = 1;
|
||||
|
||||
private List<IBaseResource> myResourceList;
|
||||
private IdentityHashMap<IBaseResource, IIdType> myResourceToIdMap;
|
||||
private Map<String, IBaseResource> myExistingIdToContainedResourceMap;
|
||||
|
||||
public Map<String, IBaseResource> getExistingIdToContainedResource() {
|
||||
if (myExistingIdToContainedResourceMap == null) {
|
||||
myExistingIdToContainedResourceMap = new HashMap<>();
|
||||
}
|
||||
return myExistingIdToContainedResourceMap;
|
||||
}
|
||||
|
||||
public IIdType addContained(IBaseResource theResource) {
|
||||
IIdType existing = getResourceToIdMap().get(theResource);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
IIdType newId = theResource.getIdElement();
|
||||
if (isBlank(newId.getValue())) {
|
||||
newId.setValue("#" + myNextContainedId++);
|
||||
} else {
|
||||
// Avoid auto-assigned contained IDs colliding with pre-existing ones
|
||||
String idPart = newId.getValue();
|
||||
if (substring(idPart, 0, 1).equals("#")) {
|
||||
idPart = idPart.substring(1);
|
||||
if (StringUtils.isNumeric(idPart)) {
|
||||
myNextContainedId = Long.parseLong(idPart) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getResourceToIdMap().put(theResource, newId);
|
||||
getOrCreateResourceList().add(theResource);
|
||||
return newId;
|
||||
}
|
||||
|
||||
public void addContained(IIdType theId, IBaseResource theResource) {
|
||||
if (!getResourceToIdMap().containsKey(theResource)) {
|
||||
getResourceToIdMap().put(theResource, theId);
|
||||
getOrCreateResourceList().add(theResource);
|
||||
}
|
||||
}
|
||||
|
||||
public List<IBaseResource> getContainedResources() {
|
||||
if (getResourceToIdMap() == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return getOrCreateResourceList();
|
||||
}
|
||||
|
||||
public IIdType getResourceId(IBaseResource theNext) {
|
||||
if (getResourceToIdMap() == null) {
|
||||
return null;
|
||||
}
|
||||
return getResourceToIdMap().get(theNext);
|
||||
}
|
||||
|
||||
private List<IBaseResource> getOrCreateResourceList() {
|
||||
if (myResourceList == null) {
|
||||
myResourceList = new ArrayList<>();
|
||||
}
|
||||
return myResourceList;
|
||||
}
|
||||
|
||||
private IdentityHashMap<IBaseResource, IIdType> getResourceToIdMap() {
|
||||
if (myResourceToIdMap == null) {
|
||||
myResourceToIdMap = new IdentityHashMap<>();
|
||||
}
|
||||
return myResourceToIdMap;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
if (myResourceToIdMap == null) {
|
||||
return true;
|
||||
}
|
||||
return myResourceToIdMap.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasExistingIdToContainedResource() {
|
||||
return myExistingIdToContainedResourceMap != null;
|
||||
}
|
||||
|
||||
public void assignIdsToContainedResources() {
|
||||
|
||||
if (!getContainedResources().isEmpty()) {
|
||||
|
||||
/*
|
||||
* The idea with the code block below:
|
||||
*
|
||||
* We want to preserve any IDs that were user-assigned, so that if it's really
|
||||
* important to someone that their contained resource have the ID of #FOO
|
||||
* or #1 we will keep that.
|
||||
*
|
||||
* For any contained resources where no ID was assigned by the user, we
|
||||
* want to manually create an ID but make sure we don't reuse an existing ID.
|
||||
*/
|
||||
|
||||
Set<String> ids = new HashSet<>();
|
||||
|
||||
// Gather any user assigned IDs
|
||||
for (IBaseResource nextResource : getContainedResources()) {
|
||||
if (getResourceToIdMap().get(nextResource) != null) {
|
||||
ids.add(getResourceToIdMap().get(nextResource).getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically assign IDs to the rest
|
||||
for (IBaseResource nextResource : getContainedResources()) {
|
||||
|
||||
while (getResourceToIdMap().get(nextResource) == null) {
|
||||
String nextCandidate = "#" + myNextContainedId;
|
||||
myNextContainedId++;
|
||||
if (!ids.add(nextCandidate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
getResourceToIdMap().put(nextResource, new IdDt(nextCandidate));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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."
|
|
@ -1,5 +1,25 @@
|
|||
package ca.uhn.fhir.jpa.bulk.api;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.util.Date;
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
package ca.uhn.fhir.jpa.bulk.job;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
package ca.uhn.fhir.jpa.bulk.job;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
public class GroupBulkExportJobParametersBuilder extends BulkExportJobParametersBuilder {
|
||||
public GroupBulkExportJobParametersBuilder setGroupId(String theGroupId) {
|
||||
this.addString(BulkExportJobConfig.GROUP_ID_PARAMETER, theGroupId);
|
||||
|
|
|
@ -11,7 +11,6 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
|
|||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IDao;
|
||||
|
@ -65,7 +64,6 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
|||
import ca.uhn.fhir.model.api.Tag;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
||||
|
@ -237,6 +235,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
private IPartitionLookupSvc myPartitionLookupSvc;
|
||||
@Autowired
|
||||
private MemoryCacheService myMemoryCacheService;
|
||||
@Autowired(required = false)
|
||||
private IFulltextSearchSvc myFulltextSearchSvc;
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamPresenceSvc(ISearchParamPresenceSvc theSearchParamPresenceSvc) {
|
||||
mySearchParamPresenceSvc = theSearchParamPresenceSvc;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IInterceptorBroadcaster getInterceptorBroadcaster() {
|
||||
|
@ -492,41 +497,37 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
/**
|
||||
* Returns true if the resource has changed (either the contents or the tags)
|
||||
*/
|
||||
protected EncodedResource populateResourceIntoEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, boolean theUpdateHash) {
|
||||
protected EncodedResource populateResourceIntoEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, boolean thePerformIndexing) {
|
||||
if (theEntity.getResourceType() == null) {
|
||||
theEntity.setResourceType(toResourceName(theResource));
|
||||
}
|
||||
|
||||
if (theResource != null) {
|
||||
List<BaseResourceReferenceDt> refs = myContext.newTerser().getAllPopulatedChildElementsOfType(theResource, BaseResourceReferenceDt.class);
|
||||
for (BaseResourceReferenceDt nextRef : refs) {
|
||||
if (nextRef.getReference().isEmpty() == false) {
|
||||
if (nextRef.getReference().hasVersionIdPart()) {
|
||||
nextRef.setReference(nextRef.getReference().toUnqualifiedVersionless());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes;
|
||||
ResourceEncodingEnum encoding;
|
||||
boolean changed = false;
|
||||
|
||||
if (theEntity.getDeleted() == null) {
|
||||
|
||||
encoding = myConfig.getResourceEncoding();
|
||||
Set<String> excludeElements = ResourceMetaParams.EXCLUDE_ELEMENTS_IN_ENCODED;
|
||||
theEntity.setFhirVersion(myContext.getVersion().getVersion());
|
||||
if (thePerformIndexing) {
|
||||
|
||||
bytes = encodeResource(theResource, encoding, excludeElements, myContext);
|
||||
encoding = myConfig.getResourceEncoding();
|
||||
Set<String> excludeElements = ResourceMetaParams.EXCLUDE_ELEMENTS_IN_ENCODED;
|
||||
theEntity.setFhirVersion(myContext.getVersion().getVersion());
|
||||
|
||||
bytes = encodeResource(theResource, encoding, excludeElements, myContext);
|
||||
|
||||
if (theUpdateHash) {
|
||||
HashFunction sha256 = Hashing.sha256();
|
||||
String hashSha256 = sha256.hashBytes(bytes).toString();
|
||||
if (hashSha256.equals(theEntity.getHashSha256()) == false) {
|
||||
changed = true;
|
||||
}
|
||||
theEntity.setHashSha256(hashSha256);
|
||||
|
||||
} else {
|
||||
|
||||
encoding = null;
|
||||
bytes = null;
|
||||
|
||||
}
|
||||
|
||||
Set<ResourceTag> allDefs = new HashSet<>();
|
||||
|
@ -543,11 +544,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
String profile = def.getResourceProfile("");
|
||||
if (isNotBlank(profile)) {
|
||||
TagDefinition profileDef = getTagOrNull(TagTypeEnum.PROFILE, NS_JPA_PROFILE, profile, null);
|
||||
if (def != null) {
|
||||
ResourceTag tag = theEntity.addTag(profileDef);
|
||||
allDefs.add(tag);
|
||||
theEntity.setHasTags(true);
|
||||
}
|
||||
|
||||
ResourceTag tag = theEntity.addTag(profileDef);
|
||||
allDefs.add(tag);
|
||||
theEntity.setHasTags(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -580,7 +580,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
encoding = ResourceEncodingEnum.DEL;
|
||||
}
|
||||
|
||||
if (changed == false) {
|
||||
if (thePerformIndexing && changed == false) {
|
||||
if (theEntity.getId() == null) {
|
||||
changed = true;
|
||||
} else {
|
||||
|
@ -988,6 +988,26 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
return updateEntity(theRequest, null, entity, updateTime, true, true, theTransactionDetails, false, true);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setEntityManager(EntityManager theEntityManager) {
|
||||
myEntityManager = theEntityManager;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamWithInlineReferencesExtractor(SearchParamWithInlineReferencesExtractor theSearchParamWithInlineReferencesExtractor) {
|
||||
mySearchParamWithInlineReferencesExtractor = theSearchParamWithInlineReferencesExtractor;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setResourceHistoryTableDao(IResourceHistoryTableDao theResourceHistoryTableDao) {
|
||||
myResourceHistoryTableDao = theResourceHistoryTableDao;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setDaoSearchParamSynchronizer(DaoSearchParamSynchronizer theDaoSearchParamSynchronizer) {
|
||||
myDaoSearchParamSynchronizer = theDaoSearchParamSynchronizer;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public ResourceTable updateEntity(RequestDetails theRequest, final IBaseResource theResource, IBasePersistedResource
|
||||
|
@ -1059,7 +1079,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
newParams.populateResourceTableSearchParamsPresentFlags(entity);
|
||||
entity.setIndexStatus(INDEX_STATUS_INDEXED);
|
||||
}
|
||||
populateFullTextFields(myContext, theResource, entity);
|
||||
|
||||
if (myFulltextSearchSvc != null && !myFulltextSearchSvc.isDisabled()) {
|
||||
populateFullTextFields(myContext, theResource, entity);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
changed = populateResourceIntoEntity(theRequest, theResource, entity, false);
|
||||
|
@ -1071,7 +1095,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
|
||||
}
|
||||
|
||||
if (!changed.isChanged() && !theForceUpdate && myConfig.isSuppressUpdatesWithNoChange()) {
|
||||
if (thePerformIndexing && changed != null && !changed.isChanged() && !theForceUpdate && myConfig.isSuppressUpdatesWithNoChange()) {
|
||||
ourLog.debug("Resource {} has not changed", entity.getIdDt().toUnqualified().getValue());
|
||||
if (theResource != null) {
|
||||
updateResourceMetadata(entity, theResource);
|
||||
|
@ -1323,7 +1347,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
}
|
||||
}
|
||||
|
||||
private void validateChildReferences(IBase theElement, String thePath) {
|
||||
private void validateChildReferenceTargetTypes(IBase theElement, String thePath) {
|
||||
if (theElement == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -1343,7 +1367,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
String newPath = thePath + "." + nextChildDef.getElementName();
|
||||
|
||||
for (IBase nextChild : values) {
|
||||
validateChildReferences(nextChild, newPath);
|
||||
validateChildReferenceTargetTypes(nextChild, newPath);
|
||||
}
|
||||
|
||||
if (nextChildDef instanceof RuntimeChildResourceDefinition) {
|
||||
|
@ -1426,8 +1450,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
throw new UnprocessableEntityException("Resource contains the 'subsetted' tag, and must not be stored as it may contain a subset of available data");
|
||||
}
|
||||
|
||||
String resName = getContext().getResourceType(theResource);
|
||||
validateChildReferences(theResource, resName);
|
||||
if (getConfig().isEnforceReferenceTargetTypes()) {
|
||||
String resName = getContext().getResourceType(theResource);
|
||||
validateChildReferenceTargetTypes(theResource, resName);
|
||||
}
|
||||
|
||||
validateMetaCount(totalMetaCount);
|
||||
|
||||
|
|
|
@ -97,6 +97,7 @@ import ca.uhn.fhir.validation.IValidationContext;
|
|||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import ca.uhn.fhir.validation.ValidationOptions;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.text.WordUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
|
@ -222,11 +223,21 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
return create(theResource, theIfNoneExist, true, new TransactionDetails(), theRequestDetails);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setTransactionService(HapiTransactionService theTransactionService) {
|
||||
myTransactionService = theTransactionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, @Nonnull TransactionDetails theTransactionDetails, RequestDetails theRequestDetails) {
|
||||
return myTransactionService.execute(theRequestDetails, tx -> doCreateForPost(theResource, theIfNoneExist, thePerformIndexing, theTransactionDetails, theRequestDetails));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setRequestPartitionHelperService(IRequestPartitionHelperSvc theRequestPartitionHelperService) {
|
||||
myRequestPartitionHelperService = theRequestPartitionHelperService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for FHIR create (POST) operations
|
||||
*/
|
||||
|
@ -1709,6 +1720,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setDaoConfig(DaoConfig theDaoConfig) {
|
||||
myDaoConfig = theDaoConfig;
|
||||
}
|
||||
|
||||
private static class IdChecker implements IValidatorModule {
|
||||
|
||||
private final ValidationModeEnum myMode;
|
||||
|
|
|
@ -54,6 +54,7 @@ import ca.uhn.fhir.util.BundleUtil;
|
|||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
|
@ -89,6 +90,11 @@ public abstract class BaseStorageDao {
|
|||
@Autowired
|
||||
protected ModelConfig myModelConfig;
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
|
||||
mySearchParamRegistry = theSearchParamRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* May be overridden by subclasses to validate resources prior to storage
|
||||
*
|
||||
|
@ -176,7 +182,7 @@ public abstract class BaseStorageDao {
|
|||
|
||||
/**
|
||||
* Handle {@link ModelConfig#getAutoVersionReferenceAtPaths() auto-populate-versions}
|
||||
*
|
||||
* <p>
|
||||
* We only do this if thePerformIndexing is true because if it's false, that means
|
||||
* we're in a FHIR transaction during the first phase of write operation processing,
|
||||
* meaning that the versions of other resources may not have need updated yet. For example
|
||||
|
@ -184,7 +190,7 @@ public abstract class BaseStorageDao {
|
|||
* is also being updated in the same transaction, during the first "no index" phase,
|
||||
* the Patient will not yet have its version number incremented, so it would be wrong
|
||||
* to use that value. During the second phase it is correct.
|
||||
*
|
||||
* <p>
|
||||
* Also note that {@link BaseTransactionProcessor} also has code to do auto-versioning
|
||||
* and it is the one that takes care of the placeholder IDs. Look for the other caller of
|
||||
* {@link #extractReferencesToAutoVersion(FhirContext, ModelConfig, IBaseResource)}
|
||||
|
|
|
@ -69,6 +69,7 @@ import ca.uhn.fhir.util.FhirTerser;
|
|||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
@ -278,6 +279,16 @@ public abstract class BaseTransactionProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setVersionAdapter(ITransactionProcessorVersionAdapter theVersionAdapter) {
|
||||
myVersionAdapter = theVersionAdapter;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setTxManager(PlatformTransactionManager theTxManager) {
|
||||
myTxManager = theTxManager;
|
||||
}
|
||||
|
||||
private IBaseBundle batch(final RequestDetails theRequestDetails, IBaseBundle theRequest) {
|
||||
ourLog.info("Beginning batch with {} resources", myVersionAdapter.getEntries(theRequest).size());
|
||||
long start = System.currentTimeMillis();
|
||||
|
@ -335,6 +346,11 @@ public abstract class BaseTransactionProcessor {
|
|||
return resp;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setHapiTransactionService(HapiTransactionService theHapiTransactionService) {
|
||||
myHapiTransactionService = theHapiTransactionService;
|
||||
}
|
||||
|
||||
private IBaseBundle processTransaction(final RequestDetails theRequestDetails, final IBaseBundle theRequest, final String theActionName) {
|
||||
validateDependencies();
|
||||
|
||||
|
@ -549,6 +565,10 @@ public abstract class BaseTransactionProcessor {
|
|||
return myContext.getVersion().newIdType().setValue(theValue);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setModelConfig(ModelConfig theModelConfig) {
|
||||
myModelConfig = theModelConfig;
|
||||
}
|
||||
|
||||
private Map<IBase, IBasePersistedResource> doTransactionWriteOperations(final RequestDetails theRequest, String theActionName, TransactionDetails theTransactionDetails, Set<IIdType> theAllIds,
|
||||
Map<IIdType, IIdType> theIdSubstitutions, Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome, IBaseBundle theResponse, IdentityHashMap<IBase, Integer> theOriginalRequestOrder, List<IBase> theEntries, StopWatch theTransactionStopWatch) {
|
||||
|
@ -612,16 +632,16 @@ public abstract class BaseTransactionProcessor {
|
|||
String existingUuid = keyToUuid.get(key);
|
||||
for (IBase nextEntry : theEntries) {
|
||||
IBaseResource nextResource = myVersionAdapter.getResource(nextEntry);
|
||||
for (ResourceReferenceInfo nextReference : myContext.newTerser().getAllResourceReferences(nextResource)) {
|
||||
for (IBaseReference nextReference : myContext.newTerser().getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class)) {
|
||||
// We're interested in any references directly to the placeholder ID, but also
|
||||
// references that have a resource target that has the placeholder ID.
|
||||
String nextReferenceId = nextReference.getResourceReference().getReferenceElement().getValue();
|
||||
if (isBlank(nextReferenceId) && nextReference.getResourceReference().getResource() != null) {
|
||||
nextReferenceId = nextReference.getResourceReference().getResource().getIdElement().getValue();
|
||||
String nextReferenceId = nextReference.getReferenceElement().getValue();
|
||||
if (isBlank(nextReferenceId) && nextReference.getResource() != null) {
|
||||
nextReferenceId = nextReference.getResource().getIdElement().getValue();
|
||||
}
|
||||
if (entryUrl.equals(nextReferenceId)) {
|
||||
nextReference.getResourceReference().setReference(existingUuid);
|
||||
nextReference.getResourceReference().setResource(null);
|
||||
nextReference.setReference(existingUuid);
|
||||
nextReference.setResource(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -923,7 +943,6 @@ public abstract class BaseTransactionProcessor {
|
|||
IIdType newId = theIdSubstitutions.get(nextId);
|
||||
ourLog.debug(" * Replacing resource ref {} with {}", nextId, newId);
|
||||
if (referencesToVersion.contains(resourceReference)) {
|
||||
DaoMethodOutcome outcome = theIdToPersistedOutcome.get(newId);
|
||||
resourceReference.setReference(newId.getValue());
|
||||
} else {
|
||||
resourceReference.setReference(newId.toVersionless().getValue());
|
||||
|
@ -1042,6 +1061,11 @@ public abstract class BaseTransactionProcessor {
|
|||
return newIdType(theToResourceName, theIdPart, null);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setDaoRegistry(DaoRegistry theDaoRegistry) {
|
||||
myDaoRegistry = theDaoRegistry;
|
||||
}
|
||||
|
||||
private IFhirResourceDao getDaoOrThrowException(Class<? extends IBaseResource> theClass) {
|
||||
IFhirResourceDao<? extends IBaseResource> dao = myDaoRegistry.getResourceDaoOrNull(theClass);
|
||||
if (dao == null) {
|
||||
|
@ -1113,6 +1137,11 @@ public abstract class BaseTransactionProcessor {
|
|||
return null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setDaoConfig(DaoConfig theDaoConfig) {
|
||||
myDaoConfig = theDaoConfig;
|
||||
}
|
||||
|
||||
public interface ITransactionProcessorVersionAdapter<BUNDLE extends IBaseBundle, BUNDLEENTRY extends IBase> {
|
||||
|
||||
void setResponseStatus(BUNDLEENTRY theBundleEntry, String theStatus);
|
||||
|
|
|
@ -62,9 +62,16 @@ public class TransactionProcessor extends BaseTransactionProcessor {
|
|||
@Override
|
||||
protected void flushSession(Map<IIdType, DaoMethodOutcome> theIdToPersistedOutcome) {
|
||||
try {
|
||||
int insertionCount;
|
||||
int updateCount;
|
||||
SessionImpl session = myEntityManager.unwrap(SessionImpl.class);
|
||||
int insertionCount = session.getActionQueue().numberOfInsertions();
|
||||
int updateCount = session.getActionQueue().numberOfUpdates();
|
||||
if (session != null) {
|
||||
insertionCount = session.getActionQueue().numberOfInsertions();
|
||||
updateCount = session.getActionQueue().numberOfUpdates();
|
||||
} else {
|
||||
insertionCount = -1;
|
||||
updateCount = -1;
|
||||
}
|
||||
|
||||
StopWatch sw = new StopWatch();
|
||||
myEntityManager.flush();
|
||||
|
|
|
@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
|||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||
import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -68,6 +69,11 @@ public class DaoSearchParamSynchronizer {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setEntityManager(EntityManager theEntityManager) {
|
||||
myEntityManager = theEntityManager;
|
||||
}
|
||||
|
||||
private <T extends BaseResourceIndex> void synchronize(ResourceTable theEntity, AddRemoveCount theAddRemoveCount, Collection<T> theNewParams, Collection<T> theExistingParams) {
|
||||
Collection<T> newParams = theNewParams;
|
||||
for (T next : newParams) {
|
||||
|
|
|
@ -47,6 +47,7 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -95,6 +96,21 @@ public class SearchParamWithInlineReferencesExtractor {
|
|||
@Autowired
|
||||
private PartitionSettings myPartitionSettings;
|
||||
|
||||
@VisibleForTesting
|
||||
public void setPartitionSettings(PartitionSettings thePartitionSettings) {
|
||||
myPartitionSettings = thePartitionSettings;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamExtractorService(SearchParamExtractorService theSearchParamExtractorService) {
|
||||
mySearchParamExtractorService = theSearchParamExtractorService;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
|
||||
mySearchParamRegistry = theSearchParamRegistry;
|
||||
}
|
||||
|
||||
public void populateFromResource(ResourceIndexedSearchParams theParams, TransactionDetails theTransactionDetails, ResourceTable theEntity, IBaseResource theResource, ResourceIndexedSearchParams theExistingParams, RequestDetails theRequest) {
|
||||
extractInlineReferences(theResource, theRequest);
|
||||
|
||||
|
@ -214,6 +230,16 @@ public class SearchParamWithInlineReferencesExtractor {
|
|||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
public void setDaoConfig(DaoConfig theDaoConfig) {
|
||||
myDaoConfig = theDaoConfig;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setContext(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the
|
||||
* matching resource.
|
||||
|
@ -276,6 +302,11 @@ public class SearchParamWithInlineReferencesExtractor {
|
|||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setDaoSearchParamSynchronizer(DaoSearchParamSynchronizer theDaoSearchParamSynchronizer) {
|
||||
myDaoSearchParamSynchronizer = theDaoSearchParamSynchronizer;
|
||||
}
|
||||
|
||||
public void storeCompositeStringUniques(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams existingParams) {
|
||||
|
||||
// Store composite string uniques
|
||||
|
|
|
@ -27,9 +27,9 @@ import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
|||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.r4.model.Meta;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
|
@ -42,11 +42,14 @@ import java.util.List;
|
|||
|
||||
public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoR4.class);
|
||||
|
||||
@Autowired
|
||||
private TransactionProcessor myTransactionProcessor;
|
||||
|
||||
@VisibleForTesting
|
||||
public void setTransactionProcessorForUnitTest(TransactionProcessor theTransactionProcessor) {
|
||||
myTransactionProcessor = theTransactionProcessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
|
|
|
@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -47,6 +48,16 @@ public class HapiTransactionService {
|
|||
private PlatformTransactionManager myTransactionManager;
|
||||
private TransactionTemplate myTxTemplate;
|
||||
|
||||
@VisibleForTesting
|
||||
public void setInterceptorBroadcaster(IInterceptorBroadcaster theInterceptorBroadcaster) {
|
||||
myInterceptorBroadcaster = theInterceptorBroadcaster;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setTransactionManager(PlatformTransactionManager theTransactionManager) {
|
||||
myTransactionManager = theTransactionManager;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
myTxTemplate = new TransactionTemplate(myTransactionManager);
|
||||
|
|
|
@ -26,10 +26,15 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
|||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
||||
import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@Service
|
||||
|
@ -40,10 +45,14 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
|
|||
|
||||
@Autowired
|
||||
private PartitionSettings myPartitionSettings;
|
||||
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
|
||||
@VisibleForTesting
|
||||
public void setDaoConfig(DaoConfig theDaoConfig) {
|
||||
myDaoConfig = theDaoConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddRemoveCount updatePresence(ResourceTable theResource, Map<String, Boolean> theParamNameToPresence) {
|
||||
AddRemoveCount retVal = new AddRemoveCount();
|
||||
|
|
|
@ -5,8 +5,6 @@ import ca.uhn.fhir.jpa.batch.api.IBatchJobSubmitter;
|
|||
import ca.uhn.fhir.jpa.batch.svc.BatchJobSubmitterImpl;
|
||||
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
|
||||
import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl;
|
||||
import ca.uhn.fhir.jpa.bulk.svc.BulkExportDaoSvc;
|
||||
import ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer;
|
||||
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
|
||||
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
|
||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
|
@ -16,10 +14,6 @@ import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
|
|||
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings;
|
||||
import org.hibernate.search.backend.lucene.cfg.LuceneIndexSettings;
|
||||
import org.hibernate.search.engine.cfg.BackendSettings;
|
||||
import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
|
|
@ -1095,14 +1095,18 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
.stream()
|
||||
.filter(t -> t.getParamName().equals("medicationadministration-ingredient-medication"))
|
||||
.collect(Collectors.toList());
|
||||
ourLog.info("Tokens: {}", tokens);
|
||||
ourLog.info("Tokens:\n * {}", tokens.stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
assertEquals(1, tokens.size(), tokens.toString());
|
||||
assertEquals(false, tokens.get(0).isMissing());
|
||||
|
||||
});
|
||||
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add("medicationadministration-ingredient-medication", new TokenParam("system","code"));
|
||||
assertEquals(1, myMedicationAdministrationDao.search(map).size().intValue());
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider search = myMedicationAdministrationDao.search(map);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertEquals(1, search.sizeOrThrowNpe());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.hl7.fhir.r4.model.StringType;
|
|||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -45,6 +46,7 @@ import static org.mockito.Mockito.mock;
|
|||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SuppressWarnings({"unchecked", "deprecation", "Duplicates"})
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
public class FhirResourceDaoR4ConcurrentWriteTest extends BaseJpaR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ConcurrentWriteTest.class);
|
||||
|
|
|
@ -32,6 +32,9 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ContainedTest.class);
|
||||
|
||||
|
@ -64,6 +67,8 @@ public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
|
|||
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdObs));
|
||||
|
||||
runInTransaction(()->{
|
||||
ourLog.info("String indexes:\n * {}", myResourceIndexedSearchParamStringDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
|
||||
Long i = myEntityManager
|
||||
.createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'subject.family' AND s.myResourceType = 'Observation'", Long.class)
|
||||
.getSingleResult();
|
||||
|
|
|
@ -306,13 +306,17 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
|
|||
o.getMeta().addTag("http://foo", "bar", "FOOBAR");
|
||||
p.getManagingOrganization().setResource(o);
|
||||
|
||||
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
|
||||
ourLog.info("Input: {}", encoded);
|
||||
assertThat(encoded, containsString("#1"));
|
||||
|
||||
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = myPatientDao.read(id);
|
||||
|
||||
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
|
||||
ourLog.info("Output: {}", encoded);
|
||||
assertThat(encoded, containsString("#1"));
|
||||
|
||||
Organization org = (Organization) p.getManagingOrganization().getResource();
|
||||
assertEquals("#1", org.getId());
|
||||
|
|
|
@ -787,7 +787,8 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
|||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
// Lookup the two existing IDs to make sure they are legit
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
myCaptureQueriesListener.logInsertQueriesForCurrentThread();
|
||||
myCaptureQueriesListener.logUpdateQueriesForCurrentThread();
|
||||
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
|
||||
assertEquals(3, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
|
||||
assertEquals(2, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
|
||||
|
|
|
@ -94,6 +94,7 @@ import org.hl7.fhir.r4.model.OperationOutcome.IssueType;
|
|||
import org.hl7.fhir.r4.model.Organization;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Period;
|
||||
import org.hl7.fhir.r4.model.Provenance;
|
||||
import org.hl7.fhir.r4.model.Quantity;
|
||||
import org.hl7.fhir.r4.model.Quantity.QuantityComparator;
|
||||
import org.hl7.fhir.r4.model.Questionnaire;
|
||||
|
@ -295,6 +296,39 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoreReferenceFromContainedToContainer() {
|
||||
Patient patient = new Patient();
|
||||
patient.setActive(true);
|
||||
|
||||
Provenance provenance = new Provenance();
|
||||
provenance.setId("#1");
|
||||
provenance.addTarget().setReference("#");
|
||||
patient.getContained().add(provenance);
|
||||
|
||||
Observation observation = new Observation();
|
||||
observation.setId("#2");
|
||||
observation.getSubject().setReference("#");
|
||||
patient.getContained().add(observation);
|
||||
|
||||
IIdType id = myPatientDao.create(patient).getId();
|
||||
|
||||
patient = myPatientDao.read(id);
|
||||
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
||||
|
||||
assertEquals(2, patient.getContained().size());
|
||||
|
||||
provenance = (Provenance) patient.getContained().get(0);
|
||||
assertEquals("#1", provenance.getId());
|
||||
assertEquals("#", provenance.getTargetFirstRep().getReference());
|
||||
|
||||
observation = (Observation) patient.getContained().get(1);
|
||||
assertEquals("#2", observation.getId());
|
||||
assertEquals("#", observation.getSubject().getReference());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTermConceptReindexingDoesntDuplicateData() {
|
||||
myDaoConfig.setSchedulingDisabled(true);
|
||||
|
|
|
@ -26,6 +26,7 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
|
|||
@AfterEach
|
||||
public void afterEach() {
|
||||
myFhirCtx.getParserOptions().setStripVersionsFromReferences(true);
|
||||
myFhirCtx.getParserOptions().getDontStripVersionsFromReferencesAtPaths().clear();
|
||||
myDaoConfig.setDeleteEnabled(new DaoConfig().isDeleteEnabled());
|
||||
myModelConfig.setRespectVersionsForSearchIncludes(new ModelConfig().isRespectVersionsForSearchIncludes());
|
||||
myModelConfig.setAutoVersionReferenceAtPaths(new ModelConfig().getAutoVersionReferenceAtPaths());
|
||||
|
@ -457,6 +458,10 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
|
|||
observation.getSubject().setReference(patientId.withVersion("1").getValue());
|
||||
IIdType observationId = myObservationDao.create(observation).getId().toUnqualified();
|
||||
|
||||
// Read the observation back
|
||||
observation = myObservationDao.read(observationId);
|
||||
assertEquals(patientId.toVersionless().getValue(), observation.getSubject().getReference());
|
||||
|
||||
// Search - Non Synchronous for *
|
||||
{
|
||||
IBundleProvider outcome = myObservationDao.search(SearchParameterMap.newSynchronous().addInclude(IBaseResource.INCLUDE_ALL));
|
||||
|
|
|
@ -168,12 +168,11 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
|
||||
myValidationSupport = wac.getBean(IValidationSupport.class);
|
||||
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
||||
SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class);
|
||||
|
||||
confProvider.setSearchParamRegistry(ourSearchParamRegistry);
|
||||
|
||||
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(20000);
|
||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(400000);
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
|
|
|
@ -15,6 +15,7 @@ import static org.hamcrest.Matchers.greaterThan;
|
|||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.in;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
|
@ -47,7 +48,9 @@ import java.util.Set;
|
|||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
|
||||
import ca.uhn.fhir.util.BundleBuilder;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
@ -849,6 +852,72 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateAndReadBackResourceWithContainedReferenceToContainer() {
|
||||
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
|
||||
|
||||
String input = "{\n" +
|
||||
" \"resourceType\": \"Organization\",\n" +
|
||||
" \"id\": \"1\",\n" +
|
||||
" \"meta\": {\n" +
|
||||
" \"tag\": [\n" +
|
||||
" {\n" +
|
||||
" \"system\": \"https://blah.org/deployment\",\n" +
|
||||
" \"code\": \"e69414dd-b5c2-462d-bcfd-9d04d6b16596\",\n" +
|
||||
" \"display\": \"DEPLOYMENT\"\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"system\": \"https://blah.org/region\",\n" +
|
||||
" \"code\": \"b47d7a5b-b159-4bed-a8f8-3258e6603adb\",\n" +
|
||||
" \"display\": \"REGION\"\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"system\": \"https://blah.org/provider\",\n" +
|
||||
" \"code\": \"28c30004-0333-40cf-9e7f-3f9e080930bd\",\n" +
|
||||
" \"display\": \"PROVIDER\"\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
" },\n" +
|
||||
" \"contained\": [\n" +
|
||||
" {\n" +
|
||||
" \"resourceType\": \"Location\",\n" +
|
||||
" \"id\": \"2\",\n" +
|
||||
" \"position\": {\n" +
|
||||
" \"longitude\": 51.443238301454289,\n" +
|
||||
" \"latitude\": 7.34196905697293\n" +
|
||||
" },\n" +
|
||||
" \"managingOrganization\": {\n" +
|
||||
" \"reference\": \"#\"\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"type\": [\n" +
|
||||
" {\n" +
|
||||
" \"coding\": [\n" +
|
||||
" {\n" +
|
||||
" \"system\": \"https://blah.org/fmc/OrganizationType\",\n" +
|
||||
" \"code\": \"CLINIC\",\n" +
|
||||
" \"display\": \"Clinic\"\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"name\": \"testOrg\"\n" +
|
||||
"}";
|
||||
|
||||
Organization org = myFhirCtx.newJsonParser().parseResource(Organization.class, input);
|
||||
IIdType id = myOrganizationDao.create(org).getId();
|
||||
org = myOrganizationDao.read(id);
|
||||
|
||||
String output = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(org);
|
||||
ourLog.info(output);
|
||||
|
||||
Location loc = (Location) org.getContained().get(0);
|
||||
assertEquals("#", loc.getManagingOrganization().getReference());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCountParam() {
|
||||
List<IBaseResource> resources = new ArrayList<>();
|
||||
|
@ -857,7 +926,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
org.setName("rpr4_testCountParam_01");
|
||||
resources.add(org);
|
||||
}
|
||||
myClient.transaction().withResources(resources).prettyPrint().encodedXml().execute();
|
||||
List<IBaseResource> outcome = myClient.transaction().withResources(resources).prettyPrint().encodedXml().execute();
|
||||
|
||||
runInTransaction(()->{
|
||||
assertEquals(100, myResourceTableDao.count());
|
||||
});
|
||||
|
||||
Bundle found = myClient
|
||||
.search()
|
||||
|
|
|
@ -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" />
|
||||
</logger>
|
||||
j
|
||||
<logger name="org.hibernate.event.internal.DefaultPersistEventListener" additivity="true" level="trace">
|
||||
<logger name="org.hibernate.event.internal.DefaultPersistEventListener" additivity="true" level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</logger>
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -98,7 +98,6 @@ public class ModelConfig {
|
|||
private Set<String> myAutoVersionReferenceAtPaths = Collections.emptySet();
|
||||
private Map<String, Set<String>> myTypeToAutoVersionReferenceAtPaths = Collections.emptyMap();
|
||||
private boolean myRespectVersionsForSearchIncludes;
|
||||
|
||||
private boolean myIndexOnContainedResources = false;
|
||||
|
||||
/**
|
||||
|
@ -731,8 +730,7 @@ public class ModelConfig {
|
|||
public void setRespectVersionsForSearchIncludes(boolean theRespectVersionsForSearchIncludes) {
|
||||
myRespectVersionsForSearchIncludes = theRespectVersionsForSearchIncludes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Should indexing and searching on contained resources be enabled on this server.
|
||||
* This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
|
||||
|
|
|
@ -257,6 +257,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
b.append("hashIdentity", myHashIdentity);
|
||||
b.append("hashSystem", myHashSystem);
|
||||
b.append("hashValue", myHashValue);
|
||||
b.append("hashSysAndValue", myHashSystemAndValue);
|
||||
return b.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -134,6 +134,11 @@ public class ResourceChangeListenerCache implements IResourceChangeListenerCache
|
|||
return retval;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setResourceChangeListenerCacheRefresher(IResourceChangeListenerCacheRefresher theResourceChangeListenerCacheRefresher) {
|
||||
myResourceChangeListenerCacheRefresher = theResourceChangeListenerCacheRefresher;
|
||||
}
|
||||
|
||||
private ResourceChangeResult refreshCacheAndNotifyListenersWithRetry() {
|
||||
Retrier<ResourceChangeResult> refreshCacheRetrier = new Retrier<>(() -> {
|
||||
synchronized (this) {
|
||||
|
|
|
@ -103,6 +103,21 @@ public class ResourceChangeListenerCacheRefresherImpl implements IResourceChange
|
|||
return retval;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSchedulerService(ISchedulerService theSchedulerService) {
|
||||
mySchedulerService = theSchedulerService;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setResourceChangeListenerRegistry(ResourceChangeListenerRegistryImpl theResourceChangeListenerRegistry) {
|
||||
myResourceChangeListenerRegistry = theResourceChangeListenerRegistry;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setResourceVersionSvc(IResourceVersionSvc theResourceVersionSvc) {
|
||||
myResourceVersionSvc = theResourceVersionSvc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceChangeResult refreshCacheAndNotifyListener(IResourceChangeListenerCache theCache) {
|
||||
ResourceChangeResult retVal = new ResourceChangeResult();
|
||||
|
|
|
@ -47,28 +47,42 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
|||
@Component
|
||||
public class ResourceChangeListenerRegistryImpl implements IResourceChangeListenerRegistry {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ResourceChangeListenerRegistryImpl.class);
|
||||
|
||||
private final Queue<ResourceChangeListenerCache> myListenerEntries = new ConcurrentLinkedQueue<>();
|
||||
@Autowired
|
||||
ResourceChangeListenerCacheFactory myResourceChangeListenerCacheFactory;
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
@Autowired
|
||||
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
||||
@Autowired
|
||||
ResourceChangeListenerCacheFactory myResourceChangeListenerCacheFactory;
|
||||
|
||||
private final Queue<ResourceChangeListenerCache> myListenerEntries = new ConcurrentLinkedQueue<>();
|
||||
@VisibleForTesting
|
||||
public void setResourceChangeListenerCacheFactory(ResourceChangeListenerCacheFactory theResourceChangeListenerCacheFactory) {
|
||||
myResourceChangeListenerCacheFactory = theResourceChangeListenerCacheFactory;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setFhirContext(FhirContext theFhirContext) {
|
||||
myFhirContext = theFhirContext;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setInMemoryResourceMatcher(InMemoryResourceMatcher theInMemoryResourceMatcher) {
|
||||
myInMemoryResourceMatcher = theInMemoryResourceMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener in order to be notified whenever a resource matching the provided SearchParameterMap
|
||||
* changes in any way. If the change happened on the same jvm process where this registry resides, then the listener will be called
|
||||
* within {@link ResourceChangeListenerCacheRefresherImpl#LOCAL_REFRESH_INTERVAL_MS} of the change happening. If the change happened
|
||||
* on a different jvm process, then the listener will be called within theRemoteRefreshIntervalMs.
|
||||
* @param theResourceName the type of the resource the listener should be notified about (e.g. "Subscription" or "SearchParameter")
|
||||
* @param theSearchParameterMap the listener will only be notified of changes to resources that match this map
|
||||
* @param theResourceChangeListener the listener that will be called whenever resource changes are detected
|
||||
*
|
||||
* @param theResourceName the type of the resource the listener should be notified about (e.g. "Subscription" or "SearchParameter")
|
||||
* @param theSearchParameterMap the listener will only be notified of changes to resources that match this map
|
||||
* @param theResourceChangeListener the listener that will be called whenever resource changes are detected
|
||||
* @param theRemoteRefreshIntervalMs the number of milliseconds between checking the database for changed resources that match the search parameter map
|
||||
* @throws ca.uhn.fhir.parser.DataFormatException if theResourceName is not a valid resource type in our FhirContext
|
||||
* @throws IllegalArgumentException if theSearchParamMap cannot be evaluated in-memory
|
||||
* @return RegisteredResourceChangeListener that stores the resource id cache, and the next refresh time
|
||||
* @throws ca.uhn.fhir.parser.DataFormatException if theResourceName is not a valid resource type in our FhirContext
|
||||
* @throws IllegalArgumentException if theSearchParamMap cannot be evaluated in-memory
|
||||
*/
|
||||
@Override
|
||||
public IResourceChangeListenerCache registerResourceResourceChangeListener(String theResourceName, SearchParameterMap theSearchParameterMap, IResourceChangeListener theResourceChangeListener, long theRemoteRefreshIntervalMs) {
|
||||
|
|
|
@ -45,8 +45,10 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
|||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
import ca.uhn.fhir.util.StringUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -81,6 +83,7 @@ import java.util.TreeSet;
|
|||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.trim;
|
||||
|
@ -532,6 +535,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
|
||||
protected abstract IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath);
|
||||
|
||||
@VisibleForTesting
|
||||
public void setContext(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
protected FhirContext getContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
@ -540,6 +548,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
return myModelConfig;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
|
||||
mySearchParamRegistry = theSearchParamRegistry;
|
||||
}
|
||||
|
||||
private Collection<RuntimeSearchParam> getSearchParams(IBaseResource theResource) {
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||
Collection<RuntimeSearchParam> retVal = mySearchParamRegistry.getActiveSearchParams(def.getName()).values();
|
||||
|
@ -644,6 +657,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setModelConfig(ModelConfig theModelConfig) {
|
||||
myModelConfig = theModelConfig;
|
||||
}
|
||||
|
||||
protected boolean shouldIndexTextComponentOfToken(RuntimeSearchParam theSearchParam) {
|
||||
return tokenTextIndexingEnabledForSearchParam(myModelConfig, theSearchParam);
|
||||
}
|
||||
|
@ -938,6 +956,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
SearchParamSet<T> retVal = new SearchParamSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
|
||||
cleanUpContainedResourceReferences(theResource, theSearchParamType, searchParams);
|
||||
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != theSearchParamType) {
|
||||
continue;
|
||||
|
@ -948,6 +969,31 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* HAPI FHIR Reference objects (e.g. {@link org.hl7.fhir.r4.model.Reference}) can hold references either by text
|
||||
* (e.g. "#3") or by resource (e.g. "new Reference(patientInstance)"). The FHIRPath evaluator only understands the
|
||||
* first way, so if there is any chance of the FHIRPath evaluator needing to descend across references, we
|
||||
* have to assign values to those references before indexing.
|
||||
*
|
||||
* Doing this cleanup isn't hugely expensive, but it's not completely free either so we only do it
|
||||
* if we think there's actually a chance
|
||||
*/
|
||||
private void cleanUpContainedResourceReferences(IBaseResource theResource, RestSearchParameterTypeEnum theSearchParamType, Collection<RuntimeSearchParam> searchParams) {
|
||||
boolean havePathWithResolveExpression = myModelConfig.isIndexOnContainedResources();
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
if (nextSpDef.getParamType() != theSearchParamType) {
|
||||
continue;
|
||||
}
|
||||
if (defaultString(nextSpDef.getPath()).contains("resolve")) {
|
||||
havePathWithResolveExpression = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (havePathWithResolveExpression) {
|
||||
myContext.newTerser().containResources(theResource, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void extractSearchParam(RuntimeSearchParam theSearchParameterDef, IBaseResource theResource, IExtractor<T> theExtractor, SearchParamSet<T> theSetToPopulate, boolean theWantLocalReferences) {
|
||||
String nextPathUnsplit = theSearchParameterDef.getPath();
|
||||
if (isBlank(nextPathUnsplit)) {
|
||||
|
@ -1020,6 +1066,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setPartitionSettings(PartitionSettings thePartitionSettings) {
|
||||
myPartitionSettings = thePartitionSettings;
|
||||
}
|
||||
|
||||
private ResourceIndexedSearchParamToken createTokenIndexIfNotBlank(String theResourceType, RuntimeSearchParam theSearchParam, String theSystem, String theValue) {
|
||||
String system = theSystem;
|
||||
String value = theValue;
|
||||
|
|
|
@ -74,10 +74,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
|
||||
public class SearchParamExtractorService {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorService.class);
|
||||
|
||||
@Autowired
|
||||
private ISearchParamExtractor mySearchParamExtractor;
|
||||
|
||||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
@Autowired
|
||||
|
@ -91,22 +89,25 @@ public class SearchParamExtractorService {
|
|||
@Autowired(required = false)
|
||||
private IResourceLinkResolver myResourceLinkResolver;
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamExtractor(ISearchParamExtractor theSearchParamExtractor) {
|
||||
mySearchParamExtractor = theSearchParamExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is responsible for scanning a resource for all of the search parameter instances. I.e. for all search parameters defined for
|
||||
* a given resource type, it extracts the associated indexes and populates {@literal theParams}.
|
||||
*/
|
||||
public void extractFromResource(RequestPartitionId theRequestPartitionId, RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference) {
|
||||
IBaseResource resource = normalizeResource(theResource);
|
||||
|
||||
// All search parameter types except Reference
|
||||
ResourceIndexedSearchParams normalParams = new ResourceIndexedSearchParams();
|
||||
extractSearchIndexParameters(theRequestDetails, normalParams, resource, theEntity);
|
||||
extractSearchIndexParameters(theRequestDetails, normalParams, theResource, theEntity);
|
||||
mergeParams(normalParams, theParams);
|
||||
|
||||
if (myModelConfig.isIndexOnContainedResources()) {
|
||||
ResourceIndexedSearchParams containedParams = new ResourceIndexedSearchParams();
|
||||
extractSearchIndexParametersForContainedResources(theRequestDetails, containedParams, resource, theEntity);
|
||||
extractSearchIndexParametersForContainedResources(theRequestDetails, containedParams, theResource, theEntity);
|
||||
mergeParams(containedParams, theParams);
|
||||
}
|
||||
|
||||
|
@ -114,11 +115,16 @@ public class SearchParamExtractorService {
|
|||
populateResourceTables(theParams, theEntity);
|
||||
|
||||
// Reference search parameters
|
||||
extractResourceLinks(theRequestPartitionId, theParams, theEntity, resource, theTransactionDetails, theFailOnInvalidReference, theRequestDetails);
|
||||
extractResourceLinks(theRequestPartitionId, theParams, theEntity, theResource, theTransactionDetails, theFailOnInvalidReference, theRequestDetails);
|
||||
|
||||
theParams.setUpdatedTime(theTransactionDetails.getTransactionDate());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setModelConfig(ModelConfig theModelConfig) {
|
||||
myModelConfig = theModelConfig;
|
||||
}
|
||||
|
||||
private void extractSearchIndexParametersForContainedResources(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, ResourceTable theEntity) {
|
||||
|
||||
FhirTerser terser = myContext.newTerser();
|
||||
|
@ -182,7 +188,7 @@ public class SearchParamExtractorService {
|
|||
}
|
||||
|
||||
private void extractSearchIndexParameters(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, ResourceTable theEntity) {
|
||||
|
||||
|
||||
// Strings
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamString> strings = extractSearchParamStrings(theResource);
|
||||
handleWarnings(theRequestDetails, myInterceptorBroadcaster, strings);
|
||||
|
@ -203,7 +209,7 @@ public class SearchParamExtractorService {
|
|||
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantitiesNormalized);
|
||||
theParams.myQuantityNormalizedParams.addAll(quantitiesNormalized);
|
||||
}
|
||||
|
||||
|
||||
// Dates
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamDate> dates = extractSearchParamDates(theResource);
|
||||
handleWarnings(theRequestDetails, myInterceptorBroadcaster, dates);
|
||||
|
@ -247,19 +253,9 @@ public class SearchParamExtractorService {
|
|||
populateResourceTable(theParams.myCoordsParams, theEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a bit hacky, but if someone has manually populated a resource (ie. my working directly with the model
|
||||
* as opposed to by parsing a serialized instance) it's possible that they have put in contained resources
|
||||
* using {@link IBaseReference#setResource(IBaseResource)}, and those contained resources have not yet
|
||||
* ended up in the Resource.contained array, meaning that FHIRPath expressions won't be able to find them.
|
||||
*
|
||||
* As a result, we to a serialize-and-parse to normalize the object. This really only affects people who
|
||||
* are calling the JPA DAOs directly, but there are a few of those...
|
||||
*/
|
||||
private IBaseResource normalizeResource(IBaseResource theResource) {
|
||||
IParser parser = myContext.newJsonParser().setPrettyPrint(false);
|
||||
theResource = parser.parseResource(parser.encodeResourceToString(theResource));
|
||||
return theResource;
|
||||
@VisibleForTesting
|
||||
public void setContext(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
private void extractResourceLinks(RequestPartitionId theRequestPartitionId, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference, RequestDetails theRequest) {
|
||||
|
@ -291,6 +287,10 @@ public class SearchParamExtractorService {
|
|||
nextId = nextReference.getResource().getIdElement();
|
||||
}
|
||||
|
||||
if (myContext.getParserOptions().isStripVersionsFromReferences() && !myContext.getParserOptions().getDontStripVersionsFromReferencesAtPaths().contains(thePathAndRef.getPath()) && nextId.hasVersionIdPart()) {
|
||||
nextId = nextId.toVersionless();
|
||||
}
|
||||
|
||||
theParams.myPopulatedResourceLinkParameters.add(thePathAndRef.getSearchParamName());
|
||||
|
||||
boolean canonical = thePathAndRef.isCanonical();
|
||||
|
@ -431,24 +431,6 @@ public class SearchParamExtractorService {
|
|||
return ResourceLink.forLocalReference(nextPathAndRef.getPath(), theEntity, targetResourceType, targetResourcePid, targetResourceIdPart, theUpdateTime, targetVersion);
|
||||
}
|
||||
|
||||
|
||||
static void handleWarnings(RequestDetails theRequestDetails, IInterceptorBroadcaster theInterceptorBroadcaster, ISearchParamExtractor.SearchParamSet<?> theSearchParamSet) {
|
||||
if (theSearchParamSet.getWarnings().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If extraction generated any warnings, broadcast an error
|
||||
for (String next : theSearchParamSet.getWarnings()) {
|
||||
StorageProcessingMessage messageHolder = new StorageProcessingMessage();
|
||||
messageHolder.setMessage(next);
|
||||
HookParams params = new HookParams()
|
||||
.add(RequestDetails.class, theRequestDetails)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
|
||||
.add(StorageProcessingMessage.class, messageHolder);
|
||||
JpaInterceptorBroadcaster.doCallHooks(theInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateResourceTable(Collection<? extends BaseResourceIndexedSearchParam> theParams, ResourceTable theResourceTable) {
|
||||
for (BaseResourceIndexedSearchParam next : theParams) {
|
||||
if (next.getResourcePid() == null) {
|
||||
|
@ -498,5 +480,22 @@ public class SearchParamExtractorService {
|
|||
public List<String> extractParamValuesAsStrings(RuntimeSearchParam theActiveSearchParam, IBaseResource theResource) {
|
||||
return mySearchParamExtractor.extractParamValuesAsStrings(theActiveSearchParam, theResource);
|
||||
}
|
||||
|
||||
static void handleWarnings(RequestDetails theRequestDetails, IInterceptorBroadcaster theInterceptorBroadcaster, ISearchParamExtractor.SearchParamSet<?> theSearchParamSet) {
|
||||
if (theSearchParamSet.getWarnings().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If extraction generated any warnings, broadcast an error
|
||||
for (String next : theSearchParamSet.getWarnings()) {
|
||||
StorageProcessingMessage messageHolder = new StorageProcessingMessage();
|
||||
messageHolder.setMessage(next);
|
||||
HookParams params = new HookParams()
|
||||
.add(RequestDetails.class, theRequestDetails)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
|
||||
.add(StorageProcessingMessage.class, messageHolder);
|
||||
JpaInterceptorBroadcaster.doCallHooks(theInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -148,6 +148,11 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry, IResourceC
|
|||
ourLog.debug("Refreshed search parameter cache in {}ms", sw.getMillis());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setFhirContext(FhirContext theFhirContext) {
|
||||
myFhirContext = theFhirContext;
|
||||
}
|
||||
|
||||
private ReadOnlySearchParamCache getBuiltInSearchParams() {
|
||||
if (myBuiltInSearchParams == null) {
|
||||
myBuiltInSearchParams = ReadOnlySearchParamCache.fromFhirContext(myFhirContext);
|
||||
|
@ -162,6 +167,11 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry, IResourceC
|
|||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setModelConfig(ModelConfig theModelConfig) {
|
||||
myModelConfig = theModelConfig;
|
||||
}
|
||||
|
||||
private long overrideBuiltinSearchParamsWithActiveJpaSearchParams(RuntimeSearchParamCache theSearchParamCache, Collection<IBaseResource> theSearchParams) {
|
||||
if (!myModelConfig.isDefaultSearchParamsCanBeOverridden() || theSearchParams == null) {
|
||||
return 0;
|
||||
|
@ -228,6 +238,11 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry, IResourceC
|
|||
return myResourceChangeListenerCache.refreshCacheIfNecessary();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setResourceChangeListenerRegistry(IResourceChangeListenerRegistry theResourceChangeListenerRegistry) {
|
||||
myResourceChangeListenerRegistry = theResourceChangeListenerRegistry;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void registerListener() {
|
||||
myResourceChangeListenerCache = myResourceChangeListenerRegistry.registerResourceResourceChangeListener("SearchParameter", SearchParameterMap.newSynchronous(), this, REFRESH_INTERVAL);
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
package ca.uhn.fhir.rest.server.method;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
|
|
|
@ -1090,6 +1090,7 @@ public class XmlParserDstu3Test {
|
|||
* See #103
|
||||
*/
|
||||
@Test
|
||||
@Disabled
|
||||
public void testEncodeAndReEncodeContainedJson() {
|
||||
Composition comp = new Composition();
|
||||
comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section0_Allergy0")));
|
||||
|
@ -1115,6 +1116,7 @@ public class XmlParserDstu3Test {
|
|||
* See #103
|
||||
*/
|
||||
@Test
|
||||
@Disabled
|
||||
public void testEncodeAndReEncodeContainedXml() {
|
||||
Composition comp = new Composition();
|
||||
comp.addSection().addEntry().setResource(new AllergyIntolerance().addNote(new Annotation().setText("Section0_Allergy0")));
|
||||
|
|
|
@ -2,10 +2,7 @@ package ca.uhn.fhir.parser;
|
|||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.test.BaseTest;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
|
@ -22,7 +19,6 @@ import org.hl7.fhir.r4.model.Composition;
|
|||
import org.hl7.fhir.r4.model.DateTimeType;
|
||||
import org.hl7.fhir.r4.model.Device;
|
||||
import org.hl7.fhir.r4.model.DocumentReference;
|
||||
import org.hl7.fhir.r4.model.Dosage;
|
||||
import org.hl7.fhir.r4.model.Encounter;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.Medication;
|
||||
|
@ -39,7 +35,6 @@ import org.hl7.fhir.r4.model.QuestionnaireResponse;
|
|||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.Type;
|
||||
import org.hl7.fhir.r4.model.UuidType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -473,8 +468,8 @@ public class JsonParserR4Test extends BaseTest {
|
|||
ourLog.info(encoded);
|
||||
mr = ourCtx.newJsonParser().parseResource(MedicationRequest.class, encoded);
|
||||
|
||||
assertEquals("#2", mr.getContained().get(0).getId());
|
||||
assertEquals("#1", mr.getContained().get(1).getId());
|
||||
assertEquals("#1", mr.getContained().get(0).getId());
|
||||
assertEquals("#2", mr.getContained().get(1).getId());
|
||||
|
||||
}
|
||||
|
||||
|
@ -929,6 +924,50 @@ public class JsonParserR4Test extends BaseTest {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ensure that a contained bundle doesn't cause a crash
|
||||
*/
|
||||
@Test
|
||||
public void testParseAndEncodePreservesContainedResourceOrder() {
|
||||
String auditEvent = "{\n" +
|
||||
" \"resourceType\": \"AuditEvent\",\n" +
|
||||
" \"contained\": [ {\n" +
|
||||
" \"resourceType\": \"Observation\",\n" +
|
||||
" \"id\": \"A\",\n" +
|
||||
" \"identifier\": [ {\n" +
|
||||
" \"value\": \"A\"\n" +
|
||||
" } ]\n" +
|
||||
" }, {\n" +
|
||||
" \"resourceType\": \"Observation\",\n" +
|
||||
" \"id\": \"B\",\n" +
|
||||
" \"identifier\": [ {\n" +
|
||||
" \"value\": \"B\"\n" +
|
||||
" } ]\n" +
|
||||
" } ],\n" +
|
||||
" \"entity\": [ {\n" +
|
||||
" \"what\": {\n" +
|
||||
" \"reference\": \"#B\"\n" +
|
||||
" }\n" +
|
||||
" }, {\n" +
|
||||
" \"what\": {\n" +
|
||||
" \"reference\": \"#A\"\n" +
|
||||
" }\n" +
|
||||
" } ]\n" +
|
||||
"}";
|
||||
|
||||
ourLog.info("Input: {}", auditEvent);
|
||||
AuditEvent ae = ourCtx.newJsonParser().parseResource(AuditEvent.class, auditEvent);
|
||||
assertEquals("#A", ae.getContained().get(0).getId());
|
||||
assertEquals("#B", ae.getContained().get(1).getId());
|
||||
assertEquals("#B", ae.getEntity().get(0).getWhat().getReference());
|
||||
assertEquals("#A", ae.getEntity().get(1).getWhat().getReference());
|
||||
|
||||
String serialized = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(ae);
|
||||
assertEquals(auditEvent, serialized);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@DatatypeDef(
|
||||
name = "UnknownPrimitiveType"
|
||||
)
|
||||
|
|
|
@ -44,6 +44,55 @@ public class XmlParserR4Test extends BaseTest {
|
|||
return c;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ensure that a contained bundle doesn't cause a crash
|
||||
*/
|
||||
@Test
|
||||
public void testParseAndEncodePreservesContainedResourceOrder() {
|
||||
String auditEvent = "<AuditEvent xmlns=\"http://hl7.org/fhir\">\n" +
|
||||
" <contained>\n" +
|
||||
" <Observation xmlns=\"http://hl7.org/fhir\">\n" +
|
||||
" <id value=\"A\"/>\n" +
|
||||
" <identifier>\n" +
|
||||
" <value value=\"A\"/>\n" +
|
||||
" </identifier>\n" +
|
||||
" </Observation>\n" +
|
||||
" </contained>\n" +
|
||||
" <contained>\n" +
|
||||
" <Observation xmlns=\"http://hl7.org/fhir\">\n" +
|
||||
" <id value=\"B\"/>\n" +
|
||||
" <identifier>\n" +
|
||||
" <value value=\"B\"/>\n" +
|
||||
" </identifier>\n" +
|
||||
" </Observation>\n" +
|
||||
" </contained>\n" +
|
||||
" <entity>\n" +
|
||||
" <what>\n" +
|
||||
" <reference value=\"#B\"/>\n" +
|
||||
" </what>\n" +
|
||||
" </entity>\n" +
|
||||
" <entity>\n" +
|
||||
" <what>\n" +
|
||||
" <reference value=\"#A\"/>\n" +
|
||||
" </what>\n" +
|
||||
" </entity>\n" +
|
||||
"</AuditEvent>";
|
||||
|
||||
ourLog.info("Input: {}", auditEvent);
|
||||
AuditEvent ae = ourCtx.newXmlParser().parseResource(AuditEvent.class, auditEvent);
|
||||
assertEquals("#A", ae.getContained().get(0).getId());
|
||||
assertEquals("#B", ae.getContained().get(1).getId());
|
||||
assertEquals("#B", ae.getEntity().get(0).getWhat().getReference());
|
||||
assertEquals("#A", ae.getEntity().get(1).getWhat().getReference());
|
||||
|
||||
String serialized = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(ae);
|
||||
assertEquals(auditEvent, serialized);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* See #402 section.text is overwritten by composition.text
|
||||
*/
|
||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.util;
|
|||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.annotation.Block;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import org.hamcrest.Matchers;
|
||||
|
@ -14,12 +15,14 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.DocumentReference;
|
||||
import org.hl7.fhir.r4.model.Element;
|
||||
import org.hl7.fhir.r4.model.Enumeration;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.Identifier;
|
||||
import org.hl7.fhir.r4.model.MarkdownType;
|
||||
import org.hl7.fhir.r4.model.Medication;
|
||||
import org.hl7.fhir.r4.model.MedicationAdministration;
|
||||
import org.hl7.fhir.r4.model.MedicationRequest;
|
||||
import org.hl7.fhir.r4.model.Money;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.Organization;
|
||||
|
@ -29,8 +32,10 @@ import org.hl7.fhir.r4.model.Practitioner;
|
|||
import org.hl7.fhir.r4.model.PrimitiveType;
|
||||
import org.hl7.fhir.r4.model.Quantity;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.model.ResourceType;
|
||||
import org.hl7.fhir.r4.model.SimpleQuantity;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.Substance;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -62,14 +67,74 @@ import static org.mockito.Mockito.when;
|
|||
public class FhirTerserR4Test {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(FhirTerserR4Test.class);
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private FhirContext myCtx = FhirContext.forCached(FhirVersionEnum.R4);
|
||||
|
||||
@Test
|
||||
public void testContainResourcesWithModify() {
|
||||
|
||||
MedicationRequest mr = new MedicationRequest();
|
||||
mr.setMedication(new Reference(new Medication().setStatus(Medication.MedicationStatus.ACTIVE)));
|
||||
mr.getRequester().setResource(new Practitioner().setActive(true));
|
||||
|
||||
FhirTerser.ContainedResources contained = myCtx.newTerser().containResources(mr, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
|
||||
|
||||
assertEquals("#1", mr.getContained().get(0).getId());
|
||||
assertEquals("#2", mr.getContained().get(1).getId());
|
||||
assertEquals(ResourceType.Medication, mr.getContained().get(0).getResourceType());
|
||||
assertEquals(ResourceType.Practitioner, mr.getContained().get(1).getResourceType());
|
||||
assertEquals("#1", mr.getMedicationReference().getReference());
|
||||
assertEquals("#2", mr.getRequester().getReference());
|
||||
|
||||
FhirTerser.ContainedResources secondPass = myCtx.newTerser().containResources(mr, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
|
||||
assertSame(contained, secondPass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainedResourcesWithModify_DoubleLink() {
|
||||
Substance ingredient = new Substance();
|
||||
ingredient.getCode().addCoding().setSystem("system").setCode("code");
|
||||
|
||||
Medication medication = new Medication();
|
||||
medication.addIngredient().setItem(new Reference(ingredient));
|
||||
|
||||
MedicationAdministration medAdmin = new MedicationAdministration();
|
||||
medAdmin.setMedication(new Reference(medication));
|
||||
|
||||
myCtx.newTerser().containResources(medAdmin, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
|
||||
|
||||
assertEquals("#1", medAdmin.getContained().get(0).getId());
|
||||
assertEquals("#2", medAdmin.getContained().get(1).getId());
|
||||
assertEquals(ResourceType.Medication, medAdmin.getContained().get(0).getResourceType());
|
||||
assertEquals(ResourceType.Substance, medAdmin.getContained().get(1).getResourceType());
|
||||
assertEquals("#1", medAdmin.getMedicationReference().getReference());
|
||||
assertEquals("#2", ((Medication)(medAdmin.getContained().get(0))).getIngredientFirstRep().getItemReference().getReference());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainResourcesWithModify_DontTouchEmbeddedResources() {
|
||||
|
||||
MedicationRequest mr = new MedicationRequest();
|
||||
mr.setMedication(new Reference(new Medication().setStatus(Medication.MedicationStatus.ACTIVE)));
|
||||
mr.getRequester().setResource(new Practitioner().setActive(true));
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.addEntry().setResource(mr);
|
||||
|
||||
myCtx.newTerser().containResources(bundle, FhirTerser.OptionsEnum.MODIFY_RESOURCE);
|
||||
|
||||
assertEquals(0, mr.getContained().size());
|
||||
assertEquals(null, mr.getRequester().getReference());
|
||||
assertEquals( null, mr.getMedicationReference().getReference());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetValuesCreateEnumeration_SetsEnumFactory() {
|
||||
|
||||
Patient patient = new Patient();
|
||||
|
||||
Enumeration<?> enumeration = (Enumeration<?>) ourCtx.newTerser().getValues(patient, "Patient.gender", Enumeration.class, true).get(0);
|
||||
Enumeration<?> enumeration = (Enumeration<?>) myCtx.newTerser().getValues(patient, "Patient.gender", Enumeration.class, true).get(0);
|
||||
assertNotNull(enumeration.getEnumFactory());
|
||||
}
|
||||
|
||||
|
@ -98,9 +163,9 @@ public class FhirTerserR4Test {
|
|||
.setUrl("Observation/obs")
|
||||
.setMethod(Bundle.HTTPVerb.PUT);
|
||||
|
||||
ourCtx.newTerser().clear(input);
|
||||
myCtx.newTerser().clear(input);
|
||||
|
||||
String output = ourCtx.newJsonParser().encodeResourceToString(input);
|
||||
String output = myCtx.newJsonParser().encodeResourceToString(input);
|
||||
ourLog.info(output);
|
||||
assertTrue(input.isEmpty());
|
||||
assertEquals("{\"resourceType\":\"Bundle\"}", output);
|
||||
|
@ -132,7 +197,7 @@ public class FhirTerserR4Test {
|
|||
.setMethod(Bundle.HTTPVerb.PUT);
|
||||
|
||||
Bundle ionputClone = new Bundle();
|
||||
ourCtx.newTerser().cloneInto(input, ionputClone, false);
|
||||
myCtx.newTerser().cloneInto(input, ionputClone, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -141,7 +206,7 @@ public class FhirTerserR4Test {
|
|||
source.setCode("CODE");
|
||||
SimpleQuantity target = new SimpleQuantity();
|
||||
|
||||
ourCtx.newTerser().cloneInto(source, target, true);
|
||||
myCtx.newTerser().cloneInto(source, target, true);
|
||||
|
||||
assertEquals("CODE", target.getCode());
|
||||
}
|
||||
|
@ -153,12 +218,12 @@ public class FhirTerserR4Test {
|
|||
source.setUnit("UNIT");
|
||||
Identifier target = new Identifier();
|
||||
|
||||
ourCtx.newTerser().cloneInto(source, target, true);
|
||||
myCtx.newTerser().cloneInto(source, target, true);
|
||||
|
||||
assertEquals("SYSTEM", target.getSystem());
|
||||
|
||||
try {
|
||||
ourCtx.newTerser().cloneInto(source, target, false);
|
||||
myCtx.newTerser().cloneInto(source, target, false);
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
// good
|
||||
|
@ -175,7 +240,7 @@ public class FhirTerserR4Test {
|
|||
patient.addExtension(new Extension("http://example.com", new StringType("FOO")));
|
||||
|
||||
Patient target = new Patient();
|
||||
ourCtx.newTerser().cloneInto(patient, target, false);
|
||||
myCtx.newTerser().cloneInto(patient, target, false);
|
||||
|
||||
List<Extension> exts = target.getExtensionsByUrl("http://example.com");
|
||||
assertEquals(1, exts.size());
|
||||
|
@ -189,7 +254,7 @@ public class FhirTerserR4Test {
|
|||
|
||||
DocumentReference target = new DocumentReference();
|
||||
|
||||
ourCtx.newTerser().cloneInto(source, target, true);
|
||||
myCtx.newTerser().cloneInto(source, target, true);
|
||||
|
||||
assertEquals(1, target.getContentFirstRep().getAttachment().getDataElement().getExtension().size());
|
||||
assertEquals("http://foo", target.getContentFirstRep().getAttachment().getDataElement().getExtension().get(0).getUrl());
|
||||
|
@ -204,7 +269,7 @@ public class FhirTerserR4Test {
|
|||
patient.addExtension((Extension) new Extension().setUrl("http://foo").addExtension(ext));
|
||||
|
||||
Patient target = new Patient();
|
||||
ourCtx.newTerser().cloneInto(patient, target, false);
|
||||
myCtx.newTerser().cloneInto(patient, target, false);
|
||||
|
||||
List<Extension> exts = target.getExtensionsByUrl("http://foo");
|
||||
assertEquals(1, exts.size());
|
||||
|
@ -218,7 +283,7 @@ public class FhirTerserR4Test {
|
|||
patient.setGender(Enumerations.AdministrativeGender.MALE);
|
||||
|
||||
Patient target = new Patient();
|
||||
ourCtx.newTerser().cloneInto(patient, target, false);
|
||||
myCtx.newTerser().cloneInto(patient, target, false);
|
||||
|
||||
assertEquals("http://hl7.org/fhir/administrative-gender", target.getGenderElement().getSystem());
|
||||
}
|
||||
|
@ -229,7 +294,7 @@ public class FhirTerserR4Test {
|
|||
source.setId("STRING_ID");
|
||||
MarkdownType target = new MarkdownType();
|
||||
|
||||
ourCtx.newTerser().cloneInto(source, target, true);
|
||||
myCtx.newTerser().cloneInto(source, target, true);
|
||||
|
||||
assertEquals("STR", target.getValueAsString());
|
||||
assertEquals("STRING_ID", target.getId());
|
||||
|
@ -241,11 +306,11 @@ public class FhirTerserR4Test {
|
|||
StringType source = new StringType("STR");
|
||||
Money target = new Money();
|
||||
|
||||
ourCtx.newTerser().cloneInto(source, target, true);
|
||||
myCtx.newTerser().cloneInto(source, target, true);
|
||||
assertTrue(target.isEmpty());
|
||||
|
||||
try {
|
||||
ourCtx.newTerser().cloneInto(source, target, false);
|
||||
myCtx.newTerser().cloneInto(source, target, false);
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
// good
|
||||
|
@ -263,7 +328,7 @@ public class FhirTerserR4Test {
|
|||
obs.addNote().setText("COMMENTS");
|
||||
|
||||
Observation target = new Observation();
|
||||
ourCtx.newTerser().cloneInto(obs, target, false);
|
||||
myCtx.newTerser().cloneInto(obs, target, false);
|
||||
|
||||
assertEquals("AAA", ((StringType) obs.getValue()).getValue());
|
||||
assertEquals("COMMENTS", obs.getNote().get(0).getText());
|
||||
|
@ -278,7 +343,7 @@ public class FhirTerserR4Test {
|
|||
obs.addNote().setText("COMMENTS");
|
||||
|
||||
Observation target = new Observation();
|
||||
ourCtx.newTerser().cloneInto(obs, target, false);
|
||||
myCtx.newTerser().cloneInto(obs, target, false);
|
||||
|
||||
assertEquals("http://foo/base/Observation/_history/123", target.getId());
|
||||
}
|
||||
|
@ -292,7 +357,7 @@ public class FhirTerserR4Test {
|
|||
obs.setValue(string);
|
||||
|
||||
Observation target = new Observation();
|
||||
ourCtx.newTerser().cloneInto(obs, target, false);
|
||||
myCtx.newTerser().cloneInto(obs, target, false);
|
||||
|
||||
assertEquals("BBB", target.getValueStringType().getId());
|
||||
}
|
||||
|
@ -307,7 +372,7 @@ public class FhirTerserR4Test {
|
|||
o.getNameElement().setValue("ORGANIZATION");
|
||||
p.getContained().add(o);
|
||||
|
||||
FhirTerser t = ourCtx.newTerser();
|
||||
FhirTerser t = myCtx.newTerser();
|
||||
List<StringType> strings = t.getAllPopulatedChildElementsOfType(p, StringType.class);
|
||||
|
||||
assertThat(toStrings(strings), containsInAnyOrder("PATIENT", "ORGANIZATION"));
|
||||
|
@ -323,7 +388,7 @@ public class FhirTerserR4Test {
|
|||
b.addEntry().setResource(p);
|
||||
b.addLink().setRelation("BUNDLE");
|
||||
|
||||
FhirTerser t = ourCtx.newTerser();
|
||||
FhirTerser t = myCtx.newTerser();
|
||||
List<StringType> strings = t.getAllPopulatedChildElementsOfType(b, StringType.class);
|
||||
|
||||
assertEquals(1, strings.size());
|
||||
|
@ -342,7 +407,7 @@ public class FhirTerserR4Test {
|
|||
Extension ext = new Extension("urn:foo", ref);
|
||||
p.addExtension(ext);
|
||||
|
||||
FhirTerser t = ourCtx.newTerser();
|
||||
FhirTerser t = myCtx.newTerser();
|
||||
List<IBaseReference> refs = t.getAllPopulatedChildElementsOfType(p, IBaseReference.class);
|
||||
assertEquals(1, refs.size());
|
||||
assertSame(ref, refs.get(0));
|
||||
|
@ -367,29 +432,29 @@ public class FhirTerserR4Test {
|
|||
.setUrl("http://acme.org/childExtension")
|
||||
.setValue(new StringType("nestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -426,7 +491,7 @@ public class FhirTerserR4Test {
|
|||
innerPatient1.setActive(true);
|
||||
outerBundle.addEntry().setResource(innerPatient1);
|
||||
|
||||
FhirTerser t = ourCtx.newTerser();
|
||||
FhirTerser t = myCtx.newTerser();
|
||||
|
||||
Collection<IBaseResource> resources;
|
||||
|
||||
|
@ -452,7 +517,7 @@ public class FhirTerserR4Test {
|
|||
patient.getContained().add(practitioner1);
|
||||
patient.addGeneralPractitioner().setReference("#practitioner1");
|
||||
|
||||
FhirTerser t = ourCtx.newTerser();
|
||||
FhirTerser t = myCtx.newTerser();
|
||||
|
||||
Collection<IBaseResource> resources;
|
||||
|
||||
|
@ -489,9 +554,9 @@ public class FhirTerserR4Test {
|
|||
.setUrl("http://acme.org/childExtension")
|
||||
.setValue(new StringType("nestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
|
@ -499,15 +564,15 @@ public class FhirTerserR4Test {
|
|||
|
||||
((BooleanType) values.get(0)).setValue(Boolean.FALSE);
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.active");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertFalse(((BooleanType) values.get(0)).booleanValue());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -516,16 +581,16 @@ public class FhirTerserR4Test {
|
|||
|
||||
((Extension) values.get(0)).setValue(new StringType("modifiedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("modifiedValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -534,16 +599,16 @@ public class FhirTerserR4Test {
|
|||
|
||||
((Extension) values.get(0)).setValue(new StringType("modifiedModifierValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("modifiedModifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -552,9 +617,9 @@ public class FhirTerserR4Test {
|
|||
|
||||
((Extension) values.get(0)).setValue(new StringType("modifiedNestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -591,9 +656,9 @@ public class FhirTerserR4Test {
|
|||
.setUrl("http://acme.org/childExtension")
|
||||
.setValue(new StringType("nestedValue2"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -604,7 +669,7 @@ public class FhirTerserR4Test {
|
|||
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
|
||||
assertEquals("value2", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -615,7 +680,7 @@ public class FhirTerserR4Test {
|
|||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
|
||||
assertEquals("modifierValue2", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -646,26 +711,26 @@ public class FhirTerserR4Test {
|
|||
.setUrl("http://acme.org/childExtension")
|
||||
.setValue(new StringType("nestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<PrimitiveType> values = ourCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
|
||||
List<PrimitiveType> values = myCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||
|
||||
List<Extension> extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
|
||||
List<Extension> extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
|
||||
assertEquals(1, extValues.size());
|
||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
||||
assertEquals("value", ((StringType) (extValues.get(0).getValue())).getValueAsString());
|
||||
|
||||
extValues = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
|
||||
extValues = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
|
||||
assertEquals(1, extValues.size());
|
||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
||||
assertEquals("modifierValue", ((StringType) (extValues.get(0).getValue())).getValueAsString());
|
||||
|
||||
extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
|
||||
extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
|
||||
assertEquals(1, extValues.size());
|
||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
||||
|
@ -691,23 +756,23 @@ public class FhirTerserR4Test {
|
|||
.setUrl("http://acme.org/childExtension")
|
||||
.setValue(new StringType("nestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<PrimitiveType> values = ourCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
|
||||
List<PrimitiveType> values = myCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||
|
||||
((BooleanType) values.get(0)).setValue(Boolean.FALSE);
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertFalse(((BooleanType) values.get(0)).booleanValue());
|
||||
|
||||
List<Extension> extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
|
||||
List<Extension> extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
|
||||
assertEquals(1, extValues.size());
|
||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
||||
|
@ -715,15 +780,15 @@ public class FhirTerserR4Test {
|
|||
|
||||
extValues.get(0).setValue(new StringType("modifiedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
|
||||
extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class);
|
||||
assertEquals(1, extValues.size());
|
||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
||||
assertEquals("modifiedValue", ((StringType) (extValues.get(0).getValue())).getValueAsString());
|
||||
|
||||
extValues = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
|
||||
extValues = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
|
||||
assertEquals(1, extValues.size());
|
||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
||||
|
@ -731,15 +796,15 @@ public class FhirTerserR4Test {
|
|||
|
||||
extValues.get(0).setValue(new StringType("modifiedModifierValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
extValues = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
|
||||
extValues = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class);
|
||||
assertEquals(1, extValues.size());
|
||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
||||
assertEquals("modifiedModifierValue", ((StringType) (extValues.get(0).getValue())).getValueAsString());
|
||||
|
||||
extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
|
||||
extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
|
||||
assertEquals(1, extValues.size());
|
||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
||||
|
@ -747,9 +812,9 @@ public class FhirTerserR4Test {
|
|||
|
||||
extValues.get(0).setValue(new StringType("modifiedNestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
|
||||
extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class);
|
||||
assertEquals(1, extValues.size());
|
||||
assertTrue(extValues.get(0).getValue() instanceof StringType);
|
||||
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
||||
|
@ -760,24 +825,24 @@ public class FhirTerserR4Test {
|
|||
public void testGetValuesWithWantedClassAndTheCreate() {
|
||||
Patient p = new Patient();
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<PrimitiveType> values = ourCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class, true);
|
||||
List<PrimitiveType> values = myCtx.newTerser().getValues(p, "Patient.active", PrimitiveType.class, true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertNull(((BooleanType) values.get(0)).getValue());
|
||||
|
||||
List<Extension> extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class, true);
|
||||
List<Extension> extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", Extension.class, true);
|
||||
assertEquals(1, extValues.size());
|
||||
assertEquals("http://acme.org/extension", extValues.get(0).getUrl());
|
||||
assertNull(extValues.get(0).getValue());
|
||||
|
||||
extValues = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class, true);
|
||||
extValues = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", Extension.class, true);
|
||||
assertEquals(1, extValues.size());
|
||||
assertEquals("http://acme.org/modifierExtension", extValues.get(0).getUrl());
|
||||
assertNull(extValues.get(0).getValue());
|
||||
|
||||
extValues = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class, true);
|
||||
extValues = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", Extension.class, true);
|
||||
assertEquals(1, extValues.size());
|
||||
assertEquals("http://acme.org/childExtension", extValues.get(0).getUrl());
|
||||
assertNull(extValues.get(0).getValue());
|
||||
|
@ -802,29 +867,29 @@ public class FhirTerserR4Test {
|
|||
.setUrl("http://acme.org/childExtension")
|
||||
.setValue(new StringType("nestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||
|
||||
// No change.
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.active", false, true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.active", false, true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", false, true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", false, true);
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -837,21 +902,21 @@ public class FhirTerserR4Test {
|
|||
|
||||
((Extension) values.get(1)).setValue(new StringType("addedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
assertTrue(values.get(1) instanceof IBaseExtension);
|
||||
assertTrue(values.get(1) instanceof Extension);
|
||||
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
|
||||
assertEquals("addedValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", false, true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", false, true);
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -864,23 +929,23 @@ public class FhirTerserR4Test {
|
|||
|
||||
((Extension) values.get(1)).setValue(new StringType("addedModifierValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
assertTrue(values.get(1) instanceof IBaseExtension);
|
||||
assertTrue(values.get(1) instanceof Extension);
|
||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
|
||||
assertEquals("addedModifierValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/childExtension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("nestedValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", false, true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", false, true);
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -893,7 +958,7 @@ public class FhirTerserR4Test {
|
|||
|
||||
((Extension) values.get(1)).setValue(new StringType("addedNestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
assertTrue(values.get(1) instanceof IBaseExtension);
|
||||
assertTrue(values.get(1) instanceof Extension);
|
||||
|
@ -905,27 +970,27 @@ public class FhirTerserR4Test {
|
|||
public void testGetValuesWithTheCreate() {
|
||||
Patient p = new Patient();
|
||||
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active", true);
|
||||
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active", true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertNull(((BooleanType) values.get(0)).getValue());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||
assertNull(((Extension) values.get(0)).getValue());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||
assertNull(((Extension) values.get(0)).getValue());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -952,29 +1017,29 @@ public class FhirTerserR4Test {
|
|||
.setUrl("http://acme.org/childExtension")
|
||||
.setValue(new StringType("nestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active");
|
||||
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||
|
||||
// No change.
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.active", true, true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.active", true, true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true, true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true, true);
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -987,21 +1052,21 @@ public class FhirTerserR4Test {
|
|||
|
||||
((Extension) values.get(1)).setValue(new StringType("addedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
assertTrue(values.get(1) instanceof IBaseExtension);
|
||||
assertTrue(values.get(1) instanceof Extension);
|
||||
assertEquals("http://acme.org/extension", ((Extension) values.get(1)).getUrl());
|
||||
assertEquals("addedValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true, true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true, true);
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -1014,21 +1079,21 @@ public class FhirTerserR4Test {
|
|||
|
||||
((Extension) values.get(1)).setValue(new StringType("addedModifierValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
assertTrue(values.get(1) instanceof IBaseExtension);
|
||||
assertTrue(values.get(1) instanceof Extension);
|
||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(1)).getUrl());
|
||||
assertEquals("addedModifierValue", ((StringType) ((Extension) values.get(1)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')");
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/childExtension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("nestedValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true, true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true, true);
|
||||
assertEquals(2, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -1041,7 +1106,7 @@ public class FhirTerserR4Test {
|
|||
|
||||
((Extension) values.get(1)).setValue(new StringType("addedNestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
assertTrue(values.get(1) instanceof IBaseExtension);
|
||||
assertTrue(values.get(1) instanceof Extension);
|
||||
|
@ -1068,29 +1133,29 @@ public class FhirTerserR4Test {
|
|||
.setUrl("http://acme.org/childExtension")
|
||||
.setValue(new StringType("nestedValue"));
|
||||
|
||||
System.out.println(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
System.out.println(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p));
|
||||
|
||||
List<IBase> values = ourCtx.newTerser().getValues(p, "Patient.active", true);
|
||||
List<IBase> values = myCtx.newTerser().getValues(p, "Patient.active", true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof PrimitiveType);
|
||||
assertTrue(values.get(0) instanceof BooleanType);
|
||||
assertTrue(((BooleanType) values.get(0)).booleanValue());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/extension')", true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/extension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("value", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.modifierExtension('http://acme.org/modifierExtension')", true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
assertEquals("http://acme.org/modifierExtension", ((Extension) values.get(0)).getUrl());
|
||||
assertEquals("modifierValue", ((StringType) ((Extension) values.get(0)).getValue()).getValueAsString());
|
||||
|
||||
values = ourCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true);
|
||||
values = myCtx.newTerser().getValues(p, "Patient.extension('http://acme.org/parentExtension').extension('http://acme.org/childExtension')", true);
|
||||
assertEquals(1, values.size());
|
||||
assertTrue(values.get(0) instanceof IBaseExtension);
|
||||
assertTrue(values.get(0) instanceof Extension);
|
||||
|
@ -1107,7 +1172,7 @@ public class FhirTerserR4Test {
|
|||
vs.getExpansion().setIdentifier("http://foo");
|
||||
|
||||
Set<String> strings = new HashSet<>();
|
||||
ourCtx.newTerser().visit(vs, new IModelVisitor() {
|
||||
myCtx.newTerser().visit(vs, new IModelVisitor() {
|
||||
@Override
|
||||
public void acceptElement(IBaseResource theResource, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) {
|
||||
if (theElement instanceof IPrimitiveType) {
|
||||
|
@ -1118,7 +1183,7 @@ public class FhirTerserR4Test {
|
|||
assertThat(strings, Matchers.contains("http://foo"));
|
||||
|
||||
strings.clear();
|
||||
ourCtx.newTerser().visit(vs, new IModelVisitor2() {
|
||||
myCtx.newTerser().visit(vs, new IModelVisitor2() {
|
||||
@Override
|
||||
public boolean acceptElement(IBase theElement, List<IBase> theContainingElementPath, List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath) {
|
||||
if (theElement instanceof IPrimitiveType) {
|
||||
|
@ -1148,7 +1213,7 @@ public class FhirTerserR4Test {
|
|||
|
||||
Patient p = new Patient();
|
||||
p.addLink().getTypeElement().setValue(LinkType.REFER);
|
||||
ourCtx.newTerser().visit(p, visitor);
|
||||
myCtx.newTerser().visit(p, visitor);
|
||||
|
||||
assertEquals(3, element.getAllValues().size());
|
||||
assertSame(p, element.getAllValues().get(0));
|
||||
|
@ -1172,7 +1237,7 @@ public class FhirTerserR4Test {
|
|||
p.addAddress().addLine("Line2");
|
||||
p.addName().setFamily("Line3");
|
||||
|
||||
FhirTerser t = ourCtx.newTerser();
|
||||
FhirTerser t = myCtx.newTerser();
|
||||
List<StringType> strings = t.getAllPopulatedChildElementsOfType(p, StringType.class);
|
||||
|
||||
assertEquals(3, strings.size());
|
||||
|
@ -1192,7 +1257,7 @@ public class FhirTerserR4Test {
|
|||
Observation obs = new Observation();
|
||||
obs.setValue(new Quantity(123L));
|
||||
|
||||
FhirTerser t = ourCtx.newTerser();
|
||||
FhirTerser t = myCtx.newTerser();
|
||||
|
||||
// As string
|
||||
{
|
||||
|
@ -1243,8 +1308,8 @@ public class FhirTerserR4Test {
|
|||
"</Observation>";
|
||||
//@formatter:on
|
||||
|
||||
Observation parsed = ourCtx.newXmlParser().parseResource(Observation.class, msg);
|
||||
FhirTerser t = ourCtx.newTerser();
|
||||
Observation parsed = myCtx.newXmlParser().parseResource(Observation.class, msg);
|
||||
FhirTerser t = myCtx.newTerser();
|
||||
|
||||
List<Reference> elems = t.getAllPopulatedChildElementsOfType(parsed, Reference.class);
|
||||
assertEquals(2, elems.size());
|
||||
|
|
Loading…
Reference in New Issue