Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions server/src/com/mirth/connect/model/Connector.java
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove this file from the PR? I think you did not mean for the remaining two changes to still be in place.

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import com.mirth.connect.donkey.util.purge.PurgeUtil;
import com.thoughtworks.xstream.annotations.XStreamAlias;

import io.swagger.v3.oas.annotations.media.Schema;

/**
* A Connector represents a connection to either a source or destination. Each Connector has an
* associated Filter and Transformer. A connector is also of a specific Transport type (TCP, HTTP,
Expand All @@ -41,6 +43,7 @@ public enum Mode {
private Integer metaDataId;
private String name;
private ConnectorProperties properties;

private Transformer transformer;
private Transformer responseTransformer;
private Filter filter;
Expand Down
88 changes: 88 additions & 0 deletions server/src/com/mirth/connect/server/servlets/SwaggerServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,8 +37,12 @@

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.ConfigurationBuilder;

import com.mirth.connect.client.core.Version;
import com.mirth.connect.donkey.model.channel.ConnectorProperties;

public class SwaggerServlet extends HttpServlet {

Expand Down Expand Up @@ -66,6 +79,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()));
Expand All @@ -81,4 +95,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<String, Schema> schemas = components.getSchemas();
if (schemas == null) {
schemas = new java.util.LinkedHashMap<>();
components.setSchemas(schemas);
}

SortedSet<Class<? extends ConnectorProperties>> 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<? extends ConnectorProperties> subtype : subtypes) {
addSubtypeSchema(schemas, connectorPropertiesSchema, subtype);
}

schemas.put("ConnectorProperties", connectorPropertiesSchema);
}

private SortedSet<Class<? extends ConnectorProperties>> findConnectorPropertiesSubtypes() {
Reflections reflections = new Reflections(new ConfigurationBuilder()
.forPackages("com.mirth.connect")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Is it possible to scan the entire classpath?

I don't think you can assume that 3rd-party connectors will use this package name (or the connectors in this repo if we eventually migrate them to other packages.)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm pretty sure that's entirely possible. I included that line for testing purposes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

.addScanners(new SubTypesScanner(false)));

SortedSet<Class<? extends ConnectorProperties>> subtypes = new TreeSet<>(
Comparator.comparing(Class::getName));
for (Class<? extends ConnectorProperties> subtype : reflections.getSubTypesOf(ConnectorProperties.class)) {
if (!subtype.isInterface() && !java.lang.reflect.Modifier.isAbstract(subtype.getModifiers())) {
subtypes.add(subtype);
}
}
return subtypes;
}

private void addSubtypeSchema(Map<String, Schema> schemas, ComposedSchema connectorPropertiesSchema, Class<? extends ConnectorProperties> subtype) {
Map<String, Schema> 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<String, Schema> subtypeSchemas, Class<? extends ConnectorProperties> subtype) {
if (subtypeSchemas == null || subtypeSchemas.isEmpty()) {
return null;
}

String simpleName = subtype.getSimpleName();
if (subtypeSchemas.containsKey(simpleName)) {
return simpleName;
}

return subtypeSchemas.keySet().iterator().next();
}
}