Merge pull request #1796 from jamesagnew/ja_20200409_report_json_error_attr_name

Improve error message reporting for invalid json - Fix #1793
This commit is contained in:
James Agnew 2020-04-09 15:11:57 -04:00 committed by GitHub
commit 1a3a40b3c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 132 additions and 58 deletions

View File

@ -0,0 +1,14 @@
package ca.uhn.fhir.parser;
class BaseErrorHandler {
String describeLocation(IParserErrorHandler.IParseLocation theLocation) {
if (theLocation == null) {
return "";
} else {
return theLocation.toString() + " ";
}
}
}

View File

@ -174,7 +174,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
theEventWriter.init();
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
encodeResourceToJsonStreamWriter(resDef, theResource, theEventWriter, null, false, theEncodeContext);
encodeResourceToJsonStreamWriter(resDef, theResource, theEventWriter, null, false, theEncodeContext);
theEventWriter.flush();
}
@ -258,9 +258,9 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
break;
} else if (valueObj instanceof Long) {
if (theChildName != null) {
theEventWriter.write(theChildName, (long)valueObj);
theEventWriter.write(theChildName, (long) valueObj);
} else {
theEventWriter.write((long)valueObj);
theEventWriter.write((long) valueObj);
}
break;
}
@ -604,7 +604,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
return maxCardinality > 1 || maxCardinality == Child.MAX_UNLIMITED;
}
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonLikeWriter theEventWriter, boolean theContainedResource, CompositeChildElement theParent, EncodeContext theEncodeContext) throws IOException, DataFormatException {
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonLikeWriter theEventWriter, boolean theContainedResource, CompositeChildElement theParent, EncodeContext theEncodeContext) throws IOException, DataFormatException {
writeCommentsPreAndPost(theNextValue, theEventWriter);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, theContainedResource, theParent, theEncodeContext);
@ -1387,10 +1387,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
}
private static void write(JsonLikeWriter theWriter, String theName, String theValue) throws IOException {
theWriter.write(theName, theValue);
}
private class HeldExtension implements Comparable<HeldExtension> {
private CompositeChildElement myChildElem;
@ -1485,7 +1481,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource, myChildElem, null, theEncodeContext, theContainedResource);
} else {
String childName = myDef.getChildNameByDatatype(myValue.getClass());
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, myParent, false, theEncodeContext);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName, false, myParent, false, theEncodeContext);
managePrimitiveExtension(myValue, theResDef, theResource, theEventWriter, def, childName, theEncodeContext, theContainedResource);
}
@ -1562,7 +1558,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
if (childDef == null) {
throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName());
}
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, false, myParent,false, theEncodeContext);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, false, myParent, false, theEncodeContext);
managePrimitiveExtension(value, theResDef, theResource, theEventWriter, childDef, childName, theEncodeContext, theContainedResource);
theEncodeContext.popPath();
@ -1574,4 +1570,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
theEventWriter.endObject();
}
}
private static void write(JsonLikeWriter theWriter, String theName, String theValue) throws IOException {
theWriter.write(theName, theValue);
}
}

View File

