OpenAPITools / OpenAPITools/openapi-generator

[BUG] [JAVA][SPRING] Interfaces don't support abstract methods

Open
#14,769 1 comment 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

OpenAPI generator should generate abstract methods in interfaces. For example, the following schema clearly shows that id, type, model, and name are shared between all schemas that inherit Vehicle

image

However, in the resulting code we end up with an interface with only one abstract method based on the discriminator.

public interface Vehicle extends Serializable {
    public String getType();
}

Desirably, this would result in an interface as follows:

public interface Vehicle extends Serializable {
    // Builder methods
    Vehicle id();
    Vehicle type(); 
    Vehicle model();
    Vehicle name();
    
    // Getters
    Integer getId();
    String getType();
    String getModel();
    String getName();
    
    // Setters
    void setId(Integer id);
    void setType(String type);
    void setModel(String model);
    void setName(String name);
}
openapi-generator version

6.4.0

OpenAPI declaration file content or url
components:
  schemas:
    Vehicle:
      type: object
      required:
        - id
        - type
      properties:
        id:
          type: integer
        type:
          type: string
        model:
          type: string
        name:
          type: string
      oneOf:
        - $ref: '#/components/schemas/Car'
        - $ref: '#/components/schemas/Plane'
      discriminator:
        propertyName: type

    Car:
      type: object
      properties:
        type:
          enum:
            - car
        has_4_wheel_drive:
          type: boolean

    Plane:
      type: object
      properties:
        type:
          enum:
            - plane
        has_reactor:
          type: boolean
        nb_passengers:
          type: integer
Generation Details

any spring generation will have the same results. this is default behavior.

Steps to reproduce

Run generator on the above yaml.

Related issues/PRs

None that I can find.

Suggest a fix

Currently, we are using vendor extensions and templates to work around this issue. We have created a template called x-implements-methods, which looks as follows:

x-implements-methods:
  - field: id
    type: Integer
  - field:  type
    type: String
  - field:  model
    type: String
  - field:  name
    type: String

and the oneof_interface.mustache now looks like this

{{>additionalOneOfTypeAnnotations}}
{{#withXml}}
{{>xmlAnnotation}}
{{/withXml}}
{{#discriminator}}
{{>typeInfoAnnotation}}
{{/discriminator}}
{{>generatedAnnotation}}
public interface {{classname}}{{#vendorExtensions.x-implements}}{{#-first}} extends {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} {
{{#discriminator}}
{{^vendorExtensions.x-jsontypeinfo-deduction}}
  public {{propertyType}} {{propertyGetter}}();
{{/vendorExtensions.x-jsontypeinfo-deduction}}
{{/discriminator}}

{{#vendorExtensions.x-implements-methods}}
  {{! begin feature: fluent setter methods}}
  {{classname}} {{field}}({{>nullableDataType}}{{#lambda.titlecase}}{{type}}{{/lambda.titlecase}} {{field}});
  {{! end feature: fluent setter methods}}

  {{! begin feature: getter and setter methods}}
  {{#lambda.titlecase}}{{type}}{{/lambda.titlecase}} {{>nullableDataType}}get{{#lambda.titlecase}}{{field}}{{/lambda.titlecase}}();
  
  void set{{#lambda.titlecase}}{{field}}{{/lambda.titlecase}}({{>nullableDataType}} {{#lambda.titlecase}}{{type}}{{/lambda.titlecase}} {{field}});
  {{! end feature: getter and setter}}
{{/vendorExtensions.x-implements-methods}}
}

We would like to see this functionality taken out of vendor extensions and added to the default behavior for when the interface is created. The beginning of this implementation is already there, since the generator is able to recognize the fields within the Vehicle schema and move them to the child objects. These fields should be saved and made accessible to the mustache templating engine directly from within the CodegenModel.java, perhaps as a field called interfaceVars.

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 supplied OpenAPI YAML and run any Spring generation to reproduce the Vehicle interface output. Read CodegenModel.java, especially the model fields involved in inherited properties, and the oneof_interface.mustache template. Done means generated interfaces expose the shared builder, getter, and setter methods without vendor extensions.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend-api-design, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.