OpenAPITools / OpenAPITools/openapi-generator

[REQ][jaxrs-spec] Generate typesafe responses from contract

Open
#7,099 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Enhancement: Feature
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Is your feature request related to a problem? Please describe.

I want my Java compiler to be able to typecheck that I can only return responses that are valid according to the contract. This is a feature of the RAML code generator I was using before, but is not available with OpenAPI/OpenAPI-Generator.

Assume I have an operation defined with the following responses in OpenAPI 3:

      responses:
        '200':
          description: successful operation
          content:
            application/xml:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Pet'
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Pet'
        '202':
          description: accepted for later processing
          content:
            application/json:
              schema:
                type: string
        '204':
          description: successful operation, but nothing found
        '400':
          description: Invalid parameters

When I generate my Java server code (using "interfaceOnly") with "jaxrs-spec", I have the option to generate with return types, resulting in this Java method for the operation above:

    @GET
    @Path("/findByTags")
    @Produces({ "application/xml", "application/json" })
    List<Pet> findPetsByTags(@QueryParam("tags") @NotNull    List<String> tags);

This is type safe, because of the List<Pet> return type, but it does not allow me to select the status code or content type I want to return in accordance with my contract.

The other option is to use the "returnResponse" option, resulting in this code:

    @GET
    @Path("/findByTags")
    @Produces({ "application/xml", "application/json" })
    Response findPetsByTags(@QueryParam("tags") @NotNull    List<String> tags);

This gives me the flexibility to choose whatever status codes, content types or data to return. But it is also of no help at all to ensure that I actually return responses that are in accordance with my contract. This is a nightmare to keep correct when evolving the application, changing the contract or even just implementing the method for the first time.

Describe the solution you'd like

The RAML code generator I used before solved this problem by generating typesafe response classes derived from the contract. It generated one helper method for each combination of status code and content type in the contract, so that the application is both type safe and able to select the desired response properties. The generated code, including a response class with the required helper methods, would look something like this:

    @GET
    @Path("/findByTags")
    @Produces({ "application/xml", "application/json" })
    FindPetsByTagsResponse findPetsByTags(@QueryParam("tags") @NotNull    List<String> tags);
    
    public static class FindPetsByTagsResponse extends org.openapitools.api.support.ResponseWrapper {
        private FindPetsByTagsResponse(Response delegate) {
            super(delegate);
        }
        public static FindPetsByTagsResponse with200ApplicationXml(List<Pet> entity) {
            return new FindPetsByTagsResponse(Response.status(200).header("Content-Type", "application/xml").entity(entity).build());
        }
        public static FindPetsByTagsResponse with200ApplicationJson(List<Pet> entity) {
            return new FindPetsByTagsResponse(Response.status(200).header("Content-Type", "application/json").entity(entity).build());
        }
        public static FindPetsByTagsResponse with202ApplicationJson(String entity) {
            return new FindPetsByTagsResponse(Response.status(202).header("Content-Type", "application/json").entity(entity).build());
        }
        public static FindPetsByTagsResponse with204() {
            return new FindPetsByTagsResponse(Response.status(204).build());
        }
        public static FindPetsByTagsResponse with400() {
            return new FindPetsByTagsResponse(Response.status(400).build());
        }
        public static FindPetsByTagsResponse withCustomResponse(Response response) {
            return new FindPetsByTagsResponse(response);
        }
    }

I already implemented the suggested scheme in a fork, but I would like to contribute this functionality as I think it adds a lot of value to the generated code. Also, this feature missing is a blocker for adopting OpenAPI-Generator in the company I work.

The withCustomResponse method is for the rare cases that one has to define further properties on the response that would otherwise not be possible.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the jaxrs-spec generator and its existing interfaceOnly, return types, and returnResponse options. Compare their generated method signatures with the proposed response wrapper, then determine how status-code and content-type combinations should be generated; done means generated Java methods provide contract-valid, typesafe response helpers.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend-api-design, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.