Skip to content

Commit

Permalink
Sort endpoints and schemas in swagger UI (#7649)
Browse files Browse the repository at this point in the history
* Sort endpoints and schemas

* Move tags order in RestApiConstants
  • Loading branch information
zilm13 authored Nov 6, 2023
1 parent 0178be5 commit a148b8b
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_SERVICE_UNAVAILABLE;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_UNSUPPORTED_MEDIA_TYPE;

import java.util.List;

public class RestApiConstants {

public static final String SLOT = "slot";
Expand Down Expand Up @@ -56,6 +58,21 @@ public class RestApiConstants {
public static final String TAG_DEBUG = "Debug";
public static final String TAG_TEKU = "Teku";

// Preferred tags order in Swagger UI
public static final List<String> PREFERRED_DISPLAY_TAGS_ORDER =
List.of(
TAG_BEACON,
TAG_VALIDATOR_REQUIRED,
TAG_VALIDATOR,
TAG_BUILDER,
TAG_REWARDS,
TAG_EVENTS,
TAG_CONFIG,
TAG_NODE,
TAG_TEKU,
TAG_DEBUG,
TAG_EXPERIMENTAL);

// Use "" + instead of Integer.toString so they are constants and can be used in annotations
public static final String RES_OK = "" + SC_OK;
public static final String RES_ACCEPTED = "" + SC_ACCEPTED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,23 @@
package tech.pegasys.teku.infrastructure.restapi.openapi;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.toSet;
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.PREFERRED_DISPLAY_TAGS_ORDER;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.javalin.http.HandlerType;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import tech.pegasys.teku.infrastructure.json.types.OpenApiTypeDefinition;
import tech.pegasys.teku.infrastructure.restapi.endpoints.EndpointMetadata;
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiEndpoint;
Expand Down Expand Up @@ -115,7 +120,9 @@ private void writeInfo(final JsonGenerator gen) throws IOException {

private void writePaths(final JsonGenerator gen) throws IOException {
gen.writeObjectFieldStart("paths");
for (final Entry<String, Map<HandlerType, RestApiEndpoint>> pathEntry : endpoints.entrySet()) {
final List<Entry<String, Map<HandlerType, RestApiEndpoint>>> sortedEndpoints =
sortRestApiEndpoints(endpoints);
for (final Entry<String, Map<HandlerType, RestApiEndpoint>> pathEntry : sortedEndpoints) {
gen.writeObjectFieldStart(pathEntry.getKey());
for (RestApiEndpoint endpoint : pathEntry.getValue().values()) {
final EndpointMetadata metadata = endpoint.getMetadata();
Expand All @@ -126,6 +133,39 @@ private void writePaths(final JsonGenerator gen) throws IOException {
gen.writeEndObject();
}

@SuppressWarnings("unchecked")
private List<Entry<String, Map<HandlerType, RestApiEndpoint>>> sortRestApiEndpoints(
final Map<String, Map<HandlerType, RestApiEndpoint>> endpoints) {
final List<Entry<String, Map<HandlerType, RestApiEndpoint>>> entries =
new ArrayList<>(endpoints.entrySet());

// fill desired order for tags
final Map<String, Integer> tagOrder = new HashMap<>();
IntStream.range(0, PREFERRED_DISPLAY_TAGS_ORDER.size())
.forEach(i -> tagOrder.put(PREFERRED_DISPLAY_TAGS_ORDER.get(i), i));

// sort by tag, then by path, tag order is not guaranteed, endpoint could have several tags,
// first appearance of any tag defines its order on UI
final int endOrder = 999;
entries.sort(
Comparator.comparingInt(
entry ->
((Entry<String, Map<HandlerType, RestApiEndpoint>>) entry)
.getValue().values().stream()
.findFirst()
.map(
restApiEndpoint ->
restApiEndpoint.getMetadata().getTags().stream()
.mapToInt(tag -> tagOrder.getOrDefault(tag, endOrder))
.min()
.orElse(endOrder))
.orElse(endOrder))
.thenComparing(
entry -> ((Entry<String, Map<HandlerType, RestApiEndpoint>>) entry).getKey()));

return entries;
}

private void writeComponents(final JsonGenerator gen) throws IOException {
gen.writeObjectFieldStart("components");
writeSecuritySchemas(gen);
Expand All @@ -146,12 +186,16 @@ private void writeSecuritySchemas(final JsonGenerator gen) throws IOException {
}

private void writeSchemas(final JsonGenerator gen) throws IOException {
final Set<OpenApiTypeDefinition> typeDefinitions =
final List<OpenApiTypeDefinition> typeDefinitions =
endpoints.values().stream()
.flatMap(pathEndpoints -> pathEndpoints.values().stream())
.flatMap(endpoint -> endpoint.getMetadata().getReferencedTypeDefinitions().stream())
.filter(type -> type.getTypeName().isPresent())
.collect(toSet());
.distinct()
.collect(Collectors.toList());
// sort by name
typeDefinitions.sort(
Comparator.comparing(typeDefinition -> typeDefinition.getTypeName().orElseThrow()));
gen.writeObjectFieldStart("schemas");
for (OpenApiTypeDefinition type : typeDefinitions) {
gen.writeFieldName(type.getTypeName().orElseThrow());
Expand Down

0 comments on commit a148b8b

Please sign in to comment.