18

I have configured my spring project using springfox 2.0. I am able to generate the open api spec with it.

 "paths": {
    "/test/testinfo": {
      "post": {
        "tags": [
          "test-controller"
        ],
        "summary": "getTestInfo",
        "operationId": "getTestInfoInfoUsingGET",
        "consumes": [
          "application/json"
        ],
        "produces": [
          "application/json"
        ]

As you can see the value of operationId is of format

[java_method_name_here]Using[HTTP_verb_here]

ex. getPetsUsingGET

This operationId is used while generating clients using swagger-codegen. Does anyone know how to customize it? I know this can be done per api using @ApiOperation but is there a more generic way to define this format for all apis?

Ganesh
  • 5,977
  • 5
  • 19
  • 25

6 Answers6

10

We can also use this approach of Nickname which overrides default operationId.

@ApiOperation(value = "", nickname = "getMeAllThePetsPlease")
@RequestMapping(value = "/pets", method = RequestMethod.GET)
public Model getAllThePets() {
    ...
}

so we will be overriding operationId: getAllThePetsByGet with getMeAllThePetsPlease.

Note: Nickname will overrides the operationId so this can be customized accordingly.

AVINASH M
  • 487
  • 3
  • 7
  • 19
  • source: https://springfox.github.io/springfox/docs/snapshot/#configuring-the-output-of-operationid-in-a-swagger-2-0-spec – WeGa Aug 20 '21 at 11:40
7

You can create your own plugin to do it. Here is an example of how we do it in springfox, using the same plugin technique.

@Component
@Order(YOUR_PLUGIN_ORDER) // > Ordered.HIGHEST_PRECEDENCE + 1000
public class OperationNicknameIntoUniqueIdReader implements OperationBuilderPlugin {
  @Override
  public void apply(OperationContext context) {

    //Create your own transformation to format the name in the way 
    //that you prefer
    String operationNameStem = transformName(context.getName());
    //Update the method name stem that is used to generate a unique id
    context.operationBuilder().codegenMethodNameStem(operationNameStem);
  }
  ...
}

Note: that whatever stem you come up with, springfox will ensure that it is unique across all the APIs. So if you had a duplicate named method, it will start the numbering scheme at the end of your unique name. e.g. if getCustomer was not unique, it will generate a unique id getCustomer_1 etc.

Dilip Krishnan
  • 5,417
  • 3
  • 37
  • 53
5

A shorter & clearer version of code based on @bdzzaid:

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public class SwaggerIncludeMissingNicknameIntoUniqueIdReader implements OperationBuilderPlugin {

    @Override
    public void apply(OperationContext context) {
        Optional<ApiOperation> methodAnnotation = context.findControllerAnnotation(ApiOperation.class);
        Operation operationBuilder = context.operationBuilder().build();

        String uniqueId = operationBuilder.getUniqueId().replaceAll("Using(GET|POST|PUT|DELETE)", "");

        // If nickname exists, populate the value of nickname annotation into uniqueId
        String fillId = methodAnnotation.transform(ApiOperation::nickname).or(uniqueId);
        context.operationBuilder().uniqueId(fillId);
        context.operationBuilder().codegenMethodNameStem(fillId);
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }
}
ch271828n
  • 15,854
  • 5
  • 53
  • 88
  • just a small note: `operationBuilder.getUniqueId();` also triggers `CachingOperationNameGenerator : Generating unique operation named xyz` - which you might not want when f.i. just working with the nickname of the operation – electrobabe Jul 05 '21 at 20:11
3

The simplest option would be implementing your own name generator, extending Springfox's one, like so:

public class SwaggerOperationIdTrimmer implements OperationNameGenerator {

private static final String DEFAULT_SPRINGFOX_PATTERN_REGEX = "Using(GET|POST|PUT|DELETE)(_[0-9])?";

@Override
public String startingWith(String operationId) {
    //Trimming of default names generated by Springfox, eliminating the extra appended information
    return operationId.replaceAll(DEFAULT_SPRINGFOX_PATTERN_REGEX, "");
}

}

The regex is key here :)

The only thing left would be instantiating it as a bean in your configuration file, adding it as primary to override the default CachingOperationNameGenerator of Springfox:

@Bean
@Primary
public SwaggerOperationIdTrimmer customOperationNameGenerator() {
    return new SwaggerOperationIdTrimmer();
}
Miguel Ruiz
  • 408
  • 1
  • 6
  • 13
0

Here a complete exemple to remove the "Using"+VERB :

It's work with :

  • Spring version 5.1.9.RELEASE
  • Springfox 2.9.2
  • Swagger version 1.5.23
import com.google.common.base.Optional;
import io.swagger.annotations.ApiOperation;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.service.Operation;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public class IncludeMissingNicknameIntoUniqueIdReader implements OperationBuilderPlugin
{
    @Override
    public void apply(OperationContext context)
    {
        Optional<ApiOperation> methodAnnotation = context.findControllerAnnotation(ApiOperation.class);
        Operation operationBuilder = context.operationBuilder().build();
        String uniqueId = operationBuilder.getUniqueId().replaceAll("Using(GET|POST|PUT|DELETE)", "");
        if (methodAnnotation.isPresent())
        {
            ApiOperation operation = methodAnnotation.get();
            if (MiscUtils.isNotEmpty(operation.nickname()))
            {
                // Populate the value of nickname annotation into uniqueId
                context.operationBuilder().uniqueId(operation.nickname());
                context.operationBuilder().codegenMethodNameStem(operation.nickname());
            }
            else
            {
                context.operationBuilder().uniqueId(uniqueId);
                context.operationBuilder().codegenMethodNameStem(uniqueId);
            }
        }
        else
        {
            context.operationBuilder().uniqueId(uniqueId);
            context.operationBuilder().codegenMethodNameStem(uniqueId);
        }
    }

    @Override
    public boolean supports(DocumentationType delimiter)
    {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }
}
bdzzaid
  • 838
  • 8
  • 15
0

This does not require additional dependency and Is also easy to customize naming. (Improvement of here)

app/api/src/main/java/com/observatory/api/config/SwaggerIncludeMissingNicknameIntoUniqueIdReader.java


package com.observatory.api.config;

import com.google.common.base.Optional;
import io.swagger.annotations.ApiOperation;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.service.Operation;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

import java.util.Locale;

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public class SwaggerIncludeMissingNicknameIntoUniqueIdReader implements OperationBuilderPlugin {

    @Override
    public void apply(OperationContext context) {
        Optional<ApiOperation> methodAnnotation = context.findControllerAnnotation(ApiOperation.class);
        Operation operationBuilder = context.operationBuilder().build();

        String uniqueId = operationBuilder.getUniqueId();
        if(operationBuilder.getTags().stream().findFirst().get().isEmpty())
            throw new RuntimeException("operationBuilder.getTags().stream().findFirst()");
        uniqueId = uniqueId.substring(0,1).toUpperCase(Locale.ROOT)+ uniqueId.substring(1);
        uniqueId = uniqueId.replaceAll("[_].+","");
        String tag = operationBuilder.getTags().stream().findFirst().get();
        tag = tag.replace("-controller","s");
        int index = tag.indexOf("-");
        while (index >= 0) {
            tag = tag.substring(0,index) + tag.substring(index+1,index+2).toUpperCase(Locale.ROOT)+ tag.substring(index+2);
            index = tag.indexOf("-");
        }
        uniqueId = tag + uniqueId;

        // If nickname exists, populate the value of nickname annotation into uniqueId
        String fillId = methodAnnotation.transform(ApiOperation::nickname).or(uniqueId);
        context.operationBuilder().uniqueId(fillId);
        context.operationBuilder().codegenMethodNameStem(fillId);
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }
}
M at
  • 1,020
  • 1
  • 12
  • 26