Merge branch 'master' of github.com:jamesagnew/hapi-fhir

This commit is contained in:
jamesagnew 2019-09-10 13:45:49 -04:00
commit ea49692106
14 changed files with 343 additions and 64 deletions

View File

@ -20,21 +20,20 @@ package ca.uhn.fhir.model.primitive;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.parser.DataFormatException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.parser.DataFormatException;
import static org.apache.commons.lang3.StringUtils.isBlank;
public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
static final long NANOS_PER_MILLIS = 1000000L;
@ -42,7 +41,8 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
private static final FastDateFormat ourHumanDateFormat = FastDateFormat.getDateInstance(FastDateFormat.MEDIUM);
private static final FastDateFormat ourHumanDateTimeFormat = FastDateFormat.getDateTimeInstance(FastDateFormat.MEDIUM, FastDateFormat.MEDIUM);
private static final FastDateFormat ourXmlDateTimeFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
public static final String NOW_DATE_CONSTANT = "%now";
private String myFractionalSeconds;
private TemporalPrecisionEnum myPrecision = null;
private TimeZone myTimeZone;
@ -635,7 +635,12 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
@Override
public void setValueAsString(String theValue) throws DataFormatException {
clearTimeZone();
super.setValueAsString(theValue);
if (NOW_DATE_CONSTANT.equalsIgnoreCase(theValue)) {
super.setValueAsString(ourXmlDateTimeFormat.format(new Date()));
} else {
super.setValueAsString(theValue);
}
}
/**

View File

@ -47,7 +47,7 @@ public abstract class BaseParamWithPrefix<T extends BaseParam> extends BaseParam
break;
} else {
char nextChar = theString.charAt(offset);
if (nextChar == '-' || Character.isDigit(nextChar)) {
if (nextChar == '-' || nextChar == '%' || Character.isDigit(nextChar)) {
break;
}
}

View File

@ -313,7 +313,7 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid
patient.setActive(true);
// Reject output
consentService.setTarget(new ConsentSvcRejectSeeingAnything());
consentService.setTarget(new ConsentSvcRejectCanSeeAnything());
HttpPost post = new HttpPost(ourServerBase + "/Patient");
post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION);
post.setEntity(toEntity(patient));
@ -356,7 +356,7 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid
ourRestServer.getInterceptorService().registerInterceptor(myConsentInterceptor);
// Reject output
consentService.setTarget(new ConsentSvcRejectSeeingAnything());
consentService.setTarget(new ConsentSvcRejectCanSeeAnything());
patient = new Patient();
patient.setId(id);
patient.setActive(true);
@ -394,6 +394,32 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid
}
@Test
public void testRejectWillSeeResource() throws IOException {
create50Observations();
ConsentSvcRejectWillSeeEvenNumbered consentService = new ConsentSvcRejectWillSeeEvenNumbered();
myConsentInterceptor = new ConsentInterceptor(consentService, IConsentContextServices.NULL_IMPL);
ourRestServer.getInterceptorService().registerInterceptor(myConsentInterceptor);
// Search for all
String url = ourServerBase + "/Observation?_pretty=true&_count=10";
ourLog.info("HTTP GET {}", url);
HttpGet get = new HttpGet(url);
get.addHeader(Constants.HEADER_ACCEPT, Constants.CT_JSON);
try (CloseableHttpResponse status = ourHttpClient.execute(get)) {
String responseString = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle result = myFhirCtx.newJsonParser().parseResource(Bundle.class, responseString);
List<IBaseResource> resources = BundleUtil.toListOfResources(myFhirCtx, result);
List<String> returnedIdValues = toUnqualifiedVersionlessIdValues(resources);
assertEquals(myObservationIdsOddOnly.subList(0, 5), returnedIdValues);
}
}
@Test
public void testGraphQL_Proceed() throws IOException {
createPatientAndOrg();
@ -727,7 +753,7 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid
}
private static class ConsentSvcRejectSeeingAnything implements IConsentService {
private static class ConsentSvcRejectCanSeeAnything implements IConsentService {
@Override
public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) {
@ -756,4 +782,43 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid
}
private static class ConsentSvcRejectWillSeeEvenNumbered implements IConsentService {
@Override
public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) {
return ConsentOutcome.PROCEED;
}
@Override
public ConsentOutcome canSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) {
return ConsentOutcome.PROCEED;
}
@Override
public ConsentOutcome willSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) {
if (theResource.getIdElement().isIdPartValidLong()) {
Long resIdLong = theResource.getIdElement().getIdPartAsLong();
if (resIdLong % 2 == 0) {
return new ConsentOutcome(ConsentOperationStatusEnum.REJECT);
}
}
return new ConsentOutcome(ConsentOperationStatusEnum.PROCEED);
}
@Override
public void completeOperationSuccess(RequestDetails theRequestDetails, IConsentContextServices theContextServices) {
// nothing
}
@Override
public void completeOperationFailure(RequestDetails theRequestDetails, BaseServerResponseException theException, IConsentContextServices theContextServices) {
// nothing
}
}
}

View File

@ -5,7 +5,11 @@ import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
import ca.uhn.fhir.jpa.util.TestUtil;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
import ca.uhn.fhir.util.UrlUtil;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.Observation;
import org.hl7.fhir.r5.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
@ -84,6 +88,20 @@ public class ResourceProviderR5Test extends BaseResourceProviderR5Test {
}
@Test
public void testDateNowSyntax() {
Observation observation = new Observation();
observation.setEffective(new DateTimeType("1965-08-09"));
IIdType oid = myObservationDao.create(observation).getId().toUnqualified();
String nowParam = UrlUtil.escapeUrlParam("%now");
Bundle output = ourClient
.search()
.byUrl("Observation?date=lt" + nowParam)
.returnBundle(Bundle.class)
.execute();
List<IIdType> ids = output.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualified()).collect(Collectors.toList());
assertThat(ids, containsInAnyOrder(oid));
}
@AfterClass
public static void afterClassClearContext() {

View File

@ -107,6 +107,16 @@
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -23,7 +23,6 @@ package ca.uhn.fhir.jpa.searchparam.matcher;
public class InMemoryMatchResult {
public static final String PARSE_FAIL = "Failed to translate parse query string";
public static final String STANDARD_PARAMETER = "Standard parameters not supported";
public static final String PREFIX = "Prefixes not supported";
public static final String CHAIN = "Chained references are not supported";
public static final String PARAM = "Param not supported";

View File

@ -29,7 +29,9 @@ import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.BaseParamWithPrefix;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -42,7 +44,6 @@ import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
@Service
public class InMemoryResourceMatcher {
@ -57,11 +58,10 @@ public class InMemoryResourceMatcher {
/**
* This method is called in two different scenarios. With a null theResource, it determines whether database matching might be required.
* Otherwise, it tries to perform the match in-memory, returning UNSUPPORTED if it's not possible.
*
* <p>
* Note that there will be cases where it returns UNSUPPORTED with a null resource, but when a non-null resource it returns supported and no match.
* This is because an earlier parameter may be matchable in-memory in which case processing stops and we never get to the parameter
* that would have required a database call.
*
*/
public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, ResourceIndexedSearchParams theSearchParams) {
@ -86,7 +86,7 @@ public class InMemoryResourceMatcher {
String theParamName = entry.getKey();
List<List<IQueryParameterType>> theAndOrParams = entry.getValue();
InMemoryMatchResult result = matchIdsWithAndOr(theParamName, theAndOrParams, resourceDefinition, theResource, theSearchParams);
if (!result.matched()){
if (!result.matched()) {
return result;
}
}
@ -102,14 +102,18 @@ public class InMemoryResourceMatcher {
if (hasQualifiers(theAndOrParams)) {
return InMemoryMatchResult.unsupportedFromParameterAndReason(theParamName, InMemoryMatchResult.STANDARD_PARAMETER);
}
if (hasPrefixes(theAndOrParams)) {
return InMemoryMatchResult.unsupportedFromParameterAndReason(theParamName, InMemoryMatchResult.PREFIX);
}
if (hasChain(theAndOrParams)) {
return InMemoryMatchResult.unsupportedFromParameterAndReason(theParamName, InMemoryMatchResult.CHAIN);
}
String resourceName = theResourceDefinition.getName();
RuntimeSearchParam paramDef = mySearchParamRegistry.getActiveSearchParam(resourceName, theParamName);
InMemoryMatchResult checkUnsupportedResult = checkUnsupportedPrefixes(theParamName, paramDef, theAndOrParams);
if (!checkUnsupportedResult.supported()) {
return checkUnsupportedResult;
}
switch (theParamName) {
case IAnyResource.SP_RES_ID:
@ -125,8 +129,7 @@ public class InMemoryResourceMatcher {
default:
String resourceName = theResourceDefinition.getName();
RuntimeSearchParam paramDef = mySearchParamRegistry.getActiveSearchParam(resourceName, theParamName);
return matchResourceParam(theParamName, theAndOrParams, theSearchParams, resourceName, paramDef);
}
}
@ -137,11 +140,12 @@ public class InMemoryResourceMatcher {
}
return theAndOrParams.stream().allMatch(nextAnd -> matchIdsOr(nextAnd, theResource));
}
private boolean matchIdsOr(List<IQueryParameterType> theOrParams, IBaseResource theResource) {
if (theResource == null) {
return true;
}
return theOrParams.stream().anyMatch(param -> param instanceof StringParam && matchId(((StringParam)param).getValue(), theResource.getIdElement()));
return theOrParams.stream().anyMatch(param -> param instanceof StringParam && matchId(((StringParam) param).getValue(), theResource.getIdElement()));
}
private boolean matchId(String theValue, IIdType theId) {
@ -183,16 +187,48 @@ public class InMemoryResourceMatcher {
}
private boolean hasChain(List<List<IQueryParameterType>> theAndOrParams) {
return theAndOrParams.stream().flatMap(List::stream).anyMatch(param -> param instanceof ReferenceParam && ((ReferenceParam)param).getChain() != null);
return theAndOrParams.stream().flatMap(List::stream).anyMatch(param -> param instanceof ReferenceParam && ((ReferenceParam) param).getChain() != null);
}
private boolean hasQualifiers(List<List<IQueryParameterType>> theAndOrParams) {
return theAndOrParams.stream().flatMap(List::stream).anyMatch(param -> param.getQueryParameterQualifier() != null);
}
private boolean hasPrefixes(List<List<IQueryParameterType>> theAndOrParams) {
Predicate<IQueryParameterType> hasPrefixPredicate = param -> param instanceof BaseParamWithPrefix &&
((BaseParamWithPrefix) param).getPrefix() != null;
return theAndOrParams.stream().flatMap(List::stream).anyMatch(hasPrefixPredicate);
private InMemoryMatchResult checkUnsupportedPrefixes(String theParamName, RuntimeSearchParam theParamDef, List<List<IQueryParameterType>> theAndOrParams) {
if (theParamDef != null) {
for (List<IQueryParameterType> theAndOrParam : theAndOrParams) {
for (IQueryParameterType param : theAndOrParam) {
if (param instanceof BaseParamWithPrefix) {
ParamPrefixEnum prefix = ((BaseParamWithPrefix) param).getPrefix();
RestSearchParameterTypeEnum paramType = theParamDef.getParamType();
if (!supportedPrefix(prefix, paramType)) {
return InMemoryMatchResult.unsupportedFromParameterAndReason(theParamName, String.format("The prefix %s is not supported for param type %s", prefix, paramType));
}
}
}
}
}
return InMemoryMatchResult.successfulMatch();
}
private boolean supportedPrefix(ParamPrefixEnum theParam, RestSearchParameterTypeEnum theParamType) {
if (theParam == null || theParamType == null) {
return true;
}
switch (theParamType) {
case DATE:
switch (theParam) {
case GREATERTHAN:
case GREATERTHAN_OR_EQUALS:
case LESSTHAN:
case LESSTHAN_OR_EQUALS:
case EQUAL:
return true;
}
break;
default:
return false;
}
return false;
}
}

View File

@ -0,0 +1,157 @@
package ca.uhn.fhir.jpa.searchparam.matcher;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import org.hl7.fhir.r5.model.BaseDateTimeType;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.Observation;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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.time.Duration;
import java.time.Instant;
import java.util.Date;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@RunWith(SpringRunner.class)
public class InMemoryResourceMatcherR5Test {
public static final String OBSERVATION_DATE = "1970-10-17";
private static final String EARLY_DATE = "1965-08-09";
private static final String LATE_DATE = "2000-06-29";
@Autowired
private
InMemoryResourceMatcher myInMemoryResourceMatcher;
@MockBean
ISearchParamRegistry mySearchParamRegistry;
private Observation myObservation;
private ResourceIndexedSearchParams mySearchParams;
@Configuration
public static class SpringConfig {
@Bean
InMemoryResourceMatcher inMemoryResourceMatcher() {
return new InMemoryResourceMatcher();
}
@Bean
MatchUrlService matchUrlService() {
return new MatchUrlService();
}
@Bean
FhirContext fhirContext() {
return FhirContext.forR5();
}
}
@Before
public void before() {
RuntimeSearchParam searchParams = new RuntimeSearchParam(null, null, null, null, "Observation.effective", RestSearchParameterTypeEnum.DATE, null, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE);
when(mySearchParamRegistry.getSearchParamByName(any(), any())).thenReturn(searchParams);
when(mySearchParamRegistry.getActiveSearchParam("Observation", "date")).thenReturn(searchParams);
myObservation = new Observation();
myObservation.setEffective(new DateTimeType(OBSERVATION_DATE));
mySearchParams = extractDateSearchParam(myObservation);
}
@Test
public void testDateUnsupportedOps() {
testDateUnsupportedOp(ParamPrefixEnum.APPROXIMATE);
testDateUnsupportedOp(ParamPrefixEnum.STARTS_AFTER);
testDateUnsupportedOp(ParamPrefixEnum.ENDS_BEFORE);
testDateUnsupportedOp(ParamPrefixEnum.NOT_EQUAL);
}
private void testDateUnsupportedOp(ParamPrefixEnum theOperator) {
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=" + theOperator.getValue() + OBSERVATION_DATE, myObservation, mySearchParams);
assertFalse(result.supported());
assertEquals("Parameter: <date> Reason: The prefix " + theOperator + " is not supported for param type DATE", result.getUnsupportedReason());
}
@Test
public void testDateSupportedOps() {
testDateSupportedOp(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, true, true, false);
testDateSupportedOp(ParamPrefixEnum.GREATERTHAN, true, false, false);
testDateSupportedOp(ParamPrefixEnum.EQUAL, false, true, false);
testDateSupportedOp(ParamPrefixEnum.LESSTHAN_OR_EQUALS, false, true, true);
testDateSupportedOp(ParamPrefixEnum.LESSTHAN, false, false, true);
}
private void testDateSupportedOp(ParamPrefixEnum theOperator, boolean theEarly, boolean theSame, boolean theLater) {
String equation = "date=" + theOperator.getValue();
{
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + EARLY_DATE, myObservation, mySearchParams);
assertTrue(result.getUnsupportedReason(), result.supported());
assertEquals(result.matched(), theEarly);
}
{
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + OBSERVATION_DATE, myObservation, mySearchParams);
assertTrue(result.getUnsupportedReason(), result.supported());
assertEquals(result.matched(), theSame);
}
{
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + LATE_DATE, myObservation, mySearchParams);
assertTrue(result.getUnsupportedReason(), result.supported());
assertEquals(result.matched(), theLater);
}
}
@Test
public void testNowPast() {
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=lt" + BaseDateTimeDt.NOW_DATE_CONSTANT, myObservation, mySearchParams);
assertTrue(result.getUnsupportedReason(), result.supported());
assertTrue(result.matched());
}
@Test
public void testNowNextWeek() {
Observation futureObservation = new Observation();
Instant nextWeek = Instant.now().plus(Duration.ofDays(7));
futureObservation.setEffective(new DateTimeType(Date.from(nextWeek)));
ResourceIndexedSearchParams searchParams = extractDateSearchParam(futureObservation);
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=gt" + BaseDateTimeDt.NOW_DATE_CONSTANT, futureObservation, searchParams);
assertTrue(result.getUnsupportedReason(), result.supported());
assertTrue(result.matched());
}
@Test
public void testNowNextMinute() {
Observation futureObservation = new Observation();
Instant nextMinute = Instant.now().plus(Duration.ofMinutes(1));
futureObservation.setEffective(new DateTimeType(Date.from(nextMinute)));
ResourceIndexedSearchParams searchParams = extractDateSearchParam(futureObservation);
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=gt" + BaseDateTimeDt.NOW_DATE_CONSTANT, futureObservation, searchParams);
assertTrue(result.getUnsupportedReason(), result.supported());
assertTrue(result.matched());
}
private ResourceIndexedSearchParams extractDateSearchParam(Observation theObservation) {
ResourceIndexedSearchParams retval = new ResourceIndexedSearchParams();
BaseDateTimeType dateValue = (BaseDateTimeType) theObservation.getEffective();
ResourceIndexedSearchParamDate dateParam = new ResourceIndexedSearchParamDate("date", dateValue.getValue(), dateValue.getValue(), dateValue.getValueAsString());
retval.myDateParams.add(dateParam);
return retval;
}
}

