View Javadoc
1   package de.dlr.shepard.common.filters;
2   
3   import io.quarkus.smallrye.openapi.OpenApiFilter;
4   import java.util.LinkedHashMap;
5   import java.util.List;
6   import java.util.Map;
7   import java.util.stream.Collectors;
8   import org.eclipse.microprofile.openapi.OASFactory;
9   import org.eclipse.microprofile.openapi.OASFilter;
10  import org.eclipse.microprofile.openapi.models.OpenAPI;
11  import org.eclipse.microprofile.openapi.models.PathItem;
12  import org.eclipse.microprofile.openapi.models.Paths;
13  import org.eclipse.microprofile.openapi.models.media.Schema;
14  
15  @OpenApiFilter(OpenApiFilter.RunStage.BUILD)
16  public class ApiPathFilter implements OASFilter {
17  
18    @Override
19    public void filterOpenAPI(OpenAPI openAPI) {
20      fixOpenApiPaths(openAPI);
21      excludeExtraHealthEndpointsAndAdjustHealthzTag(openAPI);
22      sortPropertiesOfHealthCheckSchemas(openAPI);
23    }
24  
25    /**
26     * Remove the quarkus-injected '/shepard/api' path part from the OpenApi paths
27     * <p>
28     * The deployment goal is to have the /shepard/api as part of the base path,
29     * but not as part of the actual endpoint path. So this part is removed.
30     *
31     */
32    private void fixOpenApiPaths(OpenAPI openAPI) {
33      Paths newPaths = OASFactory.createPaths();
34  
35      Map<String, PathItem> paths = openAPI.getPaths().getPathItems();
36      for (var entry : paths.entrySet()) {
37        // modify i.e. /shepard/api/collections -> /collections
38        String modifiedPath = entry.getKey().replace("/shepard/api", "");
39        newPaths.addPathItem(modifiedPath, entry.getValue());
40      }
41      openAPI.setPaths(newPaths);
42    }
43  
44    /**
45     * Clean the health check endpoints in OpenAPI documentation.
46     */
47    private void excludeExtraHealthEndpointsAndAdjustHealthzTag(OpenAPI openAPI) {
48      Paths newPaths = OASFactory.createPaths();
49  
50      Map<String, PathItem> paths = openAPI.getPaths().getPathItems();
51  
52      newPaths.setPathItems(
53        paths
54          .entrySet()
55          .stream()
56          .filter(path -> !List.of("/healthz/ready", "/healthz/live", "/healthz/started").contains(path.getKey()))
57          .map(path -> {
58            if (
59              path.getValue().getGET() == null || !path.getValue().getGET().getTags().contains("MicroProfile Health")
60            ) return path;
61  
62            PathItem newPathItem = path.getValue();
63            newPathItem.getGET().setTags(List.of("healthz"));
64            newPathItem.getGET().setOperationId("getServerHealth");
65            return Map.entry(path.getKey(), newPathItem);
66          })
67          .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
68      );
69      openAPI.setPaths(newPaths);
70    }
71  
72    private void sortPropertiesOfHealthCheckSchemas(OpenAPI openAPI) {
73      Map<String, Schema> schemas = openAPI.getComponents().getSchemas();
74  
75      openAPI
76        .getComponents()
77        .setSchemas(
78          schemas
79            .entrySet()
80            .stream()
81            .map(schema -> {
82              if (!List.of("HealthCheck", "HealthResponse").contains(schema.getKey())) {
83                return schema;
84              }
85  
86              schema
87                .getValue()
88                .setProperties(
89                  schema
90                    .getValue()
91                    .getProperties()
92                    .entrySet()
93                    .stream()
94                    .sorted(Map.Entry.comparingByKey())
95                    .collect(
96                      Collectors.toMap(
97                        Map.Entry::getKey,
98                        Map.Entry::getValue,
99                        (oldValue, newValue) -> oldValue,
100                       LinkedHashMap::new
101                     )
102                   )
103               );
104             return schema;
105           })
106           .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
107       );
108   }
109 }