Forum Discussion

julienQc's avatar
julienQc
Occasional Contributor
7 years ago
Solved

No OpenAPI visible endpoint for endpoint declared in interface with default implementation

I'm writing a generic system de create REST endpoints.
I'm using Jersey + Spring in my project but my problem is only related to OpenAPI .
I use Swagger 2 (OpenAPI 3.0)

I'm declaring all my endpoints and their documentation in interfaces, with a default implementation. So in my class I don't have to declare the methods which already have a default implementation.

Here is a sample of my code :

public interface SampleEndpoints {


@POST
@Path("/")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Created"),
@ApiResponse(responseCode = "400", description = "Bad Request"),
@ApiResponse(responseCode = "403", description = "Forbidden"),
@ApiResponse(responseCode = "404", description = "Not Found")})
default String create(Object param ) {
return "ok";
}

@GET
@Path("/{id}")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Ok"),
@ApiResponse(responseCode = "400", description = "Bad Request"),
@ApiResponse(responseCode = "403", description = "Forbidden"),
@ApiResponse(responseCode = "404", description = "Not Found")})
String find(String id);

}


The controller class

@Component
@Path("/cars")
public class CarsController implements SampleEndpoints {

public String find(String id) {
return "Yes I've found it";
}

}


As you can see I don't have to implement the create method because its default implementation is sufficient.


My problem is that the create method doesn't appear in the generated openapi.json (but the find method is there, with all the informations)

If instead of an interface I declare SampleEndpoints as a class, and make my CarsController inherit of SampleEndpoints, then the open Api documentation works.

So I'd like to know if there is a way to make it works with interface ?

I can't manage to find a solution, but using interface instead of inheritance is mandatory because my controller needs to implements multiple interfaces.

 

Thanks for all your ideas :)

 

 

  • richie 

    Provide payload as below

     

    resource=${#Project#RESOURCE}
    &client_id=${#Project#CLIENT_ID}
    &client_secret=${#Project#CLIENT_SECRET}
    &grant_type=client_crendentials

    You can define property file as show below, and create a file for each enrionment.

    dev.properties

    RESOURCE=<dev-resource>
    CLIENT_ID=<dev-client-id>
    CLIENT_SECRET=<dev-client-secret>

    And import the above file at project level, refer below documentation

    https://www.soapui.org/scripting-properties/working-with-properties.html

     

    So, you can similarly, use properties for END POINTS / other varying values per environment into property file and use them in the project as mentioned in the payload content.

     

    Now all you need is single project, common test cases. No separate test suites / test cases per environment any more, just use appropriate property file, import them into project before running the tests.

    If you are running the tests command line, even properties can be directly loaded runtime without even modifying the existing values in the project.

4 Replies

  • I am not able to reproduce the issue with the example provided, both find and create get correctly resolved; which swagger artifact and version are you using, and how is that configured? and whatever signficant detail.

    • julienQc's avatar
      julienQc
      Occasional Contributor

      Hi,

       

      Thanks for your answer.

      I've written a sample project to show the problem.

      Here is the repository.

      https://github.com/githubjul/test-swagger-with-interfaces

       

      The creation of this test allows me to understand exactly where is the problem, it's a little more tricky than I thought.

       

      First of all, you're absolutely right : there is no problem with default method in interfaces.

       

      My problem is a mix between default method in interface and inheritance.

      First interface :

       

       

      public interface OriginalEndpoint<C> {
      
          String originalEndpoint(C c);
      }

      Second interface :

       

       

       

      public interface SecondEndpoint<C> extends OriginalEndpoint<C> {
      
          @GET
          @Path("/{id}")
          @ApiResponses(value = {
                  @ApiResponse(responseCode = "200", description = "OK"),
                  @ApiResponse(responseCode = "400", description = "Bad Request"),
                  @ApiResponse(responseCode = "403", description = "Forbidden"),
                  @ApiResponse(responseCode = "404", description = "Not Found")})
          default String secondEnpoint(C param) {
              return "secondEnpoint-ok";
          }
      
          @Override
          @GET
          @Path("/original/{id}")
          @ApiResponses(value = {
                  @ApiResponse(responseCode = "200", description = "OK"),
                  @ApiResponse(responseCode = "400", description = "Bad Request"),
                  @ApiResponse(responseCode = "403", description = "Forbidden"),
                  @ApiResponse(responseCode = "404", description = "Not Found")})
          default String originalEndpoint(C param) {
              return "originalEndpoint-ok";
          }
      
      
      }

       

       

       

      In the swagger generated :

       

      • secondEnpoint ( Get on «/{id}) is present
      • originalEndpoint ( Get on  «/original/{id}») is not present

      The reason I used this inheritance in interfaces is because I have multiple default implementations for same «base» interfaces. One default implementation for Jersey, and another one for Spring MVC