Merge pull request #2355 from hapifhir/mb-20210203-date-param-validation

Improve date parameter validation
This commit is contained in:
michaelabuckley 2021-02-05 11:34:23 -05:00 committed by GitHub
commit 6640395d6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 99 additions and 27 deletions

View File

@ -20,6 +20,8 @@ package ca.uhn.fhir.rest.param;
* #L%
*/
import ca.uhn.fhir.parser.DataFormatException;
import static org.apache.commons.lang3.StringUtils.isBlank;
public abstract class BaseParamWithPrefix<T extends BaseParam> extends BaseParam {
@ -58,8 +60,9 @@ public abstract class BaseParamWithPrefix<T extends BaseParam> extends BaseParam
if (!isBlank(prefix)) {
myPrefix = ParamPrefixEnum.forValue(prefix);
if (myPrefix == null) {
// prefix doesn't match standard values. Try legacy values
switch (prefix) {
case ">=":
myPrefix = ParamPrefixEnum.GREATERTHAN_OR_EQUALS;
@ -76,15 +79,13 @@ public abstract class BaseParamWithPrefix<T extends BaseParam> extends BaseParam
case "~":
myPrefix = ParamPrefixEnum.APPROXIMATE;
break;
default :
ourLog.warn("Invalid prefix being ignored: {}", prefix);
case "=":
myPrefix = ParamPrefixEnum.EQUAL;
break;
default :
throw new DataFormatException("Invalid prefix: \"" + prefix + "\"");
}
if (myPrefix != null) {
ourLog.warn("Date parameter has legacy prefix '{}' which has been removed from FHIR. This should be replaced with '{}'", prefix, myPrefix);
}
ourLog.warn("Date parameter has legacy prefix '{}' which has been removed from FHIR. This should be replaced with '{}'", prefix, myPrefix.getValue());
}
}
@ -107,4 +108,5 @@ public abstract class BaseParamWithPrefix<T extends BaseParam> extends BaseParam
myPrefix = thePrefix;
return (T) this;
}
}

View File

