Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
c544dd4053
|
@ -13,69 +13,68 @@ import ca.uhn.fhir.rest.param.StringParam;
|
|||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
||||
|
||||
@SuppressWarnings("null")
|
||||
//START SNIPPET: provider
|
||||
// START SNIPPET: provider
|
||||
public class PagingPatientProvider implements IResourceProvider {
|
||||
|
||||
/**
|
||||
* Search for Patient resources matching a given family name
|
||||
*/
|
||||
@Search
|
||||
public IBundleProvider search(@RequiredParam(name = Patient.SP_FAMILY) StringParam theFamily) {
|
||||
final InstantDt searchTime = InstantDt.withCurrentTime();
|
||||
/**
|
||||
* Search for Patient resources matching a given family name
|
||||
*/
|
||||
@Search
|
||||
public IBundleProvider search(@RequiredParam(name = Patient.SP_FAMILY) StringParam theFamily) {
|
||||
final InstantDt searchTime = InstantDt.withCurrentTime();
|
||||
|
||||
/*
|
||||
* First, we'll search the database for a set of database row IDs that
|
||||
* match the given search criteria. That way we can keep just the
|
||||
* row IDs around, and load the actual resources on demand later
|
||||
* as the client pages through them.
|
||||
*/
|
||||
final List<Long> matchingResourceIds = null; // <-- implement this
|
||||
/**
|
||||
* First, we'll search the database for a set of database row IDs that
|
||||
* match the given search criteria. That way we can keep just the row IDs
|
||||
* around, and load the actual resources on demand later as the client
|
||||
* pages through them.
|
||||
*/
|
||||
final List<Long> matchingResourceIds = null; // <-- implement this
|
||||
|
||||
/*
|
||||
* Return a bundle provider which can page through the IDs and
|
||||
* return the resources that go with them.
|
||||
*/
|
||||
return new IBundleProvider() {
|
||||
/**
|
||||
* Return a bundle provider which can page through the IDs and return the
|
||||
* resources that go with them.
|
||||
*/
|
||||
return new IBundleProvider() {
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return matchingResourceIds.size();
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return matchingResourceIds.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
|
||||
int end = Math.max(theToIndex, matchingResourceIds.size() - 1);
|
||||
List<Long> idsToReturn = matchingResourceIds.subList(theFromIndex, end);
|
||||
return loadResourcesByIds(idsToReturn);
|
||||
}
|
||||
@Override
|
||||
public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
|
||||
int end = Math.max(theToIndex, matchingResourceIds.size() - 1);
|
||||
List<Long> idsToReturn = matchingResourceIds.subList(theFromIndex, end);
|
||||
return loadResourcesByIds(idsToReturn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstantDt getPublished() {
|
||||
return searchTime;
|
||||
}
|
||||
@Override
|
||||
public InstantDt getPublished() {
|
||||
return searchTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer preferredPageSize() {
|
||||
// Typically this method just returns null
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of patient resources given their IDs
|
||||
*/
|
||||
private List<IBaseResource> loadResourcesByIds(List<Long> theIdsToReturn) {
|
||||
// .. implement this search against the database ..
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Load a list of patient resources given their IDs
|
||||
*/
|
||||
private List<IBaseResource> loadResourcesByIds(List<Long> theIdsToReturn) {
|
||||
// .. implement this search against the database ..
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
}
|
||||
//END SNIPPET: provider
|
||||
// END SNIPPET: provider
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package ca.uhn.fhir.parser;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
|
||||
|
||||
|
@ -24,7 +27,13 @@ import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
|
|||
*/
|
||||
|
||||
/**
|
||||
* The default error handler, which logs issues but does not abort parsing
|
||||
* The default error handler, which logs issues but does not abort parsing, with only one exception:
|
||||
* <p>
|
||||
* The {@link #invalidValue(ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation, String, String)}
|
||||
* method will throw a {@link DataFormatException} by default since ignoring this type of error
|
||||
* can lead to data loss (since invalid values are silently ignored). See
|
||||
* {@link #setErrorOnInvalidValue(boolean)} for information on this.
|
||||
* </p>
|
||||
*
|
||||
* @see IParser#setParserErrorHandler(IParserErrorHandler)
|
||||
* @see FhirContext#setParserErrorHandler(IParserErrorHandler)
|
||||
|
@ -32,6 +41,8 @@ import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
|
|||
public class LenientErrorHandler implements IParserErrorHandler {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LenientErrorHandler.class);
|
||||
private static final StrictErrorHandler STRICT_ERROR_HANDLER = new StrictErrorHandler();
|
||||
private boolean myErrorOnInvalidValue = true;
|
||||
private boolean myLogErrors;
|
||||
|
||||
/**
|
||||
|
@ -67,11 +78,30 @@ public class LenientErrorHandler implements IParserErrorHandler {
|
|||
|
||||
@Override
|
||||
public void invalidValue(IParseLocation theLocation, String theValue, String theError) {
|
||||
if (myLogErrors) {
|
||||
ourLog.warn("Invalid attribute value \"{}\": {}", theValue, theError);
|
||||
if (isBlank(theValue) || myErrorOnInvalidValue == false) {
|
||||
if (myLogErrors) {
|
||||
ourLog.warn("Invalid attribute value \"{}\": {}", theValue, theError);
|
||||
}
|
||||
} else {
|
||||
STRICT_ERROR_HANDLER.invalidValue(theLocation, theValue, theError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>false</code> (default is <code>true</code>) invalid values will be logged. By
|
||||
* default invalid attribute values cause this error handler to throw a {@link DataFormatException} (unlike
|
||||
* other methods in this class which default to simply logging errors).
|
||||
* <p>
|
||||
* Note that empty values (e.g. <code>""</code>) will not lead to an error when this is set to
|
||||
* <code>true</code>, only invalid values (e.g. a gender code of <code>foo</code>)
|
||||
* </p>
|
||||
*
|
||||
* @see #setErrorOnInvalidValue(boolean)
|
||||
*/
|
||||
public boolean isErrorOnInvalidValue() {
|
||||
return myErrorOnInvalidValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void missingRequiredElement(IParseLocation theLocation, String theElementName) {
|
||||
if (myLogErrors) {
|
||||
|
@ -79,6 +109,22 @@ public class LenientErrorHandler implements IParserErrorHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>false</code> (default is <code>true</code>) invalid values will be logged. By
|
||||
* default invalid attribute values cause this error handler to throw a {@link DataFormatException} (unlike
|
||||
* other methods in this class which default to simply logging errors).
|
||||
* <p>
|
||||
* Note that empty values (e.g. <code>""</code>) will not lead to an error when this is set to
|
||||
* <code>true</code>, only invalid values (e.g. a gender code of <code>foo</code>)
|
||||
* </p>
|
||||
* @return Returns a reference to <code>this</code> for easy method chaining
|
||||
* @see #isErrorOnInvalidValue()
|
||||
*/
|
||||
public LenientErrorHandler setErrorOnInvalidValue(boolean theErrorOnInvalidValue) {
|
||||
myErrorOnInvalidValue = theErrorOnInvalidValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
|
||||
if (myLogErrors) {
|
||||
|
|
|
@ -2309,7 +2309,13 @@ class ParserState<T> {
|
|||
if ("".equals(theValue)) {
|
||||
myErrorHandler.invalidValue(null, theValue, "Attribute values must not be empty (\"\")");
|
||||
} else {
|
||||
myInstance.setValueAsString(theValue);
|
||||
try {
|
||||
myInstance.setValueAsString(theValue);
|
||||
} catch (DataFormatException e) {
|
||||
myErrorHandler.invalidValue(null, theValue, e.getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
myErrorHandler.invalidValue(null, theValue, e.getMessage());
|
||||
}
|
||||
}
|
||||
} else if ("id".equals(theName)) {
|
||||
if (myInstance instanceof IIdentifiableElement) {
|
||||
|
|
|
@ -77,6 +77,15 @@ public class RestfulServerUtils {
|
|||
|
||||
private static final HashSet<String> TEXT_ENCODE_ELEMENTS = new HashSet<String>(Arrays.asList("Bundle", "*.text", "*.(mandatory)"));
|
||||
|
||||
private static String actualDstu3FhirVersion = FhirVersionEnum.DSTU3.getFhirVersionString();
|
||||
static {
|
||||
try {
|
||||
Class c = Class.forName("org.hl7.fhir.dstu3.model.Constants");
|
||||
actualDstu3FhirVersion = (String) c.getDeclaredField("VERSION").get(null);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void configureResponseParser(RequestDetails theRequestDetails, IParser parser) {
|
||||
// Pretty print
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theRequestDetails.getServer(), theRequestDetails);
|
||||
|
@ -757,12 +766,16 @@ public class RestfulServerUtils {
|
|||
myEncoding = theEncoding;
|
||||
if (theContentType != null) {
|
||||
if (theContentType.equals(EncodingEnum.JSON_PLAIN_STRING) || theContentType.equals(EncodingEnum.XML_PLAIN_STRING)) {
|
||||
myNonLegacy = !theCtx.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3);
|
||||
FhirVersionEnum ctxtEnum = theCtx.getVersion().getVersion();
|
||||
myNonLegacy = ctxtEnum.isNewerThan(FhirVersionEnum.DSTU3)
|
||||
|| (ctxtEnum.isEquivalentTo(FhirVersionEnum.DSTU3) && !"1.4.0".equals(actualDstu3FhirVersion));
|
||||
} else {
|
||||
myNonLegacy = EncodingEnum.isNonLegacy(theContentType);
|
||||
}
|
||||
} else {
|
||||
if (theCtx.getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) {
|
||||
FhirVersionEnum ctxtEnum = theCtx.getVersion().getVersion();
|
||||
if (ctxtEnum.isOlderThan(FhirVersionEnum.DSTU3)
|
||||
|| (ctxtEnum.isEquivalentTo(FhirVersionEnum.DSTU3) && "1.4.0".equals(actualDstu3FhirVersion))) {
|
||||
myNonLegacy = null;
|
||||
} else {
|
||||
myNonLegacy = Boolean.TRUE;
|
||||
|
|
|
@ -79,27 +79,7 @@ public class TestUtil {
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* Set some system properties randomly after each test.. this is kind of hackish,
|
||||
* but it helps us make sure we don't have any tests that depend on a particular
|
||||
* environment
|
||||
*/
|
||||
Locale[] availableLocales = { Locale.CANADA, Locale.GERMANY, Locale.TAIWAN };
|
||||
Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
|
||||
ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
|
||||
if (Math.random() < 0.5) {
|
||||
ourLog.info("Tests are using WINDOWS line endings and ISO-8851-1");
|
||||
System.setProperty("file.encoding", "ISO-8859-1");
|
||||
System.setProperty("line.separator", "\r\n");
|
||||
} else {
|
||||
ourLog.info("Tests are using UNIX line endings and UTF-8");
|
||||
System.setProperty("file.encoding", "UTF-8");
|
||||
System.setProperty("line.separator", "\n");
|
||||
}
|
||||
String availableTimeZones[] = { "GMT+08:00", "GMT-05:00", "GMT+00:00", "GMT+03:30" };
|
||||
String timeZone = availableTimeZones[(int) (Math.random() * availableTimeZones.length)];
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
|
||||
ourLog.info("Tests are using time zone: {}", TimeZone.getDefault().getID());
|
||||
randomizeLocale();
|
||||
|
||||
/*
|
||||
* If we're running a CI build, set all loggers to TRACE level to ensure coverage
|
||||
|
@ -116,4 +96,28 @@ public class TestUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set some system properties randomly after each test.. this is kind of hackish,
|
||||
* but it helps us make sure we don't have any tests that depend on a particular
|
||||
* environment
|
||||
*/
|
||||
public static void randomizeLocale() {
|
||||
Locale[] availableLocales = { Locale.CANADA, Locale.GERMANY, Locale.TAIWAN };
|
||||
Locale.setDefault(availableLocales[(int) (Math.random() * availableLocales.length)]);
|
||||
ourLog.info("Tests are running in locale: " + Locale.getDefault().getDisplayName());
|
||||
if (Math.random() < 0.5) {
|
||||
ourLog.info("Tests are using WINDOWS line endings and ISO-8851-1");
|
||||
System.setProperty("file.encoding", "ISO-8859-1");
|
||||
System.setProperty("line.separator", "\r\n");
|
||||
} else {
|
||||
ourLog.info("Tests are using UNIX line endings and UTF-8");
|
||||
System.setProperty("file.encoding", "UTF-8");
|
||||
System.setProperty("line.separator", "\n");
|
||||
}
|
||||
String availableTimeZones[] = { "GMT+08:00", "GMT-05:00", "GMT+00:00", "GMT+03:30" };
|
||||
String timeZone = availableTimeZones[(int) (Math.random() * availableTimeZones.length)];
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
|
||||
ourLog.info("Tests are using time zone: {}", TimeZone.getDefault().getID());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -146,7 +148,11 @@ public class ValidationContext<T> extends BaseValidationContext<T> implements IV
|
|||
@Override
|
||||
public IBaseResource getResource() {
|
||||
if (myParsed == null) {
|
||||
myParsed = getResourceAsStringEncoding().newParser(getFhirContext()).parseResource(getResourceAsString());
|
||||
IParser parser = getResourceAsStringEncoding().newParser(getFhirContext());
|
||||
LenientErrorHandler errorHandler = new LenientErrorHandler();
|
||||
errorHandler.setErrorOnInvalidValue(false);
|
||||
parser.setParserErrorHandler(errorHandler);
|
||||
myParsed = parser.parseResource(getResourceAsString());
|
||||
}
|
||||
return myParsed;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,13 @@ import static org.junit.Assert.assertThat;
|
|||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.CodeType;
|
||||
|
@ -34,6 +40,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
|
|||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
|
@ -54,6 +61,30 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
myExtensionalVsId = myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
/**
|
||||
* #516
|
||||
*/
|
||||
@Test
|
||||
public void testInvalidFilter() throws Exception {
|
||||
String string = IOUtils.toString(getClass().getResourceAsStream("/bug_516_invalid_expansion.json"), StandardCharsets.UTF_8);
|
||||
HttpPost post = new HttpPost(ourServerBase+"/ValueSet/%24expand");
|
||||
post.setEntity(new StringEntity(string, ContentType.parse(Constants.CT_FHIR_JSON_NEW)));
|
||||
|
||||
CloseableHttpResponse resp = ourHttpClient.execute(post);
|
||||
try {
|
||||
|
||||
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(respString);
|
||||
|
||||
ourLog.info(resp.toString());
|
||||
|
||||
assertEquals(400, resp.getStatusLine().getStatusCode());
|
||||
assertThat(respString, containsString("Unknown FilterOperator code 'n'"));
|
||||
|
||||
}finally {
|
||||
IOUtils.closeQuietly(resp);
|
||||
}
|
||||
}
|
||||
|
||||
private CodeSystem createExternalCs() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"resourceType": "ValueSet",
|
||||
"url": "http://sample/ValueSet/education-levels",
|
||||
"version": "1",
|
||||
"name": "Education Levels",
|
||||
"status": "draft",
|
||||
"compose": {
|
||||
"include": [
|
||||
{
|
||||
"filter": [
|
||||
{
|
||||
"property": "n",
|
||||
"op": "n",
|
||||
"value": "365460000"
|
||||
}
|
||||
],
|
||||
"system": "http://snomed.info/sct"
|
||||
}
|
||||
],
|
||||
"exclude": [
|
||||
{
|
||||
"concept": [
|
||||
{
|
||||
"code": "224298008"
|
||||
},
|
||||
{
|
||||
"code": "365460000"
|
||||
},
|
||||
{
|
||||
"code": "473462005"
|
||||
},
|
||||
{
|
||||
"code": "424587006"
|
||||
}
|
||||
],
|
||||
"system": "http://snomed.info/sct"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "A\nselection of Education Levels",
|
||||
"text": {
|
||||
"status": "generated",
|
||||
"div": "<div\nxmlns=\"http://www.w3.org/1999/xhtml\"><h2>Education\nLevels</h2><tt>http://csiro.au/ValueSet/education-levels</tt><p>A selection of\nEducation Levels</p></div>"
|
||||
},
|
||||
"experimental": true,
|
||||
"date": "2016-07-26"
|
||||
}
|
|
@ -1019,14 +1019,12 @@
|
|||
<content type="text/xml">
|
||||
<Patient xmlns="http://hl7.org/fhir">
|
||||
<identifier>
|
||||
<system value=""/>
|
||||
<value value="NEED"/>
|
||||
<assigner>
|
||||
<display value="FHIR"/>
|
||||
</assigner>
|
||||
</identifier>
|
||||
<identifier>
|
||||
<system value=""/>
|
||||
<value value="E3289"/>
|
||||
<assigner>
|
||||
<display value="EPIC"/>
|
||||
|
|
|
@ -27,6 +27,7 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
@ -49,10 +50,13 @@ public class JpaServerDemo extends RestfulServer {
|
|||
* We want to support FHIR DSTU2 format. This means that the server
|
||||
* will use the DSTU2 bundle format and other DSTU2 encoding changes.
|
||||
*
|
||||
* If you want to use DSTU1 instead, change the following line, and change the 2 occurrences of dstu2 in web.xml to dstu1
|
||||
* If you want to use DSTU1 instead, change the following line, and
|
||||
* change the 2 occurrences of dstu2 in web.xml to dstu1
|
||||
*/
|
||||
FhirVersionEnum fhirVersion = FhirVersionEnum.DSTU2;
|
||||
setFhirContext(new FhirContext(fhirVersion));
|
||||
FhirContext context = new FhirContext(fhirVersion);
|
||||
|
||||
setFhirContext(context);
|
||||
|
||||
// Get the spring context from the web container (it's declared in web.xml)
|
||||
myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
|
||||
|
|
|
@ -20,6 +20,8 @@ import ca.uhn.fhir.model.dstu.resource.Patient;
|
|||
import ca.uhn.fhir.model.dstu.valueset.ContactSystemEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class ResourceValidatorTest {
|
||||
|
@ -72,23 +74,34 @@ public class ResourceValidatorTest {
|
|||
/**
|
||||
* See issue #50
|
||||
*/
|
||||
@Test(expected=DataFormatException.class)
|
||||
@Test()
|
||||
public void testOutOfBoundsDate() {
|
||||
Patient p = new Patient();
|
||||
p.setBirthDate(new DateTimeDt("2000-12-31"));
|
||||
|
||||
// Put in an invalid date
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p).replace("2000-12-31", "2000-15-31");
|
||||
IParser parser = ourCtx.newXmlParser();
|
||||
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||
|
||||
String encoded = parser.setPrettyPrint(true).encodeResourceToString(p).replace("2000-12-31", "2000-15-31");
|
||||
ourLog.info(encoded);
|
||||
|
||||
assertThat(encoded, StringContains.containsString("2000-15-31"));
|
||||
|
||||
ValidationResult result = ourCtx.newValidator().validateWithResult(encoded);
|
||||
String resultString = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
|
||||
FhirValidator validator = ourCtx.newValidator();
|
||||
ValidationResult result = validator.validateWithResult(encoded);
|
||||
String resultString = parser.setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
|
||||
ourLog.info(resultString);
|
||||
|
||||
assertEquals(2, ((OperationOutcome)result.toOperationOutcome()).getIssue().size());
|
||||
assertThat(resultString, StringContains.containsString("cvc-datatype-valid.1.2.3"));
|
||||
|
||||
try {
|
||||
parser.parseResource(encoded);
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Invalid attribute value \"2000-15-31\": Invalid date/time format: \"2000-15-31\"", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -170,7 +170,10 @@ public class JsonParserDstu2Test {
|
|||
@Test
|
||||
public void testParseEmptyValue() {
|
||||
String input = "{\"resourceType\":\"QuestionnaireResponse\",\"id\":\"123\",\"authored\":\"\",\"group\":{\"linkId\":\"\"}}";
|
||||
QuestionnaireResponse qr = ourCtx.newJsonParser().parseResource(QuestionnaireResponse.class, input);
|
||||
IParser parser = ourCtx.newJsonParser();
|
||||
|
||||
parser.setParserErrorHandler(new LenientErrorHandler().setErrorOnInvalidValue(false));
|
||||
QuestionnaireResponse qr = parser.parseResource(QuestionnaireResponse.class, input);
|
||||
|
||||
assertEquals("QuestionnaireResponse/123", qr.getIdElement().getValue());
|
||||
assertEquals(null, qr.getAuthored());
|
||||
|
|
|
@ -2524,7 +2524,10 @@ public class XmlParserDstu2Test {
|
|||
"</Patient>";
|
||||
//@formatter:on
|
||||
|
||||
ourCtx.newXmlParser().parseResource(resource);
|
||||
|
||||
IParser parser = ourCtx.newXmlParser();
|
||||
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||
parser.parseResource(resource);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -40,10 +40,12 @@ import ca.uhn.fhir.model.dstu2.valueset.ContactPointSystemEnum;
|
|||
import ca.uhn.fhir.model.dstu2.valueset.NarrativeStatusEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.UnitsOfTimeEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateDt;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.parser.XmlParserDstu2Test.TestPatientFor327;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.schematron.SchematronBaseValidator;
|
||||
|
@ -71,26 +73,33 @@ public class ResourceValidatorDstu2Test {
|
|||
/**
|
||||
* See issue #50
|
||||
*/
|
||||
@Test(expected=DataFormatException.class)
|
||||
@Test()
|
||||
public void testOutOfBoundsDate() {
|
||||
Patient p = new Patient();
|
||||
p.setBirthDate(new DateDt("2000-15-31"));
|
||||
p.setBirthDate(new DateDt("2000-12-31"));
|
||||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
|
||||
// Put in an invalid date
|
||||
IParser parser = ourCtx.newXmlParser();
|
||||
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||
|
||||
String encoded = parser.setPrettyPrint(true).encodeResourceToString(p).replace("2000-12-31", "2000-15-31");
|
||||
ourLog.info(encoded);
|
||||
|
||||
assertThat(encoded, StringContains.containsString("2000-15-31"));
|
||||
|
||||
p = ourCtx.newXmlParser().parseResource(Patient.class, encoded);
|
||||
assertEquals("2000-15-31", p.getBirthDateElement().getValueAsString());
|
||||
assertEquals("2001-03-31", new SimpleDateFormat("yyyy-MM-dd").format(p.getBirthDate()));
|
||||
|
||||
ValidationResult result = ourCtx.newValidator().validateWithResult(p);
|
||||
String resultString = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
|
||||
ValidationResult result = ourCtx.newValidator().validateWithResult(encoded);
|
||||
String resultString = parser.setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
|
||||
ourLog.info(resultString);
|
||||
|
||||
assertEquals(2, ((OperationOutcome) result.toOperationOutcome()).getIssue().size());
|
||||
assertThat(resultString, StringContains.containsString("2000-15-31"));
|
||||
assertEquals(2, ((OperationOutcome)result.toOperationOutcome()).getIssue().size());
|
||||
assertThat(resultString, StringContains.containsString("cvc-pattern-valid"));
|
||||
|
||||
try {
|
||||
parser.parseResource(encoded);
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Invalid attribute value \"2000-15-31\": Invalid date/time format: \"2000-15-31\"", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.dstu3.model;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.hamcrest.Matchers.emptyOrNullString;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
@ -334,7 +335,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
|||
myFractionalSeconds = "";
|
||||
}
|
||||
|
||||
setPrecision(precision);
|
||||
myPrecision = precision;
|
||||
return cal.getTime();
|
||||
|
||||
}
|
||||
|
@ -372,8 +373,8 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
|||
if (isBlank(theValue)) {
|
||||
throwBadDateFormat(theWholeValue);
|
||||
} else if (theValue.charAt(0) == 'Z') {
|
||||
clearTimeZone();
|
||||
setTimeZoneZulu(true);
|
||||
myTimeZone = null;
|
||||
myTimeZoneZulu = true;
|
||||
} else if (theValue.length() != 6) {
|
||||
throwBadDateFormat(theWholeValue, "Timezone offset must be in the form \"Z\", \"-HH:mm\", or \"+HH:mm\"");
|
||||
} else if (theValue.charAt(3) != ':' || !(theValue.charAt(0) == '+' || theValue.charAt(0) == '-')) {
|
||||
|
@ -381,8 +382,8 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
|||
} else {
|
||||
parseInt(theWholeValue, theValue.substring(1, 3), 0, 23);
|
||||
parseInt(theWholeValue, theValue.substring(4, 6), 0, 59);
|
||||
clearTimeZone();
|
||||
setTimeZone(TimeZone.getTimeZone("GMT" + theValue));
|
||||
myTimeZoneZulu = false;
|
||||
myTimeZone = TimeZone.getTimeZone("GMT" + theValue);
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -390,12 +391,14 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
|||
|
||||
public BaseDateTimeType setTimeZone(TimeZone theTimeZone) {
|
||||
myTimeZone = theTimeZone;
|
||||
myTimeZoneZulu = false;
|
||||
updateStringValue();
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseDateTimeType setTimeZoneZulu(boolean theTimeZoneZulu) {
|
||||
myTimeZoneZulu = theTimeZoneZulu;
|
||||
myTimeZone = null;
|
||||
updateStringValue();
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -70,13 +70,13 @@ public abstract class PrimitiveType<T> extends Type implements IPrimitiveType<T>
|
|||
}
|
||||
|
||||
public void fromStringValue(String theValue) {
|
||||
myStringValue = theValue;
|
||||
if (theValue == null) {
|
||||
myCoercedValue = null;
|
||||
} else {
|
||||
// NB this might be null
|
||||
myCoercedValue = parse(theValue);
|
||||
}
|
||||
myStringValue = theValue;
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.parser;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
|
||||
|
@ -46,7 +48,14 @@ public class ErrorHandlerTest {
|
|||
new LenientErrorHandler().containedResourceWithNoId(null);
|
||||
new LenientErrorHandler().unknownReference(null, null);
|
||||
new LenientErrorHandler().incorrectJsonType(null, null, ValueType.ARRAY, ValueType.SCALAR);
|
||||
new LenientErrorHandler().invalidValue(null, null, null);
|
||||
new LenientErrorHandler().setErrorOnInvalidValue(false).invalidValue(null, "FOO", "");
|
||||
new LenientErrorHandler().invalidValue(null, null, "");
|
||||
try {
|
||||
new LenientErrorHandler().invalidValue(null, "FOO", "");
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
// good, this one method defaults to causing an error
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = DataFormatException.class)
|
||||
|
|
|
@ -13,35 +13,78 @@ import static org.junit.Assert.assertSame;
|
|||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.isNull;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hamcrest.core.StringContains;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Address.AddressUse;
|
||||
import org.hl7.fhir.dstu3.model.Address.AddressUseEnumFactory;
|
||||
import org.hl7.fhir.dstu3.model.Attachment;
|
||||
import org.hl7.fhir.dstu3.model.AuditEvent;
|
||||
import org.hl7.fhir.dstu3.model.Basic;
|
||||
import org.hl7.fhir.dstu3.model.Binary;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
|
||||
import org.hl7.fhir.dstu3.model.Claim;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.Communication;
|
||||
import org.hl7.fhir.dstu3.model.Condition;
|
||||
import org.hl7.fhir.dstu3.model.Condition.ConditionVerificationStatus;
|
||||
import org.hl7.fhir.dstu3.model.Conformance;
|
||||
import org.hl7.fhir.dstu3.model.Conformance.UnknownContentCode;
|
||||
import org.hl7.fhir.dstu3.model.Coverage;
|
||||
import org.hl7.fhir.dstu3.model.DateTimeType;
|
||||
import org.hl7.fhir.dstu3.model.DateType;
|
||||
import org.hl7.fhir.dstu3.model.DecimalType;
|
||||
import org.hl7.fhir.dstu3.model.DiagnosticReport;
|
||||
import org.hl7.fhir.dstu3.model.EnumFactory;
|
||||
import org.hl7.fhir.dstu3.model.Enumeration;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.dstu3.model.ExplanationOfBenefit;
|
||||
import org.hl7.fhir.dstu3.model.Extension;
|
||||
import org.hl7.fhir.dstu3.model.HumanName;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Identifier.IdentifierUse;
|
||||
import org.hl7.fhir.dstu3.model.Linkage;
|
||||
import org.hl7.fhir.dstu3.model.Medication;
|
||||
import org.hl7.fhir.dstu3.model.MedicationRequest;
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.PrimitiveType;
|
||||
import org.hl7.fhir.dstu3.model.Quantity;
|
||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse;
|
||||
import org.hl7.fhir.dstu3.model.Reference;
|
||||
import org.hl7.fhir.dstu3.model.RelatedPerson;
|
||||
import org.hl7.fhir.dstu3.model.SampledData;
|
||||
import org.hl7.fhir.dstu3.model.SimpleQuantity;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.UriType;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.junit.*;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
|
@ -92,7 +135,7 @@ public class JsonParserDstu3Test {
|
|||
IParser p = ourCtx.newJsonParser().setPrettyPrint(true);
|
||||
String encoded = p.encodeResourceToString(resource);
|
||||
ourLog.info(encoded);
|
||||
|
||||
|
||||
assertEquals(3, countMatches(encoded, "resourceType"));
|
||||
}
|
||||
|
||||
|
@ -121,15 +164,18 @@ public class JsonParserDstu3Test {
|
|||
*/
|
||||
@Test
|
||||
public void testParseEmptyValue() {
|
||||
String input = "{\"resourceType\":\"QuestionnaireResponse\",\"id\":\"123\",\"authored\":\"\",\"item\":[{\"item\":[{\"linkId\":\"\"}]}]}";
|
||||
QuestionnaireResponse qr = ourCtx.newJsonParser().parseResource(QuestionnaireResponse.class, input);
|
||||
|
||||
String input = "{\"resourceType\":\"QuestionnaireResponse\",\"id\":\"123\",\"authored\":\"\",\"group\":{\"linkId\":\"\"}}";
|
||||
IParser parser = ourCtx.newJsonParser();
|
||||
|
||||
parser.setParserErrorHandler(new LenientErrorHandler().setErrorOnInvalidValue(false));
|
||||
QuestionnaireResponse qr = parser.parseResource(QuestionnaireResponse.class, input);
|
||||
|
||||
assertEquals("QuestionnaireResponse/123", qr.getIdElement().getValue());
|
||||
assertEquals(null, qr.getAuthored());
|
||||
assertEquals(null, qr.getAuthoredElement().getValue());
|
||||
assertEquals(null, qr.getAuthoredElement().getValueAsString());
|
||||
assertEquals(null, qr.getItemFirstRep().getItemFirstRep().getLinkId());
|
||||
assertEquals(null, qr.getItemFirstRep().getItemFirstRep().getLinkIdElement().getValue());
|
||||
assertEquals(null, qr.getItemFirstRep().getLinkId());
|
||||
assertEquals(null, qr.getItemFirstRep().getLinkIdElement().getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1193,6 +1239,78 @@ public class JsonParserDstu3Test {
|
|||
assertEquals("{\"resourceType\":\"Observation\",\"valueQuantity\":{\"value\":0.0000000000000001}}", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* #516
|
||||
*/
|
||||
@Test(expected = DataFormatException.class)
|
||||
public void testInvalidEnumValue() {
|
||||
String res = "{ \"resourceType\": \"ValueSet\", \"url\": \"http://sample/ValueSet/education-levels\", \"version\": \"1\", \"name\": \"Education Levels\", \"status\": \"draft\", \"compose\": { \"include\": [ { \"filter\": [ { \"property\": \"n\", \"op\": \"n\", \"value\": \"365460000\" } ], \"system\": \"http://snomed.info/sct\" } ], \"exclude\": [ { \"concept\": [ { \"code\": \"224298008\" }, { \"code\": \"365460000\" }, { \"code\": \"473462005\" }, { \"code\": \"424587006\" } ], \"system\": \"http://snomed.info/sct\" } ] }, \"description\": \"A selection of Education Levels\", \"text\": { \"status\": \"generated\", \"div\": \"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\"><h2>Education Levels</h2><tt>http://csiro.au/ValueSet/education-levels</tt><p>A selection of Education Levels</p></div>\" }, \"experimental\": true, \"date\": \"2016-07-26\" }";
|
||||
IParser parser = ourCtx.newJsonParser();
|
||||
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||
ValueSet parsed = parser.parseResource(ValueSet.class, res);
|
||||
fail("DataFormat Invalid attribute exception should be thrown");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidEnumValueBlank() {
|
||||
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
|
||||
|
||||
String res = "{ \"resourceType\": \"Patient\", \"gender\": \"\" }";
|
||||
IParser parser = ourCtx.newJsonParser();
|
||||
parser.setParserErrorHandler(errorHandler);
|
||||
Patient parsed = parser.parseResource(Patient.class, res);
|
||||
|
||||
assertEquals(null, parsed.getGenderElement().getValue());
|
||||
assertEquals(null, parsed.getGenderElement().getValueAsString());
|
||||
|
||||
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(errorHandler, times(1)).invalidValue(isNull(IParseLocation.class), eq(""), msgCaptor.capture());
|
||||
assertEquals("Attribute values must not be empty (\"\")", msgCaptor.getValue());
|
||||
|
||||
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
|
||||
assertEquals("{\"resourceType\":\"Patient\"}", encoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidEnumValueInvalid() {
|
||||
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
|
||||
|
||||
String res = "{ \"resourceType\": \"Patient\", \"gender\": \"foo\" }";
|
||||
IParser parser = ourCtx.newJsonParser();
|
||||
parser.setParserErrorHandler(errorHandler);
|
||||
Patient parsed = parser.parseResource(Patient.class, res);
|
||||
|
||||
assertEquals(null, parsed.getGenderElement().getValue());
|
||||
assertEquals("foo", parsed.getGenderElement().getValueAsString());
|
||||
|
||||
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(errorHandler, times(1)).invalidValue(isNull(IParseLocation.class), eq("foo"), msgCaptor.capture());
|
||||
assertEquals("Unknown AdministrativeGender code 'foo'", msgCaptor.getValue());
|
||||
|
||||
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
|
||||
assertEquals("{\"resourceType\":\"Patient\",\"gender\":\"foo\"}", encoded);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidDateTimeValueInvalid() throws Exception {
|
||||
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
|
||||
|
||||
String res = "{ \"resourceType\": \"Observation\", \"valueDateTime\": \"foo\" }";
|
||||
IParser parser = ourCtx.newJsonParser();
|
||||
parser.setParserErrorHandler(errorHandler);
|
||||
Observation parsed = parser.parseResource(Observation.class, res);
|
||||
|
||||
assertEquals(null, parsed.getValueDateTimeType().getValue());
|
||||
assertEquals("foo", parsed.getValueDateTimeType().getValueAsString());
|
||||
|
||||
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(errorHandler, times(1)).invalidValue(isNull(IParseLocation.class), eq("foo"), msgCaptor.capture());
|
||||
assertEquals("Invalid date/time format: \"foo\"", msgCaptor.getValue());
|
||||
|
||||
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
|
||||
assertEquals("{\"resourceType\":\"Observation\",\"valueDateTime\":\"foo\"}", encoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* #65
|
||||
*/
|
||||
|
|
|
@ -2855,7 +2855,7 @@ public class XmlParserDstu3Test {
|
|||
/**
|
||||
* See #366
|
||||
*/
|
||||
@Test(expected = DataFormatException.class)
|
||||
@Test()
|
||||
public void testParseInvalidBoolean() {
|
||||
//@formatter:off
|
||||
String resource = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
|
||||
|
@ -2863,7 +2863,19 @@ public class XmlParserDstu3Test {
|
|||
"</Patient>";
|
||||
//@formatter:on
|
||||
|
||||
ourCtx.newXmlParser().parseResource(resource);
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
||||
try {
|
||||
p.parseResource(resource);
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Invalid attribute value \"1\": Invalid boolean string: '1'", e.getMessage());
|
||||
}
|
||||
|
||||
LenientErrorHandler errorHandler = new LenientErrorHandler();
|
||||
assertEquals(true, errorHandler.isErrorOnInvalidValue());
|
||||
errorHandler.setErrorOnInvalidValue(false);
|
||||
p.setParserErrorHandler(errorHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -3,8 +3,10 @@ package org.hl7.fhir.dstu3.hapi.validation;
|
|||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -12,14 +14,24 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hamcrest.core.StringContains;
|
||||
import org.hl7.fhir.dstu3.model.CodeableConcept;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.Condition;
|
||||
import org.hl7.fhir.dstu3.model.Condition.ConditionVerificationStatus;
|
||||
import org.hl7.fhir.dstu3.model.DateType;
|
||||
import org.hl7.fhir.dstu3.model.Narrative.NarrativeStatus;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.Reference;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.parser.XmlParserDstu3Test;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
|
@ -37,6 +49,39 @@ public class ResourceValidatorDstu3Test {
|
|||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
/**
|
||||
* See issue #50
|
||||
*/
|
||||
@Test()
|
||||
public void testOutOfBoundsDate() {
|
||||
Patient p = new Patient();
|
||||
p.setBirthDateElement(new DateType("2000-12-31"));
|
||||
|
||||
// Put in an invalid date
|
||||
IParser parser = ourCtx.newXmlParser();
|
||||
parser.setParserErrorHandler(new StrictErrorHandler());
|
||||
|
||||
String encoded = parser.setPrettyPrint(true).encodeResourceToString(p).replace("2000-12-31", "2000-15-31");
|
||||
ourLog.info(encoded);
|
||||
|
||||
assertThat(encoded, StringContains.containsString("2000-15-31"));
|
||||
|
||||
ValidationResult result = ourCtx.newValidator().validateWithResult(encoded);
|
||||
String resultString = parser.setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
|
||||
ourLog.info(resultString);
|
||||
|
||||
assertEquals(2, ((OperationOutcome)result.toOperationOutcome()).getIssue().size());
|
||||
assertThat(resultString, StringContains.containsString("cvc-pattern-valid"));
|
||||
|
||||
try {
|
||||
parser.parseResource(encoded);
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: Invalid attribute value \"2000-15-31\": Invalid date/time format: \"2000-15-31\"", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure that the elements that appear in all resources (meta, language, extension, etc) all appear in the correct order
|
||||
*/
|
||||
|
|
|
@ -93,15 +93,42 @@
|
|||
to using Thymeleaf 3. Thanks to GitHub user @gsureshkumar
|
||||
for reporting!
|
||||
</action>
|
||||
<action type="add" issue="525">
|
||||
When parsing invalid enum values in STU3,
|
||||
report errors through the parserErrorHandler,
|
||||
not by throwing an exception. Thanks to
|
||||
Michael Lawley for the pull request!
|
||||
</action>
|
||||
<action type="add" issue="516">
|
||||
When parsing DSTU3 resources with enumerated
|
||||
types that contain invalid values, the parser will now
|
||||
invoke the parserErrorHandler. For example, when parsing
|
||||
<![CDATA[
|
||||
<code>{"resourceType":"Patient", "gender":"foo"}</code>
|
||||
]]>
|
||||
the previous behaviour was to throw an InvalidArgumentException.
|
||||
Now, the parserErrorHandler is invoked. In addition, thw
|
||||
LenientErrorHandler has been modified so that this one case
|
||||
will result in a DataFormatException. This has the effect
|
||||
that servers which receive an invalid enum velue will return
|
||||
an HTTP 400 instead of an HTTP 500. Thanks to Jim
|
||||
Steel for reporting!
|
||||
</action>
|
||||
<action type="add">
|
||||
Enhancements to the tinder-plugin's generic template features
|
||||
of the "generate-multi-files" and "generate-single-file"
|
||||
Maven goals as well as the Ant hapi-tinder task. Velocity
|
||||
templates now have access to the full tinder data model
|
||||
for resources and data types. These "generic" tasks can
|
||||
now use Velocimacro library files outside the
|
||||
tinder plugin JAR and can specify any of the Velocity
|
||||
configuration properties such as 'macro.provide.scope.control'
|
||||
of the <![CDATA[<i>generate-multi-files</i> and <i>generate-single-file</i>
|
||||
Maven goals as well as the Ant <i>hapi-tinder</i> task.
|
||||
<ul>
|
||||
<li>Provides the full Tinder data model by adding composites, valuesets, and profiles to resourcesw.</li>
|
||||
<li>Supports generating files for resources, composites, valuesets, and profiles</li>
|
||||
<li>Supports Velocimacro files outside the tinder-plugin JAR</li>
|
||||
<li>Provides filename prefix as well as suffix properties</li>
|
||||
<li>Can specify any of the Velocity configuration parameters such as
|
||||
<i>macro.provide.scope.control</i> which allows safe macro recursion</li>
|
||||
<li>Templates can now drill down into the referenced children for a ResourceBlockCopy</li>
|
||||
<li>Normalization of properties across all three generic tasks</li>
|
||||
</ul>
|
||||
]]>
|
||||
</action>
|
||||
</release>
|
||||
<release version="2.1" date="2016-11-11">
|
||||
|
|
|
@ -14,12 +14,6 @@
|
|||
sections below.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<b>Resource Validation</b>
|
||||
is validation of the raw or parsed resource against
|
||||
the official FHIR validation rules (e.g. Schema/Schematron/Profile/StructureDefinition/ValueSet)
|
||||
as well as against custom profiles which have been developed.
|
||||
</li>
|
||||
<li>
|
||||
<b>Parser Validation</b>
|
||||
is validation at runtime during the parsing
|
||||
|
@ -31,10 +25,77 @@
|
|||
that no data is being lost during parsing, but is less comprehensive than resource validation
|
||||
against raw text data.
|
||||
</li>
|
||||
<li>
|
||||
<b>Resource Validation</b>
|
||||
is validation of the raw or parsed resource against
|
||||
the official FHIR validation rules (e.g. Schema/Schematron/Profile/StructureDefinition/ValueSet)
|
||||
as well as against custom profiles which have been developed.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- Parser Validation -->
|
||||
<section name="Parser Validation">
|
||||
|
||||
<p>
|
||||
Parser validation is controlled by calling
|
||||
<code>setParserErrorHandler(IParserErrorHandler)</code>
|
||||
on
|
||||
either the FhirContext or on individual parser instances. This method
|
||||
takes an
|
||||
<code>IParserErrorHandler</code>
|
||||
, which is a callback that
|
||||
will be invoked any time a parse issue is detected.
|
||||
</p>
|
||||
<p>
|
||||
There are two implementations of
|
||||
<code>IParserErrorHandler</code>
|
||||
worth
|
||||
mentioning. You can also supply your own implementation if you want.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="./apidocs/ca/uhn/fhir/parser/LenientErrorHandler.html">LenientErrorHandler</a>
|
||||
logs any errors but does not abort parsing. By default this handler is used, and it
|
||||
logs errors at "warning" level. It can also be configured to silently
|
||||
ignore issues.
|
||||
</li>
|
||||
<li>
|
||||
<a href="./apidocs/ca/uhn/fhir/parser/StrictErrorHandler.html">StrictErrorHandler</a>
|
||||
throws a
|
||||
<code>DataFormatException</code>
|
||||
if any errors are detected.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
The following example shows how to configure a parser to use strict validation.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="parserValidation" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
You can also configure the error handler at the FhirContext level, which is useful
|
||||
for clients.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="clientValidation" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
FhirContext level validators can also be useful on servers.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="serverValidation" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- RESOURCE VALIDATION -->
|
||||
|
||||
<section name="Resource Validation">
|
||||
|
@ -223,66 +284,6 @@
|
|||
|
||||
</section>
|
||||
|
||||
<section name="Parser Validation">
|
||||
|
||||
<p>
|
||||
Parser validation is controlled by calling
|
||||
<code>setParserErrorHandler(IParserErrorHandler)</code>
|
||||
on
|
||||
either the FhirContext or on individual parser instances. This method
|
||||
takes an
|
||||
<code>IParserErrorHandler</code>
|
||||
, which is a callback that
|
||||
will be invoked any time a parse issue is detected.
|
||||
</p>
|
||||
<p>
|
||||
There are two implementations of
|
||||
<code>IParserErrorHandler</code>
|
||||
worth
|
||||
mentioning. You can also supply your own implementation if you want.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="./apidocs/ca/uhn/fhir/parser/LenientErrorHandler.html">LenientErrorHandler</a>
|
||||
logs any errors but does not abort parsing. By default this handler is used, and it
|
||||
logs errors at "warning" level. It can also be configured to silently
|
||||
ignore issues.
|
||||
</li>
|
||||
<li>
|
||||
<a href="./apidocs/ca/uhn/fhir/parser/StrictErrorHandler.html">StrictErrorHandler</a>
|
||||
throws a
|
||||
<code>DataFormatException</code>
|
||||
if any errors are detected.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
The following example shows how to configure a parser to use strict validation.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="parserValidation" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
You can also configure the error handler at the FhirContext level, which is useful
|
||||
for clients.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="clientValidation" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
FhirContext level validators can also be useful on servers.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="serverValidation" />
|
||||
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
|
||||
</macro>
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</document>
|
||||
|
|
Loading…
Reference in New Issue