yahoo / elide

Elide is a Java library that lets you stand up a GraphQL/JSON-API web service with minimal effort.
https://elide.io
Other
1k stars 229 forks source link

InvalidValueException doesn't help resolving issues #2845

Open eedijs opened 1 year ago

eedijs commented 1 year ago

Expected Behaviour

When a field receives an invalid value, return the error for it:

Java 8 date/time type `java.time.Instant` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
 at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.example.package.AttributeClass["thisWillFail"])

Current Behavior

Currently, the error message is very "cryptic" - it's impossible to tell what went wrong, which field was sent an invalid value. I will give you an example in Steps to Reproduce, but here's is the response I receive when a single or multiple values are invalid:

InvalidValueException: Invalid value: {field1=value1, field2=value2, field3=value3, ...} // and so on

Possible Solution

An improvement, in my opinion, would be to either show an error for the first field that fails and has an error, or even better - gather all fields with errors and list them in the response.

Steps to Reproduce (for bugs)

Here is a simple example class:

@Include
@Entity
@Table(name = "main_class")
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@SequenceGenerator(name = "seq", sequenceName = "main_class_entity")
public class MainClass {

    private String field1;

    private String field2;

    @Embedded
    private AttributeClass attributes;

    @Getter
    @Setter
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Embeddable
    public static class AttributeClass {

        private String field1;
        private String field2;
        private String field3;
        private Instant thisWillFail;
        private String field4;
        private String field5;
        private String field6;
    }
}

So if I send a request like this:

{
    "data": {
        "type": "mainclass",
        "attributes": {
            "field1": "string1",
            "field2": "string2",
            "attributes": {
                "field1": "string1",
                "field2": "string2",
                "field3": "string3",
                "thisWillFail": "invalid_instant_value",
                "field4": "string4",
                "field5": "string5",
                "field6": "string6"
            }
        }
    }
}

The error response for the invalid field will be:

InvalidValueException: Invalid value: {field1=string1, field2=string2, field3=string3, thisWillFail=invalid_instant_value, field4=string4, field5=string5, field6=string6}

Your Environment

eedijs commented 1 year ago

Related to this issue - the FromMapConverter creates a new instance of ObjectMapper which has no registered modules, therefore I cannot convert an Instant value from incoming json:

public class FromMapConverter implements Converter {
    private static final ObjectMapper MAPPER = new ObjectMapper();

Do you have any plans for either using Spring Boot ObjectMapper beans or making your instances of ObjectMapper as configurable beans?

aklish commented 1 year ago

Can you add the Elide annotations to the example model or provide a working example based on Elide-spring-boot of the issue?

eedijs commented 1 year ago

Apologies for the late reply, but I've updated my main comment's example with the same annotations that I use.

eedijs commented 1 year ago

Hi @aklish, have you had the time to look at this issue? For now, I've implemented my custom ErrorMapper, which changes the InvalidValueException a bit and returns the same exception with a changed message (the verbose message contains the actual error):

    private InvalidValueException getchangedInvalidValueException(Exception exception) {
        InvalidValueException invalidValueException = (InvalidValueException) exception;

        if (StringUtils.isEmpty(invalidValueException.getVerboseMessage())) {
            return invalidValueException;
        }

        return new InvalidValueException(invalidValueException.getVerboseMessage().replace("Invalid value: ", ""), invalidValueException.getVerboseMessage());
    }