Merge branch 'master' into batch-empi-job

This commit is contained in:
Tadgh 2020-07-03 10:34:45 -07:00
commit 19f3b7b121
84 changed files with 1972 additions and 983 deletions

51
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,51 @@
name: "Code scanning - action"
on:
push:
pull_request:
schedule:
- cron: '0 8 * * 1'
jobs:
CodeQL-Build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -44,7 +44,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.5</version>
<version>8.0.16</version>
</dependency>
<!-- This dependency includes the core HAPI-FHIR classes -->

View File

@ -48,7 +48,7 @@
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <version>6.0.5</version>-->
<version>5.1.40</version>
<version>8.0.16</version>
</dependency>
<!--
<dependency>

View File

@ -21,13 +21,32 @@ package ca.uhn.fhir.context;
*/
import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.api.annotation.*;
import ca.uhn.fhir.model.api.BaseIdentifiableElement;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.IResourceBlock;
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Compartment;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.util.ReflectionUtil;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseDatatypeElement;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IBaseXhtml;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import javax.annotation.Nonnull;
import java.io.IOException;
@ -37,8 +56,18 @@ import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isBlank;
@ -390,7 +419,7 @@ class ModelScanner {
for (String nextName : searchParam.compositeOf()) {
RuntimeSearchParam param = nameToParam.get(nextName);
if (param == null) {
ourLog.warn("Search parameter {}.{} declares that it is a composite with compositeOf value '{}' but that is not a valid parametr name itself. Valid values are: {}",
ourLog.warn("Search parameter {}.{} declares that it is a composite with compositeOf value '{}' but that is not a valid parameter name itself. Valid values are: {}",
theResourceDef.getName(), searchParam.name(), nextName, nameToParam.keySet());
continue;
}

View File

@ -20,6 +20,13 @@ package ca.uhn.fhir.context;
* #L%
*/
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.UrlUtil;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IDomainResource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -28,15 +35,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IDomainResource;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.util.UrlUtil;
public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefinition<IBaseResource> {
private Class<? extends IBaseResource> myBaseType;

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.context;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
@ -55,6 +56,7 @@ public class RuntimeSearchParam {
private final RuntimeSearchParamStatusEnum myStatus;
private final String myUri;
private final Map<String, List<IBaseExtension<?, ?>>> myExtensions = new HashMap<>();
private IPhoneticEncoder myPhoneticEncoder;
/**
* Constructor
@ -245,7 +247,6 @@ public class RuntimeSearchParam {
return myProvidesMembershipInCompartments;
}
public enum RuntimeSearchParamStatusEnum {
ACTIVE,
DRAFT,
@ -253,4 +254,15 @@ public class RuntimeSearchParam {
UNKNOWN
}
public RuntimeSearchParam setPhoneticEncoder(IPhoneticEncoder thePhoneticEncoder) {
myPhoneticEncoder = thePhoneticEncoder;
return this;
}
public String encode(String theString) {
if (myPhoneticEncoder == null || theString == null) {
return theString;
}
return myPhoneticEncoder.encode(theString);
}
}

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.empi.rules.metric.matcher;
package ca.uhn.fhir.context.phonetic;
/*-
* #%L
* HAPI FHIR - Enterprise Master Patient Index
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
@ -25,22 +25,29 @@ import org.apache.commons.codec.StringEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StringEncoderMatcher implements IEmpiStringMatcher {
private static final Logger ourLog = LoggerFactory.getLogger(StringEncoderMatcher.class);
public class ApacheEncoder implements IPhoneticEncoder {
private static final Logger ourLog = LoggerFactory.getLogger(ApacheEncoder.class);
private final String myName;
private final StringEncoder myStringEncoder;
public StringEncoderMatcher(StringEncoder theStringEncoder) {
public ApacheEncoder(String theName, StringEncoder theStringEncoder) {
myName = theName;
myStringEncoder = theStringEncoder;
}
@Override
public boolean matches(String theLeftString, String theRightString) {
public String name() {
return myName;
}
@Override
public String encode(String theString) {
try {
return myStringEncoder.encode(theLeftString).equals(myStringEncoder.encode(theRightString));
return myStringEncoder.encode(theString);
} catch (EncoderException e) {
ourLog.error("Failed to match strings '{}' and '{}' using encoder {}", theLeftString, theRightString, myStringEncoder.getClass().getName(), e);
}
return false;
ourLog.error("Failed to encode string " + theString, e);
return theString;
}
}
}

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.empi.rules.metric.matcher;
package ca.uhn.fhir.context.phonetic;
/*-
* #%L
* HAPI FHIR - Enterprise Master Patient Index
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
@ -20,11 +20,13 @@ package ca.uhn.fhir.empi.rules.metric.matcher;
* #L%
*/
import org.apache.commons.codec.language.Metaphone;
public interface IPhoneticEncoder {
String name();
public class MetaphoneStringMatcher implements IEmpiStringMatcher {
@Override
public boolean matches(String theLeftString, String theRightString) {
return new Metaphone().isMetaphoneEqual(theLeftString, theRightString);
}
/**
* Encode the provided string using a phonetic encoder like Soundex
* @param theString
* @return
*/
String encode(String theString);
}

View File

@ -0,0 +1,53 @@
package ca.uhn.fhir.context.phonetic;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.apache.commons.codec.language.Caverphone1;
import org.apache.commons.codec.language.Caverphone2;
import org.apache.commons.codec.language.ColognePhonetic;
import org.apache.commons.codec.language.DoubleMetaphone;
import org.apache.commons.codec.language.MatchRatingApproachEncoder;
import org.apache.commons.codec.language.Metaphone;
import org.apache.commons.codec.language.Nysiis;
import org.apache.commons.codec.language.RefinedSoundex;
import org.apache.commons.codec.language.Soundex;
public enum PhoneticEncoderEnum {
CAVERPHONE1(new ApacheEncoder("CAVERPHONE1", new Caverphone1())),
CAVERPHONE2(new ApacheEncoder("CAVERPHONE2", new Caverphone2())),
COLOGNE(new ApacheEncoder("COLOGNE", new ColognePhonetic())),
DOUBLE_METAPHONE(new ApacheEncoder("DOUBLE_METAPHONE", new DoubleMetaphone())),
MATCH_RATING_APPROACH(new ApacheEncoder("MATCH_RATING_APPROACH", new MatchRatingApproachEncoder())),
METAPHONE(new ApacheEncoder("METAPHONE", new Metaphone())),
NYSIIS(new ApacheEncoder("NYSIIS", new Nysiis())),
REFINED_SOUNDEX(new ApacheEncoder("REFINED_SOUNDEX", new RefinedSoundex())),
SOUNDEX(new ApacheEncoder("SOUNDEX", new Soundex()));
private final IPhoneticEncoder myPhoneticEncoder;
PhoneticEncoderEnum(IPhoneticEncoder thePhoneticEncoder) {
myPhoneticEncoder = thePhoneticEncoder;
}
public IPhoneticEncoder getPhoneticEncoder() {
return myPhoneticEncoder;
}
}

View File

@ -252,15 +252,6 @@ public class Constants {
* Operation name for the $lastn operation
*/
public static final String OPERATION_LASTN = "$lastn";
/**
* <p>
* This extension represents the equivalent of the
* <code>Resource.meta.source</code> field within R4+ resources, and is for
* use in DSTU3 resources. It should contain a value of type <code>uri</code>
* and will be located on the Resource.meta
* </p>
*/
public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source";
public static final String PARAM_FHIRPATH = "_fhirpath";
public static final String PARAM_TYPE = "_type";

View File

@ -9,6 +9,7 @@ import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.binder.QueryParameterAndBinder;
@ -386,4 +387,10 @@ public class ParameterUtil {
return b.toString();
}
/**
* Returns true if the value is :iterate or :recurse (the former name of :iterate) for an _include parameter
*/
public static boolean isIncludeIterate(String theQualifier) {
return Constants.PARAM_INCLUDE_QUALIFIER_RECURSE.equals(theQualifier) || Constants.PARAM_INCLUDE_QUALIFIER_ITERATE.equals(theQualifier);
}
}

View File

@ -0,0 +1,111 @@
package ca.uhn.fhir.util;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class HapiExtensions {
/**
* <p>
* This extension should be of type <code>string</code> and should be
* placed on the <code>Subscription.channel</code> element
* </p>
*/
public static final String EXT_SUBSCRIPTION_SUBJECT_TEMPLATE = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template";
/**
* This extension URL indicates whether a REST HOOK delivery should
* include the version ID when delivering.
* <p>
* This extension should be of type <code>boolean</code> and should be
* placed on the <code>Subscription.channel</code> element.
* </p>
*/
public static final String EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-strip-version-ids";
/**
* This extension URL indicates whether a REST HOOK delivery should
* reload the resource and deliver the latest version always. This
* could be useful for example if a resource which triggers a
* subscription gets updated many times in short succession and there
* is no value in delivering the older versions.
* <p>
* Note that if the resource is now deleted, this may cause
* the delivery to be cancelled altogether.
* </p>
*
* <p>
* This extension should be of type <code>boolean</code> and should be
* placed on the <code>Subscription.channel</code> element.
* </p>
*/
public static final String EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-deliver-latest-version";
/**
* Indicate which strategy will be used to match this subscription
*/
public static final String EXT_SUBSCRIPTION_MATCHING_STRATEGY = "http://hapifhir.io/fhir/StructureDefinition/subscription-matching-strategy";
/**
* <p>
* This extension should be of type <code>string</code> and should be
* placed on the <code>Subscription.channel</code> element
* </p>
*/
public static final String EXT_SUBSCRIPTION_EMAIL_FROM = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-from";
/**
* Extension ID for external binary references
*/
public static final String EXT_EXTERNALIZED_BINARY_ID = "http://hapifhir.io/fhir/StructureDefinition/externalized-binary-id";
/**
* For subscription, deliver a bundle containing a search result instead of just a single resource
*/
public static final String EXT_SUBSCRIPTION_PAYLOAD_SEARCH_CRITERIA = "http://hapifhir.io/fhir/StructureDefinition/subscription-payload-search-criteria";
/**
* Message added to expansion valueset
*/
public static final String EXT_VALUESET_EXPANSION_MESSAGE = "http://hapifhir.io/fhir/StructureDefinition/valueset-expansion-message";
/**
* Extension URL for extension on a SearchParameter indicating that text values should not be indexed
*/
public static final String EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING = "http://hapifhir.io/fhir/StructureDefinition/searchparameter-token-suppress-text-index";
/**
* <p>
* This extension represents the equivalent of the
* <code>Resource.meta.source</code> field within R4+ resources, and is for
* use in DSTU3 resources. It should contain a value of type <code>uri</code>
* and will be located on the Resource.meta
* </p>
*/
public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source";
public static final String EXT_SP_UNIQUE = "http://hapifhir.io/fhir/StructureDefinition/sp-unique";
/**
* Non instantiable
*/
private HapiExtensions() {
}
}

View File

@ -24,7 +24,6 @@ import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.api.Constants;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
@ -58,7 +57,7 @@ public class MetaUtil {
IBaseHasExtensions metaWithExtensions = theMeta;
List<? extends IBaseExtension<?, ?>> extensions = metaWithExtensions.getExtension();
for (IBaseExtension extension : extensions) {
if (Constants.EXT_META_SOURCE.equals(extension.getUrl())) {
if (HapiExtensions.EXT_META_SOURCE.equals(extension.getUrl())) {
IPrimitiveType<String> value = (IPrimitiveType<String>) extension.getValue();
return value.getValueAsString();
}
@ -96,7 +95,7 @@ public class MetaUtil {
MetaUtil.setSource(theContext, theResource.getMeta(), theValue);
} else if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
IBaseExtension<?, ?> sourceExtension = ((IBaseHasExtensions) theResource.getMeta()).addExtension();
sourceExtension.setUrl(Constants.EXT_META_SOURCE);
sourceExtension.setUrl(HapiExtensions.EXT_META_SOURCE);
IPrimitiveType<String> value = (IPrimitiveType<String>) theContext.getElementDefinition("uri").newInstance();
value.setValue(theValue);
sourceExtension.setValue(value);

View File

@ -0,0 +1,130 @@
package ca.uhn.fhir.util;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.thymeleaf.util.Validate;
/**
* This class can be used to build a Bundle resource to be used as a FHIR transaction.
*
* This is not yet complete, and doesn't support all FHIR features. <b>USE WITH CAUTION</b> as the API
* may change.
*
* @since 5.1.0
*/
public class TransactionBuilder {
private final FhirContext myContext;
private final IBaseBundle myBundle;
private final RuntimeResourceDefinition myBundleDef;
private final BaseRuntimeChildDefinition myEntryChild;
private final BaseRuntimeElementDefinition<?> myEntryDef;
private final BaseRuntimeChildDefinition myEntryResourceChild;
private final BaseRuntimeChildDefinition myEntryFullUrlChild;
private final BaseRuntimeChildDefinition myEntryRequestChild;
private final BaseRuntimeElementDefinition<?> myEntryRequestDef;
private final BaseRuntimeChildDefinition myEntryRequestUrlChild;
private final BaseRuntimeChildDefinition myEntryRequestMethodChild;
private final BaseRuntimeElementDefinition<?> myEntryRequestMethodDef;
/**
* Constructor
*/
public TransactionBuilder(FhirContext theContext) {
myContext = theContext;
myBundleDef = myContext.getResourceDefinition("Bundle");
myBundle = (IBaseBundle) myBundleDef.newInstance();
BaseRuntimeChildDefinition typeChild = myBundleDef.getChildByName("type");
IPrimitiveType<?> type = (IPrimitiveType<?>) typeChild.getChildByName("type").newInstance(typeChild.getInstanceConstructorArguments());
type.setValueAsString("transaction");
typeChild.getMutator().setValue(myBundle, type);
myEntryChild = myBundleDef.getChildByName("entry");
myEntryDef = myEntryChild.getChildByName("entry");
myEntryResourceChild = myEntryDef.getChildByName("resource");
myEntryFullUrlChild = myEntryDef.getChildByName("fullUrl");
myEntryRequestChild = myEntryDef.getChildByName("request");
myEntryRequestDef = myEntryRequestChild.getChildByName("request");
myEntryRequestUrlChild = myEntryRequestDef.getChildByName("url");
myEntryRequestMethodChild = myEntryRequestDef.getChildByName("method");
myEntryRequestMethodDef = myEntryRequestMethodChild.getChildByName("method");
}
/**
* Adds an entry containing an update (PUT) request
*
* @param theResource The resource to update
*/
public TransactionBuilder addUpdateEntry(IBaseResource theResource) {
Validate.notNull(theResource, "theResource must not be null");
Validate.notEmpty(theResource.getIdElement().getValue(), "theResource must have an ID");
IBase entry = myEntryDef.newInstance();
myEntryChild.getMutator().addValue(myBundle, entry);
// Bundle.entry.fullUrl
IPrimitiveType<?> fullUrl = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
fullUrl.setValueAsString(theResource.getIdElement().getValue());
myEntryFullUrlChild.getMutator().setValue(entry, fullUrl);
// Bundle.entry.resource
myEntryResourceChild.getMutator().setValue(entry, theResource);
// Bundle.entry.request
IBase request = myEntryRequestDef.newInstance();
myEntryRequestChild.getMutator().setValue(entry, request);
// Bundle.entry.request.url
IPrimitiveType<?> url = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
url.setValueAsString(theResource.getIdElement().toUnqualifiedVersionless().getValue());
myEntryRequestUrlChild.getMutator().setValue(request, url);
// Bundle.entry.request.url
IPrimitiveType<?> method = (IPrimitiveType<?>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
method.setValueAsString("PUT");
myEntryRequestMethodChild.getMutator().setValue(request, method);
return this;
}
public IBaseBundle getBundle() {
return myBundle;
}
}

View File

@ -0,0 +1,4 @@
---
type: add
issue: 1936
title: "Phonetic name search is now supported via ISearchParamRegistry.setStringEncoder()."

View File

@ -0,0 +1,5 @@
---
type: add
issue: 1951
title: "REST Hook subscriptions may now specify a search expression to be used to fetch a collection of
resources to deliver when a subscription matches."

View File

@ -158,46 +158,6 @@ The following metrics are currently supported:
</tr>
</thead>
<tbody>
<tr>
<td>STRING</td>
<td>matcher</td>
<td>
Match the values as strings. This matcher should be used with tokens (e.g. gender).
</td>
<td>MCTAVISH = McTavish when exact = false, MCTAVISH != McTavish when exact = true</td>
</tr>
<tr>
<td>SUBSTRING</td>
<td>matcher</td>
<td>
True if one string starts with the other.
</td>
<td>Bill = Billy, Egbert = Bert</td>
</tr>
<tr>
<td>METAPHONE</td>
<td>matcher</td>
<td>
<a href="https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/Metaphone.html">Apache Metaphone</a>
</td>
<td>Dury = Durie, Allsop != Allsob, Smith != Schmidt</td>
</tr>
<tr>
<td>DOUBLE_METAPHONE</td>
<td>matcher</td>
<td>
<a href="https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/DoubleMetaphone.html">Apache Double Metaphone</a>
</td>
<td>Dury = Durie, Allsop = Allsob, Smith != Schmidt</td>
</tr>
<tr>
<td>SOUNDEX</td>
<td>matcher</td>
<td>
<a href="https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/Soundex.html">Apache Soundex</a>
</td>
<td>Jon = John, Thomas != Tom</td>
</tr>
<tr>
<td>CAVERPHONE1</td>
<td>matcher</td>
@ -214,6 +174,78 @@ The following metrics are currently supported:
</td>
<td>Gail = Gael, Gail = Gale, Thomas != Tom</td>
</tr>
<tr>
<td>COLOGNE</td>
<td>matcher</td>
<td>
<a href="https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/ColognePhonetic.html">Apache Cologne Phonetic</a>
</td>
<td></td>
</tr>
<tr>
<td>DOUBLE_METAPHONE</td>
<td>matcher</td>
<td>
<a href="https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/DoubleMetaphone.html">Apache Double Metaphone</a>
</td>
<td>Dury = Durie, Allsop = Allsob, Smith != Schmidt</td>
</tr>
<tr>
<td>MATCH_RATING_APPROACH</td>
<td>matcher</td>
<td>
<a href="https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/MatchRatingApproachEncoder.html">Apache Match Rating Approach Encoder</a>
</td>
<td></td>
</tr>
<tr>
<td>METAPHONE</td>
<td>matcher</td>
<td>
<a href="https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/Metaphone.html">Apache Metaphone</a>
</td>
<td>Dury = Durie, Allsop != Allsob, Smith != Schmidt</td>
</tr>
<tr>
<td>NYSIIS</td>
<td>matcher</td>
<td>
<a href="https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/Nysiis.html">Apache Nysiis</a>
</td>
<td></td>
</tr>
<tr>
<td>REFINED_SOUNDEX</td>
<td>matcher</td>
<td>
<a href="https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/RefinedSoundex.html">Apache Refined Soundex</a>
</td>
<td></td>
</tr>
<tr>
<td>SOUNDEX</td>
<td>matcher</td>
<td>
<a href="https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/language/Soundex.html">Apache Soundex</a>
</td>
<td>Jon = John, Thomas != Tom</td>
</tr>
<tr>
<td>STRING</td>
<td>matcher</td>
<td>
Match the values as strings. This matcher should be used with tokens (e.g. gender).
</td>
<td>MCTAVISH = McTavish when exact = false, MCTAVISH != McTavish when exact = true</td>
</tr>
<tr>
<td>SUBSTRING</td>
<td>matcher</td>
<td>
True if one string starts with the other.
</td>
<td>Bill = Billy, Egbert = Bert</td>
</tr>
<tr>
<td>DATE</td>
<td>matcher</td>

View File

@ -4,8 +4,8 @@ import ca.uhn.fhir.jpa.api.model.WarmCacheEntry;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
import ca.uhn.fhir.util.HapiExtensions;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.Validate;
@ -1417,7 +1417,7 @@ public class DaoConfig {
/**
* If set to <code>true</code> (default is <code>true</code>), indexes will be
* created for search parameters marked as {@link SearchParamConstants#EXT_SP_UNIQUE}.
* created for search parameters marked as {@link HapiExtensions#EXT_SP_UNIQUE}.
* This is a HAPI FHIR specific extension which can be used to specify that no more than one
* resource can exist which matches a given criteria, using a database constraint to
* enforce this.
@ -1428,7 +1428,7 @@ public class DaoConfig {
/**
* If set to <code>true</code> (default is <code>true</code>), indexes will be
* created for search parameters marked as {@link SearchParamConstants#EXT_SP_UNIQUE}.
* created for search parameters marked as {@link HapiExtensions#EXT_SP_UNIQUE}.
* This is a HAPI FHIR specific extension which can be used to specify that no more than one
* resource can exist which matches a given criteria, using a database constraint to
* enforce this.

View File

@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.AttachmentUtil;
import ca.uhn.fhir.util.BinaryUtil;
import ca.uhn.fhir.util.DateUtils;
import ca.uhn.fhir.util.HapiExtensions;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
@ -209,11 +210,11 @@ public class BinaryAccessProvider {
theTarget
.getTarget()
.getExtension()
.removeIf(t -> JpaConstants.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()));
.removeIf(t -> HapiExtensions.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()));
theTarget.setData(null);
IBaseExtension<?, ?> ext = theTarget.getTarget().addExtension();
ext.setUrl(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
ext.setUrl(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID);
ext.setUserData(JpaConstants.EXTENSION_EXT_SYSTEMDEFINED, Boolean.TRUE);
IPrimitiveType<String> blobIdString = (IPrimitiveType<String>) myCtx.getElementDefinition("string").newInstance();
blobIdString.setValueAsString(theBlobId);

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.IModelVisitor2;
import org.apache.commons.io.FileUtils;
import org.hl7.fhir.instance.model.api.IBase;
@ -56,7 +57,7 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static ca.uhn.fhir.jpa.model.util.JpaConstants.EXT_EXTERNALIZED_BINARY_ID;
import static ca.uhn.fhir.util.HapiExtensions.EXT_EXTERNALIZED_BINARY_ID;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Interceptor
@ -106,7 +107,7 @@ public class BinaryStorageInterceptor {
List<String> attachmentIds = binaryElements
.stream()
.flatMap(t -> ((IBaseHasExtensions) t).getExtension().stream())
.filter(t -> JpaConstants.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()))
.filter(t -> HapiExtensions.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()))
.map(t -> ((IPrimitiveType<?>) t.getValue()).getValueAsString())
.collect(Collectors.toList());

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.binstore;
* #L%
*/
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.util.HapiExtensions;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
@ -51,7 +51,7 @@ interface IBinaryTarget {
return getTarget()
.getExtension()
.stream()
.filter(t -> JpaConstants.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()))
.filter(t -> HapiExtensions.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()))
.filter(t -> t.getValue() instanceof IPrimitiveType)
.map(t -> (IPrimitiveType<String>) t.getValue())
.map(t -> t.getValue())

View File

@ -83,6 +83,7 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.MetaUtil;
import ca.uhn.fhir.util.XmlUtil;
import com.google.common.annotations.VisibleForTesting;
@ -1130,7 +1131,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
source = ((IBaseHasExtensions) theResource.getMeta())
.getExtension()
.stream()
.filter(t -> Constants.EXT_META_SOURCE.equals(t.getUrl()))
.filter(t -> HapiExtensions.EXT_META_SOURCE.equals(t.getUrl()))
.filter(t -> t.getValue() instanceof IPrimitiveType)
.map(t -> ((IPrimitiveType<?>) t.getValue()).getValueAsString())
.findFirst()

View File

@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.dao.dstu3;
*/
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDaoObservation;
import ca.uhn.fhir.jpa.dao.ObservationLastNIndexPersistSvc;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
@ -32,16 +31,12 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.dstu3.model.Observation;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
public class FhirResourceDaoObservationDstu3 extends BaseHapiFhirResourceDaoObservation<Observation> {
@Autowired
ObservationLastNIndexPersistSvc myObservationLastNIndexPersistSvc;
@Override
public IBundleProvider observationsLastN(SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
* #L%
*/
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.model.api.IQueryParameterType;
@ -30,7 +31,7 @@ import java.util.List;
public interface IPredicateBuilder {
@Nullable
Predicate addPredicate(String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
List<? extends IQueryParameterType> theList,
SearchFilterParser.CompareOperation operation,
RequestPartitionId theRequestPartitionId);

View File

@ -20,13 +20,22 @@ package ca.uhn.fhir.jpa.dao.predicate;
* #L%
*/
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.model.entity.*;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import javax.persistence.criteria.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Subquery;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -56,40 +65,40 @@ public class PredicateBuilder {
myPredicateBuilderUri = thePredicateBuilderFactory.newPredicateBuilderUri(theSearchBuilder);
}
void addPredicateCoords(String theResourceName, String theParamName, List<? extends IQueryParameterType> theNextAnd, RequestPartitionId theRequestPartitionId) {
myPredicateBuilderCoords.addPredicate(theResourceName, theParamName, theNextAnd, null, theRequestPartitionId);
void addPredicateCoords(String theResourceName, RuntimeSearchParam theSearchParam, List<? extends IQueryParameterType> theNextAnd, RequestPartitionId theRequestPartitionId) {
myPredicateBuilderCoords.addPredicate(theResourceName, theSearchParam, theNextAnd, null, theRequestPartitionId);
}
Predicate addPredicateDate(String theResourceName, String theParamName, List<? extends IQueryParameterType> theNextAnd, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderDate.addPredicate(theResourceName, theParamName, theNextAnd, theOperation, theRequestPartitionId);
Predicate addPredicateDate(String theResourceName, RuntimeSearchParam theSearchParam, List<? extends IQueryParameterType> theNextAnd, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderDate.addPredicate(theResourceName, theSearchParam, theNextAnd, theOperation, theRequestPartitionId);
}
Predicate addPredicateNumber(String theResourceName, String theParamName, List<? extends IQueryParameterType> theNextAnd, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderNumber.addPredicate(theResourceName, theParamName, theNextAnd, theOperation, theRequestPartitionId);
Predicate addPredicateNumber(String theResourceName, RuntimeSearchParam theSearchParam, List<? extends IQueryParameterType> theNextAnd, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderNumber.addPredicate(theResourceName, theSearchParam, theNextAnd, theOperation, theRequestPartitionId);
}
Predicate addPredicateQuantity(String theResourceName, String theParamName, List<? extends IQueryParameterType> theNextAnd, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderQuantity.addPredicate(theResourceName, theParamName, theNextAnd, theOperation, theRequestPartitionId);
Predicate addPredicateQuantity(String theResourceName, RuntimeSearchParam theSearchParam, List<? extends IQueryParameterType> theNextAnd, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderQuantity.addPredicate(theResourceName, theSearchParam, theNextAnd, theOperation, theRequestPartitionId);
}
void addPredicateString(String theResourceName, String theParamName, List<? extends IQueryParameterType> theNextAnd, RequestPartitionId theRequestPartitionId) {
myPredicateBuilderString.addPredicate(theResourceName, theParamName, theNextAnd, SearchFilterParser.CompareOperation.sw, theRequestPartitionId);
void addPredicateString(String theResourceName, RuntimeSearchParam theSearchParam, List<? extends IQueryParameterType> theNextAnd, RequestPartitionId theRequestPartitionId) {
myPredicateBuilderString.addPredicate(theResourceName, theSearchParam, theNextAnd, SearchFilterParser.CompareOperation.sw, theRequestPartitionId);
}
Predicate addPredicateString(String theResourceName, String theParamName, List<? extends IQueryParameterType> theNextAnd, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderString.addPredicate(theResourceName, theParamName, theNextAnd, theOperation, theRequestPartitionId);
Predicate addPredicateString(String theResourceName,RuntimeSearchParam theSearchParam, List<? extends IQueryParameterType> theNextAnd, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderString.addPredicate(theResourceName, theSearchParam, theNextAnd, theOperation, theRequestPartitionId);
}
void addPredicateTag(List<List<IQueryParameterType>> theAndOrParams, String theParamName, RequestPartitionId theRequestPartitionId) {
myPredicateBuilderTag.addPredicateTag(theAndOrParams, theParamName, theRequestPartitionId);
}
Predicate addPredicateToken(String theResourceName, String theParamName, List<? extends IQueryParameterType> theNextAnd, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderToken.addPredicate(theResourceName, theParamName, theNextAnd, theOperation, theRequestPartitionId);
Predicate addPredicateToken(String theResourceName, RuntimeSearchParam theSearchParam, List<? extends IQueryParameterType> theNextAnd, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderToken.addPredicate(theResourceName, theSearchParam, theNextAnd, theOperation, theRequestPartitionId);
}
Predicate addPredicateUri(String theResourceName, String theName, List<? extends IQueryParameterType> theSingletonList, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderUri.addPredicate(theResourceName, theName, theSingletonList, theOperation, theRequestPartitionId);
Predicate addPredicateUri(String theResourceName, RuntimeSearchParam theSearchParam, List<? extends IQueryParameterType> theSingletonList, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderUri.addPredicate(theResourceName, theSearchParam, theSingletonList, theOperation, theRequestPartitionId);
}
public void searchForIdsWithAndOr(String theResourceName, String theNextParamName, List<List<IQueryParameterType>> theAndOrParams, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
@ -112,12 +121,12 @@ public class PredicateBuilder {
return myPredicateBuilderResourceId.addPredicateResourceId(theValues, theResourceName, theOperation, theRequestPartitionId);
}
Predicate createPredicateString(IQueryParameterType theLeftValue, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theStringJoin, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderString.createPredicateString(theLeftValue, theResourceName, theName, theBuilder, theStringJoin, theRequestPartitionId);
Predicate createPredicateString(IQueryParameterType theLeftValue, String theResourceName, RuntimeSearchParam theSearchParam, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theStringJoin, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderString.createPredicateString(theLeftValue, theResourceName, theSearchParam, theBuilder, theStringJoin, theRequestPartitionId);
}
Collection<Predicate> createPredicateToken(List<IQueryParameterType> theTokens, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theTokenJoin, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderToken.createPredicateToken(theTokens, theResourceName, theName, theBuilder, theTokenJoin, theRequestPartitionId);
Collection<Predicate> createPredicateToken(List<IQueryParameterType> theTokens, String theResourceName, RuntimeSearchParam theSearchParam, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theTokenJoin, RequestPartitionId theRequestPartitionId) {
return myPredicateBuilderToken.createPredicateToken(theTokens, theResourceName, theSearchParam, theBuilder, theTokenJoin, theRequestPartitionId);
}
Predicate createPredicateDate(IQueryParameterType theLeftValue, String theResourceName, String theName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> theDateJoin, RequestPartitionId theRequestPartitionId) {

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
* #L%
*/
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
@ -55,7 +56,7 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre
private Predicate createPredicateCoords(IQueryParameterType theParam,
String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
CriteriaBuilder theBuilder,
From<?, ResourceIndexedSearchParamCoords> theFrom,
RequestPartitionId theRequestPartitionId) {
@ -119,7 +120,7 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre
longitudePredicate = longitudePredicateFromBox(theBuilder, theFrom, box);
}
Predicate singleCode = theBuilder.and(latitudePredicate, longitudePredicate);
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, theRequestPartitionId);
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theSearchParam.getName(), theFrom, singleCode, theRequestPartitionId);
}
private Predicate latitudePredicateFromBox(CriteriaBuilder theBuilder, From<?, ResourceIndexedSearchParamCoords> theFrom, SearchBox theBox) {
@ -145,14 +146,14 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre
@Override
public Predicate addPredicate(String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
List<? extends IQueryParameterType> theList,
SearchFilterParser.CompareOperation theOperation,
RequestPartitionId theRequestPartitionId) {
From<?, ResourceIndexedSearchParamCoords> join = myQueryStack.createJoin(SearchBuilderJoinEnum.COORDS, theParamName);
From<?, ResourceIndexedSearchParamCoords> join = myQueryStack.createJoin(SearchBuilderJoinEnum.COORDS, theSearchParam.getName());
if (theList.get(0).getMissing() != null) {
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
addPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), join, theRequestPartitionId);
return null;
}
@ -163,7 +164,7 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre
Predicate singleCode = createPredicateCoords(nextOr,
theResourceName,
theParamName,
theSearchParam,
myCriteriaBuilder,
join,
theRequestPartitionId);

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
* #L%
*/
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
@ -56,27 +57,28 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
@Override
public Predicate addPredicate(String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
List<? extends IQueryParameterType> theList,
SearchFilterParser.CompareOperation operation,
RequestPartitionId theRequestPartitionId) {
String paramName = theSearchParam.getName();
boolean newJoin = false;
if (myJoinMap == null) {
myJoinMap = new HashMap<>();
}
String key = theResourceName + " " + theParamName;
String key = theResourceName + " " + paramName;
From<?, ResourceIndexedSearchParamDate> join = myJoinMap.get(key);
if (join == null) {
join = myQueryStack.createJoin(SearchBuilderJoinEnum.DATE, theParamName);
join = myQueryStack.createJoin(SearchBuilderJoinEnum.DATE, paramName);
myJoinMap.put(key, join);
newJoin = true;
}
if (theList.get(0).getMissing() != null) {
Boolean missing = theList.get(0).getMissing();
addPredicateParamMissingForNonReference(theResourceName, theParamName, missing, join, theRequestPartitionId);
addPredicateParamMissingForNonReference(theResourceName, paramName, missing, join, theRequestPartitionId);
return null;
}
@ -94,7 +96,7 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
Predicate orPredicates = myCriteriaBuilder.or(toArray(codePredicates));
if (newJoin) {
Predicate identityAndValuePredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, orPredicates, theRequestPartitionId);
Predicate identityAndValuePredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, paramName, join, orPredicates, theRequestPartitionId);
myQueryStack.addPredicateWithImplicitTypeSelection(identityAndValuePredicate);
} else {
myQueryStack.addPredicateWithImplicitTypeSelection(orPredicates);

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
* #L%
*/
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
@ -51,15 +52,15 @@ class PredicateBuilderNumber extends BasePredicateBuilder implements IPredicateB
@Override
public Predicate addPredicate(String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
List<? extends IQueryParameterType> theList,
SearchFilterParser.CompareOperation operation,
RequestPartitionId theRequestPartitionId) {
From<?, ResourceIndexedSearchParamNumber> join = myQueryStack.createJoin(SearchBuilderJoinEnum.NUMBER, theParamName);
From<?, ResourceIndexedSearchParamNumber> join = myQueryStack.createJoin(SearchBuilderJoinEnum.NUMBER, theSearchParam.getName());
if (theList.get(0).getMissing() != null) {
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
addPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), join, theRequestPartitionId);
return null;
}
@ -97,8 +98,8 @@ class PredicateBuilderNumber extends BasePredicateBuilder implements IPredicateB
String invalidMessageName = "invalidNumberPrefix";
Predicate predicateNumeric = createPredicateNumeric(theResourceName, theParamName, join, myCriteriaBuilder, nextOr, prefix, value, fromObj, invalidMessageName, theRequestPartitionId);
Predicate predicateOuter = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, predicateNumeric, theRequestPartitionId);
Predicate predicateNumeric = createPredicateNumeric(theResourceName, theSearchParam.getName(), join, myCriteriaBuilder, nextOr, prefix, value, fromObj, invalidMessageName, theRequestPartitionId);
Predicate predicateOuter = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theSearchParam.getName(), join, predicateNumeric, theRequestPartitionId);
codePredicates.add(predicateOuter);
} else {

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
* #L%
*/
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
@ -52,15 +53,15 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
@Override
public Predicate addPredicate(String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
List<? extends IQueryParameterType> theList,
SearchFilterParser.CompareOperation theOperation,
RequestPartitionId theRequestPartitionId) {
From<?, ResourceIndexedSearchParamQuantity> join = myQueryStack.createJoin(SearchBuilderJoinEnum.QUANTITY, theParamName);
From<?, ResourceIndexedSearchParamQuantity> join = myQueryStack.createJoin(SearchBuilderJoinEnum.QUANTITY, theSearchParam.getName());
if (theList.get(0).getMissing() != null) {
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
addPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), join, theRequestPartitionId);
return null;
}
@ -68,7 +69,7 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
addPartitionIdPredicate(theRequestPartitionId, join, codePredicates);
for (IQueryParameterType nextOr : theList) {
Predicate singleCode = createPredicateQuantity(nextOr, theResourceName, theParamName, myCriteriaBuilder, join, theOperation, theRequestPartitionId);
Predicate singleCode = createPredicateQuantity(nextOr, theResourceName, theSearchParam.getName(), myCriteriaBuilder, join, theOperation, theRequestPartitionId);
codePredicates.add(singleCode);
}

View File

@ -62,7 +62,6 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.HasOrListParam;
import ca.uhn.fhir.rest.param.HasParam;
import ca.uhn.fhir.rest.param.NumberParam;
import ca.uhn.fhir.rest.param.ParameterUtil;
@ -599,12 +598,12 @@ class PredicateBuilderReference extends BasePredicateBuilder {
switch (nextParamDef.getParamType()) {
case DATE:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
myPredicateBuilder.addPredicateDate(theResourceName, theParamName, nextAnd, null, theRequestPartitionId);
myPredicateBuilder.addPredicateDate(theResourceName, nextParamDef, nextAnd, null, theRequestPartitionId);
}
break;
case QUANTITY:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
myPredicateBuilder.addPredicateQuantity(theResourceName, theParamName, nextAnd, null, theRequestPartitionId);
myPredicateBuilder.addPredicateQuantity(theResourceName, nextParamDef, nextAnd, null, theRequestPartitionId);
}
break;
case REFERENCE:
@ -614,21 +613,21 @@ class PredicateBuilderReference extends BasePredicateBuilder {
break;
case STRING:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
myPredicateBuilder.addPredicateString(theResourceName, theParamName, nextAnd, SearchFilterParser.CompareOperation.sw, theRequestPartitionId);
myPredicateBuilder.addPredicateString(theResourceName, nextParamDef, nextAnd, SearchFilterParser.CompareOperation.sw, theRequestPartitionId);
}
break;
case TOKEN:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
if ("Location.position".equals(nextParamDef.getPath())) {
myPredicateBuilder.addPredicateCoords(theResourceName, theParamName, nextAnd, theRequestPartitionId);
myPredicateBuilder.addPredicateCoords(theResourceName, nextParamDef, nextAnd, theRequestPartitionId);
} else {
myPredicateBuilder.addPredicateToken(theResourceName, theParamName, nextAnd, null, theRequestPartitionId);
myPredicateBuilder.addPredicateToken(theResourceName, nextParamDef, nextAnd, null, theRequestPartitionId);
}
}
break;
case NUMBER:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
myPredicateBuilder.addPredicateNumber(theResourceName, theParamName, nextAnd, null, theRequestPartitionId);
myPredicateBuilder.addPredicateNumber(theResourceName, nextParamDef, nextAnd, null, theRequestPartitionId);
}
break;
case COMPOSITE:
@ -638,14 +637,14 @@ class PredicateBuilderReference extends BasePredicateBuilder {
break;
case URI:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
myPredicateBuilder.addPredicateUri(theResourceName, theParamName, nextAnd, SearchFilterParser.CompareOperation.eq, theRequestPartitionId);
myPredicateBuilder.addPredicateUri(theResourceName, nextParamDef, nextAnd, SearchFilterParser.CompareOperation.eq, theRequestPartitionId);
}
break;
case HAS:
case SPECIAL:
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
if ("Location.position".equals(nextParamDef.getPath())) {
myPredicateBuilder.addPredicateCoords(theResourceName, theParamName, nextAnd, theRequestPartitionId);
myPredicateBuilder.addPredicateCoords(theResourceName, nextParamDef, nextAnd, theRequestPartitionId);
}
}
break;
@ -755,13 +754,13 @@ class PredicateBuilderReference extends BasePredicateBuilder {
} else {
RestSearchParameterTypeEnum typeEnum = searchParam.getParamType();
if (typeEnum == RestSearchParameterTypeEnum.URI) {
return myPredicateBuilder.addPredicateUri(theResourceName, theFilter.getParamPath().getName(), Collections.singletonList(new UriParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
return myPredicateBuilder.addPredicateUri(theResourceName, searchParam, Collections.singletonList(new UriParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.STRING) {
return myPredicateBuilder.addPredicateString(theResourceName, theFilter.getParamPath().getName(), Collections.singletonList(new StringParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
return myPredicateBuilder.addPredicateString(theResourceName, searchParam, Collections.singletonList(new StringParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.DATE) {
return myPredicateBuilder.addPredicateDate(theResourceName, theFilter.getParamPath().getName(), Collections.singletonList(new DateParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
return myPredicateBuilder.addPredicateDate(theResourceName, searchParam, Collections.singletonList(new DateParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.NUMBER) {
return myPredicateBuilder.addPredicateNumber(theResourceName, theFilter.getParamPath().getName(), Collections.singletonList(new NumberParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
return myPredicateBuilder.addPredicateNumber(theResourceName, searchParam, Collections.singletonList(new NumberParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.REFERENCE) {
String paramName = theFilter.getParamPath().getName();
SearchFilterParser.CompareOperation operation = theFilter.getOperation();
@ -771,7 +770,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
ReferenceParam referenceParam = new ReferenceParam(resourceType, chain, value);
return addPredicate(theResourceName, paramName, Collections.singletonList(referenceParam), operation, theRequest, theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.QUANTITY) {
return myPredicateBuilder.addPredicateQuantity(theResourceName, theFilter.getParamPath().getName(), Collections.singletonList(new QuantityParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
return myPredicateBuilder.addPredicateQuantity(theResourceName, searchParam, Collections.singletonList(new QuantityParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.COMPOSITE) {
throw new InvalidRequestException("Composite search parameters not currently supported with _filter clauses");
} else if (typeEnum == RestSearchParameterTypeEnum.TOKEN) {
@ -780,7 +779,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
null,
null,
theFilter.getValue());
return myPredicateBuilder.addPredicateToken(theResourceName, theFilter.getParamPath().getName(), Collections.singletonList(param), theFilter.getOperation(), theRequestPartitionId);
return myPredicateBuilder.addPredicateToken(theResourceName, searchParam, Collections.singletonList(param), theFilter.getOperation(), theRequestPartitionId);
}
}
return null;
@ -1033,13 +1032,13 @@ class PredicateBuilderReference extends BasePredicateBuilder {
switch (theParam.getParamType()) {
case STRING: {
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> stringJoin = theRoot.join("myParamsString", JoinType.INNER);
retVal = myPredicateBuilder.createPredicateString(leftValue, theResourceName, theParam.getName(), myCriteriaBuilder, stringJoin, theRequestPartitionId);
retVal = myPredicateBuilder.createPredicateString(leftValue, theResourceName, theParam, myCriteriaBuilder, stringJoin, theRequestPartitionId);
break;
}
case TOKEN: {
From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> tokenJoin = theRoot.join("myParamsToken", JoinType.INNER);
List<IQueryParameterType> tokens = Collections.singletonList(leftValue);
Collection<Predicate> tokenPredicates = myPredicateBuilder.createPredicateToken(tokens, theResourceName, theParam.getName(), myCriteriaBuilder, tokenJoin, theRequestPartitionId);
Collection<Predicate> tokenPredicates = myPredicateBuilder.createPredicateToken(tokens, theResourceName, theParam, myCriteriaBuilder, tokenJoin, theRequestPartitionId);
retVal = myCriteriaBuilder.and(tokenPredicates.toArray(new Predicate[0]));
break;
}

View File

@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.dao.predicate;
* #L%
*/
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
@ -31,7 +31,6 @@ import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@ -44,25 +43,21 @@ import java.util.List;
@Component
@Scope("prototype")
class PredicateBuilderString extends BasePredicateBuilder implements IPredicateBuilder {
@Autowired
DaoConfig myDaoConfig;
PredicateBuilderString(SearchBuilder theSearchBuilder) {
super(theSearchBuilder);
}
@Override
public Predicate addPredicate(String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
List<? extends IQueryParameterType> theList,
SearchFilterParser.CompareOperation theOperation,
RequestPartitionId theRequestPartitionId) {
From<?, ResourceIndexedSearchParamString> join = myQueryStack.createJoin(SearchBuilderJoinEnum.STRING, theParamName);
From<?, ResourceIndexedSearchParamString> join = myQueryStack.createJoin(SearchBuilderJoinEnum.STRING, theSearchParam.getName());
if (theList.get(0).getMissing() != null) {
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
addPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), join, theRequestPartitionId);
return null;
}
@ -70,7 +65,7 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
addPartitionIdPredicate(theRequestPartitionId, join, codePredicates);
for (IQueryParameterType nextOr : theList) {
Predicate singleCode = createPredicateString(nextOr, theResourceName, theParamName, myCriteriaBuilder, join, theOperation, theRequestPartitionId);
Predicate singleCode = createPredicateString(nextOr, theResourceName, theSearchParam, myCriteriaBuilder, join, theOperation, theRequestPartitionId);
codePredicates.add(singleCode);
}
@ -83,13 +78,13 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
public Predicate createPredicateString(IQueryParameterType theParameter,
String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
CriteriaBuilder theBuilder,
From<?, ResourceIndexedSearchParamString> theFrom,
RequestPartitionId theRequestPartitionId) {
return createPredicateString(theParameter,
theResourceName,
theParamName,
theSearchParam,
theBuilder,
theFrom,
null,
@ -98,12 +93,13 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
private Predicate createPredicateString(IQueryParameterType theParameter,
String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
CriteriaBuilder theBuilder,
From<?, ResourceIndexedSearchParamString> theFrom,
SearchFilterParser.CompareOperation operation,
RequestPartitionId theRequestPartitionId) {
String rawSearchTerm;
String paramName = theSearchParam.getName();
if (theParameter instanceof TokenParam) {
TokenParam id = (TokenParam) theParameter;
if (!id.isText()) {
@ -117,6 +113,8 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
if (!myDaoConfig.isAllowContainsSearches()) {
throw new MethodNotAllowedException(":contains modifier is disabled on this server");
}
} else {
rawSearchTerm = theSearchParam.encode(rawSearchTerm);
}
} else if (theParameter instanceof IPrimitiveDatatype<?>) {
IPrimitiveDatatype<?> id = (IPrimitiveDatatype<?>) theParameter;
@ -126,7 +124,7 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
}
if (rawSearchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
throw new InvalidRequestException("Parameter[" + theParamName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed ("
throw new InvalidRequestException("Parameter[" + paramName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed ("
+ ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
}
@ -152,12 +150,12 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
singleCode = theBuilder.and(singleCode, exactCode);
}
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, theRequestPartitionId);
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, paramName, theFrom, singleCode, theRequestPartitionId);
}
boolean exactMatch = theParameter instanceof StringParam && ((StringParam) theParameter).isExact();
if (exactMatch) {
// Exact match
Long hash = ResourceIndexedSearchParamString.calculateHashExact(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName, rawSearchTerm);
Long hash = ResourceIndexedSearchParamString.calculateHashExact(getPartitionSettings(), theRequestPartitionId, theResourceName, paramName, rawSearchTerm);
return theBuilder.equal(theFrom.get("myHashExact").as(Long.class), hash);
} else {
// Normalized Match
@ -185,34 +183,34 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
Predicate predicate;
if ((operation == null) ||
(operation == SearchFilterParser.CompareOperation.sw)) {
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(getPartitionSettings(), theRequestPartitionId, myDaoConfig.getModelConfig(), theResourceName, theParamName, normalizedString);
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(getPartitionSettings(), theRequestPartitionId, myDaoConfig.getModelConfig(), theResourceName, paramName, normalizedString);
Predicate hashCode = theBuilder.equal(theFrom.get("myHashNormalizedPrefix").as(Long.class), hash);
Predicate singleCode = theBuilder.like(theFrom.get("myValueNormalized").as(String.class), likeExpression);
predicate = theBuilder.and(hashCode, singleCode);
} else if ((operation == SearchFilterParser.CompareOperation.ew) ||
(operation == SearchFilterParser.CompareOperation.co)) {
Predicate singleCode = theBuilder.like(theFrom.get("myValueNormalized").as(String.class), likeExpression);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, theRequestPartitionId);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, paramName, theFrom, singleCode, theRequestPartitionId);
} else if (operation == SearchFilterParser.CompareOperation.eq) {
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(getPartitionSettings(), theRequestPartitionId, myDaoConfig.getModelConfig(), theResourceName, theParamName, normalizedString);
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(getPartitionSettings(), theRequestPartitionId, myDaoConfig.getModelConfig(), theResourceName, paramName, normalizedString);
Predicate hashCode = theBuilder.equal(theFrom.get("myHashNormalizedPrefix").as(Long.class), hash);
Predicate singleCode = theBuilder.like(theFrom.get("myValueNormalized").as(String.class), normalizedString);
predicate = theBuilder.and(hashCode, singleCode);
} else if (operation == SearchFilterParser.CompareOperation.ne) {
Predicate singleCode = theBuilder.notEqual(theFrom.get("myValueNormalized").as(String.class), likeExpression);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, theRequestPartitionId);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, paramName, theFrom, singleCode, theRequestPartitionId);
} else if (operation == SearchFilterParser.CompareOperation.gt) {
Predicate singleCode = theBuilder.greaterThan(theFrom.get("myValueNormalized").as(String.class), likeExpression);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, theRequestPartitionId);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, paramName, theFrom, singleCode, theRequestPartitionId);
} else if (operation == SearchFilterParser.CompareOperation.lt) {
Predicate singleCode = theBuilder.lessThan(theFrom.get("myValueNormalized").as(String.class), likeExpression);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, theRequestPartitionId);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, paramName, theFrom, singleCode, theRequestPartitionId);
} else if (operation == SearchFilterParser.CompareOperation.ge) {
Predicate singleCode = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueNormalized").as(String.class), likeExpression);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, theRequestPartitionId);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, paramName, theFrom, singleCode, theRequestPartitionId);
} else if (operation == SearchFilterParser.CompareOperation.le) {
Predicate singleCode = theBuilder.lessThanOrEqualTo(theFrom.get("myValueNormalized").as(String.class), likeExpression);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode, theRequestPartitionId);
predicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, paramName, theFrom, singleCode, theRequestPartitionId);
} else {
throw new IllegalArgumentException("Don't yet know how to handle operation " + operation + " on a string");
}

View File

@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.dao.predicate;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
@ -31,7 +30,6 @@ import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
@ -72,8 +70,6 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
@Autowired
private ITermReadSvc myTerminologySvc;
@Autowired
private ISearchParamRegistry mySearchParamRegistry;
@Autowired
private ModelConfig myModelConfig;
PredicateBuilderToken(SearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder) {
@ -83,14 +79,14 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
@Override
public Predicate addPredicate(String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
List<? extends IQueryParameterType> theList,
SearchFilterParser.CompareOperation theOperation,
RequestPartitionId theRequestPartitionId) {
if (theList.get(0).getMissing() != null) {
From<?, ResourceIndexedSearchParamToken> join = myQueryStack.createJoin(SearchBuilderJoinEnum.TOKEN, theParamName);
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
From<?, ResourceIndexedSearchParamToken> join = myQueryStack.createJoin(SearchBuilderJoinEnum.TOKEN, theSearchParam.getName());
addPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), join, theRequestPartitionId);
return null;
}
@ -104,8 +100,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
if (id.isText()) {
// Check whether the :text modifier is actually enabled here
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
boolean tokenTextIndexingEnabled = BaseSearchParamExtractor.tokenTextIndexingEnabledForSearchParam(myModelConfig, param);
boolean tokenTextIndexingEnabled = BaseSearchParamExtractor.tokenTextIndexingEnabledForSearchParam(myModelConfig, theSearchParam);
if (!tokenTextIndexingEnabled) {
String msg;
if (myModelConfig.isSuppressStringIndexingInTokens()) {
@ -116,7 +111,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
throw new MethodNotAllowedException(msg);
}
myPredicateBuilder.addPredicateString(theResourceName, theParamName, theList, theRequestPartitionId);
myPredicateBuilder.addPredicateString(theResourceName, theSearchParam, theList, theRequestPartitionId);
break;
}
}
@ -128,10 +123,10 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
return null;
}
From<?, ResourceIndexedSearchParamToken> join = myQueryStack.createJoin(SearchBuilderJoinEnum.TOKEN, theParamName);
From<?, ResourceIndexedSearchParamToken> join = myQueryStack.createJoin(SearchBuilderJoinEnum.TOKEN, theSearchParam.getName());
addPartitionIdPredicate(theRequestPartitionId, join, codePredicates);
Collection<Predicate> singleCode = createPredicateToken(tokens, theResourceName, theParamName, myCriteriaBuilder, join, theOperation, theRequestPartitionId);
Collection<Predicate> singleCode = createPredicateToken(tokens, theResourceName, theSearchParam, myCriteriaBuilder, join, theOperation, theRequestPartitionId);
assert singleCode != null;
codePredicates.addAll(singleCode);
@ -144,14 +139,14 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
public Collection<Predicate> createPredicateToken(Collection<IQueryParameterType> theParameters,
String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
CriteriaBuilder theBuilder,
From<?, ResourceIndexedSearchParamToken> theFrom,
RequestPartitionId theRequestPartitionId) {
return createPredicateToken(
theParameters,
theResourceName,
theParamName,
theSearchParam,
theBuilder,
theFrom,
null,
@ -160,12 +155,13 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
private Collection<Predicate> createPredicateToken(Collection<IQueryParameterType> theParameters,
String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
CriteriaBuilder theBuilder,
From<?, ResourceIndexedSearchParamToken> theFrom,
SearchFilterParser.CompareOperation operation,
RequestPartitionId theRequestPartitionId) {
final List<VersionIndependentConcept> codes = new ArrayList<>();
String paramName = theSearchParam.getName();
TokenParamModifier modifier = null;
for (IQueryParameterType nextParameter : theParameters) {
@ -195,12 +191,12 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
throw new InvalidRequestException(
"Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
"Parameter[" + paramName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
}
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
throw new InvalidRequestException(
"Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
"Parameter[" + paramName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
}
/*
@ -210,12 +206,12 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
if (modifier == TokenParamModifier.IN) {
codes.addAll(myTerminologySvc.expandValueSet(null, code));
} else if (modifier == TokenParamModifier.ABOVE) {
system = determineSystemIfMissing(theParamName, code, system);
validateHaveSystemAndCodeForToken(theParamName, code, system);
system = determineSystemIfMissing(theSearchParam, code, system);
validateHaveSystemAndCodeForToken(paramName, code, system);
codes.addAll(myTerminologySvc.findCodesAbove(system, code));
} else if (modifier == TokenParamModifier.BELOW) {
system = determineSystemIfMissing(theParamName, code, system);
validateHaveSystemAndCodeForToken(theParamName, code, system);
system = determineSystemIfMissing(theSearchParam, code, system);
validateHaveSystemAndCodeForToken(paramName, code, system);
codes.addAll(myTerminologySvc.findCodesBelow(system, code));
} else {
codes.add(new VersionIndependentConcept(system, code));
@ -240,32 +236,30 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
// System only
List<VersionIndependentConcept> systemOnlyCodes = sortedCodesList.stream().filter(t -> isBlank(t.getCode())).collect(Collectors.toList());
if (!systemOnlyCodes.isEmpty()) {
retVal.add(addPredicate(theResourceName, theParamName, theBuilder, theFrom, systemOnlyCodes, modifier, SearchBuilderTokenModeEnum.SYSTEM_ONLY, theRequestPartitionId));
retVal.add(addPredicate(theResourceName, paramName, theBuilder, theFrom, systemOnlyCodes, modifier, SearchBuilderTokenModeEnum.SYSTEM_ONLY, theRequestPartitionId));
}
// Code only
List<VersionIndependentConcept> codeOnlyCodes = sortedCodesList.stream().filter(t -> t.getSystem() == null).collect(Collectors.toList());
if (!codeOnlyCodes.isEmpty()) {
retVal.add(addPredicate(theResourceName, theParamName, theBuilder, theFrom, codeOnlyCodes, modifier, SearchBuilderTokenModeEnum.VALUE_ONLY, theRequestPartitionId));
retVal.add(addPredicate(theResourceName, paramName, theBuilder, theFrom, codeOnlyCodes, modifier, SearchBuilderTokenModeEnum.VALUE_ONLY, theRequestPartitionId));
}
// System and code
List<VersionIndependentConcept> systemAndCodeCodes = sortedCodesList.stream().filter(t -> isNotBlank(t.getCode()) && t.getSystem() != null).collect(Collectors.toList());
if (!systemAndCodeCodes.isEmpty()) {
retVal.add(addPredicate(theResourceName, theParamName, theBuilder, theFrom, systemAndCodeCodes, modifier, SearchBuilderTokenModeEnum.SYSTEM_AND_VALUE, theRequestPartitionId));
retVal.add(addPredicate(theResourceName, paramName, theBuilder, theFrom, systemAndCodeCodes, modifier, SearchBuilderTokenModeEnum.SYSTEM_AND_VALUE, theRequestPartitionId));
}
return retVal;
}
private String determineSystemIfMissing(String theParamName, String code, String theSystem) {
private String determineSystemIfMissing(RuntimeSearchParam theSearchParam, String code, String theSystem) {
String retVal = theSystem;
if (retVal == null) {
RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceName);
RuntimeSearchParam param = mySearchParamRegistry.getSearchParamByName(resourceDef, theParamName);
if (param != null) {
if (theSearchParam != null) {
Set<String> valueSetUris = Sets.newHashSet();
for (String nextPath : param.getPathsSplit()) {
for (String nextPath : theSearchParam.getPathsSplit()) {
BaseRuntimeChildDefinition def = myContext.newTerser().getDefinition(myResourceType, nextPath);
if (def instanceof BaseRuntimeDeclaredChildDefinition) {
String valueSet = ((BaseRuntimeDeclaredChildDefinition) def).getBindingValueSet();

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
* #L%
*/
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
@ -53,15 +54,16 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
@Override
public Predicate addPredicate(String theResourceName,
String theParamName,
RuntimeSearchParam theSearchParam,
List<? extends IQueryParameterType> theList,
SearchFilterParser.CompareOperation operation,
RequestPartitionId theRequestPartitionId) {
From<?, ResourceIndexedSearchParamUri> join = myQueryStack.createJoin(SearchBuilderJoinEnum.URI, theParamName);
String paramName = theSearchParam.getName();
From<?, ResourceIndexedSearchParamUri> join = myQueryStack.createJoin(SearchBuilderJoinEnum.URI, paramName);
if (theList.get(0).getMissing() != null) {
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
addPredicateParamMissingForNonReference(theResourceName, paramName, theList.get(0).getMissing(), join, theRequestPartitionId);
return null;
}
@ -91,8 +93,8 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
*
* If we ever need to make this more efficient, lucene could certainly be used as an optimization.
*/
ourLog.info("Searching for candidate URI:above parameters for Resource[{}] param[{}]", myResourceName, theParamName);
Collection<String> candidates = myResourceIndexedSearchParamUriDao.findAllByResourceTypeAndParamName(myResourceName, theParamName);
ourLog.info("Searching for candidate URI:above parameters for Resource[{}] param[{}]", myResourceName, paramName);
Collection<String> candidates = myResourceIndexedSearchParamUriDao.findAllByResourceTypeAndParamName(myResourceName, paramName);
List<String> toFind = new ArrayList<>();
for (String next : candidates) {
if (value.length() >= next.length()) {
@ -107,13 +109,13 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
}
Predicate uriPredicate = join.get("myUri").as(String.class).in(toFind);
Predicate hashAndUriPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, uriPredicate, theRequestPartitionId);
Predicate hashAndUriPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, paramName, join, uriPredicate, theRequestPartitionId);
codePredicates.add(hashAndUriPredicate);
} else if (param.getQualifier() == UriParamQualifierEnum.BELOW) {
Predicate uriPredicate = myCriteriaBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value));
Predicate hashAndUriPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, uriPredicate, theRequestPartitionId);
Predicate hashAndUriPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, paramName, join, uriPredicate, theRequestPartitionId);
codePredicates.add(hashAndUriPredicate);
} else {
@ -124,7 +126,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
Predicate uriPredicate = null;
if (operation == null || operation == SearchFilterParser.CompareOperation.eq) {
long hashUri = ResourceIndexedSearchParamUri.calculateHashUri(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName, value);
long hashUri = ResourceIndexedSearchParamUri.calculateHashUri(getPartitionSettings(), theRequestPartitionId, theResourceName, paramName, value);
Predicate hashPredicate = myCriteriaBuilder.equal(join.get("myHashUri"), hashUri);
codePredicates.add(hashPredicate);
} else if (operation == SearchFilterParser.CompareOperation.ne) {
@ -149,7 +151,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
}
if (uriPredicate != null) {
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName);
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), theRequestPartitionId, theResourceName, paramName);
Predicate hashIdentityPredicate = myCriteriaBuilder.equal(join.get("myHashIdentity"), hashIdentity);
codePredicates.add(myCriteriaBuilder.and(hashIdentityPredicate, uriPredicate));
}
@ -175,7 +177,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
Predicate orPredicate = myCriteriaBuilder.or(toArray(codePredicates));
Predicate outerPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName,
theParamName,
paramName,
join,
orPredicate,
theRequestPartitionId);

View File

@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.dao.r4;
*/
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDaoObservation;
import ca.uhn.fhir.jpa.dao.ObservationLastNIndexPersistSvc;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
@ -32,7 +31,6 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Observation;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@ -46,9 +44,6 @@ public class FhirResourceDaoObservationR4 extends BaseHapiFhirResourceDaoObserva
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager;
@Autowired
ObservationLastNIndexPersistSvc myObservationLastNIndexPersistSvc;
@Override
public IBundleProvider observationsLastN(SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {

View File

@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.dao.r5;
*/
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDaoObservation;
import ca.uhn.fhir.jpa.dao.ObservationLastNIndexPersistSvc;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
@ -32,16 +31,12 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.Observation;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
public class FhirResourceDaoObservationR5 extends BaseHapiFhirResourceDaoObservation<Observation> {
@Autowired
ObservationLastNIndexPersistSvc myObservationLastNIndexPersistSvc;
@Override
public IBundleProvider observationsLastN(SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptParentChildLinkDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
@ -61,6 +62,8 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
protected ITermConceptDao myConceptDao;
@Autowired
protected ITermCodeSystemDao myCodeSystemDao;
@Autowired
protected ITermCodeSystemVersionDao myCodeSystemVersionDao;
@Autowired
protected PlatformTransactionManager myTransactionMgr;
private boolean myProcessDeferred = true;
@ -142,7 +145,12 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
ourLog.info("Saving {} deferred concepts...", count);
while (codeCount < count && myDeferredConcepts.size() > 0) {
TermConcept next = myDeferredConcepts.remove(0);
if(myCodeSystemVersionDao.findById(next.getCodeSystemVersion().getPid()).isPresent()) {
codeCount += myCodeSystemStorageSvc.saveConcept(next);
} else {
ourLog.warn("Unable to save deferred TermConcept {} because Code System {} version PID {} is no longer valid. Code system may have since been replaced.",
next.getCode(), next.getCodeSystemVersion().getCodeSystemDisplayName(), next.getCodeSystemVersion().getPid());
}
}
if (codeCount > 0) {
@ -340,4 +348,9 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
void setConceptDaoForUnitTest(ITermConceptDao theConceptDao) {
myConceptDao = theConceptDao;
}
@VisibleForTesting
void setCodeSystemVersionDaoForUnitTest(ITermCodeSystemVersionDao theCodeSystemVersionDao) {
myCodeSystemVersionDao = theCodeSystemVersionDao;
}
}

View File

@ -22,10 +22,10 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.HapiExtensions;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.ValueSet;
@ -59,7 +59,7 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V
@Override
public void addMessage(String theMessage) {
addExtension()
.setUrl(JpaConstants.EXT_VALUESET_EXPANSION_MESSAGE)
.setUrl(HapiExtensions.EXT_VALUESET_EXPANSION_MESSAGE)
.setValue(new StringType(theMessage));
}

View File

@ -0,0 +1,135 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import ca.uhn.fhir.context.phonetic.ApacheEncoder;
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.rest.param.StringParam;
import org.apache.commons.codec.language.Soundex;
import org.hl7.fhir.dstu3.model.Enumerations;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.SearchParameter;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
public class FhirResourceDaoDstu3PhoneticSearchNoFtTest extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3PhoneticSearchNoFtTest.class);
public static final String GALE = "Gale";
public static final String GAIL = "Gail";
public static final String NAME_SOUNDEX_SP = "nameSoundex";
public static final String ADDRESS_LINE_SOUNDEX_SP = "addressLineSoundex";
private static final String BOB = "BOB";
private static final String ADDRESS = "123 Nohili St";
private static final String ADDRESS_CLOSE = "123 Nohily St";
private static final String ADDRESS_FAR = "123 College St";
@Autowired
ISearchParamRegistry mySearchParamRegistry;
@Before
public void beforeDisableResultReuse() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
myDaoConfig.setFetchSizeDefaultMaximum(new DaoConfig().getFetchSizeDefaultMaximum());
createSoundexSearchParameter(NAME_SOUNDEX_SP, PhoneticEncoderEnum.SOUNDEX, "Patient.name");
createSoundexSearchParameter(ADDRESS_LINE_SOUNDEX_SP, PhoneticEncoderEnum.SOUNDEX, "Patient.address.line");
mySearchParamRegistry.forceRefresh();
mySearchParamRegistry.setPhoneticEncoder(new ApacheEncoder(PhoneticEncoderEnum.SOUNDEX.name(), new Soundex()));
}
@After
public void resetStringEncoder() {
mySearchParamRegistry.setPhoneticEncoder(null);
}
@Test
public void testSoundex() {
Soundex soundex = new Soundex();
assertEquals(soundex.encode(GALE), soundex.encode(GAIL));
assertNotEquals(soundex.encode(GALE), soundex.encode(BOB));
assertEquals(soundex.encode(ADDRESS), soundex.encode(ADDRESS_CLOSE));
assertNotEquals(soundex.encode(ADDRESS), soundex.encode(ADDRESS_FAR));
ourLog.info("Encoded address: {}", soundex.encode(ADDRESS));
}
@Test
public void phoneticMatch() {
Patient patient;
patient = new Patient();
patient.addName().addGiven(GALE);
patient.addAddress().addLine(ADDRESS);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
IIdType pId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
List<ResourceIndexedSearchParamString> stringParams = myResourceIndexedSearchParamStringDao.findAll();
assertThat(stringParams, hasSize(6));
List<String> stringParamNames = stringParams.stream().map(ResourceIndexedSearchParamString::getParamName).collect(Collectors.toList());
assertThat(stringParamNames, containsInAnyOrder(Patient.SP_NAME, Patient.SP_GIVEN, Patient.SP_PHONETIC, NAME_SOUNDEX_SP, Patient.SP_ADDRESS, ADDRESS_LINE_SOUNDEX_SP));
assertSearchMatch(pId, Patient.SP_PHONETIC, GALE);
assertSearchMatch(pId, Patient.SP_PHONETIC, GAIL);
assertNoMatch(Patient.SP_PHONETIC, BOB);
assertSearchMatch(pId, NAME_SOUNDEX_SP, GAIL);
assertSearchMatch(pId, NAME_SOUNDEX_SP, GALE);
assertNoMatch(NAME_SOUNDEX_SP, BOB);
assertSearchMatch(pId, ADDRESS_LINE_SOUNDEX_SP, ADDRESS);
assertSearchMatch(pId, ADDRESS_LINE_SOUNDEX_SP, ADDRESS_CLOSE);
assertNoMatch(ADDRESS_LINE_SOUNDEX_SP, ADDRESS_FAR);
}
private void assertSearchMatch(IIdType thePId1, String theSp, String theValue) {
SearchParameterMap map;
map = new SearchParameterMap();
map.add(theSp, new StringParam(theValue));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), contains(toValues(thePId1)));
}
private void assertNoMatch(String theSp, String theValue) {
SearchParameterMap map;
map = new SearchParameterMap();
map.add(theSp, new StringParam(theValue));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), hasSize(0));
}
private void createSoundexSearchParameter(String theCode, PhoneticEncoderEnum theEncoder, String theFhirPath) {
SearchParameter searchParameter = new SearchParameter();
searchParameter.addBase("Patient");
searchParameter.setCode(theCode);
searchParameter.setType(Enumerations.SearchParamType.STRING);
searchParameter.setTitle("Test Soundex");
searchParameter.setExpression(theFhirPath);
// Maybe use in the future? RuntimeSearchParam doesn't store this...
// searchParameter.setXpathUsage(SearchParameter.XPathUsageType.PHONETIC);
searchParameter.setStatus(Enumerations.PublicationStatus.ACTIVE);
searchParameter.addExtension()
.setUrl(JpaConstants.EXT_SEARCHPARAM_PHONETIC_ENCODER)
.setValue(new StringType(theEncoder.name()));
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(searchParameter));
mySearchParameterDao.create(searchParameter, mySrd).getId().toUnqualifiedVersionless();
}
}

View File

@ -10,6 +10,7 @@ import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.util.HapiExtensions;
import org.apache.commons.text.RandomStringGenerator;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.StringType;
@ -45,12 +46,12 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
when(mySrd.getRequestId()).thenReturn(requestId);
Patient pt0 = new Patient();
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.setActive(true);
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
Patient pt1 = new Patient();
pt1.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:1"));
pt1.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:1"));
pt1.setActive(true);
IIdType pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
@ -61,7 +62,7 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
IBundleProvider result = myPatientDao.search(params);
assertThat(toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(pt0id.getValue()));
pt0 = (Patient) result.getResources(0, 1).get(0);
assertEquals("urn:source:0#a_request_id", pt0.getMeta().getExtensionString(Constants.EXT_META_SOURCE));
assertEquals("urn:source:0#a_request_id", pt0.getMeta().getExtensionString(HapiExtensions.EXT_META_SOURCE));
// Search by request ID
params = new SearchParameterMap();
@ -86,17 +87,17 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
when(mySrd.getRequestId()).thenReturn(requestId);
Patient pt0 = new Patient();
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.setActive(true);
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
Patient pt1 = new Patient();
pt1.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:1"));
pt1.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:1"));
pt1.setActive(true);
IIdType pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:2"));
pt2.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:2"));
pt2.setActive(true);
myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
@ -117,17 +118,17 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
when(mySrd.getRequestId()).thenReturn(requestId);
Patient pt0 = new Patient();
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.setActive(true);
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
Patient pt1 = new Patient();
pt1.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:1"));
pt1.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:1"));
pt1.setActive(true);
IIdType pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:2"));
pt2.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:2"));
pt2.setActive(true);
myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
@ -147,7 +148,7 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
when(mySrd.getRequestId()).thenReturn(requestId);
Patient pt0 = new Patient();
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.setActive(true);
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
@ -165,19 +166,19 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
public void testSourceNotPreservedAcrossUpdate() {
Patient pt0 = new Patient();
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.setActive(true);
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
pt0 = myPatientDao.read(pt0id);
assertEquals("urn:source:0", pt0.getMeta().getExtensionString(Constants.EXT_META_SOURCE));
assertEquals("urn:source:0", pt0.getMeta().getExtensionString(HapiExtensions.EXT_META_SOURCE));
pt0.getMeta().getExtension().clear();
pt0.setActive(false);
myPatientDao.update(pt0);
pt0 = myPatientDao.read(pt0id.withVersion("2"));
assertEquals(null, pt0.getMeta().getExtensionString(Constants.EXT_META_SOURCE));
assertEquals(null, pt0.getMeta().getExtensionString(HapiExtensions.EXT_META_SOURCE));
}
@ -187,19 +188,19 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
when(mySrd.getRequestId()).thenReturn("0000000000000000");
Patient pt0 = new Patient();
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:0"));
pt0.setActive(true);
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
pt0 = myPatientDao.read(pt0id);
assertEquals(null, pt0.getMeta().getExtensionString(Constants.EXT_META_SOURCE));
assertEquals(null, pt0.getMeta().getExtensionString(HapiExtensions.EXT_META_SOURCE));
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:1"));
pt0.getMeta().addExtension(HapiExtensions.EXT_META_SOURCE, new StringType("urn:source:1"));
pt0.setActive(false);
myPatientDao.update(pt0);
pt0 = myPatientDao.read(pt0id.withVersion("2"));
assertEquals(null, pt0.getMeta().getExtensionString(Constants.EXT_META_SOURCE));
assertEquals(null, pt0.getMeta().getExtensionString(HapiExtensions.EXT_META_SOURCE));
// Search without source param
SearchParameterMap params = new SearchParameterMap();

View File

@ -17,7 +17,6 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
@ -51,6 +50,7 @@ import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.param.UriParamQualifierEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.util.HapiExtensions;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@ -4842,7 +4842,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
sp.setCode("code");
sp.setExpression("Observation.code");
sp.addExtension()
.setUrl(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING)
.setUrl(HapiExtensions.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING)
.setValue(new BooleanType(true));
ourLog.info("SP:\n{}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(sp));
mySearchParameterDao.update(sp);

View File

@ -11,7 +11,6 @@ import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.util.SpringObjectCaster;
@ -22,6 +21,7 @@ import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*;
@ -140,7 +140,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
.setExpression("Patient")
.setDefinition("SearchParameter/patient-birthdate");
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
@ -182,7 +182,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
.setExpression("Coverage")
.setDefinition("/SearchParameter/coverage-identifier");
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
mySearchParamRegistry.forceRefresh();
@ -210,7 +210,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
.setExpression("Observation")
.setDefinition("/SearchParameter/observation-subject");
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
mySearchParamRegistry.forceRefresh();
@ -238,7 +238,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
.setExpression("Patient")
.setDefinition("/SearchParameter/patient-identifier");
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
mySearchParamRegistry.forceRefresh();
@ -266,7 +266,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
.setExpression("Patient")
.setDefinition("/SearchParameter/patient-identifier");
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
mySearchParamRegistry.forceRefresh();
@ -303,7 +303,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
.setExpression("Patient")
.setDefinition("SearchParameter/patient-organization");
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
@ -355,7 +355,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
.setExpression("Observation")
.setDefinition("SearchParameter/obs-code");
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
@ -487,7 +487,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
.setExpression("ServiceRequest")
.setDefinition(identifierParamId); // SearchParameter?base=ServiceRequest&name=identifier
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.create(sp);
mySearchParamRegistry.forceRefresh();

View File

@ -36,6 +36,7 @@ import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Condition;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Group;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Narrative;
@ -1291,6 +1292,27 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
assertThat(encoded, containsString("No issues detected"));
}
/**
* See #1780
*/
@Test
public void testExpand() {
ValueSet vs = new ValueSet();
vs.setUrl("test.com/testValueSet");
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
vs.getCompose()
.addInclude().setSystem("http://hl7.org/fhir/action-cardinality-behavior");
IIdType id = myValueSetDao.create(vs).getId().toUnqualifiedVersionless();
myTermReadSvc.preExpandDeferredValueSetsToTerminologyTables();
ValueSet expansion = myValueSetDao.expand(id, null, 0, 10000, mySrd);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion));
assertEquals(2, expansion.getExpansion().getContains().size());
}
@AfterClass
public static void afterClassClearContext() {

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor;
@ -8,7 +7,6 @@ import ca.uhn.fhir.interceptor.api.Interceptor;
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.IFhirResourceDao;
import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ForcedId;
@ -22,7 +20,6 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.SqlQuery;
import ca.uhn.fhir.rest.api.Constants;
@ -40,7 +37,7 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.test.utilities.ITestDataBuilder;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
@ -69,7 +66,6 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast;
@ -2418,7 +2414,7 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
.setExpression("Patient")
.setDefinition("SearchParameter/patient-birthdate");
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);

View File

@ -203,9 +203,6 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
List<ObservationJson> observationDocuments = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx);
assertEquals(100, observationDocuments.size());
//List<CodeJson> codeDocuments = elasticsearchSvc.queryAllIndexedObservationCodesForTest();
//assertEquals(2, codeDocuments.size());
// Check that all observations were indexed.
searchParameterMap = new SearchParameterMap();
searchParameterMap.add(Observation.SP_SUBJECT, multiSubjectParams);

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
@ -11,13 +12,13 @@ import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.extractor.PathAndRef;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.TestUtil;
import com.google.common.collect.Sets;
import org.hl7.fhir.r4.model.BooleanType;
@ -123,7 +124,7 @@ public class SearchParamExtractorR4Test {
public void testTokenText_DisabledInSearchParam_Coding() {
RuntimeSearchParam existingCodeSp = mySearchParamRegistry.getActiveSearchParams("Observation").get("code");
RuntimeSearchParam codeSearchParam = new RuntimeSearchParam(existingCodeSp);
codeSearchParam.addExtension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new Extension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new BooleanType(true)));
codeSearchParam.addExtension(HapiExtensions.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new Extension(HapiExtensions.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new BooleanType(true)));
mySearchParamRegistry.addSearchParam(codeSearchParam);
Observation obs = new Observation();
@ -176,7 +177,7 @@ public class SearchParamExtractorR4Test {
RuntimeSearchParam existingCodeSp = mySearchParamRegistry.getActiveSearchParams("Observation").get("code");
RuntimeSearchParam codeSearchParam = new RuntimeSearchParam(existingCodeSp);
codeSearchParam.addExtension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new Extension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new BooleanType(false)));
codeSearchParam.addExtension(HapiExtensions.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new Extension(HapiExtensions.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new BooleanType(false)));
mySearchParamRegistry.addSearchParam(codeSearchParam);
Observation obs = new Observation();
@ -230,7 +231,7 @@ public class SearchParamExtractorR4Test {
public void testTokenText_DisabledInSearchParam_Identifier() {
RuntimeSearchParam existingCodeSp = mySearchParamRegistry.getActiveSearchParams("Observation").get("identifier");
RuntimeSearchParam codeSearchParam = new RuntimeSearchParam(existingCodeSp);
codeSearchParam.addExtension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new Extension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new BooleanType(true)));
codeSearchParam.addExtension(HapiExtensions.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new Extension(HapiExtensions.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new BooleanType(true)));
mySearchParamRegistry.addSearchParam(codeSearchParam);
@ -401,6 +402,11 @@ public class SearchParamExtractorR4Test {
public Collection<RuntimeSearchParam> getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef) {
return null;
}
@Override
public void setPhoneticEncoder(IPhoneticEncoder thePhoneticEncoder) {
// nothing
}
}
@AfterClass

View File

@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.BaseValidatingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
import ca.uhn.fhir.validation.IValidatorModule;
@ -4042,7 +4043,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
{
Patient readPatient = (Patient) ourClient.read().resource("Patient").withId(patientid).execute();
assertThat(readPatient.getMeta().getExtensionString(Constants.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
assertThat(readPatient.getMeta().getExtensionString(HapiExtensions.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
}
patient.setId(patientid);
@ -4050,12 +4051,12 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourClient.update().resource(patient).execute();
{
Patient readPatient = (Patient) ourClient.read().resource("Patient").withId(patientid).execute();
assertThat(readPatient.getMeta().getExtensionString(Constants.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
assertThat(readPatient.getMeta().getExtensionString(HapiExtensions.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
readPatient.addName().setFamily("testUpdateWithSource");
ourClient.update().resource(readPatient).execute();
readPatient = (Patient) ourClient.read().resource("Patient").withId(patientid).execute();
assertThat(readPatient.getMeta().getExtensionString(Constants.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
assertThat(readPatient.getMeta().getExtensionString(HapiExtensions.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
}
}

View File

@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.util.HapiExtensions;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
@ -208,7 +209,7 @@ public class BinaryAccessProviderR4Test extends BaseResourceProviderR4Test {
assertEquals(15, attachment.getSize());
assertEquals(null, attachment.getData());
assertEquals("2", ref.getMeta().getVersionId());
attachmentId = attachment.getDataElement().getExtensionString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
attachmentId = attachment.getDataElement().getExtensionString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID);
assertThat(attachmentId, matchesPattern("[a-zA-Z0-9]{100}"));
}
@ -266,7 +267,7 @@ public class BinaryAccessProviderR4Test extends BaseResourceProviderR4Test {
assertEquals(15, attachment.getSize());
assertEquals(null, attachment.getData());
assertEquals("2", ref.getMeta().getVersionId());
attachmentId = attachment.getDataElement().getExtensionString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
attachmentId = attachment.getDataElement().getExtensionString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID);
assertThat(attachmentId, matchesPattern("[a-zA-Z0-9]{100}"));
}
@ -301,7 +302,7 @@ public class BinaryAccessProviderR4Test extends BaseResourceProviderR4Test {
dr.addContent()
.getAttachment()
.getDataElement()
.addExtension(JpaConstants.EXT_EXTERNALIZED_BINARY_ID, new StringType("0000-1111") );
.addExtension(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID, new StringType("0000-1111") );
try {
ourClient.create().resource(dr).execute();
@ -347,7 +348,7 @@ public class BinaryAccessProviderR4Test extends BaseResourceProviderR4Test {
assertEquals(4, attachment.getSize());
assertArrayEquals(SOME_BYTES_2, attachment.getData());
assertEquals("2", ref.getMeta().getVersionId());
attachmentId = attachment.getExtensionString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
attachmentId = attachment.getExtensionString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID);
assertEquals(null, attachmentId);
}
@ -397,7 +398,7 @@ public class BinaryAccessProviderR4Test extends BaseResourceProviderR4Test {
assertEquals(ContentType.IMAGE_JPEG.getMimeType(), target.getContentType());
assertEquals(null, target.getData());
assertEquals("2", target.getMeta().getVersionId());
attachmentId = target.getDataElement().getExtensionString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
attachmentId = target.getDataElement().getExtensionString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID);
assertThat(attachmentId, matchesPattern("[a-zA-Z0-9]{100}"));
}
@ -458,7 +459,7 @@ public class BinaryAccessProviderR4Test extends BaseResourceProviderR4Test {
assertEquals(ContentType.IMAGE_JPEG.getMimeType(), target.getContentType());
assertEquals(null, target.getData());
assertEquals("2", target.getMeta().getVersionId());
attachmentId = target.getDataElement().getExtensionString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
attachmentId = target.getDataElement().getExtensionString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID);
assertThat(attachmentId, matchesPattern("[a-zA-Z0-9]{100}"));
}
@ -522,7 +523,7 @@ public class BinaryAccessProviderR4Test extends BaseResourceProviderR4Test {
String response = IOUtils.toString(resp.getEntity().getContent(), Constants.CHARSET_UTF8);
DocumentReference ref = myFhirCtx.newJsonParser().parseResource(DocumentReference.class, response);
Attachment attachment = ref.getContentFirstRep().getAttachment();
attachmentId = attachment.getDataElement().getExtensionString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
attachmentId = attachment.getDataElement().getExtensionString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID);
assertThat(attachmentId, matchesPattern("[a-zA-Z0-9]{100}"));
}

View File

@ -5,8 +5,8 @@ import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.HapiExtensions;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Binary;
import org.hl7.fhir.r4.model.DocumentReference;
@ -74,7 +74,7 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
IIdType id = outcome.getId().toUnqualifiedVersionless();
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getResource());
ourLog.info("Encoded: {}", encoded);
assertThat(encoded, containsString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, containsString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, not(containsString("\"data\"")));
// Now read it back and make sure it was de-externalized
@ -98,7 +98,7 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
IIdType id = outcome.getId().toUnqualifiedVersionless();
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getResource());
ourLog.info("Encoded: {}", encoded);
assertThat(encoded, containsString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, containsString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, not(containsString("\"data\"")));
// Now read it back and make sure it was de-externalized
@ -123,7 +123,7 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getResource());
ourLog.info("Encoded: {}", encoded);
assertThat(encoded, containsString("\"data\": \"BAMCAQ==\""));
assertThat(encoded, not(containsString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID)));
assertThat(encoded, not(containsString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID)));
// Now read it back and make sure it was de-externalized
Binary output = myBinaryDao.read(id, mySrd);
@ -146,14 +146,14 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
IIdType id = outcome.getId().toUnqualifiedVersionless();
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getResource());
ourLog.info("Encoded: {}", encoded);
assertThat(encoded, containsString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, containsString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, not(containsString("\"data\"")));
// Now read it back and make sure it was de-externalized
Binary output = myBinaryDao.read(id, mySrd);
assertEquals("application/octet-stream", output.getContentType());
assertArrayEquals(SOME_BYTES, output.getData());
assertNotNull(output.getDataElement().getExtensionByUrl(JpaConstants.EXT_EXTERNALIZED_BINARY_ID).getValue());
assertNotNull(output.getDataElement().getExtensionByUrl(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID).getValue());
}
@ -171,7 +171,7 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
IIdType id = outcome.getId().toUnqualifiedVersionless();
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getResource());
ourLog.info("Encoded: {}", encoded);
assertThat(encoded, containsString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, containsString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, not(containsString("\"data\"")));
// Now update
@ -212,9 +212,9 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
IIdType id = outcome.getId().toUnqualifiedVersionless();
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getResource());
ourLog.info("Encoded: {}", encoded);
assertThat(encoded, containsString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, containsString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, not(containsString("\"data\"")));
String binaryId = docRef.getContentFirstRep().getAttachment().getDataElement().getExtensionString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
String binaryId = docRef.getContentFirstRep().getAttachment().getDataElement().getExtensionString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID);
assertThat(binaryId, not(blankOrNullString()));
// Now update
@ -223,7 +223,7 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
docRef.setStatus(Enumerations.DocumentReferenceStatus.CURRENT);
docRef.getContentFirstRep().getAttachment().setContentType("application/octet-stream");
docRef.getContentFirstRep().getAttachment().getDataElement().addExtension(
JpaConstants.EXT_EXTERNALIZED_BINARY_ID,
HapiExtensions.EXT_EXTERNALIZED_BINARY_ID,
new StringType(binaryId)
);
outcome = myDocumentReferenceDao.update(docRef, mySrd);
@ -259,9 +259,9 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
IIdType id = outcome.getId().toUnqualifiedVersionless();
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getResource());
ourLog.info("Encoded: {}", encoded);
assertThat(encoded, containsString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, containsString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, not(containsString("\"data\"")));
String binaryId = docRef.getContentFirstRep().getAttachment().getDataElement().getExtensionString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
String binaryId = docRef.getContentFirstRep().getAttachment().getDataElement().getExtensionString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID);
assertThat(binaryId, not(blankOrNullString()));
// Now update
@ -271,13 +271,13 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
content = docRef.addContent();
content.getAttachment().setContentType("application/octet-stream");
content.getAttachment().getDataElement().addExtension(
JpaConstants.EXT_EXTERNALIZED_BINARY_ID,
HapiExtensions.EXT_EXTERNALIZED_BINARY_ID,
new StringType(binaryId)
);
content2 = docRef.addContent();
content2.getAttachment().setContentType("application/octet-stream");
content2.getAttachment().getDataElement().addExtension(
JpaConstants.EXT_EXTERNALIZED_BINARY_ID,
HapiExtensions.EXT_EXTERNALIZED_BINARY_ID,
new StringType("12345-67890")
);
@ -305,14 +305,14 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test {
IIdType id = outcome.getId().toUnqualifiedVersionless();
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getResource());
ourLog.info("Encoded: {}", encoded);
assertThat(encoded, containsString(JpaConstants.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, containsString(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID));
assertThat(encoded, not(containsString("\"data\"")));
// Now read it back and make sure it was de-externalized
Binary output = myBinaryDao.read(id, mySrd);
assertEquals("application/octet-stream", output.getContentType());
assertEquals(null, output.getData());
assertNotNull(output.getDataElement().getExtensionByUrl(JpaConstants.EXT_EXTERNALIZED_BINARY_ID).getValue());
assertNotNull(output.getDataElement().getExtensionByUrl(HapiExtensions.EXT_EXTERNALIZED_BINARY_ID).getValue());
}

View File

@ -6,12 +6,12 @@ import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -104,7 +104,7 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test {
.setExpression("Patient")
.setDefinition("SearchParameter/patient-birthdate");
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);

View File

@ -7,13 +7,15 @@ import ca.uhn.fhir.jpa.subscription.channel.impl.LinkedBlockingChannel;
import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.test.utilities.JettyUtil;
import ca.uhn.fhir.util.BundleUtil;
import com.google.common.collect.Lists;
import net.ttddyy.dsproxy.QueryCount;
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
@ -22,9 +24,19 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*;
import org.junit.*;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Subscription;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
@ -36,28 +48,23 @@ import java.util.List;
@Ignore
public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSubscriptionsR4Test.class);
private static Server ourListenerServer;
protected static int ourListenerPort;
protected static List<String> ourContentTypes = Collections.synchronizedList(new ArrayList<>());
protected static List<String> ourHeaders = Collections.synchronizedList(new ArrayList<>());
protected static List<Bundle> ourTransactions = Collections.synchronizedList(Lists.newArrayList());
protected static List<Observation> ourCreatedObservations = Collections.synchronizedList(Lists.newArrayList());
protected static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
private static Server ourListenerServer;
private static SingleQueryCountHolder ourCountHolder;
@Autowired
private SingleQueryCountHolder myCountHolder;
private static String ourListenerServerBase;
@Autowired
protected SubscriptionTestUtil mySubscriptionTestUtil;
@Autowired
protected SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
protected CountingInterceptor myCountingInterceptor;
protected static List<Observation> ourCreatedObservations = Collections.synchronizedList(Lists.newArrayList());
protected static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
private static String ourListenerServerBase;
protected List<IIdType> mySubscriptionIds = Collections.synchronizedList(new ArrayList<>());
@Autowired
private SingleQueryCountHolder myCountHolder;
@After
public void afterUnregisterRestHookListener() {
@ -87,6 +94,7 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
public void beforeReset() throws Exception {
ourCreatedObservations.clear();
ourUpdatedObservations.clear();
ourTransactions.clear();
ourContentTypes.clear();
ourHeaders.clear();
@ -162,8 +170,7 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
}
public static class ObservationListener implements IResourceProvider {
public static class ObservationResourceProvider implements IResourceProvider {
@Create
public MethodOutcome create(@ResourceParam Observation theObservation, HttpServletRequest theRequest) {
@ -202,6 +209,17 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
}
public static class PlainProvider {
@Transaction
public Bundle transaction(@TransactionParam Bundle theInput) {
ourLog.info("Received transaction update");
ourTransactions.add(theInput);
return theInput;
}
}
@AfterClass
public static void reportTotalSelects() {
ourLog.info("Total database select queries: {}", getQueryCount().getSelect());
@ -215,8 +233,8 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
public static void startListenerServer() throws Exception {
RestfulServer ourListenerRestServer = new RestfulServer(FhirContext.forR4());
ObservationListener obsListener = new ObservationListener();
ourListenerRestServer.setResourceProviders(obsListener);
ourListenerRestServer.registerProvider(new ObservationResourceProvider());
ourListenerRestServer.registerProvider(new PlainProvider());
ourListenerServer = new Server(0);

View File

@ -85,6 +85,8 @@ public class SubscriptionValidatingInterceptorTest {
@Test
public void testValidate_RestHook_NoEndpoint() {
when(myDaoRegistry.isResourceTypeSupported(eq("Patient"))).thenReturn(true);
Subscription subscription = new Subscription();
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
subscription.setCriteria("Patient?identifier=foo");
@ -102,6 +104,8 @@ public class SubscriptionValidatingInterceptorTest {
@Test
public void testValidate_RestHook_NoType() {
when(myDaoRegistry.isResourceTypeSupported(eq("Patient"))).thenReturn(true);
Subscription subscription = new Subscription();
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
subscription.setCriteria("Patient?identifier=foo");

View File

@ -1,10 +1,10 @@
package ca.uhn.fhir.jpa.subscription.email;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.util.HapiExtensions;
import com.icegreen.greenmail.store.FolderException;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetup;
@ -166,10 +166,10 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
Assert.assertNotNull(subscriptionTemp);
subscriptionTemp.getChannel().addExtension()
.setUrl(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM)
.setUrl(HapiExtensions.EXT_SUBSCRIPTION_EMAIL_FROM)
.setValue(new StringType("mailto:myfrom@from.com"));
subscriptionTemp.getChannel().addExtension()
.setUrl(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE)
.setUrl(HapiExtensions.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE)
.setValue(new StringType("This is a subject"));
subscriptionTemp.setIdElement(subscriptionTemp.getIdElement().toUnqualifiedVersionless());
@ -220,10 +220,10 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
Subscription subscriptionTemp = ourClient.read(Subscription.class, sub1.getId());
Assert.assertNotNull(subscriptionTemp);
subscriptionTemp.getChannel().addExtension()
.setUrl(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM)
.setUrl(HapiExtensions.EXT_SUBSCRIPTION_EMAIL_FROM)
.setValue(new StringType("myfrom@from.com"));
subscriptionTemp.getChannel().addExtension()
.setUrl(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE)
.setUrl(HapiExtensions.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE)
.setValue(new StringType("This is a subject"));
subscriptionTemp.setIdElement(subscriptionTemp.getIdElement().toUnqualifiedVersionless());

View File

@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.subscription.resthook;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
import ca.uhn.fhir.jpa.subscription.NotificationServlet;
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
@ -18,6 +17,7 @@ import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.test.utilities.JettyUtil;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.MetaUtil;
import com.google.common.collect.Lists;
import org.eclipse.jetty.server.Server;
@ -158,7 +158,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
String databaseCriteria = "Observation?code=17861-6&context.type=IHD";
Subscription subscription = createSubscription(databaseCriteria, null, ourNotificationListenerServer);
List<Coding> tag = subscription.getMeta().getTag();
assertEquals(JpaConstants.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.get(0).getSystem());
assertEquals(HapiExtensions.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.get(0).getSystem());
assertEquals(SubscriptionMatchingStrategy.DATABASE.toString(), tag.get(0).getCode());
}
@ -168,7 +168,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
Subscription subscription = createSubscription(inMemoryCriteria, null, ourNotificationListenerServer);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(subscription));
List<Coding> tag = subscription.getMeta().getTag();
assertEquals(JpaConstants.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.get(0).getSystem());
assertEquals(HapiExtensions.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.get(0).getSystem());
assertEquals(SubscriptionMatchingStrategy.IN_MEMORY.toString(), tag.get(0).getCode());
}
@ -497,7 +497,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
List<Coding> tags = subscriptionOrig.getMeta().getTag();
assertEquals(1, tags.size());
Coding tag = tags.get(0);
assertEquals(JpaConstants.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.getSystem());
assertEquals(HapiExtensions.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.getSystem());
assertEquals(SubscriptionMatchingStrategy.IN_MEMORY.toString(), tag.getCode());
assertEquals("In-memory", tag.getDisplay());
@ -515,7 +515,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
tags = theSubscription.getMeta().getTag();
assertEquals(1, tags.size());
tag = tags.get(0);
assertEquals(JpaConstants.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.getSystem());
assertEquals(HapiExtensions.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.getSystem());
assertEquals(SubscriptionMatchingStrategy.IN_MEMORY.toString(), tag.getCode());
assertEquals("In-memory", tag.getDisplay());
}
@ -530,7 +530,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
List<Coding> tags = subscriptionOrig.getMeta().getTag();
assertEquals(1, tags.size());
Coding tag = tags.get(0);
assertEquals(JpaConstants.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.getSystem());
assertEquals(HapiExtensions.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.getSystem());
assertEquals(SubscriptionMatchingStrategy.DATABASE.toString(), tag.getCode());
assertEquals("Database", tag.getDisplay());
@ -542,7 +542,7 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
tags = subscription.getMeta().getTag();
assertEquals(1, tags.size());
tag = tags.get(0);
assertEquals(JpaConstants.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.getSystem());
assertEquals(HapiExtensions.EXT_SUBSCRIPTION_MATCHING_STRATEGY, tag.getSystem());
assertEquals(SubscriptionMatchingStrategy.DATABASE.toString(), tag.getCode());
assertEquals("Database", tag.getDisplay());
}

View File

@ -1,12 +1,12 @@
package ca.uhn.fhir.jpa.subscription.resthook;
import ca.uhn.fhir.jpa.config.StoppableSubscriptionDeliveringRestHookSubscriber;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR4Test;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.HapiExtensions;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*;
@ -337,7 +337,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
subscription1
.getChannel()
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true"));
.addExtension(HapiExtensions.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true"));
ourLog.info("** About to update subscription");
int modCount = (int) myCountingInterceptor.getSentCount("Subscription");
@ -413,7 +413,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
Subscription subscription = newSubscription(criteria1, payload);
subscription
.getChannel()
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true"));
.addExtension(HapiExtensions.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true"));
ourClient.create().resource(subscription).execute();
waitForActivatedSubscriptionCount(1);
@ -1013,4 +1013,39 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
}
@Test
public void testDeliverSearchResult() throws Exception {
{
Subscription subscription = newSubscription("Observation?", "application/json");
subscription.addExtension(HapiExtensions.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_CRITERIA, new StringType("Observation?_id=${matched_resource_id}&_include=*"));
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(subscription));
MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
mySubscriptionIds.add(methodOutcome.getId());
waitForActivatedSubscriptionCount(1);
}
{
Patient patient = new Patient();
patient.setActive(true);
IIdType patientId = ourClient.create().resource(patient).execute().getId();
Observation observation = new Observation();
observation.addExtension().setUrl("Observation#accessType").setValue(new Coding().setCode("Catheter"));
observation.getSubject().setReferenceElement(patientId.toUnqualifiedVersionless());
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
assertEquals(true, methodOutcome.getCreated());
waitForQueueToDrain();
waitForSize(1, ourTransactions);
ourLog.info("Received transaction: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(ourTransactions.get(0)));
Bundle xact = ourTransactions.get(0);
assertEquals(2, xact.getEntry().size());
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(ourTransactions.get(0)));
}
}
}

View File

@ -1,12 +1,12 @@
package ca.uhn.fhir.jpa.subscription.resthook;
import ca.uhn.fhir.jpa.config.StoppableSubscriptionDeliveringRestHookSubscriber;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR5Test;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.HapiExtensions;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.r5.model.*;
import org.junit.After;
@ -299,7 +299,7 @@ public class RestHookTestR5Test extends BaseSubscriptionsR5Test {
assertEquals(observation1.getIdElement().getVersionIdPart(), idElement.getVersionIdPart());
subscription1
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true"));
.addExtension(HapiExtensions.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true"));
ourLog.info("** About to update subscription");
int modCount = myCountingInterceptor.getSentCount("Subscription");
@ -375,7 +375,7 @@ public class RestHookTestR5Test extends BaseSubscriptionsR5Test {
Subscription subscription = newSubscription(criteria1, payload);
subscription
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true"));
.addExtension(HapiExtensions.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true"));
ourClient.create().resource(subscription).execute();
waitForActivatedSubscriptionCount(1);

View File

@ -0,0 +1,44 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeType;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test {
public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system";
private CodeSystem createCodeSystemWithMoreThan100Concepts() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
for (int i = 0; i < 125; i++) {
codeSystem.addConcept(new CodeSystem.ConceptDefinitionComponent(new CodeType("codeA " + i)));
}
return codeSystem;
}
@Test
public void testStoreNewCodeSystemVersionForExistingCodeSystem() {
CodeSystem upload = createCodeSystemWithMoreThan100Concepts();
ResourceTable codeSystemResourceEntity = (ResourceTable)myCodeSystemDao.create(upload, mySrd).getEntity();
runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(upload, codeSystemResourceEntity));
myTerminologyDeferredStorageSvc.setProcessDeferred(true);
myTerminologyDeferredStorageSvc.saveDeferred();
myTerminologyDeferredStorageSvc.saveDeferred();
assertEquals(125, myTermConceptDao.count());
}
}

View File

@ -1,7 +1,9 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
@ -11,6 +13,8 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.transaction.PlatformTransactionManager;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.*;
@ -24,6 +28,8 @@ public class TermDeferredStorageSvcImplTest {
private ITermCodeSystemStorageSvc myTermConceptStorageSvc;
@Mock
private ITermConceptDao myConceptDao;
@Mock
private ITermCodeSystemVersionDao myTermCodeSystemVersionDao;
@Test
public void testSaveDeferredWithExecutionSuspended() {
@ -38,16 +44,45 @@ public class TermDeferredStorageSvcImplTest {
TermConcept concept = new TermConcept();
concept.setCode("CODE_A");
TermCodeSystemVersion myTermCodeSystemVersion = new TermCodeSystemVersion();
myTermCodeSystemVersion.setId(1L);
concept.setCodeSystemVersion(myTermCodeSystemVersion);
TermDeferredStorageSvcImpl svc = new TermDeferredStorageSvcImpl();
svc.setTransactionManagerForUnitTest(myTxManager);
svc.setCodeSystemStorageSvcForUnitTest(myTermConceptStorageSvc);
svc.setDaoConfigForUnitTest(new DaoConfig());
when(myTermCodeSystemVersionDao.findById(anyLong())).thenReturn(Optional.of(myTermCodeSystemVersion));
svc.setCodeSystemVersionDaoForUnitTest(myTermCodeSystemVersionDao);
svc.setProcessDeferred(true);
svc.addConceptToStorageQueue(concept);
svc.saveDeferred();
verify(myTermConceptStorageSvc, times(1)).saveConcept(same(concept));
verifyNoMoreInteractions(myTermConceptStorageSvc);
}
@Test
public void testSaveDeferred_Concept_StaleCodeSystemVersion() {
TermConcept concept = new TermConcept();
concept.setCode("CODE_A");
TermCodeSystemVersion myTermCodeSystemVersion = new TermCodeSystemVersion();
concept.setCodeSystemVersion(myTermCodeSystemVersion);
TermDeferredStorageSvcImpl svc = new TermDeferredStorageSvcImpl();
svc.setTransactionManagerForUnitTest(myTxManager);
svc.setCodeSystemStorageSvcForUnitTest(myTermConceptStorageSvc);
svc.setDaoConfigForUnitTest(new DaoConfig());
svc.setCodeSystemVersionDaoForUnitTest(myTermCodeSystemVersionDao);
svc.setProcessDeferred(true);
svc.addConceptToStorageQueue(concept);
svc.saveDeferred();
verify(myTermConceptStorageSvc, times(0)).saveConcept(same(concept));
verifyNoMoreInteractions(myTermConceptStorageSvc);
}
@Test

View File

@ -10,9 +10,9 @@ import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.HapiExtensions;
import com.google.common.collect.Lists;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.CodeSystem;
@ -796,7 +796,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test {
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
ourLog.info(encoded);
Extension extensionByUrl = outcome.getExpansion().getExtensionByUrl(JpaConstants.EXT_VALUESET_EXPANSION_MESSAGE);
Extension extensionByUrl = outcome.getExpansion().getExtensionByUrl(HapiExtensions.EXT_VALUESET_EXPANSION_MESSAGE);
assertEquals("Unknown CodeSystem URI \"http://unknown-system\" referenced from ValueSet", extensionByUrl.getValueAsPrimitive().getValueAsString());
}

View File

@ -17,7 +17,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.5</version>
<version>8.0.16</version>
</dependency>
<!--
<dependency>

View File

@ -166,62 +166,6 @@ public class JpaConstants {
*/
public static final String OPERATION_LASTN = "$lastn";
/**
* <p>
* This extension should be of type <code>string</code> and should be
* placed on the <code>Subscription.channel</code> element
* </p>
*/
public static final String EXT_SUBSCRIPTION_SUBJECT_TEMPLATE = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template";
/**
* This extension URL indicates whether a REST HOOK delivery should
* include the version ID when delivering.
* <p>
* This extension should be of type <code>boolean</code> and should be
* placed on the <code>Subscription.channel</code> element.
* </p>
*/
public static final String EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-strip-version-ids";
/**
* This extension URL indicates whether a REST HOOK delivery should
* reload the resource and deliver the latest version always. This
* could be useful for example if a resource which triggers a
* subscription gets updated many times in short succession and there
* is no value in delivering the older versions.
* <p>
* Note that if the resource is now deleted, this may cause
* the delivery to be cancelled altogether.
* </p>
*
* <p>
* This extension should be of type <code>boolean</code> and should be
* placed on the <code>Subscription.channel</code> element.
* </p>
*/
public static final String EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-deliver-latest-version";
/**
* Indicate which strategy will be used to match this subscription
*/
public static final String EXT_SUBSCRIPTION_MATCHING_STRATEGY = "http://hapifhir.io/fhir/StructureDefinition/subscription-matching-strategy";
/**
* <p>
* This extension should be of type <code>string</code> and should be
* placed on the <code>Subscription.channel</code> element
* </p>
*/
public static final String EXT_SUBSCRIPTION_EMAIL_FROM = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-from";
/**
* Extension ID for external binary references
*/
public static final String EXT_EXTERNALIZED_BINARY_ID = "http://hapifhir.io/fhir/StructureDefinition/externalized-binary-id";
/**
* Placed in system-generated extensions
*/
public static final String EXTENSION_EXT_SYSTEMDEFINED = JpaConstants.class.getName() + "_EXTENSION_EXT_SYSTEMDEFINED";
/**
* Message added to expansion valueset
*/
public static final String EXT_VALUESET_EXPANSION_MESSAGE = "http://hapifhir.io/fhir/StructureDefinition/valueset-expansion-message";
/**
* Parameter for the $export operation
*/
@ -242,11 +186,15 @@ public class JpaConstants {
* Parameter for the $export operation
*/
public static final String PARAM_EXPORT_TYPE_FILTER = "_typeFilter";
/**
* URL for extension on a SearchParameter indicating that text values should not be indexed
*/
public static final String EXTENSION_EXT_SYSTEMDEFINED = JpaConstants.class.getName() + "_EXTENSION_EXT_SYSTEMDEFINED";
/**
* Extension URL for extension on a SearchParameter indicating that text values should not be indexed
* URL for extension on a Phonetic String SearchParameter indicating that text values should be phonetically indexed with the named encoder
*/
public static final String EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING = "http://hapifhir.io/fhir/StructureDefinition/searchparameter-token-suppress-text-index";
public static final String EXT_SEARCHPARAM_PHONETIC_ENCODER = "http://hapifhir.io/fhir/StructureDefinition/searchparameter-phonetic-encoder";
/**
* Non-instantiable

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
@ -50,7 +51,7 @@ public class MatchUrlService {
@Autowired
private ISearchParamRegistry mySearchParamRegistry;
public SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition theResourceDefinition) {
public SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition theResourceDefinition, Flag... theFlags) {
SearchParameterMap paramMap = new SearchParameterMap();
List<NameValuePair> parameters = translateMatchUrl(theMatchUrl);
@ -79,6 +80,13 @@ public class MatchUrlService {
for (String nextParamName : nameToParamLists.keySet()) {
List<QualifiedParamList> paramList = nameToParamLists.get(nextParamName);
if (theFlags != null) {
for (Flag next : theFlags) {
next.process(nextParamName, paramList, paramMap);
}
}
if (Constants.PARAM_LASTUPDATED.equals(nextParamName)) {
if (paramList != null && paramList.size() > 0) {
if (paramList.size() > 2) {
@ -131,8 +139,8 @@ public class MatchUrlService {
return UrlUtil.translateMatchUrl(theMatchUrl);
}
private IQueryParameterAnd newInstanceAnd(String theParamType) {
Class<? extends IQueryParameterAnd> clazz = ResourceMetaParams.RESOURCE_META_AND_PARAMS.get(theParamType);
private IQueryParameterAnd<?> newInstanceAnd(String theParamType) {
Class<? extends IQueryParameterAnd<?>> clazz = ResourceMetaParams.RESOURCE_META_AND_PARAMS.get(theParamType);
return ReflectionUtil.newInstance(clazz);
}
@ -140,4 +148,44 @@ public class MatchUrlService {
Class<? extends IQueryParameterType> clazz = ResourceMetaParams.RESOURCE_META_PARAMS.get(theParamType);
return ReflectionUtil.newInstance(clazz);
}
public abstract static class Flag {
/**
* Constructor
*/
Flag() {
// nothing
}
abstract void process(String theParamName, List<QualifiedParamList> theValues, SearchParameterMap theMapToPopulate);
}
/**
* Indicates that the parser should process _include and _revinclude (by default these are not handled)
*/
public static Flag processIncludes() {
return new Flag() {
@Override
void process(String theParamName, List<QualifiedParamList> theValues, SearchParameterMap theMapToPopulate) {
if (Constants.PARAM_INCLUDE.equals(theParamName)) {
for (QualifiedParamList nextQualifiedList : theValues) {
for (String nextValue : nextQualifiedList) {
theMapToPopulate.addInclude(new Include(nextValue, ParameterUtil.isIncludeIterate(nextQualifiedList.getQualifier())));
}
}
} else if (Constants.PARAM_REVINCLUDE.equals(theParamName)) {
for (QualifiedParamList nextQualifiedList : theValues) {
for (String nextValue : nextQualifiedList) {
theMapToPopulate.addInclude(new Include(nextValue, ParameterUtil.isIncludeIterate(nextQualifiedList.getQualifier())));
}
}
}
}
};
}
}

View File

@ -22,7 +22,5 @@ package ca.uhn.fhir.jpa.searchparam;
public class SearchParamConstants {
public static final String EXT_SP_UNIQUE = "http://hapifhir.io/fhir/StructureDefinition/sp-unique";
public static final String UCUM_NS = "http://unitsofmeasure.org";
}

View File

@ -33,6 +33,7 @@ import ca.uhn.fhir.jpa.searchparam.matcher.IndexedSearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParameterCanonicalizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -81,6 +82,12 @@ public class SearchParamConfig {
return new SearchParamExtractorService();
}
@Bean
@Lazy
public SearchParameterCanonicalizer searchParameterCanonicalizer(FhirContext theFhirContext) {
return new SearchParameterCanonicalizer(theFhirContext);
}
@Bean
public IndexedSearchParamExtractor indexedSearchParamExtractor() {
return new IndexedSearchParamExtractor();

View File

@ -37,12 +37,12 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
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.HapiExtensions;
import ca.uhn.fhir.util.StringUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@ -81,8 +81,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim;
public abstract class BaseSearchParamExtractor implements ISearchParamExtractor {
private static final Pattern SPLIT = Pattern.compile("\\||( or )");
private static final Pattern SPLIT = Pattern.compile("\\||( or )");
private static final Pattern SPLIT_R4 = Pattern.compile("\\|");
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSearchParamExtractor.class);
@Autowired
@ -95,6 +95,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private ModelConfig myModelConfig;
@Autowired
private PartitionSettings myPartitionSettings;
private Set<String> myIgnoredForSearchDatatypes;
private BaseRuntimeChildDefinition myQuantityValueValueChild;
private BaseRuntimeChildDefinition myQuantitySystemValueChild;
@ -972,12 +973,10 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
for (String next : families) {
createStringIndexIfNotBlank(theResourceType, theParams, theSearchParam, next);
}
List<String> givens = extractValuesAsStrings(myHumanNameGivenValueChild, theValue);
for (String next : givens) {
createStringIndexIfNotBlank(theResourceType, theParams, theSearchParam, next);
}
}
private void addString_Quantity(String theResourceType, Set<ResourceIndexedSearchParamString> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
@ -1099,11 +1098,13 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
String searchParamName = theSearchParam.getName();
String valueNormalized = StringUtil.normalizeStringForSearchIndexing(value);
if (valueNormalized.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
valueNormalized = valueNormalized.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
String valueEncoded = theSearchParam.encode(valueNormalized);
if (valueEncoded.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
valueEncoded = valueEncoded.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(myPartitionSettings, getModelConfig(), theResourceType, searchParamName, valueNormalized, value);
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(myPartitionSettings, getModelConfig(), theResourceType, searchParamName, valueEncoded, value);
Set params = theParams;
params.add(nextEntity);
@ -1374,7 +1375,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
public static boolean tokenTextIndexingEnabledForSearchParam(ModelConfig theModelConfig, RuntimeSearchParam theSearchParam) {
Optional<Boolean> noSuppressForSearchParam = theSearchParam.getExtensions(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING).stream()
Optional<Boolean> noSuppressForSearchParam = theSearchParam.getExtensions(HapiExtensions.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING).stream()
.map(IBaseExtension::getValue)
.map(val -> (IPrimitiveType<?>) val)
.map(IPrimitiveType::getValueAsString)

View File

@ -85,6 +85,7 @@ public class SearchParamExtractorService {
@Autowired(required = false)
private IResourceLinkResolver myResourceLinkResolver;
/**
* 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}.

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.searchparam.registry;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import java.util.Collection;
@ -59,4 +60,12 @@ public interface ISearchParamRegistry {
RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName);
Collection<RuntimeSearchParam> getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef);
/**
* When indexing a HumanName, if a StringEncoder is set in the context, then the "phonetic" search parameter will normalize
* the String using this encoder.
*
* @since 5.1.0
*/
void setPhoneticEncoder(IPhoneticEncoder thePhoneticEncoder);
}

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.searchparam.registry;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
@ -32,35 +33,35 @@ import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.retry.Retrier;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.DatatypeUtil;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.SearchParameterUtil;
import ca.uhn.fhir.util.StopWatch;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.SearchParameter;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class SearchParamRegistryImpl implements ISearchParamRegistry {
@ -76,8 +77,12 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry {
private FhirContext myFhirContext;
@Autowired
private ISchedulerService mySchedulerService;
@Autowired
private SearchParameterCanonicalizer mySearchParameterCanonicalizer;
private Map<String, Map<String, RuntimeSearchParam>> myBuiltInSearchParams;
private IPhoneticEncoder myPhoneticEncoder;
private volatile Map<String, List<JpaRuntimeSearchParam>> myActiveUniqueSearchParams = Collections.emptyMap();
private volatile Map<String, Map<Set<String>, List<JpaRuntimeSearchParam>>> myActiveParamNamesToUniqueSearchParams = Collections.emptyMap();
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
@ -175,6 +180,8 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry {
uniqueSearchParams.add(nextCandidateCasted);
}
}
setPhoneticEncoder(nextCandidate);
}
}
@ -228,21 +235,11 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry {
myActiveParamNamesToUniqueSearchParams = activeParamNamesToUniqueSearchParams;
}
@VisibleForTesting
void setFhirContextForUnitTest(FhirContext theFhirContext) {
myFhirContext = theFhirContext;
}
@PostConstruct
public void postConstruct() {
myBuiltInSearchParams = createBuiltInSearchParamMap(myFhirContext);
}
@VisibleForTesting
public void setSearchParamProviderForUnitTest(ISearchParamProvider theSearchParamProvider) {
mySearchParamProvider = theSearchParamProvider;
}
public int doRefresh(long theRefreshInterval) {
if (System.currentTimeMillis() - theRefreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch();
@ -280,7 +277,7 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry {
continue;
}
RuntimeSearchParam runtimeSp = canonicalizeSearchParameter(nextSp);
RuntimeSearchParam runtimeSp = mySearchParameterCanonicalizer.canonicalizeSearchParameter(nextSp);
if (runtimeSp == null) {
continue;
}
@ -337,361 +334,6 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry {
}
}
protected RuntimeSearchParam canonicalizeSearchParameter(IBaseResource theSearchParameter) {
switch (myFhirContext.getVersion().getVersion()) {
case DSTU2:
return canonicalizeSearchParameterDstu2((ca.uhn.fhir.model.dstu2.resource.SearchParameter) theSearchParameter);
case DSTU3:
return canonicalizeSearchParameterDstu3((org.hl7.fhir.dstu3.model.SearchParameter) theSearchParameter);
case R4:
return canonicalizeSearchParameterR4((org.hl7.fhir.r4.model.SearchParameter) theSearchParameter);
case DSTU2_HL7ORG:
case DSTU2_1:
// Non-supported - these won't happen so just fall through
case R5:
default:
return canonicalizeSearchParameterR5((org.hl7.fhir.r5.model.SearchParameter) theSearchParameter);
}
}
private RuntimeSearchParam canonicalizeSearchParameterDstu2(ca.uhn.fhir.model.dstu2.resource.SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getXpath();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getTypeElement().getValueAsEnum()) {
case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE_DATETIME:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatusElement().getValueAsEnum()) {
case ACTIVE:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
break;
}
}
Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path)) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null;
}
}
IIdType id = theNextSp.getIdElement();
String uri = "";
boolean unique = false;
List<ExtensionDt> uniqueExts = theNextSp.getUndeclaredExtensionsByUrl(SearchParamConstants.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
unique = true;
}
}
}
List<JpaRuntimeSearchParam.Component> components = Collections.emptyList();
Collection<? extends IPrimitiveType<String>> base = Collections.singletonList(theNextSp.getBaseElement());
JpaRuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, base);
extractExtensions(theNextSp, retVal);
return retVal;
}
private RuntimeSearchParam canonicalizeSearchParameterDstu3(org.hl7.fhir.dstu3.model.SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) {
case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
case NULL:
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
break;
case UNKNOWN:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.UNKNOWN;
break;
case NULL:
break;
}
}
Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null;
}
}
IIdType id = theNextSp.getIdElement();
String uri = "";
boolean unique = false;
List<Extension> uniqueExts = theNextSp.getExtensionsByUrl(SearchParamConstants.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
unique = true;
}
}
}
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
for (SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition()));
}
JpaRuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
extractExtensions(theNextSp, retVal);
return retVal;
}
private RuntimeSearchParam canonicalizeSearchParameterR4(org.hl7.fhir.r4.model.SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) {
case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
case SPECIAL:
paramType = RestSearchParameterTypeEnum.SPECIAL;
break;
case NULL:
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
break;
case UNKNOWN:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.UNKNOWN;
break;
case NULL:
break;
}
}
Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null;
}
}
IIdType id = theNextSp.getIdElement();
String uri = "";
boolean unique = false;
List<org.hl7.fhir.r4.model.Extension> uniqueExts = theNextSp.getExtensionsByUrl(SearchParamConstants.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
unique = true;
}
}
}
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
for (org.hl7.fhir.r4.model.SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), new Reference(next.getDefinition())));
}
JpaRuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
extractExtensions(theNextSp, retVal);
return retVal;
}
private RuntimeSearchParam canonicalizeSearchParameterR5(org.hl7.fhir.r5.model.SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) {
case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
case SPECIAL:
paramType = RestSearchParameterTypeEnum.SPECIAL;
break;
case NULL:
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
break;
case UNKNOWN:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.UNKNOWN;
break;
case NULL:
break;
}
}
Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null;
}
}
IIdType id = theNextSp.getIdElement();
String uri = "";
boolean unique = false;
List<org.hl7.fhir.r5.model.Extension> uniqueExts = theNextSp.getExtensionsByUrl(SearchParamConstants.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
unique = true;
}
}
}
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
for (org.hl7.fhir.r5.model.SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), new org.hl7.fhir.r5.model.Reference(next.getDefinition())));
}
JpaRuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
extractExtensions(theNextSp, retVal);
return retVal;
}
@Override
public RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName) {
@ -760,26 +402,6 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry {
return Collections.unmodifiableMap(myActiveSearchParams);
}
@VisibleForTesting
void setSchedulerServiceForUnitTest(ISchedulerService theSchedulerService) {
mySchedulerService = theSchedulerService;
}
/**
* Extracts any extensions from the resource and populates an extension field in the
*/
protected void extractExtensions(IBaseResource theSearchParamResource, JpaRuntimeSearchParam theRuntimeSearchParam) {
if (theSearchParamResource instanceof IBaseHasExtensions) {
List<? extends IBaseExtension<?, ?>> extensions = ((IBaseHasExtensions) theSearchParamResource).getExtension();
for (IBaseExtension<?, ?> next : extensions) {
String nextUrl = next.getUrl();
if (isNotBlank(nextUrl)) {
theRuntimeSearchParam.addExtension(nextUrl, next);
}
}
}
}
public static Map<String, Map<String, RuntimeSearchParam>> createBuiltInSearchParamMap(FhirContext theFhirContext) {
Map<String, Map<String, RuntimeSearchParam>> resourceNameToSearchParams = new HashMap<>();
@ -797,4 +419,31 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry {
}
return Collections.unmodifiableMap(resourceNameToSearchParams);
}
/**
* All SearchParameters with the name "phonetic" encode the normalized index value using this phonetic encoder.
*
* @since 5.1.0
*/
public void setPhoneticEncoder(IPhoneticEncoder thePhoneticEncoder) {
myPhoneticEncoder = thePhoneticEncoder;
if (myActiveSearchParams == null) {
return;
}
for (Map<String, RuntimeSearchParam> activeUniqueSearchParams : myActiveSearchParams.values()) {
for (RuntimeSearchParam searchParam : activeUniqueSearchParams.values()) {
setPhoneticEncoder(searchParam);
}
}
}
private void setPhoneticEncoder(RuntimeSearchParam searchParam) {
if ("phonetic".equals(searchParam.getName())) {
ourLog.debug("Setting search param {} on {} phonetic encoder to {}",
searchParam.getName(), searchParam.getPath(), myPhoneticEncoder == null ? "null" : myPhoneticEncoder.name());
searchParam.setPhoneticEncoder(myPhoneticEncoder);
}
}
}