@ -2,23 +2,91 @@ package ca.uhn.fhir.rest.param;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import org.junit.jupiter.api.Test;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.TimeZone;
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.APPROXIMATE;
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.EQUAL;
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.NOT_EQUAL;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.*;
public class DateParamTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DateParamTest.class);
@Test
public void testBasicDateParse() {
DateParam input = new DateParam("2020-01-01");
// too bad value is a j.u.Date instead of a new JSR-310 type
// DataParam parses using default tz, so go backwards.
ZonedDateTime zonedDateTime = input.getValue().toInstant().atZone(ZoneId.systemDefault());
assertEquals(2020,zonedDateTime.getYear());
assertEquals(Month.JANUARY,zonedDateTime.getMonth());
assertEquals(1,zonedDateTime.getDayOfMonth());
assertNull(input.getPrefix());
}
@Test
public void testBadDateFormat() {
try {
new DateParam("09-30-1960");
fail();
} catch (DataFormatException e) {
// expected
}
}
@Test
public void testPrefixParse() {
DateParam input = new DateParam("gt2020-01-01");
assertEquals(ParamPrefixEnum.GREATERTHAN, input.getPrefix());
}
/**
* We support legacy prefixes in addition to the standard ParamPrefixEnum values.
*
* Testing here since BaseParamWithPrefix is abstract.
*/
@Test
public void testLegacyPrefixParse() {
assertEquals(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, translateLegacyPrefix(">="));
assertEquals(ParamPrefixEnum.GREATERTHAN, translateLegacyPrefix(">"));
assertEquals(ParamPrefixEnum.LESSTHAN_OR_EQUALS, translateLegacyPrefix("<="));
assertEquals(ParamPrefixEnum.LESSTHAN, translateLegacyPrefix("<"));
assertEquals(ParamPrefixEnum.APPROXIMATE, translateLegacyPrefix("~"));
assertEquals(ParamPrefixEnum.EQUAL, translateLegacyPrefix("="));
}
private ParamPrefixEnum translateLegacyPrefix(String legacyPrefix) {
DateParam input = new DateParam(legacyPrefix + "2020-01-01");
return input.getPrefix();
}
@Test
public void testJunkDateIssue2361() {
// Issue 2361 - the string "junk" wasn't returning 400 as expected.
// Parsed as a prefix instead, and discarded.
// https://github.com/hapifhir/hapi-fhir/issues/2361
try {
new DateParam("junk");
fail();
} catch (DataFormatException e) {
// expected
}
}
// merged from ca.uhn.fhir.rest.param.DateParamTest
@Test
public void testConstructors() {
new DateParam();
@ -32,19 +100,17 @@ public class DateParamTest {
@Test
public void testParse() {
Date date = new Date();
DateParam param = new DateParam();
param.setValueAsString("gt2016-06-09T20:38:14.591-05:00");
assertEquals(ParamPrefixEnum.GREATERTHAN, param.getPrefix());
assertEquals("2016-06-09T20:38:14.591-05:00", param.getValueAsString());
ourLog.info("PRE: " + param.getValue());
ourLog.info("PRE: " + param.getValue().getTime());
ourLog.debug("PRE: " + param.getValue());
ourLog.debug("PRE: " + param.getValue().getTime());
InstantDt dt = new InstantDt(new Date(param.getValue().getTime()));
dt.setTimeZone(TimeZone.getTimeZone("America/Toronto"));
ourLog.info("POST: " + dt.getValue());
ourLog.debug("POST: " + dt.getValue());
assertEquals("2016-06-09T21:38:14.591-04:00", dt.getValueAsString());
}
@ -53,15 +119,15 @@ public class DateParamTest {
DateParam param = new DateParam();
param.setValueAsString("2016-06-09T20:38Z");
assertEquals(null, param.getPrefix());
assertNull(param.getPrefix());
assertEquals("2016-06-09T20:38Z", param.getValueAsString());
ourLog.info("PRE: " + param.getValue());
ourLog.info("PRE: " + param.getValue().getTime());
ourLog.debug("PRE: " + param.getValue());
ourLog.debug("PRE: " + param.getValue().getTime());
InstantDt dt = new InstantDt(new Date(param.getValue().getTime()));
dt.setTimeZone(TimeZone.getTimeZone("America/Toronto"));
ourLog.info("POST: " + dt.getValue());
ourLog.debug("POST: " + dt.getValue());
assertEquals("2016-06-09T16:38:00.000-04:00", dt.getValueAsString());
}
@ -70,15 +136,15 @@ public class DateParamTest {
DateParam param = new DateParam();
param.setValueAsString("2016-06-09T20:38");
assertEquals(null, param.getPrefix());
assertNull(param.getPrefix());
assertEquals("2016-06-09T20:38", param.getValueAsString());
ourLog.info("PRE: " + param.getValue());
ourLog.info("PRE: " + param.getValue().getTime());
ourLog.debug("PRE: " + param.getValue());
ourLog.debug("PRE: " + param.getValue().getTime());
InstantDt dt = new InstantDt(new Date(param.getValue().getTime()));
dt.setTimeZone(TimeZone.getTimeZone("America/Toronto"));
ourLog.info("POST: " + dt.getValue());
ourLog.debug("POST: " + dt.getValue());
assertThat(dt.getValueAsString(), startsWith("2016-06-09T"));
assertThat(dt.getValueAsString(), endsWith("8:00.000-04:00"));
}
@ -91,12 +157,12 @@ public class DateParamTest {
assertEquals(ParamPrefixEnum.GREATERTHAN, param.getPrefix());
assertEquals("2016-06-09T20:38Z", param.getValueAsString());
ourLog.info("PRE: " + param.getValue());
ourLog.info("PRE: " + param.getValue().getTime());
ourLog.debug("PRE: " + param.getValue());
ourLog.debug("PRE: " + param.getValue().getTime());
InstantDt dt = new InstantDt(new Date(param.getValue().getTime()));
dt.setTimeZone(TimeZone.getTimeZone("America/Toronto"));
ourLog.info("POST: " + dt.getValue());
ourLog.debug("POST: " + dt.getValue());
assertEquals("2016-06-09T16:38:00.000-04:00", dt.getValueAsString());
}

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 2361
title: "Unrecognized search param prefix strings were silently discarded. This is now an error."

View File

@ -436,7 +436,7 @@ public class GenericClientTest {
.encodedJson()
.execute();
} catch (FhirClientConnectionException e) {
assertEquals(0, e.getStatusCode());
assertEquals(500, e.getStatusCode());
assertThat(e.getMessage(), containsString("Failed to parse response from server when performing DELETE to URL"));
}