aleyandraaa
aleyandraaa

Reputation: 113

How do I write a custom sorter to sort my springdoc swagger tags by name in the UI?

I am using springdoc-openapi with the latest version (1.3.0). Now I would like sort my tags in the UI by "name" property.

I know about the "springdoc.swagger-ui.tagsSorter" configuration and that I can use a custom sorter function. But I cannot find examples how the function should look like.

I tried the following which does not seem to work:

springdoc.swagger-ui.tagsSorter=(a, b) => a.get("name").localeCompare(b.get("name"))

Upvotes: 11

Views: 22051

Answers (4)

zakmck
zakmck

Reputation: 3014

The following must be useful. I want to list tags in a custom order, based on the importance and logical grouping of the tags. So I use this OpenApiCustomiser:

@Configuration
public class SwaggerConfig
{
    private static final String[] TAG_FRAGS_IN_ORDER = {
        "Important Service",
        "Another Service",
        "Third Service"
    };
    
// I use tag fragments, in order to avoid to rewrite
// the tags twice
    private static int getTagPriority ( Tag tag )
    {
        for ( int i = 0; i < TAG_FRAGS_IN_ORDER.length; i++ )
        {
            var frag = TAG_FRAGS_IN_ORDER [ i ];
            if ( StringUtils.contains ( tag.getName (), frag ) ) return i;
        }
        // All the others will go in lexicographical order  
        return Integer.MAX_VALUE;
    }
    
    
    /**
     * The /v1 endpoints
     */
    @Bean
    public GroupedOpenApi version1Group ()
    {
        return GroupedOpenApi.builder ()
        .group ( "v1" )
        .pathsToMatch ( "/v1/**" )
        .addOpenApiCustomizer ( oapi ->
        {
            List<Tag> tags = oapi.getTags ();

// Sort based on priorities 
            tags.sort ( 
                Comparator.comparingInt ( SwaggerConfig::getTagPriority )
                // Last resort for tags of least/same priority: lexicographically
                .thenComparing ( Tag::getName, Comparator.naturalOrder () )
            );

            oapi.setTags ( tags );
        })
        .build ();
    }

    /**
     * the /test endpoints
     */
    @Bean
    public GroupedOpenApi testGroup ()
    {
    ...
    }

    /**
     * Common header
     */
    @Bean
    public OpenAPI apiInfo ()
    {
        return new OpenAPI ().info ( 
            new Info ()
            .title ( "Sample API" )
            .description ( "..." )
        );      
    }


}

I use the trick of searching fragments in the tag names, because they have quite long names and I don't want to maintain a list separately. This is an approach that is simpler than other more sophisticated ones.

For instance, an alternative could be defining a custom annotation, eg, @TagOrder, use it in the controller classed, then search for @Tag and @TagOrder in the classpath and finally sort tags using the two annotations.

Upvotes: 0

Pari Rajarathinam
Pari Rajarathinam

Reputation: 76

For sorting schemas , paths and tags in OpenApi.

    @Bean
    public OpenApiCustomiser openApiCustomiser() {
        return openApi -> {
            Map<String, Schema> schemas = openApi.getComponents().getSchemas();
            openApi.getComponents().setSchemas(new TreeMap<>(schemas));
        };
    }

    @Bean
    public OpenApiCustomiser sortPathsAndTagsAlphabetically() {
        return openApi -> {
            Map<String, PathItem> paths = openApi.getPaths();
            Paths sortedPaths = new Paths();
            TreeMap<String, PathItem> sortedTree = new TreeMap<String, PathItem>(paths);

            Set<Map.Entry<String, PathItem>> pathItems = sortedTree.entrySet();
            Map<String, Map.Entry<String, PathItem>> distinctTagMap = new TreeMap<String, Map.Entry<String, PathItem>>();
            for ( Map.Entry<String, PathItem> entry:pathItems) {
                PathItem pathItem = entry.getValue();
                Operation getOp = pathItem.getGet();
                if(getOp != null) {
                    String tag = getOp.getTags().get(0);
                    if (!distinctTagMap.containsKey(tag)) {
                        distinctTagMap.put(tag, entry);
                    }
                }
                Operation postOp = pathItem.getPost();
                if(postOp != null){
                    String tag1 = postOp.getTags().get(0);
                    if(!distinctTagMap.containsKey(tag1)){
                        distinctTagMap.put(tag1,entry);
                    }
                }

                Operation putOp = pathItem.getPut();
                if(putOp != null) {
                    String tag2 = putOp.getTags().get(0);
                    if (!distinctTagMap.containsKey(tag2)) {
                        distinctTagMap.put(tag2, entry);
                    }
                }
            }

            LinkedHashMap<String, PathItem> customOrderMap = new LinkedHashMap<String, PathItem>();
            for (Map.Entry<String, PathItem> entry: distinctTagMap.values()) {
                customOrderMap.put(entry.getKey(), entry.getValue());
            }
            for(Map.Entry<String, PathItem> entry : sortedTree.entrySet()) {
                customOrderMap.putIfAbsent(entry.getKey(), entry.getValue());
            }
            sortedPaths.putAll(customOrderMap);
            openApi.setPaths(sortedPaths);

        };
    }

Upvotes: 3

ADJ
ADJ

Reputation: 1300

With reference from @brianbro's answer, as suggested at https://springdoc.org/faq.html#how-can-i-sort-endpoints-alphabetically

I added

@Tag(name="1. Admin endpoints")

@Tag(name = "2. Everyone's enpoints!")

and below prop to application.yml :

springdoc.swagger-ui.tagsSorter=alpha

And can see them sorted according to numbering on my swagger UI.

Upvotes: 20

brianbro
brianbro

Reputation: 4789

By default, you can sort tags alphabetically:

You can have control on the tags order, using OpenApiCustomiser and define your own Comparator:

@Bean
public OpenApiCustomiser sortTagsAlphabetically() {
    return openApi -> openApi.setTags(openApi.getTags()
            .stream()
            .sorted(Comparator.comparing(tag -> StringUtils.stripAccents(tag.getName())))
            .collect(Collectors.toList()));
}

Upvotes: 13

Related Questions