View File

@ -0,0 +1,451 @@
package ca.uhn.fhir.jpa.searchparam.registry;
/*-
* #%L
* HAPI FHIR Search Parameters
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.DatatypeUtil;
import ca.uhn.fhir.util.HapiExtensions;
import org.apache.commons.lang3.EnumUtils;
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.SearchParameter;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Service
public class SearchParameterCanonicalizer {
private static final Logger ourLog = LoggerFactory.getLogger(SearchParameterCanonicalizer.class);
private final FhirContext myFhirContext;
@Autowired
public SearchParameterCanonicalizer(FhirContext theFhirContext) {
myFhirContext = theFhirContext;
}
protected RuntimeSearchParam canonicalizeSearchParameter(IBaseResource theSearchParameter) {
JpaRuntimeSearchParam retVal;
switch (myFhirContext.getVersion().getVersion()) {
case DSTU2:
retVal = canonicalizeSearchParameterDstu2((ca.uhn.fhir.model.dstu2.resource.SearchParameter) theSearchParameter);
break;
case DSTU3:
retVal = canonicalizeSearchParameterDstu3((org.hl7.fhir.dstu3.model.SearchParameter) theSearchParameter);
break;
case R4:
retVal = canonicalizeSearchParameterR4((org.hl7.fhir.r4.model.SearchParameter) theSearchParameter);
break;
case R5:
retVal = canonicalizeSearchParameterR5((org.hl7.fhir.r5.model.SearchParameter) theSearchParameter);
break;
case DSTU2_HL7ORG:
case DSTU2_1:
// Non-supported - these won't happen so just fall through
default:
throw new InternalErrorException("SearchParameter canonicalization not supported for FHIR version" + myFhirContext.getVersion().getVersion());
}
extractExtensions(theSearchParameter, retVal);
return retVal;
}
private JpaRuntimeSearchParam canonicalizeSearchParameterDstu2(ca.uhn.fhir.model.dstu2.resource.SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getXpath();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getTypeElement().getValueAsEnum()) {
case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE_DATETIME:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatusElement().getValueAsEnum()) {
case ACTIVE:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
break;
}
}
Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path)) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null;
}
}
IIdType id = theNextSp.getIdElement();
String uri = "";
boolean unique = false;
List<ExtensionDt> uniqueExts = theNextSp.getUndeclaredExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
unique = true;
}
}
}
List<JpaRuntimeSearchParam.Component> components = Collections.emptyList();
Collection<? extends IPrimitiveType<String>> base = Collections.singletonList(theNextSp.getBaseElement());
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, base);
}
private JpaRuntimeSearchParam canonicalizeSearchParameterDstu3(org.hl7.fhir.dstu3.model.SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) {
case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
case NULL:
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
break;
case UNKNOWN:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.UNKNOWN;
break;
case NULL:
break;
}
}
Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null;
}
}
IIdType id = theNextSp.getIdElement();
String uri = "";
boolean unique = false;
List<Extension> uniqueExts = theNextSp.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
unique = true;
}
}
}
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
for (SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition()));
}
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
}
private JpaRuntimeSearchParam canonicalizeSearchParameterR4(org.hl7.fhir.r4.model.SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) {
case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
case SPECIAL:
paramType = RestSearchParameterTypeEnum.SPECIAL;
break;
case NULL:
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
break;
case UNKNOWN:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.UNKNOWN;
break;
case NULL:
break;
}
}
Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null;
}
}
IIdType id = theNextSp.getIdElement();
String uri = "";
boolean unique = false;
List<org.hl7.fhir.r4.model.Extension> uniqueExts = theNextSp.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
unique = true;
}
}
}
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
for (org.hl7.fhir.r4.model.SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), new Reference(next.getDefinition())));
}
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
}
private JpaRuntimeSearchParam canonicalizeSearchParameterR5(org.hl7.fhir.r5.model.SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) {
case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
case SPECIAL:
paramType = RestSearchParameterTypeEnum.SPECIAL;
break;
case NULL:
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.RETIRED;
break;
case UNKNOWN:
status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.UNKNOWN;
break;
case NULL:
break;
}
}
Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null;
}
}
IIdType id = theNextSp.getIdElement();
String uri = "";
boolean unique = false;
List<org.hl7.fhir.r5.model.Extension> uniqueExts = theNextSp.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
unique = true;
}
}
}
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
for (org.hl7.fhir.r5.model.SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), new org.hl7.fhir.r5.model.Reference(next.getDefinition())));
}
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
}
/**
* Extracts any extensions from the resource and populates an extension field in the
*/
protected void extractExtensions(IBaseResource theSearchParamResource, JpaRuntimeSearchParam theRuntimeSearchParam) {
if (theSearchParamResource instanceof IBaseHasExtensions) {
List<? extends IBaseExtension<?, ?>> extensions = ((IBaseHasExtensions) theSearchParamResource).getExtension();
for (IBaseExtension<?, ?> next : extensions) {
String nextUrl = next.getUrl();
if (isNotBlank(nextUrl)) {
theRuntimeSearchParam.addExtension(nextUrl, next);
if (JpaConstants.EXT_SEARCHPARAM_PHONETIC_ENCODER.equals(nextUrl)) {
setEncoder(theRuntimeSearchParam, next.getValue());
}
}
}
}
}
private void setEncoder(JpaRuntimeSearchParam theRuntimeSearchParam, IBaseDatatype theValue) {
if (theValue instanceof IPrimitiveType) {
String stringValue = ((IPrimitiveType<?>) theValue).getValueAsString();
PhoneticEncoderEnum encoderEnum = EnumUtils.getEnum(PhoneticEncoderEnum.class, stringValue);
if (encoderEnum != null) {
theRuntimeSearchParam.setPhoneticEncoder(encoderEnum.getPhoneticEncoder());
} else {
ourLog.error("Invalid PhoneticEncoderEnum value '" + stringValue + "'");
}
}
}
}

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
@ -291,6 +292,11 @@ public class SearchParamExtractorDstu3Test {
public Collection<RuntimeSearchParam> getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef) {
return null;
}
@Override
public void setPhoneticEncoder(IPhoneticEncoder thePhoneticEncoder) {
// nothing
}
}
@AfterClass

