4017 creating a searchparameter with an expression referencing metasecurity will return error hapi 1118 (#4037)
* Adding initial test reproducing the issue. * some comments and small refactoring to run pipeline to see the extent of the damage that this can cause. * Parsing dstu3 expression with FHIRpath parser * fixed code. * omitting searchParameter expression(xpath) validation for DSTU2 * Added test for search parameter with many expressions * changed deprecated method. * removed and modified custom search parameters tests for DSTU3 and DSTU2. * fixed code * Added change log * Test testIndexFailsIfInvalidSearchParameterExists() is back. * Update createResourceSearchParameter_withExpressionMetaSecurity_succeeds() test * Update createResourceSearchParameter_withExpressionMetaSecurity_succeeds() test * Refactoring if statement and exception message. * Refactoring tests and rewording changelog. * Fixing tests. * Fixing more tests. * Addressing comments from first code review. * Fixing issue number. Co-authored-by: peartree <etienne.poirier@smilecdr.com>
This commit is contained in:
parent
4cebcfb766
commit
2e6b3d16d6
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 4017
|
||||||
|
jira: SMILE-5046
|
||||||
|
title: "Previously, creating a DSTU3 SearchParameter with an expression that does not start with a resource type would throw an error. This has been corrected."
|
|
@ -1,22 +1,19 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
|
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
import ca.uhn.fhir.util.ElementUtil;
|
||||||
import ca.uhn.fhir.util.HapiExtensions;
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
import org.hl7.fhir.r4.model.Enumerations;
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
|
@ -123,7 +120,7 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
||||||
throw new UnprocessableEntityException(Msg.code(1113) + "SearchParameter.base is missing");
|
throw new UnprocessableEntityException(Msg.code(1113) + "SearchParameter.base is missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isUnique = theResource.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE).stream().anyMatch(t-> "true".equals(t.getValueAsPrimitive().getValueAsString()));
|
boolean isUnique = hasAnyExtensionUniqueSetTo(theResource, true);
|
||||||
|
|
||||||
if (theResource.getType() != null && theResource.getType().name().equals(Enumerations.SearchParamType.COMPOSITE.name()) && isBlank(theResource.getExpression())) {
|
if (theResource.getType() != null && theResource.getType().name().equals(Enumerations.SearchParamType.COMPOSITE.name()) && isBlank(theResource.getExpression())) {
|
||||||
|
|
||||||
|
@ -135,8 +132,6 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
String expression = theResource.getExpression().trim();
|
|
||||||
|
|
||||||
if (isUnique) {
|
if (isUnique) {
|
||||||
if (theResource.getComponent().size() == 0) {
|
if (theResource.getComponent().size() == 0) {
|
||||||
throw new UnprocessableEntityException(Msg.code(1115) + "SearchParameter is marked as unique but has no components");
|
throw new UnprocessableEntityException(Msg.code(1115) + "SearchParameter is marked as unique but has no components");
|
||||||
|
@ -148,52 +143,55 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
|
FhirVersionEnum fhirVersion = theContext.getVersion().getVersion();
|
||||||
|
if (fhirVersion.isOlderThan(FhirVersionEnum.DSTU3)) {
|
||||||
// DSTU3 and below
|
// omitting validation for DSTU2_HL7ORG, DSTU2_1 and DSTU2
|
||||||
String[] expressionSplit = theSearchParamExtractor.split(expression);
|
|
||||||
for (String nextPath : expressionSplit) {
|
|
||||||
nextPath = nextPath.trim();
|
|
||||||
|
|
||||||
int dotIdx = nextPath.indexOf('.');
|
|
||||||
if (dotIdx == -1) {
|
|
||||||
throw new UnprocessableEntityException(Msg.code(1117) + "Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name.");
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
String resourceName = nextPath.substring(0, dotIdx);
|
|
||||||
try {
|
|
||||||
theContext.getResourceDefinition(resourceName);
|
|
||||||
} catch (DataFormatException e) {
|
|
||||||
throw new UnprocessableEntityException(Msg.code(1118) + "Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
|
||||||
if (theDaoConfig.isValidateSearchParameterExpressionsOnSave()) {
|
if (theDaoConfig.isValidateSearchParameterExpressionsOnSave()) {
|
||||||
IBaseResource temporaryInstance = theContext.getResourceDefinition(resourceName).newInstance();
|
|
||||||
|
validateExpressionPath(theResource);
|
||||||
|
|
||||||
|
String expression = getExpression(theResource);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
theContext.newFluentPath().evaluate(temporaryInstance, nextPath, IBase.class);
|
theContext.newFhirPath().parse(expression);
|
||||||
} catch (Exception e) {
|
} catch (Exception exception) {
|
||||||
String msg = theContext.getLocalizer().getMessageSanitized(FhirResourceDaoSearchParameterR4.class, "invalidSearchParamExpression", nextPath, e.getMessage());
|
throw new UnprocessableEntityException(Msg.code(1121) + "Invalid FHIRPath format for SearchParameter.expression \"" + expression + "\": " + exception.getMessage());
|
||||||
throw new UnprocessableEntityException(Msg.code(1119) + msg, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (!isUnique && theResource.getType() != Enumerations.SearchParamType.COMPOSITE && theResource.getType() != Enumerations.SearchParamType.SPECIAL && !REGEX_SP_EXPRESSION_HAS_PATH.matcher(expression).matches()) {
|
|
||||||
throw new UnprocessableEntityException(Msg.code(1120) + "SearchParameter.expression value \"" + expression + "\" is invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
// R4 and above
|
|
||||||
try {
|
|
||||||
theContext.newFluentPath().parse(expression);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new UnprocessableEntityException(Msg.code(1121) + "Invalid SearchParameter.expression value \"" + expression + "\": " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} // if have expression
|
|
||||||
|
|
||||||
|
private static void validateExpressionPath(SearchParameter theSearchParameter){
|
||||||
|
String expression = getExpression(theSearchParameter);
|
||||||
|
|
||||||
|
boolean isResourceOfTypeComposite = theSearchParameter.getType() == Enumerations.SearchParamType.COMPOSITE;
|
||||||
|
boolean isResourceOfTypeSpecial = theSearchParameter.getType() == Enumerations.SearchParamType.SPECIAL;
|
||||||
|
boolean expressionHasPath = REGEX_SP_EXPRESSION_HAS_PATH.matcher(expression).matches();
|
||||||
|
|
||||||
|
boolean isUnique = hasAnyExtensionUniqueSetTo(theSearchParameter, true);
|
||||||
|
|
||||||
|
if ( !isUnique && !isResourceOfTypeComposite && !isResourceOfTypeSpecial && !expressionHasPath ) {
|
||||||
|
throw new UnprocessableEntityException(Msg.code(1120) + "SearchParameter.expression value \"" + expression + "\" is invalid due to missing/incorrect path");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getExpression(SearchParameter theSearchParameter){
|
||||||
|
return theSearchParameter.getExpression().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasAnyExtensionUniqueSetTo(SearchParameter theSearchParameter, boolean theValue){
|
||||||
|
String theValueAsString = Boolean.toString(theValue);
|
||||||
|
|
||||||
|
return theSearchParameter
|
||||||
|
.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE)
|
||||||
|
.stream()
|
||||||
|
.anyMatch(t-> theValueAsString.equals(t.getValueAsPrimitive().getValueAsString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,23 +111,6 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateInvalidParamInvalidResourceName() {
|
|
||||||
SearchParameter fooSp = new SearchParameter();
|
|
||||||
fooSp.setBase(ResourceTypeEnum.PATIENT);
|
|
||||||
fooSp.setCode("foo");
|
|
||||||
fooSp.setType(SearchParamTypeEnum.TOKEN);
|
|
||||||
fooSp.setXpath("PatientFoo.gender");
|
|
||||||
fooSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
|
|
||||||
fooSp.setStatus(ConformanceResourceStatusEnum.ACTIVE);
|
|
||||||
try {
|
|
||||||
mySearchParameterDao.create(fooSp, mySrd);
|
|
||||||
fail();
|
|
||||||
} catch (UnprocessableEntityException e) {
|
|
||||||
assertEquals(Msg.code(1118) + "Invalid SearchParameter.expression value \"PatientFoo.gender\": " + Msg.code(1684) + "Unknown resource name \"PatientFoo\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateInvalidParamNoPath() {
|
public void testCreateInvalidParamNoPath() {
|
||||||
SearchParameter fooSp = new SearchParameter();
|
SearchParameter fooSp = new SearchParameter();
|
||||||
|
@ -144,23 +127,6 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateInvalidParamNoResourceName() {
|
|
||||||
SearchParameter fooSp = new SearchParameter();
|
|
||||||
fooSp.setBase(ResourceTypeEnum.PATIENT);
|
|
||||||
fooSp.setCode("foo");
|
|
||||||
fooSp.setType(SearchParamTypeEnum.TOKEN);
|
|
||||||
fooSp.setXpath("gender");
|
|
||||||
fooSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
|
|
||||||
fooSp.setStatus(ConformanceResourceStatusEnum.ACTIVE);
|
|
||||||
try {
|
|
||||||
mySearchParameterDao.create(fooSp, mySrd);
|
|
||||||
fail();
|
|
||||||
} catch (UnprocessableEntityException e) {
|
|
||||||
assertEquals(Msg.code(1117) + "Invalid SearchParameter.expression value \"gender\". Must start with a resource name.", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateInvalidParamParamNullStatus() {
|
public void testCreateInvalidParamParamNullStatus() {
|
||||||
|
|
||||||
|
|
|
@ -120,23 +120,6 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateInvalidParamInvalidResourceName() {
|
|
||||||
SearchParameter fooSp = new SearchParameter();
|
|
||||||
fooSp.addBase("Patient");
|
|
||||||
fooSp.setCode("foo");
|
|
||||||
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
|
|
||||||
fooSp.setTitle("FOO SP");
|
|
||||||
fooSp.setExpression("PatientFoo.gender");
|
|
||||||
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
|
|
||||||
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
|
|
||||||
try {
|
|
||||||
mySearchParameterDao.create(fooSp, mySrd);
|
|
||||||
fail();
|
|
||||||
} catch (UnprocessableEntityException e) {
|
|
||||||
assertEquals(Msg.code(1118) + "Invalid SearchParameter.expression value \"PatientFoo.gender\": " + Msg.code(1684) + "Unknown resource name \"PatientFoo\" (this name is not known in FHIR version \"DSTU3\")", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateInvalidParamNoPath() {
|
public void testCreateInvalidParamNoPath() {
|
||||||
|
@ -155,23 +138,6 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateInvalidParamNoResourceName() {
|
|
||||||
SearchParameter fooSp = new SearchParameter();
|
|
||||||
fooSp.addBase("Patient");
|
|
||||||
fooSp.setCode("foo");
|
|
||||||
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
|
|
||||||
fooSp.setTitle("FOO SP");
|
|
||||||
fooSp.setExpression("gender");
|
|
||||||
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
|
|
||||||
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
|
|
||||||
try {
|
|
||||||
mySearchParameterDao.create(fooSp, mySrd);
|
|
||||||
fail();
|
|
||||||
} catch (UnprocessableEntityException e) {
|
|
||||||
assertEquals(Msg.code(1117) + "Invalid SearchParameter.expression value \"gender\". Must start with a resource name.", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateInvalidParamParamNullStatus() {
|
public void testCreateInvalidParamParamNullStatus() {
|
||||||
|
@ -322,10 +288,11 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
mySearchParameterDao.create(threadIdSp, mySrd);
|
mySearchParameterDao.create(threadIdSp, mySrd);
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertThat(e.getMessage(), startsWith(Msg.code(1119) + "The expression \"Communication.payload[1].contentAttachment is not null\" can not be evaluated and may be invalid: "));
|
assertThat(e.getMessage(), startsWith(Msg.code(1121) + "Invalid FHIRPath format for SearchParameter.expression \"Communication.payload[1].contentAttachment is not null\": Error at 1, 4: Premature ExpressionNode termination at unexpected token \"null\""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #863
|
* See #863
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||||
import ca.uhn.fhir.jpa.entity.Search;
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.util.QueryParameterUtils;
|
import ca.uhn.fhir.jpa.util.QueryParameterUtils;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
|
@ -15,6 +16,7 @@ import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||||
|
@ -27,6 +29,7 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
|
@ -103,6 +106,7 @@ import org.hl7.fhir.dstu3.model.Organization;
|
||||||
import org.hl7.fhir.dstu3.model.Parameters;
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
import org.hl7.fhir.dstu3.model.Patient;
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
import org.hl7.fhir.dstu3.model.Period;
|
import org.hl7.fhir.dstu3.model.Period;
|
||||||
|
import org.hl7.fhir.dstu3.model.Person;
|
||||||
import org.hl7.fhir.dstu3.model.PlanDefinition;
|
import org.hl7.fhir.dstu3.model.PlanDefinition;
|
||||||
import org.hl7.fhir.dstu3.model.Practitioner;
|
import org.hl7.fhir.dstu3.model.Practitioner;
|
||||||
import org.hl7.fhir.dstu3.model.ProcedureRequest;
|
import org.hl7.fhir.dstu3.model.ProcedureRequest;
|
||||||
|
@ -112,6 +116,7 @@ import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType;
|
||||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse;
|
import org.hl7.fhir.dstu3.model.QuestionnaireResponse;
|
||||||
import org.hl7.fhir.dstu3.model.Reference;
|
import org.hl7.fhir.dstu3.model.Reference;
|
||||||
import org.hl7.fhir.dstu3.model.RelatedArtifact;
|
import org.hl7.fhir.dstu3.model.RelatedArtifact;
|
||||||
|
import org.hl7.fhir.dstu3.model.SearchParameter;
|
||||||
import org.hl7.fhir.dstu3.model.StringType;
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||||
import org.hl7.fhir.dstu3.model.Subscription;
|
import org.hl7.fhir.dstu3.model.Subscription;
|
||||||
|
@ -151,7 +156,6 @@ import java.util.TreeSet;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static ca.uhn.fhir.test.utilities.CustomMatchersUtil.assertDoesNotContainAnyOf;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
|
@ -261,6 +265,49 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
assertEquals(0, returnedBundle.getEntry().size());
|
assertEquals(0, returnedBundle.getEntry().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createResourceSearchParameter_withExpressionMetaSecurity_succeeds(){
|
||||||
|
String spCallingName = "securitySP";
|
||||||
|
String secCode = "secCode";
|
||||||
|
|
||||||
|
SearchParameter searchParameter = new SearchParameter();
|
||||||
|
searchParameter.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
searchParameter.setCode(spCallingName);
|
||||||
|
searchParameter.addBase("Patient").addBase("Account");
|
||||||
|
searchParameter.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
searchParameter.setExpression("meta.security");
|
||||||
|
|
||||||
|
ourClient.create().resource(searchParameter).execute();
|
||||||
|
mySearchParamRegistry.forceRefresh();
|
||||||
|
|
||||||
|
IIdType expectedPatientId = createPatientWithMeta(new Meta().addSecurity(new Coding().setCode(secCode)));
|
||||||
|
IIdType dontCare = createPatientWithMeta(new Meta().addSecurity(new Coding().setCode("L")));
|
||||||
|
|
||||||
|
Bundle searchResultBundle = ourClient.search().forResource(Patient.class).where(new StringClientParam(spCallingName).matches().value(secCode)).returnBundle(Bundle.class).execute();
|
||||||
|
|
||||||
|
List<String> foundPatients = toUnqualifiedVersionlessIdValues(searchResultBundle);
|
||||||
|
|
||||||
|
assertEquals(1, foundPatients.size());
|
||||||
|
|
||||||
|
assertEquals(foundPatients.get(0), expectedPatientId.getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSearchParameter_with2Expressions_succeeds(){
|
||||||
|
SearchParameter searchParameter = new SearchParameter();
|
||||||
|
searchParameter.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
searchParameter.setCode("myGender");
|
||||||
|
searchParameter.addBase("Patient").addBase("Person");
|
||||||
|
searchParameter.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
searchParameter.setExpression("Patient.gender|Person.gender");
|
||||||
|
|
||||||
|
MethodOutcome result= ourClient.create().resource(searchParameter).execute();
|
||||||
|
|
||||||
|
assertEquals(true, result.getCreated());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuppressNoExtensibleWarnings() {
|
public void testSuppressNoExtensibleWarnings() {
|
||||||
RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor();
|
RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor();
|
||||||
|
@ -3178,7 +3225,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(found);
|
List<IIdType> patients = toUnqualifiedVersionlessIds(found);
|
||||||
assertThat(patients, hasItems(id2));
|
assertThat(patients, hasItems(id2));
|
||||||
assertDoesNotContainAnyOf(patients, List.of(id1a, id1b));
|
assertThat(patients, not(hasItems(id1a, id1b)));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
|
@ -3190,7 +3237,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
.execute();
|
.execute();
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(found);
|
List<IIdType> patients = toUnqualifiedVersionlessIds(found);
|
||||||
assertThat(patients.toString(), patients, not(hasItem(id2)));
|
assertThat(patients.toString(), patients, not(hasItems(id2)));
|
||||||
assertThat(patients.toString(), patients, (hasItems(id1a, id1b)));
|
assertThat(patients.toString(), patients, (hasItems(id1a, id1b)));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -3204,7 +3251,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(found);
|
List<IIdType> patients = toUnqualifiedVersionlessIds(found);
|
||||||
assertThat(patients, (hasItems(id1a, id1b)));
|
assertThat(patients, (hasItems(id1a, id1b)));
|
||||||
assertThat(patients, not(hasItem(id2)));
|
assertThat(patients, not(hasItems(id2)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3701,7 +3748,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
List<String> ids = toUnqualifiedVersionlessIdValues(bundle);
|
List<String> ids = toUnqualifiedVersionlessIdValues(bundle);
|
||||||
assertThat(ids, contains(oid1));
|
assertThat(ids, contains(oid1));
|
||||||
assertThat(ids, not(hasItem(oid2)));
|
assertThat(ids, not(contains(oid2)));
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(resp);
|
IOUtils.closeQuietly(resp);
|
||||||
}
|
}
|
||||||
|
@ -3888,7 +3935,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
List<String> ids = toUnqualifiedVersionlessIdValues(bundle);
|
List<String> ids = toUnqualifiedVersionlessIdValues(bundle);
|
||||||
assertThat(ids, contains(id1.getValue()));
|
assertThat(ids, contains(id1.getValue()));
|
||||||
assertThat(ids, not(hasItem(id2.getValue())));
|
assertThat(ids, not(contains(id2.getValue())));
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(resp);
|
IOUtils.closeQuietly(resp);
|
||||||
}
|
}
|
||||||
|
@ -4781,5 +4828,11 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
return new InstantDt(theDate).getValueAsString();
|
return new InstantDt(theDate).getValueAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IIdType createPatientWithMeta(Meta theMeta){
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setMeta(theMeta);
|
||||||
|
return ourClient.create().resource(patient).execute().getId().toUnqualifiedVersionless();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -782,7 +782,7 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
||||||
mySearchParameterDao.create(threadIdSp, mySrd);
|
mySearchParameterDao.create(threadIdSp, mySrd);
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertThat(e.getMessage(), startsWith(Msg.code(1121) + "Invalid SearchParameter.expression value \"Communication.payload[1].contentAttachment is not null\""));
|
assertThat(e.getMessage(), startsWith(Msg.code(1121) + "Invalid FHIRPath format for SearchParameter.expression \"Communication.payload[1].contentAttachment is not null\""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ public class FhirResourceDaoSearchParameterR4Test {
|
||||||
myDao.validateResourceForStorage(nextSearchParameter, null);
|
myDao.validateResourceForStorage(nextSearchParameter, null);
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertEquals(Msg.code(1121) + "Invalid SearchParameter.expression value \"Patient.ex[[[\": Error in ?? at 1, 1: Found [ expecting a token name", e.getMessage());
|
assertEquals(Msg.code(1121) + "Invalid FHIRPath format for SearchParameter.expression \"Patient.ex[[[\": Error in ?? at 1, 1: Found [ expecting a token name", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,7 +141,7 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
myClient.create().resource(sp).execute();
|
myClient.create().resource(sp).execute();
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertEquals("HTTP 422 Unprocessable Entity: " + Msg.code(1120) + "SearchParameter.expression value \"Patient\" is invalid", e.getMessage());
|
assertEquals("HTTP 422 Unprocessable Entity: " + Msg.code(1120) + "SearchParameter.expression value \"Patient\" is invalid due to missing/incorrect path", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
|
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
|
||||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.term.ZipCollectionBuilder;
|
import ca.uhn.fhir.jpa.term.ZipCollectionBuilder;
|
||||||
import ca.uhn.fhir.jpa.test.config.TestR4Config;
|
import ca.uhn.fhir.jpa.test.config.TestR4Config;
|
||||||
import ca.uhn.fhir.jpa.util.QueryParameterUtils;
|
import ca.uhn.fhir.jpa.util.QueryParameterUtils;
|
||||||
|
@ -23,6 +24,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||||
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
|
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
|
||||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.client.apache.ResourceEntity;
|
import ca.uhn.fhir.rest.client.apache.ResourceEntity;
|
||||||
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
@ -38,6 +40,7 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
|
@ -127,6 +130,7 @@ import org.hl7.fhir.r4.model.Organization;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.Period;
|
import org.hl7.fhir.r4.model.Period;
|
||||||
|
import org.hl7.fhir.r4.model.Person;
|
||||||
import org.hl7.fhir.r4.model.Practitioner;
|
import org.hl7.fhir.r4.model.Practitioner;
|
||||||
import org.hl7.fhir.r4.model.Procedure;
|
import org.hl7.fhir.r4.model.Procedure;
|
||||||
import org.hl7.fhir.r4.model.Quantity;
|
import org.hl7.fhir.r4.model.Quantity;
|
||||||
|
@ -288,6 +292,41 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createResourceSearchParameter_withExpressionMetaSecurity_succeeds(){
|
||||||
|
SearchParameter searchParameter = new SearchParameter();
|
||||||
|
searchParameter.setId("resource-security");
|
||||||
|
searchParameter.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
searchParameter.setName("Security");
|
||||||
|
searchParameter.setCode("_security");
|
||||||
|
searchParameter.addBase("Patient").addBase("Account");
|
||||||
|
searchParameter.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
searchParameter.setExpression("meta.security");
|
||||||
|
|
||||||
|
IIdType id = myClient.update().resource(searchParameter).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
assertNotNull(id);
|
||||||
|
assertEquals("resource-security", id.getIdPart());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createSearchParameter_with2Expressions_succeeds(){
|
||||||
|
|
||||||
|
SearchParameter searchParameter = new SearchParameter();
|
||||||
|
|
||||||
|
searchParameter.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
searchParameter.setCode("myGender");
|
||||||
|
searchParameter.addBase("Patient").addBase("Person");
|
||||||
|
searchParameter.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
searchParameter.setExpression("Patient.gender|Person.gender");
|
||||||
|
|
||||||
|
MethodOutcome result= myClient.create().resource(searchParameter).execute();
|
||||||
|
|
||||||
|
assertEquals(true, result.getCreated());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParameterWithNoValueThrowsError_InvalidRootParam() throws IOException {
|
public void testParameterWithNoValueThrowsError_InvalidRootParam() throws IOException {
|
||||||
SearchParameter searchParameter = new SearchParameter();
|
SearchParameter searchParameter = new SearchParameter();
|
||||||
|
|
Loading…
Reference in New Issue