@ -38,7 +38,7 @@ import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
* @see IParser#setParserErrorHandler(IParserErrorHandler)
* @see FhirContext#setParserErrorHandler(IParserErrorHandler)
*/
public class LenientErrorHandler implements IParserErrorHandler {
public class LenientErrorHandler extends BaseErrorHandler implements IParserErrorHandler {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LenientErrorHandler.class);
private static final StrictErrorHandler STRICT_ERROR_HANDLER = new StrictErrorHandler();
@ -84,7 +84,7 @@ public class LenientErrorHandler implements IParserErrorHandler {
public void invalidValue(IParseLocation theLocation, String theValue, String theError) {
if (isBlank(theValue) || myErrorOnInvalidValue == false) {
if (myLogErrors) {
ourLog.warn("Invalid attribute value \"{}\": {}", theValue, theError);
ourLog.warn("{}Invalid attribute value \"{}\": {}", describeLocation(theLocation), theValue, theError);
}
} else {
STRICT_ERROR_HANDLER.invalidValue(theLocation, theValue, theError);
@ -133,35 +133,35 @@ public class LenientErrorHandler implements IParserErrorHandler {
@Override
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
if (myLogErrors) {
ourLog.warn("Multiple repetitions of non-repeatable element '{}' found while parsing", theElementName);
ourLog.warn("{}Multiple repetitions of non-repeatable element '{}' found while parsing", describeLocation(theLocation), theElementName);
}
}
@Override
public void unknownAttribute(IParseLocation theLocation, String theElementName) {
if (myLogErrors) {
ourLog.warn("Unknown attribute '{}' found while parsing", theElementName);
ourLog.warn("{}Unknown attribute '{}' found while parsing",describeLocation(theLocation), theElementName);
}
}
@Override
public void unknownElement(IParseLocation theLocation, String theElementName) {
if (myLogErrors) {
ourLog.warn("Unknown element '{}' found while parsing", theElementName);
ourLog.warn("{}Unknown element '{}' found while parsing", describeLocation(theLocation), theElementName);
}
}
@Override
public void unknownReference(IParseLocation theLocation, String theReference) {
if (myLogErrors) {
ourLog.warn("Resource has invalid reference: {}", theReference);
ourLog.warn("{}Resource has invalid reference: {}", describeLocation(theLocation), theReference);
}
}
@Override
public void extensionContainsValueAndNestedExtensions(IParseLocation theLocation) {
if (myLogErrors) {
ourLog.warn("Extension contains both a value and nested extensions: {}", theLocation);
ourLog.warn("{}Extension contains both a value and nested extensions", describeLocation(theLocation));
}
}

View File

@ -54,6 +54,13 @@ class ParseLocation implements IParseLocation {
@Override
public String toString() {
return defaultString(myParentElementName);
return "[element=\"" + defaultString(myParentElementName) + "\"]";
}
/**
* Factory method
*/
static ParseLocation fromElementName(String theChildName) {
return new ParseLocation(theChildName);
}
}

View File

@ -191,7 +191,7 @@ class ParserState<T> {
return thePrimitiveTarget.newInstance(theDefinition.getInstanceConstructorArguments());
}
public IPrimitiveType<?> getPrimitiveInstance(BaseRuntimeChildDefinition theChild, RuntimePrimitiveDatatypeDefinition thePrimitiveTarget) {
public IPrimitiveType<?> getPrimitiveInstance(BaseRuntimeChildDefinition theChild, RuntimePrimitiveDatatypeDefinition thePrimitiveTarget, String theChildName) {
return thePrimitiveTarget.newInstance(theChild.getInstanceConstructorArguments());
}
@ -456,7 +456,7 @@ class ParserState<T> {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = newPrimitiveInstance(myDefinition, primitiveTarget);
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance, theLocalPart);
push(newState);
return;
}
@ -583,9 +583,9 @@ class ParserState<T> {
case PRIMITIVE_DATATYPE: {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance;
newChildInstance = getPrimitiveInstance(child, primitiveTarget);
newChildInstance = getPrimitiveInstance(child, primitiveTarget, theChildName);
child.getMutator().addValue(myInstance, newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance, theChildName);
push(newState);
return;
}
@ -752,7 +752,7 @@ class ParserState<T> {
RuntimePrimitiveDatatypeDefinition primitiveTarget = (RuntimePrimitiveDatatypeDefinition) target;
IPrimitiveType<?> newChildInstance = newInstance(primitiveTarget);
myExtension.setValue(newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance);
PrimitiveState newState = new PrimitiveState(getPreResourceState(), newChildInstance, theLocalPart);
push(newState);
return;
}
@ -824,7 +824,7 @@ class ParserState<T> {
break;
case "lastUpdated":
InstantDt updated = new InstantDt();
push(new PrimitiveState(getPreResourceState(), updated));
push(new PrimitiveState(getPreResourceState(), updated, theLocalPart));
myMap.put(ResourceMetadataKeyEnum.UPDATED, updated);
break;
case "security":
@ -850,7 +850,7 @@ class ParserState<T> {
newProfiles = new ArrayList<>(1);
}
IdDt profile = new IdDt();
push(new PrimitiveState(getPreResourceState(), profile));
push(new PrimitiveState(getPreResourceState(), profile, theLocalPart));
newProfiles.add(profile);
myMap.put(ResourceMetadataKeyEnum.PROFILES, Collections.unmodifiableList(newProfiles));
break;
@ -891,7 +891,7 @@ class ParserState<T> {
private class MetaVersionElementState extends BaseState {
private ResourceMetadataMap myMap;
private final ResourceMetadataMap myMap;
MetaVersionElementState(ParserState<T>.PreResourceState thePreResourceState, ResourceMetadataMap theMap) {
super(thePreResourceState);
@ -1268,23 +1268,27 @@ class ParserState<T> {
}
private class PrimitiveState extends BaseState {
private final String myChildName;
private IPrimitiveType<?> myInstance;
PrimitiveState(PreResourceState thePreResourceState, IPrimitiveType<?> theInstance) {
PrimitiveState(PreResourceState thePreResourceState, IPrimitiveType<?> theInstance, String theChildName) {
super(thePreResourceState);
myInstance = theInstance;
myChildName = theChildName;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if ("value".equals(theName)) {
if ("".equals(theValue)) {
myErrorHandler.invalidValue(null, theValue, "Attribute values must not be empty (\"\")");
ParseLocation location = ParseLocation.fromElementName(myChildName);
myErrorHandler.invalidValue(location, theValue, "Attribute value must not be empty (\"\")");
} else {
try {
myInstance.setValueAsString(theValue);
} catch (DataFormatException | IllegalArgumentException e) {
myErrorHandler.invalidValue(null, theValue, e.getMessage());
ParseLocation location = ParseLocation.fromElementName(myChildName);
myErrorHandler.invalidValue(location, theValue, e.getMessage());
}
}
} else if ("id".equals(theName)) {
@ -1295,7 +1299,8 @@ class ParserState<T> {
} else if (myInstance instanceof IBaseResource) {
new IdDt(theValue).applyTo((org.hl7.fhir.instance.model.api.IBaseResource) myInstance);
} else {
myErrorHandler.unknownAttribute(null, theName);
ParseLocation location = ParseLocation.fromElementName(myChildName);
myErrorHandler.unknownAttribute(location, theName);
}
} else {
super.attributeValue(theName, theValue);
@ -1343,7 +1348,7 @@ class ParserState<T> {
@Override
public void enteringNewElement(String theNamespace, String theChildName) throws DataFormatException {
if ("id".equals(theChildName)) {
push(new PrimitiveState(getPreResourceState(), myInstance.getId()));
push(new PrimitiveState(getPreResourceState(), myInstance.getId(), theChildName));
} else if ("meta".equals(theChildName)) {
push(new MetaElementState(getPreResourceState(), myInstance.getResourceMetadata()));
} else {

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.parser;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.json.JsonLikeValue.ScalarType;
import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
import ca.uhn.fhir.util.UrlUtil;
/*
* #%L
@ -31,7 +32,7 @@ import ca.uhn.fhir.parser.json.JsonLikeValue.ValueType;
* @see IParser#setParserErrorHandler(IParserErrorHandler)
* @see FhirContext#setParserErrorHandler(IParserErrorHandler)
*/
public class StrictErrorHandler implements IParserErrorHandler {
public class StrictErrorHandler extends BaseErrorHandler implements IParserErrorHandler {
@Override
public void containedResourceWithNoId(IParseLocation theLocation) {
@ -46,7 +47,7 @@ public class StrictErrorHandler implements IParserErrorHandler {
@Override
public void invalidValue(IParseLocation theLocation, String theValue, String theError) {
throw new DataFormatException("Invalid attribute value \"" + theValue + "\": " + theError);
throw new DataFormatException(describeLocation(theLocation) + "Invalid attribute value \"" + UrlUtil.sanitizeUrlPart(theValue) + "\": " + theError);
}
@Override
@ -65,27 +66,27 @@ public class StrictErrorHandler implements IParserErrorHandler {
@Override
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
throw new DataFormatException("Multiple repetitions of non-repeatable element '" + theElementName + "' found during parse");
throw new DataFormatException(describeLocation(theLocation) + "Multiple repetitions of non-repeatable element '" + theElementName + "' found during parse");
}
@Override
public void unknownAttribute(IParseLocation theLocation, String theAttributeName) {
throw new DataFormatException("Unknown attribute '" + theAttributeName + "' found during parse");
throw new DataFormatException(describeLocation(theLocation) + "Unknown attribute '" + theAttributeName + "' found during parse");
}
@Override
public void unknownElement(IParseLocation theLocation, String theElementName) {
throw new DataFormatException("Unknown element '" + theElementName + "' found during parse");
throw new DataFormatException(describeLocation(theLocation) + "Unknown element '" + theElementName + "' found during parse");
}
@Override
public void unknownReference(IParseLocation theLocation, String theReference) {
throw new DataFormatException("Resource has invalid reference: " + theReference);
throw new DataFormatException(describeLocation(theLocation) + "Resource has invalid reference: " + theReference);
}
@Override
public void extensionContainsValueAndNestedExtensions(IParseLocation theLocation) {
throw new DataFormatException("Extension contains both a value and nested extensions: " + theLocation);
throw new DataFormatException(describeLocation(theLocation) + "Extension contains both a value and nested extensions");
}
}

View File

@ -0,0 +1,7 @@
---
type: add
issue: 1793
title: "When parsing JSON resources, if an element contains an invalid value, the Parser Error Handler
did not have access to the actual name of the element being parsed. This meant that errors lacked useful
detail in order to diagnose the issue. This has been corrected. Thanks to GitHub user
@jwalter for reporting!"

View File

@ -13,6 +13,7 @@ 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.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.mock;
@ -1098,8 +1099,8 @@ public class JsonParserDstu2_1Test {
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());
verify(errorHandler, times(1)).invalidValue(any(IParseLocation.class), eq(""), msgCaptor.capture());
assertEquals("Attribute value must not be empty (\"\")", msgCaptor.getValue());
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
assertEquals("{\"resourceType\":\"Patient\"}", encoded);
@ -1118,7 +1119,7 @@ public class JsonParserDstu2_1Test {
assertEquals("foo", parsed.getGenderElement().getValueAsString());
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
verify(errorHandler, times(1)).invalidValue(isNull(IParseLocation.class), eq("foo"), msgCaptor.capture());
verify(errorHandler, times(1)).invalidValue(any(IParseLocation.class), eq("foo"), msgCaptor.capture());
assertEquals("Unknown AdministrativeGender code 'foo'", msgCaptor.getValue());
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
@ -1138,7 +1139,7 @@ public class JsonParserDstu2_1Test {
assertEquals("foo", parsed.getValueDateTimeType().getValueAsString());
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
verify(errorHandler, times(1)).invalidValue(isNull(IParseLocation.class), eq("foo"), msgCaptor.capture());
verify(errorHandler, times(1)).invalidValue(any(IParseLocation.class), eq("foo"), msgCaptor.capture());
assertEquals("Invalid date/time format: \"foo\"", msgCaptor.getValue());
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);

View File

@ -2379,7 +2379,7 @@ public class XmlParserDstu2_1Test {
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());
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: [element=\"active\"] Invalid attribute value \"1\": Invalid boolean string: '1'", e.getMessage());
}
LenientErrorHandler errorHandler = new LenientErrorHandler();

View File

@ -82,7 +82,7 @@ public class ResourceValidatorDstu2Test {
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());
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: [element=\"birthDate\"] Invalid attribute value \"2000-15-31\": Invalid date/time format: \"2000-15-31\"", e.getMessage());
}
}

View File

@ -1522,7 +1522,7 @@ public class JsonParserDstu3Test {
assertEquals("foo", parsed.getValueDateTimeType().getValueAsString());
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
verify(errorHandler, times(1)).invalidValue(isNull(), eq("foo"), msgCaptor.capture());
verify(errorHandler, times(1)).invalidValue(any(), eq("foo"), msgCaptor.capture());
assertEquals("Invalid date/time format: \"foo\"", msgCaptor.getValue());
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
@ -1554,8 +1554,8 @@ public class JsonParserDstu3Test {
assertEquals(null, parsed.getGenderElement().getValueAsString());
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
verify(errorHandler, times(1)).invalidValue(isNull(), eq(""), msgCaptor.capture());
assertEquals("Attribute values must not be empty (\"\")", msgCaptor.getValue());
verify(errorHandler, times(1)).invalidValue(any(), eq(""), msgCaptor.capture());
assertEquals("Attribute value must not be empty (\"\")", msgCaptor.getValue());
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
assertEquals("{\"resourceType\":\"Patient\"}", encoded);
@ -1574,7 +1574,7 @@ public class JsonParserDstu3Test {
assertEquals("foo", parsed.getGenderElement().getValueAsString());
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
verify(errorHandler, times(1)).invalidValue(isNull(), eq("foo"), msgCaptor.capture());
verify(errorHandler, times(1)).invalidValue(any(), eq("foo"), msgCaptor.capture());
assertEquals("Unknown AdministrativeGender code 'foo'", msgCaptor.getValue());
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);

View File

@ -1562,7 +1562,7 @@ public class XmlParserDstu3Test {
parser.encodeResourceToString(p);
fail();
} catch (DataFormatException e) {
assertEquals("Extension contains both a value and nested extensions: Patient(res).extension", e.getMessage());
assertEquals("[element=\"Patient(res).extension\"] Extension contains both a value and nested extensions", e.getMessage());
}
}
@ -3139,7 +3139,7 @@ public class XmlParserDstu3Test {
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());
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: [element=\"active\"] Invalid attribute value \"1\": Invalid boolean string: '1'", e.getMessage());
}
LenientErrorHandler errorHandler = new LenientErrorHandler();

View File

@ -100,7 +100,7 @@ public class ClientServerValidationTestHl7OrgDstu2 {
@Test
public void testServerReturnsWrongVersionForDstu2() throws Exception {
String wrongFhirVersion = "3.0.1";
String wrongFhirVersion = "3.0.2";
assertThat(wrongFhirVersion, is(FhirVersionEnum.DSTU3.getFhirVersionString())); // asserting that what we assume to be the DSTU3 FHIR version is still correct
Conformance conf = new Conformance();
conf.setFhirVersion(wrongFhirVersion);
@ -119,7 +119,7 @@ public class ClientServerValidationTestHl7OrgDstu2 {
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/1"));
fail();
} catch (FhirClientInappropriateForServerException e) {
assertThat(e.toString(), containsString("The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"3.0.1\" which corresponds to DSTU3, but this client is configured to use DSTU2_HL7ORG (via the FhirContext)"));
assertThat(e.toString(), containsString("The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"3.0.2\" which corresponds to DSTU3, but this client is configured to use DSTU2_HL7ORG (via the FhirContext)"));
}
}

View File

@ -350,7 +350,7 @@ public class JsonParserR4Test extends BaseTest {
parser.encodeResourceToString(p);
fail();
} catch (DataFormatException e) {
assertEquals("Extension contains both a value and nested extensions: Patient(res).extension", e.getMessage());
assertEquals("[element=\"Patient(res).extension\"] Extension contains both a value and nested extensions", e.getMessage());
}
}
@ -569,6 +569,32 @@ public class JsonParserR4Test extends BaseTest {
}
/**
* See #1793
*/
@Test
public void testParseEmptyAttribute() {
String input = "{\n" +
" \"resourceType\": \"Patient\",\n" +
" \"identifier\": [\n" +
" {\n" +
" \"system\": \"https://example.com\",\n" +
" \"value\": \"\"\n" +
" }\n" +
" ]\n" +
"}";
IParser jsonParser = ourCtx.newJsonParser();
jsonParser.setParserErrorHandler(new StrictErrorHandler());
try {
jsonParser.parseResource(Patient.class, input);
fail();
} catch (DataFormatException e) {
assertEquals("[element=\"value\"] Invalid attribute value \"\": Attribute value must not be empty (\"\")", e.getMessage());
}
}
@Test
public void testParseExtensionOnPrimitive() throws IOException {
String input = IOUtils.toString(JsonParserR4Test.class.getResourceAsStream("/extension-on-line.txt"), Constants.CHARSET_UTF8);

View File

@ -1,8 +1,10 @@
package ca.uhn.fhir.rest.server.interceptor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.test.utilities.server.HashMapResourceProviderRule;
import ca.uhn.fhir.test.utilities.server.RestfulServerRule;
import ca.uhn.test.concurrency.PointcutLatch;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Patient;
@ -28,13 +30,17 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.awaitility.Awaitility.await;
import static org.awaitility.Awaitility.waitAtMost;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.matchesPattern;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
@ -63,11 +69,18 @@ public class ResponseSizeCapturingInterceptorTest {
}
@Test
public void testReadResource() {
public void testReadResource() throws InterruptedException {
PointcutLatch createLatch = new PointcutLatch(Pointcut.SERVER_PROCESSING_COMPLETED);
createLatch.setExpectedCount(1);
ourServerRule.getRestfulServer().getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_PROCESSING_COMPLETED, createLatch);
Patient resource = new Patient();
resource.setActive(true);
IIdType id = ourServerRule.getFhirClient().create().resource(resource).execute().getId().toUnqualifiedVersionless();
createLatch.awaitExpected();
ourServerRule.getRestfulServer().getInterceptorService().unregisterInterceptor(createLatch);
myInterceptor.registerConsumer(myConsumer);
List<String> stacks = Collections.synchronizedList(new ArrayList<>());
@ -84,8 +97,8 @@ public class ResponseSizeCapturingInterceptorTest {
resource = ourServerRule.getFhirClient().read().resource(Patient.class).withId(id).execute();
assertEquals(true, resource.getActive());
await().until(()->stacks.size() > 0);
await().until(()->stacks.stream().collect(Collectors.joining("\n")), not(matchesPattern(Pattern.compile(".*INVOCATION.*INVOCATION.*", Pattern.MULTILINE | Pattern.DOTALL))));
verify(myConsumer, timeout(Duration.ofSeconds(10)).times(1)).accept(myResultCaptor.capture());
assertEquals(100, myResultCaptor.getValue().getWrittenChars());
}

View File

@ -72,7 +72,7 @@ public class ResourceValidatorDstu2_1Test {
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());
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: [element=\"birthDate\"] Invalid attribute value \"2000-15-31\": Invalid date/time format: \"2000-15-31\"", e.getMessage());
}
}

View File

@ -98,7 +98,7 @@ public class ResourceValidatorDstu3Test {
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());
assertEquals("DataFormatException at [[row,col {unknown-source}]: [2,4]]: [element=\"birthDate\"] Invalid attribute value \"2000-15-31\": Invalid date/time format: \"2000-15-31\"", e.getMessage());
}
}