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:
MykolaMedynskyiSCDR 2022-10-13 11:05:04 -04:00 committed by GitHub
parent 4cebcfb766
commit 2e6b3d16d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 158 additions and 130 deletions

View File

@ -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."

View File

@ -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()));
} }
} }

View File

@ -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() {

View File

@ -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
*/ */

View File

@ -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();
}
} }

View File

@ -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\""));
} }
} }

View File

@ -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());
} }
} }

View File

@ -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());
} }
} }

View File

@ -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();