diff --git a/server/src/com/mirth/connect/server/servlets/SwaggerServlet.java b/server/src/com/mirth/connect/server/servlets/SwaggerServlet.java index 5ac2d1186..0b51ff26d 100644 --- a/server/src/com/mirth/connect/server/servlets/SwaggerServlet.java +++ b/server/src/com/mirth/connect/server/servlets/SwaggerServlet.java @@ -10,16 +10,25 @@ package com.mirth.connect.server.servlets; import com.mirth.connect.client.core.BrandingConstants; + +import io.swagger.v3.core.converter.ModelConverters; import io.swagger.v3.jaxrs2.integration.ServletOpenApiContextBuilder; import io.swagger.v3.oas.integration.OpenApiConfigurationException; import io.swagger.v3.oas.integration.SwaggerConfiguration; +import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.media.ComposedSchema; +import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.servers.Server; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.stream.Collectors; import javax.servlet.ServletConfig; @@ -28,8 +37,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.reflections.Reflections; +import org.reflections.scanners.SubTypesScanner; +import org.reflections.util.ClasspathHelper; +import org.reflections.util.ConfigurationBuilder; import com.mirth.connect.client.core.Version; +import com.mirth.connect.donkey.model.channel.ConnectorProperties; public class SwaggerServlet extends HttpServlet { @@ -66,6 +80,7 @@ public void init(ServletConfig config) throws ServletException { .version(apiVersion.toString()); oas.info(info); + addConnectorPropertiesSchemas(oas); SwaggerConfiguration oasConfig = new SwaggerConfiguration() .openAPI(oas) .resourceClasses(resourceClasses.stream().map(Class::getName).collect(Collectors.toSet())); @@ -81,4 +96,78 @@ public void init(ServletConfig config) throws ServletException { throw new ServletException(e.getMessage(), e); } } + + private void addConnectorPropertiesSchemas(OpenAPI openApi) { + Components components = openApi.getComponents(); + if (components == null) { + components = new Components(); + openApi.setComponents(components); + } + + Map schemas = components.getSchemas(); + if (schemas == null) { + schemas = new java.util.LinkedHashMap<>(); + components.setSchemas(schemas); + } + + SortedSet> subtypes = findConnectorPropertiesSubtypes(); + if (subtypes.isEmpty()) { + return; + } + + ComposedSchema connectorPropertiesSchema = new ComposedSchema(); + Schema existingSchema = schemas.get("ConnectorProperties"); + if (existingSchema instanceof ComposedSchema) { + connectorPropertiesSchema = (ComposedSchema) existingSchema; + } + connectorPropertiesSchema.setOneOf(new ArrayList<>()); + + for (Class subtype : subtypes) { + addSubtypeSchema(schemas, connectorPropertiesSchema, subtype); + } + + schemas.put("ConnectorProperties", connectorPropertiesSchema); + } + + private SortedSet> findConnectorPropertiesSubtypes() { + Reflections reflections = new Reflections(new ConfigurationBuilder() + .addUrls(ClasspathHelper.forClassLoader()) + .addScanners(new SubTypesScanner(false))); + + SortedSet> subtypes = new TreeSet<>( + Comparator.comparing(Class::getName)); + for (Class subtype : reflections.getSubTypesOf(ConnectorProperties.class)) { + if (!subtype.isInterface() && !java.lang.reflect.Modifier.isAbstract(subtype.getModifiers())) { + subtypes.add(subtype); + } + } + return subtypes; + } + + private void addSubtypeSchema(Map schemas, ComposedSchema connectorPropertiesSchema, Class subtype) { + Map subtypeSchemas = ModelConverters.getInstance().readAll(subtype); + if (subtypeSchemas != null) { + subtypeSchemas.forEach(schemas::putIfAbsent); + } + + String schemaName = resolveSchemaName(subtypeSchemas, subtype); + if (schemaName != null) { + Schema refSchema = new Schema<>(); + refSchema.set$ref("#/components/schemas/" + schemaName); + connectorPropertiesSchema.addOneOfItem(refSchema); + } + } + + private String resolveSchemaName(Map subtypeSchemas, Class subtype) { + if (subtypeSchemas == null || subtypeSchemas.isEmpty()) { + return null; + } + + String simpleName = subtype.getSimpleName(); + if (subtypeSchemas.containsKey(simpleName)) { + return simpleName; + } + + return subtypeSchemas.keySet().iterator().next(); + } } \ No newline at end of file