View File

@ -549,7 +549,7 @@ public class InMemorySubscriptionMatcherR3Test extends BaseSubscriptionDstu3Test
CommunicationRequest cr = new CommunicationRequest();
cr.getRequester().getAgent().setReference("Organization/O1276");
cr.setOccurrence(new DateTimeType("2019-02-08T00:01:00-05:00"));
assertUnsupported(cr, criteria);
assertMatched(cr, criteria);
}

View File

@ -178,7 +178,7 @@ public class ConsentInterceptor {
alreadySeenResources.put(newOperationOutcome, true);
} else {
String resourceId = nextResource.getIdElement().getValue();
theRequestDetails.getFhirContext().newTerser().clear(nextResource);
thePreResourceShowDetails.setResource(i, null);
nextResource.setId(resourceId);
}
break;

View File

@ -5,7 +5,6 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidatorModule;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.apache.commons.lang3.Validate;

View File

@ -1,12 +1,6 @@
package org.hl7.fhir.instance.hapi.validation;
import static org.apache.commons.lang3.StringUtils.isBlank;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.validation.IValidationContext;
@ -14,11 +8,7 @@ import ca.uhn.fhir.validation.IValidatorModule;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder;
@ -27,16 +17,10 @@ import org.fhir.ucum.UcumService;
import org.hl7.fhir.convertors.NullVersionConverterAdvisor50;
import org.hl7.fhir.convertors.VersionConvertorAdvisor50;
import org.hl7.fhir.convertors.VersionConvertor_10_50;
import org.hl7.fhir.convertors.VersionConvertor_10_50;
import org.hl7.fhir.dstu2.model.*;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.dstu2.model.CodeableConcept;
import org.hl7.fhir.dstu2.model.Coding;
import org.hl7.fhir.dstu2.model.Questionnaire;
import org.hl7.fhir.dstu2.model.Resource;
import org.hl7.fhir.dstu2.model.StructureDefinition;
import org.hl7.fhir.dstu2.model.ValueSet;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.ParserType;
@ -58,20 +42,17 @@ import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.concurrent.TimeUnit;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import static org.apache.commons.lang3.StringUtils.isBlank;
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {

View File

@ -30,9 +30,6 @@ import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

View File

@ -121,6 +121,18 @@
The GraphQL provider did not wrap the respone in a "data" element as described in the FHIR
specification. This has been corrected.
</action>
<action type="add">
Added support for comparing resource dates to the current time via a new variable %now. E.g.
Procedure?date=gt%now would match future procedures.
</action>
<action type="add">
Add support for in-memory matching on date comparisons ge,gt,eq,lt,le.
</action>
<action type="fix">
When using the Consent Service and denying a resource via the "Will See Resource" method, the resource ID
and version were still returned to the user. This has been corrected so that no details about
the resource are leaked.
</action>
</release>
<release version="4.0.1" date="2019-09-03" description="Igloo (Point Release)">
<action type="fix">