View File

@ -16,6 +16,7 @@ import ca.uhn.fhir.context.RuntimePrimitiveDatatypeNarrativeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
@ -300,6 +301,11 @@ public class SearchParamExtractorMegaTest {
public Collection<RuntimeSearchParam> getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef) {
return null;
}
@Override
public void setPhoneticEncoder(IPhoneticEncoder thePhoneticEncoder) {
// nothing
}
}
}

View File

@ -2,6 +2,8 @@ package ca.uhn.fhir.jpa.searchparam.registry;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -13,25 +15,50 @@ import org.hl7.fhir.r4.model.StringType;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Map;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@RunWith(SpringRunner.class)
public class SearchParamRegistryImplTest {
@Autowired
SearchParamRegistryImpl mySearchParamRegistry;
@Mock
@MockBean
private ISchedulerService mySchedulerService;
@Mock
@MockBean
private ISearchParamProvider mySearchParamProvider;
private int myAnswerCount = 0;
@MockBean
private ModelConfig myModelConfig;
@MockBean
private IInterceptorBroadcaster myInterceptorBroadcaster;
@Configuration
static class SpringConfig {
@Bean
FhirContext fhirContext() { return FhirContext.forR4(); }
@Bean
ISearchParamRegistry searchParamRegistry() { return new SearchParamRegistryImpl(); }
@Bean
SearchParameterCanonicalizer searchParameterCanonicalizer(FhirContext theFhirContext) {
return new SearchParameterCanonicalizer(theFhirContext);
}
}
private int myAnswerCount = 0;
@Before
public void before() {
@ -42,56 +69,40 @@ public class SearchParamRegistryImplTest {
public void testRefreshAfterExpiry() {
when(mySearchParamProvider.search(any())).thenReturn(new SimpleBundleProvider());
SearchParamRegistryImpl registry = new SearchParamRegistryImpl();
registry.setSchedulerServiceForUnitTest(mySchedulerService);
registry.setFhirContextForUnitTest(FhirContext.forR4());
registry.setSearchParamProviderForUnitTest(mySearchParamProvider);
registry.postConstruct();
registry.requestRefresh();
assertEquals(146, registry.doRefresh(100000));
mySearchParamRegistry.requestRefresh();
assertEquals(146, mySearchParamRegistry.doRefresh(100000));
// Second time we don't need to run because we ran recently
assertEquals(0, registry.doRefresh(100000));
assertEquals(0, mySearchParamRegistry.doRefresh(100000));
assertEquals(146, registry.getActiveSearchParams().size());
assertEquals(146, mySearchParamRegistry.getActiveSearchParams().size());
}
@Test
public void testRefreshCacheIfNecessary() {
SearchParamRegistryImpl registry = new SearchParamRegistryImpl();
when(mySearchParamProvider.search(any())).thenReturn(new SimpleBundleProvider());
when(mySearchParamProvider.refreshCache(any(), anyLong())).thenAnswer(t -> {
registry.doRefresh(t.getArgument(1, Long.class));
mySearchParamRegistry.doRefresh(t.getArgument(1, Long.class));
return 0;
});
registry.setSchedulerServiceForUnitTest(mySchedulerService);
registry.setFhirContextForUnitTest(FhirContext.forR4());
registry.setSearchParamProviderForUnitTest(mySearchParamProvider);
registry.postConstruct();
registry.requestRefresh();
mySearchParamRegistry.requestRefresh();
assertTrue(registry.refreshCacheIfNecessary());
assertFalse(registry.refreshCacheIfNecessary());
assertTrue(mySearchParamRegistry.refreshCacheIfNecessary());
assertFalse(mySearchParamRegistry.refreshCacheIfNecessary());
registry.requestRefresh();
assertTrue(registry.refreshCacheIfNecessary());
mySearchParamRegistry.requestRefresh();
assertTrue(mySearchParamRegistry.refreshCacheIfNecessary());
}
@Test
public void testGetActiveUniqueSearchParams_Empty() {
SearchParamRegistryImpl registry = new SearchParamRegistryImpl();
assertThat(registry.getActiveUniqueSearchParams("Patient"), Matchers.empty());
assertThat(mySearchParamRegistry.getActiveUniqueSearchParams("Patient"), Matchers.empty());
}
@Test
public void testGetActiveSearchParams() {
SearchParamRegistryImpl registry = new SearchParamRegistryImpl();
registry.setFhirContextForUnitTest(FhirContext.forR4());
registry.postConstruct();
when(mySearchParamProvider.search(any())).thenReturn(new SimpleBundleProvider());
when(mySearchParamProvider.refreshCache(any(), anyLong())).thenAnswer(t -> {
if (myAnswerCount == 0) {
@ -99,21 +110,16 @@ public class SearchParamRegistryImplTest {
throw new InternalErrorException("this is an error!");
}
registry.doRefresh(0);
mySearchParamRegistry.doRefresh(0);
return 0;
});
registry.setSearchParamProviderForUnitTest(mySearchParamProvider);
Map<String, RuntimeSearchParam> outcome = registry.getActiveSearchParams("Patient");
Map<String, RuntimeSearchParam> outcome = mySearchParamRegistry.getActiveSearchParams("Patient");
}
@Test
public void testExtractExtensions() {
SearchParamRegistryImpl registry = new SearchParamRegistryImpl();
registry.setFhirContextForUnitTest(FhirContext.forR4());
registry.postConstruct();
SearchParameter searchParameter = new SearchParameter();
searchParameter.setCode("foo");
searchParameter.setStatus(Enumerations.PublicationStatus.ACTIVE);
@ -129,12 +135,12 @@ public class SearchParamRegistryImplTest {
when(mySearchParamProvider.search(any())).thenReturn(new SimpleBundleProvider(searchParameter));
when(mySearchParamProvider.refreshCache(any(), anyLong())).thenAnswer(t -> {
registry.doRefresh(0);
mySearchParamRegistry.doRefresh(0);
return 0;
});
registry.setSearchParamProviderForUnitTest(mySearchParamProvider);
Map<String, RuntimeSearchParam> outcome = registry.getActiveSearchParams("Patient");
mySearchParamRegistry.forceRefresh();
Map<String, RuntimeSearchParam> outcome = mySearchParamRegistry.getActiveSearchParams("Patient");
RuntimeSearchParam converted = outcome.get("foo");
assertNotNull(converted);

View File

@ -74,7 +74,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel
public void handleMessage(ResourceDeliveryMessage theMessage) throws MessagingException, URISyntaxException {
CanonicalSubscription subscription = theMessage.getSubscription();
// Interceptor call: SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY
// Interceptor call: SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY
HookParams params = new HookParams()
.add(CanonicalSubscription.class, subscription)
.add(ResourceDeliveryMessage.class, theMessage);
@ -104,7 +104,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel
deliverPayload(theMessage, subscription, channelProducer);
// Interceptor call: SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY
// Interceptor call: SUBSCRIPTION_AFTER_MESSAGE_DELIVERY
params = new HookParams()
.add(CanonicalSubscription.class, subscription)
.add(ResourceDeliveryMessage.class, theMessage);

View File

@ -25,11 +25,14 @@ import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.subscription.match.deliver.BaseSubscriptionDeliverySubscriber;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IHttpClient;
@ -40,7 +43,9 @@ import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.TransactionBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
@ -49,6 +54,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.messaging.MessagingException;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@ -65,6 +71,9 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
@Autowired
private DaoRegistry myDaoRegistry;
@Autowired
private MatchUrlService myMatchUrlService;
/**
* Constructor
*/
@ -81,41 +90,23 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
protected void doDelivery(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, EncodingEnum thePayloadType, IGenericClient theClient, IBaseResource thePayloadResource) {
IClientExecutable<?, ?> operation;
switch (theMsg.getOperationType()) {
case CREATE:
case UPDATE:
if (thePayloadResource == null || thePayloadResource.isEmpty()) {
if (thePayloadType != null) {
operation = theClient.create().resource(thePayloadResource);
if (isNotBlank(theSubscription.getPayloadSearchCriteria())) {
operation = createDeliveryRequestTransaction(theSubscription, theClient, thePayloadResource);
} else if (thePayloadType != null) {
operation = createDeliveryRequestNormal(theMsg, theClient, thePayloadResource);
} else {
sendNotification(theMsg);
return;
}
} else {
if (thePayloadType != null) {
operation = theClient.update().resource(thePayloadResource);
} else {
sendNotification(theMsg);
return;
}
}
break;
case DELETE:
operation = theClient.delete().resourceById(theMsg.getPayloadId(myFhirContext));
break;
default:
ourLog.warn("Ignoring delivery message of type: {}", theMsg.getOperationType());
return;
operation = null;
}
if (operation != null) {
if (thePayloadType != null) {
operation.encoded(thePayloadType);
}
String payloadId = null;
if (thePayloadResource != null) {
payloadId = thePayloadResource.getIdElement().toUnqualified().getValue();
}
String payloadId = thePayloadResource.getIdElement().toUnqualified().getValue();
ourLog.info("Delivering {} rest-hook payload {} for {}", theMsg.getOperationType(), payloadId, theSubscription.getIdElement(myFhirContext).toUnqualifiedVersionless().getValue());
try {
@ -125,11 +116,56 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
ourLog.error("Exception: ", e);
throw e;
}
}
}
@Nullable
private IClientExecutable<?, ?> createDeliveryRequestNormal(ResourceDeliveryMessage theMsg, IGenericClient theClient, IBaseResource thePayloadResource) {
IClientExecutable<?, ?> operation;
switch (theMsg.getOperationType()) {
case CREATE:
case UPDATE:
operation = theClient.update().resource(thePayloadResource);
break;
case DELETE:
operation = theClient.delete().resourceById(theMsg.getPayloadId(myFhirContext));
break;
default:
ourLog.warn("Ignoring delivery message of type: {}", theMsg.getOperationType());
operation = null;
break;
}
return operation;
}
private IClientExecutable<?, ?> createDeliveryRequestTransaction(CanonicalSubscription theSubscription, IGenericClient theClient, IBaseResource thePayloadResource) {
IClientExecutable<?, ?> operation;
String resType = theSubscription.getPayloadSearchCriteria().substring(0, theSubscription.getPayloadSearchCriteria().indexOf('?'));
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(resType);
RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(resType);
String payloadUrl = theSubscription.getPayloadSearchCriteria();
Map<String, String> valueMap = new HashMap<>(1);
valueMap.put("matched_resource_id", thePayloadResource.getIdElement().toUnqualifiedVersionless().getValue());
payloadUrl = new StringSubstitutor(valueMap).replace(payloadUrl);
SearchParameterMap payloadSearchMap = myMatchUrlService.translateMatchUrl(payloadUrl, resourceDefinition, MatchUrlService.processIncludes());
payloadSearchMap.setLoadSynchronous(true);
IBundleProvider searchResults = dao.search(payloadSearchMap);
TransactionBuilder builder = new TransactionBuilder(myFhirContext);
for (IBaseResource next : searchResults.getResources(0, searchResults.size())) {
builder.addUpdateEntry(next);
}
operation = theClient.transaction().withBundle(builder.getBundle());
return operation;
}
public IBaseResource getResource(IIdType payloadId) throws ResourceGoneException {
RuntimeResourceDefinition resourceDef = myFhirContext.getResourceDefinition(payloadId.getResourceType());
IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceDef.getImplementingClass());
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(resourceDef.getImplementingClass());
return dao.read(payloadId.toVersionless());
}
@ -237,8 +273,7 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
// close connection in order to return a possible cached connection to the connection pool
response.close();
} catch (IOException e) {
ourLog.error("Error trying to reach " + theMsg.getSubscription().getEndpointUrl());
e.printStackTrace();
ourLog.error("Error trying to reach {}: {}", theMsg.getSubscription().getEndpointUrl(), e.toString());
throw new ResourceNotFoundException(e.getMessage());
}
}

View File

@ -22,13 +22,13 @@ package ca.uhn.fhir.jpa.subscription.match.registry;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionMatchingStrategy;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.util.HapiExtensions;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
@ -113,14 +113,15 @@ public class SubscriptionCanonicalizer {
retVal.setChannelExtensions(extractExtension(subscription));
retVal.setIdElement(subscription.getIdElement());
retVal.setPayloadString(subscription.getChannel().getPayload());
retVal.setPayloadSearchCriteria(getExtensionString(subscription, HapiExtensions.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_CRITERIA));
if (retVal.getChannelType() == CanonicalSubscriptionChannelType.EMAIL) {
String from;
String subjectTemplate;
try {
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
from = subscription.getChannel().getExtensionString(HapiExtensions.EXT_SUBSCRIPTION_EMAIL_FROM);
subjectTemplate = subscription.getChannel().getExtensionString(HapiExtensions.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
} catch (FHIRException theE) {
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
}
@ -133,8 +134,8 @@ public class SubscriptionCanonicalizer {
String stripVersionIds;
String deliverLatestVersion;
try {
stripVersionIds = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
deliverLatestVersion = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
stripVersionIds = subscription.getChannel().getExtensionString(HapiExtensions.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
deliverLatestVersion = subscription.getChannel().getExtensionString(HapiExtensions.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
} catch (FHIRException theE) {
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
}
@ -208,13 +209,14 @@ public class SubscriptionCanonicalizer {
retVal.setChannelExtensions(extractExtension(subscription));
retVal.setIdElement(subscription.getIdElement());
retVal.setPayloadString(subscription.getChannel().getPayload());
retVal.setPayloadSearchCriteria(getExtensionString(subscription, HapiExtensions.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_CRITERIA));
if (retVal.getChannelType() == CanonicalSubscriptionChannelType.EMAIL) {
String from;
String subjectTemplate;
try {
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
from = subscription.getChannel().getExtensionString(HapiExtensions.EXT_SUBSCRIPTION_EMAIL_FROM);
subjectTemplate = subscription.getChannel().getExtensionString(HapiExtensions.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
} catch (FHIRException theE) {
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
}
@ -226,8 +228,8 @@ public class SubscriptionCanonicalizer {
String stripVersionIds;
String deliverLatestVersion;
try {
stripVersionIds = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
deliverLatestVersion = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
stripVersionIds = subscription.getChannel().getExtensionString(HapiExtensions.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
deliverLatestVersion = subscription.getChannel().getExtensionString(HapiExtensions.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
} catch (FHIRException theE) {
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
}
@ -261,13 +263,14 @@ public class SubscriptionCanonicalizer {
retVal.setChannelExtensions(extractExtension(subscription));
retVal.setIdElement(subscription.getIdElement());
retVal.setPayloadString(subscription.getContentType());
retVal.setPayloadSearchCriteria(getExtensionString(subscription, HapiExtensions.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_CRITERIA));
if (retVal.getChannelType() == CanonicalSubscriptionChannelType.EMAIL) {
String from;
String subjectTemplate;
try {
from = getExtensionString(subscription, JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
subjectTemplate = getExtensionString(subscription, JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
from = getExtensionString(subscription, HapiExtensions.EXT_SUBSCRIPTION_EMAIL_FROM);
subjectTemplate = getExtensionString(subscription, HapiExtensions.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
} catch (FHIRException theE) {
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
}
@ -279,8 +282,8 @@ public class SubscriptionCanonicalizer {
String stripVersionIds;
String deliverLatestVersion;
try {
stripVersionIds = getExtensionString(subscription, JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
deliverLatestVersion = getExtensionString(subscription, JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
stripVersionIds = getExtensionString(subscription, HapiExtensions.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
deliverLatestVersion = getExtensionString(subscription, HapiExtensions.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
} catch (FHIRException theE) {
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
}
@ -382,7 +385,7 @@ public class SubscriptionCanonicalizer {
meta
.getTag()
.stream()
.filter(t -> JpaConstants.EXT_SUBSCRIPTION_MATCHING_STRATEGY.equals(t.getSystem()))
.filter(t -> HapiExtensions.EXT_SUBSCRIPTION_MATCHING_STRATEGY.equals(t.getSystem()))
.forEach(t -> {
t.setCode(null);
t.setSystem(null);
@ -403,7 +406,7 @@ public class SubscriptionCanonicalizer {
} else {
throw new IllegalStateException("Unknown " + SubscriptionMatchingStrategy.class.getSimpleName() + ": " + theStrategy);
}
meta.addTag().setSystem(JpaConstants.EXT_SUBSCRIPTION_MATCHING_STRATEGY).setCode(value).setDisplay(display);
meta.addTag().setSystem(HapiExtensions.EXT_SUBSCRIPTION_MATCHING_STRATEGY).setCode(value).setDisplay(display);
}
public String getSubscriptionStatus(IBaseResource theSubscription) {

View File

@ -34,7 +34,11 @@ import org.hl7.fhir.r4.model.Subscription;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -64,6 +68,8 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
private RestHookDetails myRestHookDetails;
@JsonProperty("extensions")
private Map<String, List<String>> myChannelExtensions;
@JsonProperty("payloadSearchCriteria")
private String myPayloadSearchCriteria;
/**
* Constructor
@ -72,6 +78,14 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
super();
}
public String getPayloadSearchCriteria() {
return myPayloadSearchCriteria;
}
public void setPayloadSearchCriteria(String thePayloadSearchCriteria) {
myPayloadSearchCriteria = thePayloadSearchCriteria;
}
/**
* For now we're using the R4 TriggerDefinition, but this
* may change in the future when things stabilize
@ -80,7 +94,6 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
myTrigger = theTrigger;
}
public CanonicalSubscriptionChannelType getChannelType() {
return myChannelType;
}
@ -276,6 +289,23 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
}
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("myIdElement", myIdElement)
.append("myStatus", myStatus)
.append("myCriteriaString", myCriteriaString)
.append("myEndpointUrl", myEndpointUrl)
.append("myPayloadString", myPayloadString)
// .append("myHeaders", myHeaders)
.append("myChannelType", myChannelType)
// .append("myTrigger", myTrigger)
// .append("myEmailDetails", myEmailDetails)
// .append("myRestHookDetails", myRestHookDetails)
// .append("myChannelExtensions", myChannelExtensions)
.toString();
}
public static class EmailDetails implements IModelJson {
@JsonProperty("from")
@ -394,21 +424,4 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
}
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("myIdElement", myIdElement)
.append("myStatus", myStatus)
.append("myCriteriaString", myCriteriaString)
.append("myEndpointUrl", myEndpointUrl)
.append("myPayloadString", myPayloadString)
// .append("myHeaders", myHeaders)
.append("myChannelType", myChannelType)
// .append("myTrigger", myTrigger)
// .append("myEmailDetails", myEmailDetails)
// .append("myRestHookDetails", myRestHookDetails)
// .append("myChannelExtensions", myChannelExtensions)
.toString();
}
}

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionMatchingStrategy;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionStrategyEvaluator;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
@ -34,6 +35,7 @@ import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.HapiExtensions;
import com.google.common.annotations.VisibleForTesting;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
@ -96,32 +98,19 @@ public class SubscriptionValidatingInterceptor {
if (!finished) {
String query = subscription.getCriteriaString();
if (isBlank(query)) {
throw new UnprocessableEntityException("Subscription.criteria must be populated");
}
validateQuery(subscription.getCriteriaString(), "Subscription.criteria");
int sep = query.indexOf('?');
if (sep <= 1) {
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
}
String resType = query.substring(0, sep);
if (resType.contains("/")) {
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
if (subscription.getPayloadSearchCriteria() != null) {
validateQuery(subscription.getPayloadSearchCriteria(), "Subscription.extension(url='" + HapiExtensions.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_CRITERIA + "')");
}
validateChannelType(subscription);
if (!myDaoRegistry.isResourceTypeSupported(resType)) {
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resType);
}
try {
SubscriptionMatchingStrategy strategy = mySubscriptionStrategyEvaluator.determineStrategy(query);
SubscriptionMatchingStrategy strategy = mySubscriptionStrategyEvaluator.determineStrategy(subscription.getCriteriaString());
mySubscriptionCanonicalizer.setMatchingStrategyTag(theSubscription, strategy);
} catch (InvalidRequestException | DataFormatException e) {
throw new UnprocessableEntityException("Invalid subscription criteria submitted: " + query + " " + e.getMessage());
throw new UnprocessableEntityException("Invalid subscription criteria submitted: " + subscription.getCriteriaString() + " " + e.getMessage());
}
if (subscription.getChannelType() == null) {
@ -129,6 +118,28 @@ public class SubscriptionValidatingInterceptor {
} else if (subscription.getChannelType() == CanonicalSubscriptionChannelType.MESSAGE) {
validateMessageSubscriptionEndpoint(subscription.getEndpointUrl());
}
}
}
public void validateQuery(String theQuery, String theFieldName) {
if (isBlank(theQuery)) {
throw new UnprocessableEntityException(theFieldName + " must be populated");
}
int sep = theQuery.indexOf('?');
if (sep <= 1) {
throw new UnprocessableEntityException(theFieldName + " must be in the form \"{Resource Type}?[params]\"");
}
String resType = theQuery.substring(0, sep);
if (resType.contains("/")) {
throw new UnprocessableEntityException(theFieldName + " must be in the form \"{Resource Type}?[params]\"");
}
if (!myDaoRegistry.isResourceTypeSupported(resType)) {
throw new UnprocessableEntityException(theFieldName + " contains invalid/unsupported resource type: " + resType);
}
}

View File

@ -18,6 +18,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@ -93,8 +94,9 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
return retVal;
}
@Primary
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;

View File

@ -19,6 +19,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@ -161,7 +162,8 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
// }
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
@Primary
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;

View File

@ -19,6 +19,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@ -155,7 +156,8 @@ public class TestR4Config extends BaseJavaConfigR4 {
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
@Primary
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;

View File

@ -19,6 +19,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@ -155,7 +156,8 @@ public class TestR5Config extends BaseJavaConfigR5 {
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
@Primary
public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;

View File

@ -21,14 +21,13 @@ package ca.uhn.fhir.empi.rules.metric;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.rules.metric.matcher.DoubleMetaphoneStringMatcher;
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
import ca.uhn.fhir.empi.rules.metric.matcher.EmpiPersonNameMatchModeEnum;
import ca.uhn.fhir.empi.rules.metric.matcher.HapiDateMatcher;
import ca.uhn.fhir.empi.rules.metric.matcher.HapiStringMatcher;
import ca.uhn.fhir.empi.rules.metric.matcher.IEmpiFieldMatcher;
import ca.uhn.fhir.empi.rules.metric.matcher.MetaphoneStringMatcher;
import ca.uhn.fhir.empi.rules.metric.matcher.NameMatcher;
import ca.uhn.fhir.empi.rules.metric.matcher.StringEncoderMatcher;
import ca.uhn.fhir.empi.rules.metric.matcher.PhoneticEncoderMatcher;
import ca.uhn.fhir.empi.rules.metric.matcher.SubstringStringMatcher;
import ca.uhn.fhir.empi.rules.metric.similarity.HapiStringSimilarity;
import ca.uhn.fhir.empi.rules.metric.similarity.IEmpiFieldSimilarity;
@ -37,9 +36,6 @@ import info.debatty.java.stringsimilarity.Jaccard;
import info.debatty.java.stringsimilarity.JaroWinkler;
import info.debatty.java.stringsimilarity.NormalizedLevenshtein;
import info.debatty.java.stringsimilarity.SorensenDice;
import org.apache.commons.codec.language.Caverphone1;
import org.apache.commons.codec.language.Caverphone2;
import org.apache.commons.codec.language.Soundex;
import org.hl7.fhir.instance.model.api.IBase;
import javax.annotation.Nullable;
@ -49,13 +45,19 @@ import javax.annotation.Nullable;
* calculating differences between strings (https://en.wikipedia.org/wiki/String_metric)
*/
public enum EmpiMetricEnum {
CAVERPHONE1(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.CAVERPHONE1))),
CAVERPHONE2(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.CAVERPHONE2))),
COLOGNE(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.COLOGNE))),
DOUBLE_METAPHONE(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.DOUBLE_METAPHONE))),
MATCH_RATING_APPROACH(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.MATCH_RATING_APPROACH))),
METAPHONE(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.METAPHONE))),
NYSIIS(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.NYSIIS))),
REFINED_SOUNDEX(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.REFINED_SOUNDEX))),
SOUNDEX(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.SOUNDEX))),
STRING(new HapiStringMatcher()),
SUBSTRING(new HapiStringMatcher(new SubstringStringMatcher())),
METAPHONE(new HapiStringMatcher(new MetaphoneStringMatcher())),
DOUBLE_METAPHONE(new HapiStringMatcher(new DoubleMetaphoneStringMatcher())),
SOUNDEX(new HapiStringMatcher(new StringEncoderMatcher(new Soundex()))),
CAVERPHONE1(new HapiStringMatcher(new StringEncoderMatcher(new Caverphone1()))),
CAVERPHONE2(new HapiStringMatcher(new StringEncoderMatcher(new Caverphone2()))),
DATE(new HapiDateMatcher()),
JARO_WINKLER(new HapiStringSimilarity(new JaroWinkler())),
COSINE(new HapiStringSimilarity(new Cosine())),

View File

@ -20,11 +20,22 @@ package ca.uhn.fhir.empi.rules.metric.matcher;
* #L%
*/
import org.apache.commons.codec.language.DoubleMetaphone;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PhoneticEncoderMatcher implements IEmpiStringMatcher {
private static final Logger ourLog = LoggerFactory.getLogger(PhoneticEncoderMatcher.class);
private final IPhoneticEncoder myStringEncoder;
public PhoneticEncoderMatcher(PhoneticEncoderEnum thePhoneticEnum) {
myStringEncoder = thePhoneticEnum.getPhoneticEncoder();
}
public class DoubleMetaphoneStringMatcher implements IEmpiStringMatcher {
@Override
public boolean matches(String theLeftString, String theRightString) {
return new DoubleMetaphone().isDoubleMetaphoneEqual(theLeftString, theRightString);
return myStringEncoder.encode(theLeftString).equals(myStringEncoder.encode(theRightString));
}
}

View File

@ -20,14 +20,6 @@ package ca.uhn.fhir.rest.server.method;
* #L%
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Include;
@ -35,9 +27,18 @@ import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
class IncludeParameter extends BaseQueryParameter {
private Set<String> myAllow;
@ -142,7 +143,7 @@ class IncludeParameter extends BaseQueryParameter {
}
String qualifier = nextParamList.getQualifier();
boolean recurse = Constants.PARAM_INCLUDE_QUALIFIER_RECURSE.equals(qualifier) || Constants.PARAM_INCLUDE_QUALIFIER_ITERATE.equals(qualifier);
boolean iterate = ParameterUtil.isIncludeIterate(qualifier);
String value = nextParamList.get(0);
if (myAllow != null && !myAllow.isEmpty()) {
@ -157,10 +158,10 @@ class IncludeParameter extends BaseQueryParameter {
if (mySpecType == String.class) {
return value;
}
return new Include(value, recurse);
return new Include(value, iterate);
}
retValCollection.add(new Include(value, recurse));
retValCollection.add(new Include(value, iterate));
}
return retValCollection;

View File

@ -0,0 +1,40 @@
package ca.uhn.fhir.util;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.codesystems.HttpVerb;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
public class TransactionBuilderTest {
private static final Logger ourLog = LoggerFactory.getLogger(TransactionBuilderTest.class);
private FhirContext myFhirContext = FhirContext.forR4();
@Test
public void testAddEntryUpdate() {
TransactionBuilder builder = new TransactionBuilder(myFhirContext);
Patient patient = new Patient();
patient.setId("http://foo/Patient/123");
patient.setActive(true);
builder.addUpdateEntry(patient);
Bundle bundle = (Bundle) builder.getBundle();
ourLog.info("Bundle:\n{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
assertEquals(Bundle.BundleType.TRANSACTION, bundle.getType());
assertEquals(1, bundle.getEntry().size());
assertSame(patient, bundle.getEntry().get(0).getResource());
assertEquals("http://foo/Patient/123", bundle.getEntry().get(0).getFullUrl());
assertEquals("Patient/123", bundle.getEntry().get(0).getRequest().getUrl());
assertEquals(Bundle.HTTPVerb.PUT, bundle.getEntry().get(0).getRequest().getMethod());
}
}