Skip to content

Incorrect Response using OpenAPI #473

Open
@melloware

Description

@melloware

Describe the bug
When mapping using OpenApi the HttpProblem definition does not match the binding.

For example:

@Provider
public class BundleRenderValidationExceptionMapper implements ExceptionMapper<BundleRenderValidationException> {
    @Override
    @APIResponse(
            responseCode = "400",
            description = "Validation of bundle failed",
            content = @Content(mediaType = "application/problem+json", schema = @Schema(implementation = HttpProblem.class))
    )
    public Response toResponse(BundleRenderValidationException exception) {
        return exception.getResponse();
    }
}

When reviewing the example document in OpenAPI:

Image

This is incorrect in multiple ways.

  1. statusCode is actually status when rendered by Jackson renderer.
  2. The headers and parameters get flattened.
  3. It does not have desciptions and examples of @Schema open api fields showing a downstream user and documenting it.
  4. It generates INCORRECT code for clients generating from OpenAPI like Typescript.

Example flat payload with MDC trace, span, parent.

{
  "status": 404,
  "title": "Not Found",
  "detail": "Logo image not found",
  "instance": "/api/v1/brand/logo",
  "traceId": "185a8b49591673349c3e7f691a1e069b",
  "spanId": "6008077d63144c9b",
  "parentId": "6008077d63144c9b"
}

This won't bind correctly to clients using our OpenAPI Spec.

working with @tmulle we have created one that documents well but we have to hand code any MDC variables like X-Request-Id which is not ideal.

/**
 * Represents an HTTP Problem Response according to RFC 7807. This class
 * encapsulates details about
 * an error that occurred during HTTP request processing.
 */
@Data
@Schema(description = "HTTP Problem Response according to RFC 7807")
public class HttpProblemResponse {

    /** A URI reference that identifies the problem type */
    @Schema(description = "A URI reference that identifies the problem type", example = "https://example.com/errors/validation")
    private URI type;

    /** A short, human-readable summary of the problem type */
    @Schema(description = "A short, human-readable summary of the problem type", example = "Not Found Error")
    private String title;

    /** The HTTP status code for this occurrence of the problem */
    @JsonProperty(value = "status")
    @Schema(description = "The HTTP status code for this occurrence of the problem", example = "400")
    private int statusCode;

    /** A human-readable explanation specific to this occurrence of the problem */
    @Schema(description = "A human-readable explanation specific to this occurrence of the problem", example = "Record not found")
    private String detail;

    /** A URI reference that identifies the specific occurrence of the problem */
    @Schema(description = "A URI reference that identifies the specific occurrence of the problem", example = "https://api.example.com/errors/123")
    private URI instance;

    /** Additional parameters providing more details about the problem */
    @Schema(description = "Additional parameters providing more details about the problem", example = "{\"timestamp\":\"2024-03-20T10:00:00Z\",\"traceId\":\"550e8400-e29b-41d4-a716-446655440000\"}")
    private Map<String, Object> parameters;

    /** HTTP headers associated with the problem response */
    @Schema(description = "HTTP headers associated with the problem response", example = "{\"Content-Type\":\"application/problem+json\",\"X-Request-ID\":\"req-123\"}")
    private Map<String, Object> headers;

    /** List of validation violations that occurred */
    @Schema(description = "List of validation constraint violations that occurred")
    private List<Violation> violations;

    /**
     * Represents a validation violation within the HTTP Problem Response. Contains
     * details about a
     * specific validation error.
     */
    @Data
    @Schema(description = "Validation constraint violation details")
    public static class Violation {
        /** The field that failed validation */
        @Schema(description = "The field that failed validation", example = "email")
        private String field;

        /** Location where the validation error occurred */
        @Schema(description = "Location where the validation error occurred", example = "body")
        private String in;

        /** Description of the validation error */
        @Schema(description = "Description of the validation error", example = "Invalid email format")
        private String message;
    }
}

This generates an OpenAPI with examples and documentation like this...

{
  "type": "https://example.com/errors/validation",
  "title": "Not Found Error",
  "status": 400,
  "detail": "Record not found",
  "instance": "https://api.example.com/errors/123",
  "parameters": {
    "timestamp": "2024-03-20T10:00:00Z",
    "traceId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "headers": {
    "Content-Type": "application/problem+json",
    "X-Request-ID": "req-123"
  },
  "violations": [
    {
      "field": "email",
      "in": "body",
      "message": "Invalid email format"
    }
  ]
}

Image

I am trying to think of a clever way of updating HttpProblem without breaking backwards compatibility but this would be a huge win for anyone using OpenAPI and giving their spec out to customers to generate code.

cc @tmulle cc @turing85 cc @cvgaviao

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions