From c9063a06a36fac80dca2f3834cdb8d94e99f5504 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Fri, 11 May 2018 10:32:42 +0100 Subject: [PATCH 01/20] Initial OpenAPI implementation. --- .../packager/microprofile-package/pom.xml | 12 + .../microprofile/openapi/pom.xml | 101 +++ .../openapi/impl/OpenApiService.java | 100 +++ .../impl/config/OpenApiConfiguration.java | 266 ++++++ .../openapi/impl/model/ComponentsImpl.java | 339 +++++++ .../openapi/impl/model/ExtensibleImpl.java | 62 ++ .../impl/model/ExternalDocumentationImpl.java | 54 ++ .../openapi/impl/model/OpenAPIImpl.java | 247 +++++ .../openapi/impl/model/OperationImpl.java | 263 ++++++ .../openapi/impl/model/PathItemImpl.java | 312 +++++++ .../openapi/impl/model/PathsImpl.java | 52 ++ .../impl/model/callbacks/CallbackImpl.java | 97 ++ .../impl/model/examples/ExampleImpl.java | 112 +++ .../impl/model/headers/HeaderImpl.java | 238 +++++ .../openapi/impl/model/info/ContactImpl.java | 74 ++ .../openapi/impl/model/info/InfoImpl.java | 139 +++ .../openapi/impl/model/info/LicenseImpl.java | 56 ++ .../openapi/impl/model/links/LinkImpl.java | 160 ++++ .../openapi/impl/model/media/ContentImpl.java | 50 ++ .../impl/model/media/DiscriminatorImpl.java | 51 ++ .../impl/model/media/EncodingImpl.java | 99 ++ .../impl/model/media/MediaTypeImpl.java | 96 ++ .../openapi/impl/model/media/SchemaImpl.java | 843 ++++++++++++++++++ .../openapi/impl/model/media/XMLImpl.java | 95 ++ .../impl/model/parameters/ParameterImpl.java | 323 +++++++ .../model/parameters/RequestBodyImpl.java | 112 +++ .../impl/model/responses/APIResponseImpl.java | 142 +++ .../model/responses/APIResponsesImpl.java | 34 + .../impl/model/security/OAuthFlowImpl.java | 100 +++ .../impl/model/security/OAuthFlowsImpl.java | 109 +++ .../impl/model/security/ScopesImpl.java | 36 + .../security/SecurityRequirementImpl.java | 44 + .../model/security/SecuritySchemeImpl.java | 206 +++++ .../impl/model/servers/ServerImpl.java | 83 ++ .../model/servers/ServerVariableImpl.java | 70 ++ .../model/servers/ServerVariablesImpl.java | 64 ++ .../openapi/impl/model/tags/TagImpl.java | 112 +++ .../openapi/impl/model/util/ModelUtils.java | 393 ++++++++ .../javax.servlet.ServletContainerInitializer | 1 + .../microprofile/pom.xml | 7 +- appserver/pom.xml | 1 + nucleus/packager/nucleus-jersey/pom.xml | 8 + nucleus/pom.xml | 13 +- 43 files changed, 5772 insertions(+), 4 deletions(-) create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/pom.xml create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExtensibleImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExternalDocumentationImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OperationImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathItemImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathsImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/callbacks/CallbackImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/ContactImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/InfoImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/LicenseImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/ContentImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/DiscriminatorImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/EncodingImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/MediaTypeImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/SchemaImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/XMLImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/ParameterImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/RequestBodyImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponseImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponsesImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowsImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/ScopesImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecurityRequirementImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecuritySchemeImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariableImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariablesImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/tags/TagImpl.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtils.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer diff --git a/appserver/packager/microprofile-package/pom.xml b/appserver/packager/microprofile-package/pom.xml index 244f73f61c9..de9fe5232b4 100644 --- a/appserver/packager/microprofile-package/pom.xml +++ b/appserver/packager/microprofile-package/pom.xml @@ -165,6 +165,18 @@ microprofile-metrics ${project.version} + + + + org.eclipse.microprofile.openapi + microprofile-openapi-api + ${microprofile-openapi.version} + + + fish.payara.microprofile.openapi + microprofile-openapi + ${project.version} + diff --git a/appserver/payara-appserver-modules/microprofile/openapi/pom.xml b/appserver/payara-appserver-modules/microprofile/openapi/pom.xml new file mode 100644 index 00000000000..3af1dce104e --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/pom.xml @@ -0,0 +1,101 @@ + + + + 4.0.0 + + + fish.payara.microprofile + microprofile + 5.182-SNAPSHOT + + + fish.payara.microprofile.openapi + microprofile-openapi + glassfish-jar + + Microprofile - OpenAPI + Implementation of MicroProfile OpenAPI + + + + org.eclipse.microprofile.config + microprofile-config-api + ${microprofile-config.version} + + + org.eclipse.microprofile.openapi + microprofile-openapi-api + ${microprofile-openapi.version} + + + org.glassfish.jersey.containers + jersey-container-servlet + ${jersey.version} + + + org.glassfish.main.web + web-glue + ${project.version} + + + org.glassfish.main.common + internal-api + ${project.version} + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + javax + javaee-api + 8.0 + + + junit + junit + + + diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java new file mode 100644 index 00000000000..997c69a1442 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java @@ -0,0 +1,100 @@ +package fish.payara.microprofile.openapi.impl; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.inject.Inject; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.glassfish.api.StartupRunLevel; +import org.glassfish.api.event.EventListener; +import org.glassfish.api.event.Events; +import org.glassfish.hk2.api.PostConstruct; +import org.glassfish.hk2.api.PreDestroy; +import org.glassfish.hk2.runlevel.RunLevel; +import org.glassfish.internal.api.Globals; +import org.glassfish.internal.data.ApplicationInfo; +import org.glassfish.internal.deployment.Deployment; +import org.glassfish.web.deployment.descriptor.WebBundleDescriptorImpl; +import org.jvnet.hk2.annotations.Service; + +import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; +import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; +import fish.payara.microprofile.openapi.impl.processor.AnnotationProcessor; +import fish.payara.microprofile.openapi.impl.processor.ApplicationProcessor; +import fish.payara.microprofile.openapi.impl.processor.BaseProcessor; +import fish.payara.microprofile.openapi.impl.processor.FileProcessor; +import fish.payara.microprofile.openapi.impl.processor.FilterProcessor; +import fish.payara.microprofile.openapi.impl.processor.ModelReaderProcessor; + +@Service(name = "microprofile-openapi-service") +@RunLevel(StartupRunLevel.VAL) +public class OpenApiService implements EventListener, PostConstruct, PreDestroy { + + private Map models; + + @Inject + private Events events; + + @Override + public void postConstruct() { + models = new LinkedHashMap<>(); + events.register(this); + } + + @Override + public void preDestroy() { + events.unregister(this); + } + + @Override + public void event(Event event) { + if (event.is(Deployment.APPLICATION_STARTED)) { + // Get the application information + ApplicationInfo appInfo = (ApplicationInfo) event.hook(); + + // Create all the relevant resources + OpenApiConfiguration appConfig = new OpenApiConfiguration(appInfo.getAppClassLoader()); + models.put(appInfo, createOpenApiDocument(appInfo.getAppClassLoader(), getContextRoot(appInfo), appConfig)); + } else if (event.is(Deployment.APPLICATION_UNLOADED)) { + ApplicationInfo appInfo = (ApplicationInfo) event.hook(); + models.remove(appInfo); + } + } + + private String getContextRoot(ApplicationInfo appInfo) { + return appInfo.getMetaData(WebBundleDescriptorImpl.class).getContextRoot(); + } + + /** + * Gets the document for the most recently deployed application. + */ + public OpenAPI getDocument() { + if (models.isEmpty()) { + return null; + } + ApplicationInfo lastInfo = null; + for (ApplicationInfo info : models.keySet()) + lastInfo = info; + return models.get(lastInfo); + } + + private OpenAPI createOpenApiDocument(ClassLoader appClassLoader, String contextRoot, OpenApiConfiguration config) { + OpenAPI document = new OpenAPIImpl(); + new ModelReaderProcessor().process(document, config); + new FileProcessor(appClassLoader).process(document, config); + new BaseProcessor(contextRoot).process(document, config); + new ApplicationProcessor(appClassLoader).process(document, config); + new AnnotationProcessor(appClassLoader).process(document, config); + new FilterProcessor().process(document, config); + return document; + } + + /** + * Retrieves an instance of this service from HK2. + */ + public static OpenApiService getInstance() { + return Globals.getStaticBaseServiceLocator().getService(OpenApiService.class); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java new file mode 100644 index 00000000000..350f64389a3 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java @@ -0,0 +1,266 @@ +package fish.payara.microprofile.openapi.impl.config; + +import static java.util.stream.Collectors.toSet; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.openapi.OASFilter; +import org.eclipse.microprofile.openapi.OASModelReader; + +public class OpenApiConfiguration { + + private static final Logger LOGGER = Logger.getLogger(OpenApiConfiguration.class.getName()); + + private static final String MODEL_READER_KEY = "mp.openapi.model.reader"; + private static final String FILTER_KEY = "mp.openapi.filter"; + private static final String SCAN_DISABLE_KEY = "mp.openapi.scan.disable"; + private static final String SCAN_PACKAGES_KEY = "mp.openapi.scan.packages"; + private static final String SCAN_CLASSES_KEY = "mp.openapi.scan.classes"; + private static final String SCAN_EXCLUDE_PACKAGES_KEY = "mp.openapi.scan.exclude.packages"; + private static final String SCAN_EXCLUDE_CLASSES_KEY = "mp.openapi.scan.exclude.classes"; + private static final String SERVERS_KEY = "mp.openapi.servers"; + private static final String PATH_PREFIX_KEY = "mp.openapi.path."; + private static final String OPERATION_PREFIX_KEY = "mp.openapi.operation."; + + private Class modelReader; + private Class filter; + private boolean scanDisable; + private List scanPackages = new ArrayList<>(); + private List> scanClasses = new ArrayList<>(); + private List excludePackages = new ArrayList<>(); + private List> excludeClasses = new ArrayList<>(); + private List servers = new ArrayList<>(); + private Map pathServerMap = new HashMap<>(); + private Map operationServerMap = new HashMap<>(); + + public OpenApiConfiguration(ClassLoader applicationClassLoader) { + // Find the correct configuration instance + if (applicationClassLoader == null) { + applicationClassLoader = getClass().getClassLoader(); + } + Config config = ConfigProvider.getConfig(applicationClassLoader); + + // Find each variable + this.modelReader = findModelReaderFromConfig(config, applicationClassLoader); + this.filter = findFilterFromConfig(config, applicationClassLoader); + this.scanDisable = findScanDisableFromConfig(config); + this.scanPackages = findScanPackagesFromConfig(config); + this.scanClasses = findScanClassesFromConfig(config, applicationClassLoader); + this.excludePackages = findExcludePackages(config); + this.excludeClasses = findExcludeClasses(config, applicationClassLoader); + this.servers = findServers(config); + this.pathServerMap = findPathServerMap(config); + this.operationServerMap = findOperationServerMap(config); + } + + public Class getModelReader() { + return modelReader; + } + + public Class getFilter() { + return filter; + } + + public boolean getScanDisable() { + return scanDisable; + } + + public List getScanPackages() { + return scanPackages; + } + + public List> getScanClasses() { + return scanClasses; + } + + public List getExcludePackages() { + return excludePackages; + } + + public List> getExcludeClasses() { + return excludeClasses; + } + + public List getServers() { + return servers; + } + + public Map getPathServerMap() { + return pathServerMap; + } + + public Map getOperationServerMap() { + return operationServerMap; + } + + public Set> getValidClasses(Set> classes) { + return classes.stream() + // If scan classes are specified, check that the class is in the list + .filter(clazz -> scanClasses.isEmpty() || scanClasses.contains(clazz)) + // If exclude classes are specified, check that the class is not the list + .filter(clazz -> excludeClasses.isEmpty() || !excludeClasses.contains(clazz)) + // If scan packages are specified, check that the class package starts with one in the list + .filter(clazz -> scanPackages.isEmpty() || scanPackages.stream().anyMatch(pkg -> clazz.getPackage().getName().startsWith(pkg))) + // If exclude packages are specified, check that the class package doesn't start with any in the list + .filter(clazz -> excludePackages.isEmpty() || !excludePackages.stream().anyMatch(pkg -> clazz.getPackage().getName().startsWith(pkg))) + .collect(toSet()); + } + + private Map findOperationServerMap(Config config) { + Map map = new HashMap<>(); + try { + for (String propertyName : config.getPropertyNames()) { + if (propertyName.startsWith(OPERATION_PREFIX_KEY)) { + operationServerMap.put(propertyName, config.getValue(propertyName, String.class)); + } + } + } catch (NoSuchElementException ex) { + // Ignore + } + return map; + } + + @SuppressWarnings("unchecked") + private Class findModelReaderFromConfig(Config config, ClassLoader classLoader) { + try { + String modelReaderClassName = config.getValue(MODEL_READER_KEY, String.class); + Class modelReaderClass = getClassFromName(modelReaderClassName, classLoader); + if (modelReaderClass != null && OASModelReader.class.isAssignableFrom(modelReaderClass)) { + return (Class) modelReaderClass; + } + } catch (NoSuchElementException ex) { + // Ignore + } + return null; + } + + @SuppressWarnings("unchecked") + private Class findFilterFromConfig(Config config, ClassLoader classLoader) { + try { + String filterClassName = config.getValue(FILTER_KEY, String.class); + Class filterClass = getClassFromName(filterClassName, classLoader); + if (filterClass != null && OASFilter.class.isAssignableFrom(filterClass)) { + return (Class) filterClass; + } + } catch (NoSuchElementException ex) { + // Ignore + } + return null; + } + + private boolean findScanDisableFromConfig(Config config) { + try { + return config.getValue(SCAN_DISABLE_KEY, Boolean.class); + } catch (NoSuchElementException ex) { + // Ignore + } + return false; + } + + @SuppressWarnings("unchecked") + private List findScanPackagesFromConfig(Config config) { + List packages = new ArrayList<>(); + try { + packages.addAll(config.getValue(SCAN_PACKAGES_KEY, List.class)); + } catch (NoSuchElementException ex) { + // Ignore + } + return packages; + } + + @SuppressWarnings("unchecked") + private List> findScanClassesFromConfig(Config config, ClassLoader classLoader) { + List> classes = new ArrayList<>(); + try { + List classNames = (List) config.getValue(SCAN_CLASSES_KEY, List.class); + for (String className : classNames) { + Class clazz = getClassFromName(className, classLoader); + if (clazz != null) { + classes.add(clazz); + } + } + } catch (NoSuchElementException ex) { + // Ignore + } + return classes; + } + + @SuppressWarnings("unchecked") + private List findExcludePackages(Config config) { + List packages = new ArrayList<>(); + try { + packages.addAll(config.getValue(SCAN_EXCLUDE_PACKAGES_KEY, List.class)); + } catch (NoSuchElementException ex) { + // Ignore + } + return packages; + } + + @SuppressWarnings("unchecked") + private List> findExcludeClasses(Config config, ClassLoader classLoader) { + List> classes = new ArrayList<>(); + try { + List classNames = config.getValue(SCAN_EXCLUDE_CLASSES_KEY, List.class); + for (String className : classNames) { + Class clazz = getClassFromName(className, classLoader); + if (clazz != null) { + classes.add(clazz); + } + } + } catch (NoSuchElementException ex) { + // Ignore + } + return classes; + } + + @SuppressWarnings("unchecked") + private List findServers(Config config) { + List serverList = new ArrayList<>(); + try { + serverList.addAll(config.getValue(SERVERS_KEY, List.class)); + } catch (NoSuchElementException ex) { + // Ignore + } + return serverList; + } + + private Map findPathServerMap(Config config) { + Map map = new HashMap<>(); + try { + for (String propertyName : config.getPropertyNames()) { + if (propertyName.startsWith(PATH_PREFIX_KEY)) { + map.put(propertyName, config.getValue(propertyName, String.class)); + } + } + } catch (NoSuchElementException ex) { + // Ignore + } + return map; + } + + private Class getClassFromName(String className, ClassLoader classLoader) { + if (classLoader == null) { + classLoader = getClass().getClassLoader(); + } + if (className == null) { + return null; + } + + try { + return classLoader.loadClass(className); + } catch (ClassNotFoundException ex) { + LOGGER.log(Level.WARNING, "Unable to find class.", ex); + } + return null; + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java new file mode 100644 index 00000000000..faeddde4157 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java @@ -0,0 +1,339 @@ +package fish.payara.microprofile.openapi.impl.model; + +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; +import org.eclipse.microprofile.openapi.models.Components; +import org.eclipse.microprofile.openapi.models.callbacks.Callback; +import org.eclipse.microprofile.openapi.models.examples.Example; +import org.eclipse.microprofile.openapi.models.headers.Header; +import org.eclipse.microprofile.openapi.models.links.Link; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.parameters.Parameter; +import org.eclipse.microprofile.openapi.models.parameters.RequestBody; +import org.eclipse.microprofile.openapi.models.responses.APIResponse; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; + +import fish.payara.microprofile.openapi.impl.model.callbacks.CallbackImpl; +import fish.payara.microprofile.openapi.impl.model.examples.ExampleImpl; +import fish.payara.microprofile.openapi.impl.model.headers.HeaderImpl; +import fish.payara.microprofile.openapi.impl.model.links.LinkImpl; +import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.ParameterImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.RequestBodyImpl; +import fish.payara.microprofile.openapi.impl.model.responses.APIResponseImpl; +import fish.payara.microprofile.openapi.impl.model.security.SecuritySchemeImpl; + +public class ComponentsImpl extends ExtensibleImpl implements Components { + + protected Map schemas = new TreeMap<>(); + protected Map responses = new TreeMap<>(); + protected Map parameters = new TreeMap<>(); + protected Map examples = new TreeMap<>(); + protected Map requestBodies = new TreeMap<>(); + protected Map headers = new TreeMap<>(); + protected Map securitySchemes = new TreeMap<>(); + protected Map links = new TreeMap<>(); + protected Map callbacks = new TreeMap<>(); + + @Override + public Map getSchemas() { + return schemas; + } + + @Override + public void setSchemas(Map schemas) { + this.schemas = schemas; + } + + @Override + public Components schemas(Map schemas) { + setSchemas(schemas); + return this; + } + + @Override + public Components addSchema(String key, Schema schema) { + schemas.put(key, schema); + return this; + } + + @Override + public Map getResponses() { + return responses; + } + + @Override + public void setResponses(Map responses) { + this.responses = responses; + } + + @Override + public Components responses(Map responses) { + setResponses(responses); + return this; + } + + @Override + public Components addResponse(String key, APIResponse response) { + responses.put(key, response); + return this; + } + + @Override + public Map getParameters() { + return parameters; + } + + @Override + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + @Override + public Components parameters(Map parameters) { + setParameters(parameters); + return this; + } + + @Override + public Components addParameter(String key, Parameter parameter) { + parameters.put(key, parameter); + return this; + } + + @Override + public Map getExamples() { + return examples; + } + + @Override + public void setExamples(Map examples) { + this.examples = examples; + } + + @Override + public Components examples(Map examples) { + setExamples(examples); + return this; + } + + @Override + public Components addExample(String key, Example example) { + examples.put(key, example); + return this; + } + + @Override + public Map getRequestBodies() { + return requestBodies; + } + + @Override + public void setRequestBodies(Map requestBodies) { + this.requestBodies = requestBodies; + } + + @Override + public Components requestBodies(Map requestBodies) { + setRequestBodies(requestBodies); + return this; + } + + @Override + public Components addRequestBody(String key, RequestBody requestBody) { + requestBodies.put(key, requestBody); + return this; + } + + @Override + public Map getHeaders() { + return headers; + } + + @Override + public void setHeaders(Map headers) { + this.headers = headers; + } + + @Override + public Components headers(Map headers) { + setHeaders(headers); + return this; + } + + @Override + public Components addHeader(String key, Header header) { + headers.put(key, header); + return this; + } + + @Override + public Map getSecuritySchemes() { + return securitySchemes; + } + + @Override + public void setSecuritySchemes(Map securitySchemes) { + this.securitySchemes = securitySchemes; + } + + @Override + public Components securitySchemes(Map securitySchemes) { + setSecuritySchemes(securitySchemes); + return this; + } + + @Override + public Components addSecurityScheme(String key, SecurityScheme securityScheme) { + securitySchemes.put(key, securityScheme); + return this; + } + + @Override + public Map getLinks() { + return links; + } + + @Override + public void setLinks(Map links) { + this.links = links; + } + + @Override + public Components links(Map links) { + setLinks(links); + return this; + } + + @Override + public Components addLink(String key, Link link) { + links.put(key, link); + return this; + } + + @Override + public Map getCallbacks() { + return callbacks; + } + + @Override + public void setCallbacks(Map callbacks) { + this.callbacks = callbacks; + } + + @Override + public Components callbacks(Map callbacks) { + setCallbacks(callbacks); + return this; + } + + @Override + public Components addCallback(String key, Callback callback) { + callbacks.put(key, callback); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.Components from, Components to, + boolean override, Map currentSchemas) { + if (from == null) { + return; + } + // Handle @Schema + if (from.schemas() != null) { + for (org.eclipse.microprofile.openapi.annotations.media.Schema schema : from.schemas()) { + if (schema.name() != null) { + Schema newSchema = new SchemaImpl(); + SchemaImpl.merge(schema, newSchema, override, currentSchemas); + to.addSchema(schema.name(), newSchema); + } + } + } + // Handle @Callback + if (from.callbacks() != null) { + for (org.eclipse.microprofile.openapi.annotations.callbacks.Callback callback : from.callbacks()) { + if (callback != null) { + if (callback.name() != null) { + Callback newCallback = new CallbackImpl(); + CallbackImpl.merge(callback, newCallback, override); + to.addCallback(callback.name(), newCallback); + } + } + } + } + // Handle @ExampleObject + if (from.examples() != null) { + for (ExampleObject example : from.examples()) { + if (example.name() != null) { + Example newExample = new ExampleImpl(); + ExampleImpl.merge(example, newExample, override); + to.addExample(example.name(), newExample); + } + } + } + // Handle @Header + if (from.headers() != null) { + for (org.eclipse.microprofile.openapi.annotations.headers.Header header : from.headers()) { + if (header.name() != null) { + Header newHeader = new HeaderImpl(); + HeaderImpl.merge(header, newHeader, override, currentSchemas); + to.addHeader(header.name(), newHeader); + } + } + } + // Handle @Link + if (from.links() != null) { + for (org.eclipse.microprofile.openapi.annotations.links.Link link : from.links()) { + if (link.name() != null) { + Link newLink = new LinkImpl(); + LinkImpl.merge(link, newLink, override); + to.addLink(link.name(), newLink); + } + } + } + // Handle @Parameter + if (from.parameters() != null) { + for (org.eclipse.microprofile.openapi.annotations.parameters.Parameter parameter : from.parameters()) { + if (parameter.name() != null) { + Parameter newParameter = new ParameterImpl(); + ParameterImpl.merge(parameter, newParameter, override, currentSchemas); + to.addParameter(parameter.name(), newParameter); + } + } + } + // Handle @RequestBody + if (from.requestBodies() != null) { + for (org.eclipse.microprofile.openapi.annotations.parameters.RequestBody requestBody : from + .requestBodies()) { + if (requestBody.name() != null) { + RequestBody newRequestBody = new RequestBodyImpl(); + RequestBodyImpl.merge(requestBody, newRequestBody, override, currentSchemas); + to.addRequestBody(requestBody.name(), newRequestBody); + } + } + } + // Handle @APIResponse + if (from.responses() != null) { + for (org.eclipse.microprofile.openapi.annotations.responses.APIResponse response : from.responses()) { + if (response.name() != null) { + APIResponse newResponse = new APIResponseImpl(); + APIResponseImpl.merge(response, newResponse, override, currentSchemas); + to.addResponse(response.name(), newResponse); + } + } + } + // Handle @SecurityScheme + if (from.securitySchemes() != null) { + for (org.eclipse.microprofile.openapi.annotations.security.SecurityScheme security : from + .securitySchemes()) { + if (security.securitySchemeName() != null) { + SecurityScheme newSecurity = new SecuritySchemeImpl(); + SecuritySchemeImpl.merge(security, newSecurity, override); + to.addSecurityScheme(security.securitySchemeName(), newSecurity); + } + } + } + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExtensibleImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExtensibleImpl.java new file mode 100644 index 00000000000..73123919f44 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExtensibleImpl.java @@ -0,0 +1,62 @@ +package fish.payara.microprofile.openapi.impl.model; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.annotations.extensions.Extension; +import org.eclipse.microprofile.openapi.models.Extensible; + +public abstract class ExtensibleImpl implements Extensible { + + protected Map extensions = new HashMap<>(); + + @Override + public Map getExtensions() { + return extensions; + } + + @Override + public void addExtension(String name, Object value) { + extensions.put(name, value); + } + + @Override + public void setExtensions(Map extensions) { + this.extensions = extensions; + } + + public static void merge(Extension from, Extensible to, boolean override) { + if (isAnnotationNull(from)) { + return; + } + if (to.getExtensions() == null) { + to.setExtensions(new LinkedHashMap<>()); + } + if (from.name() != null && !from.name().isEmpty()) { + Object value = mergeProperty(to.getExtensions().get(from.name()), convertExtensionValue(from.value()), + override); + to.getExtensions().put(from.name(), value); + } + } + + public static Object convertExtensionValue(String value) { + if (value == null) { + return null; + } + // Could be an array + if (value.contains(",")) { + // Remove leading and trailing brackets, then parse to an array + String[] possibleArray = value.replaceAll("^[\\[\\{\\(]", "").replaceAll("[\\]\\}\\)]$", "").split(","); + + if (possibleArray.length > 1) { + return possibleArray; + } + } + return value; + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExternalDocumentationImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExternalDocumentationImpl.java new file mode 100644 index 00000000000..20dac9062a6 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExternalDocumentationImpl.java @@ -0,0 +1,54 @@ +package fish.payara.microprofile.openapi.impl.model; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import org.eclipse.microprofile.openapi.models.ExternalDocumentation; + +public class ExternalDocumentationImpl extends ExtensibleImpl implements ExternalDocumentation { + + protected String description; + protected String url; + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public ExternalDocumentation description(String description) { + setDescription(description); + return this; + } + + @Override + public String getUrl() { + return url; + } + + @Override + public void setUrl(String url) { + this.url = url; + } + + @Override + public ExternalDocumentation url(String url) { + setUrl(url); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.ExternalDocumentation from, ExternalDocumentation to, + boolean override) { + if (isAnnotationNull(from)) { + return; + } + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + to.setUrl(mergeProperty(to.getUrl(), from.url(), override)); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java new file mode 100644 index 00000000000..42ae9db3486 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java @@ -0,0 +1,247 @@ +package fish.payara.microprofile.openapi.impl.model; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; +import org.eclipse.microprofile.openapi.models.Components; +import org.eclipse.microprofile.openapi.models.ExternalDocumentation; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.Paths; +import org.eclipse.microprofile.openapi.models.info.Info; +import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.models.servers.Server; +import org.eclipse.microprofile.openapi.models.tags.Tag; + +import fish.payara.microprofile.openapi.impl.model.info.InfoImpl; +import fish.payara.microprofile.openapi.impl.model.security.SecurityRequirementImpl; +import fish.payara.microprofile.openapi.impl.model.servers.ServerImpl; +import fish.payara.microprofile.openapi.impl.model.tags.TagImpl; + +public class OpenAPIImpl extends ExtensibleImpl implements OpenAPI { + + protected String openapi; + protected Info info; + protected ExternalDocumentation externalDocs; + protected List servers = new ArrayList<>(); + protected List security = new ArrayList<>(); + protected List tags = new ArrayList<>(); + protected Paths paths = new PathsImpl(); + protected Components components = new ComponentsImpl(); + + @Override + public String getOpenapi() { + return openapi; + } + + @Override + public void setOpenapi(String openapi) { + this.openapi = openapi; + } + + @Override + public OpenAPI openapi(String openapi) { + setOpenapi(openapi); + return this; + } + + @Override + public Info getInfo() { + return info; + } + + @Override + public void setInfo(Info info) { + this.info = info; + } + + @Override + public OpenAPI info(Info info) { + setInfo(info); + return this; + } + + @Override + public ExternalDocumentation getExternalDocs() { + return externalDocs; + } + + @Override + public void setExternalDocs(ExternalDocumentation externalDocs) { + this.externalDocs = externalDocs; + } + + @Override + public OpenAPI externalDocs(ExternalDocumentation externalDocs) { + setExternalDocs(externalDocs); + return this; + } + + @Override + public List getServers() { + return servers; + } + + @Override + public void setServers(List servers) { + this.servers = servers; + } + + @Override + public OpenAPI servers(List servers) { + setServers(servers); + return this; + } + + @Override + public OpenAPI addServer(Server server) { + servers.add(server); + return this; + } + + @Override + public List getSecurity() { + return security; + } + + @Override + public void setSecurity(List security) { + this.security = security; + } + + @Override + public OpenAPI security(List security) { + setSecurity(security); + return this; + } + + @Override + public OpenAPI addSecurityRequirement(SecurityRequirement securityRequirement) { + security.add(securityRequirement); + return this; + } + + @Override + public List getTags() { + return tags; + } + + @Override + public void setTags(List tags) { + this.tags = tags; + } + + @Override + public OpenAPI tags(List tags) { + setTags(tags); + return this; + } + + @Override + public OpenAPI addTag(Tag tag) { + tags.add(tag); + return this; + } + + @Override + public Paths getPaths() { + return paths; + } + + @Override + public void setPaths(Paths paths) { + this.paths = paths; + } + + @Override + public OpenAPI paths(Paths paths) { + setPaths(paths); + return this; + } + + @Override + public OpenAPI path(String name, PathItem path) { + paths.addPathItem(name, path); + return this; + } + + @Override + public Components getComponents() { + return components; + } + + @Override + public void setComponents(Components components) { + this.components = components; + } + + @Override + public OpenAPI components(Components components) { + setComponents(components); + return this; + } + + public static void merge(OpenAPIDefinition from, OpenAPI to, boolean override) { + if (isAnnotationNull(from)) { + return; + } + // Handle @Info + if (!isAnnotationNull(from.info())) { + if (to.getInfo() == null) { + to.setInfo(new InfoImpl()); + } + InfoImpl.merge(from.info(), to.getInfo(), override); + } + // Handle @Servers + if (from.servers() != null) { + for (org.eclipse.microprofile.openapi.annotations.servers.Server server : from.servers()) { + if (!isAnnotationNull(server)) { + Server newServer = new ServerImpl(); + ServerImpl.merge(server, newServer, true); + if (!to.getServers().contains(newServer)) { + to.addServer(newServer); + } + } + } + } + // Handle @ExternalDocumentation + if (!isAnnotationNull(from.externalDocs())) { + if (to.getExternalDocs() == null) { + to.setExternalDocs(new ExternalDocumentationImpl()); + } + ExternalDocumentationImpl.merge(from.externalDocs(), to.getExternalDocs(), override); + } + // Handle @SecurityRequirement + if (from.security() != null) { + for (org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement requirement : from + .security()) { + if (!isAnnotationNull(requirement)) { + SecurityRequirement newRequirement = new SecurityRequirementImpl(); + SecurityRequirementImpl.merge(requirement, newRequirement, override); + if (!to.getSecurity().contains(newRequirement)) { + to.addSecurityRequirement(newRequirement); + } + } + } + } + // Handle @Tags + if (from.tags() != null) { + for (org.eclipse.microprofile.openapi.annotations.tags.Tag tag : from.tags()) { + if (!isAnnotationNull(tag)) { + if (to.getTags() == null) { + to.setTags(new ArrayList<>()); + } + Tag newTag = new TagImpl(); + TagImpl.merge(tag, newTag, override); + to.addTag(newTag); + } + } + } + // Handle @Components + ComponentsImpl.merge(from.components(), to.getComponents(), override, null); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OperationImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OperationImpl.java new file mode 100644 index 00000000000..df1f23aa73c --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OperationImpl.java @@ -0,0 +1,263 @@ +package fish.payara.microprofile.openapi.impl.model; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.ExternalDocumentation; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.callbacks.Callback; +import org.eclipse.microprofile.openapi.models.parameters.Parameter; +import org.eclipse.microprofile.openapi.models.parameters.RequestBody; +import org.eclipse.microprofile.openapi.models.responses.APIResponses; +import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.models.servers.Server; + +import fish.payara.microprofile.openapi.impl.model.responses.APIResponsesImpl; + +public class OperationImpl extends ExtensibleImpl implements Operation { + + protected List tags = new ArrayList<>(); + protected String summary; + protected String description; + protected ExternalDocumentation externalDocs; + protected String operationId; + protected List parameters = new ArrayList<>(); + protected RequestBody requestBody; + protected APIResponses responses = new APIResponsesImpl(); + protected Map callbacks = new HashMap<>(); + protected Boolean deprecated; + protected List security = new ArrayList<>(); + protected List servers = new ArrayList<>(); + + @Override + public List getTags() { + return tags; + } + + @Override + public void setTags(List tags) { + this.tags = tags; + } + + @Override + public Operation tags(List tags) { + setTags(tags); + return this; + } + + @Override + public Operation addTag(String tag) { + tags.add(tag); + return this; + } + + @Override + public String getSummary() { + return summary; + } + + @Override + public void setSummary(String summary) { + this.summary = summary; + } + + @Override + public Operation summary(String summary) { + setSummary(summary); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public Operation description(String description) { + setDescription(description); + return this; + } + + @Override + public ExternalDocumentation getExternalDocs() { + return externalDocs; + } + + @Override + public void setExternalDocs(ExternalDocumentation externalDocs) { + this.externalDocs = externalDocs; + } + + @Override + public Operation externalDocs(ExternalDocumentation externalDocs) { + setExternalDocs(externalDocs); + return this; + } + + @Override + public String getOperationId() { + return operationId; + } + + @Override + public void setOperationId(String operationId) { + this.operationId = operationId; + } + + @Override + public Operation operationId(String operationId) { + setOperationId(operationId); + return this; + } + + @Override + public List getParameters() { + return parameters; + } + + @Override + public void setParameters(List parameters) { + this.parameters = parameters; + } + + @Override + public Operation parameters(List parameters) { + setParameters(parameters); + return this; + } + + @Override + public Operation addParameter(Parameter parameter) { + parameters.add(parameter); + return this; + } + + @Override + public RequestBody getRequestBody() { + return requestBody; + } + + @Override + public void setRequestBody(RequestBody requestBody) { + this.requestBody = requestBody; + } + + @Override + public Operation requestBody(RequestBody requestBody) { + setRequestBody(requestBody); + return this; + } + + @Override + public APIResponses getResponses() { + return responses; + } + + @Override + public void setResponses(APIResponses responses) { + this.responses = responses; + } + + @Override + public Operation responses(APIResponses responses) { + setResponses(responses); + return this; + } + + @Override + public Map getCallbacks() { + return callbacks; + } + + @Override + public void setCallbacks(Map callbacks) { + this.callbacks = callbacks; + } + + @Override + public Operation callbacks(Map callbacks) { + setCallbacks(callbacks); + return this; + } + + @Override + public Boolean getDeprecated() { + return deprecated; + } + + @Override + public void setDeprecated(Boolean deprecated) { + this.deprecated = deprecated; + } + + @Override + public Operation deprecated(Boolean deprecated) { + setDeprecated(deprecated); + return this; + } + + @Override + public List getSecurity() { + return security; + } + + @Override + public void setSecurity(List security) { + this.security = security; + } + + @Override + public Operation security(List security) { + setSecurity(security); + return this; + } + + @Override + public Operation addSecurityRequirement(SecurityRequirement securityReq) { + security.add(securityReq); + return this; + } + + @Override + public List getServers() { + return servers; + } + + @Override + public void setServers(List servers) { + this.servers = servers; + } + + @Override + public Operation servers(List servers) { + setServers(servers); + return this; + } + + @Override + public Operation addServer(Server server) { + servers.add(server); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.Operation from, Operation to, + boolean override) { + if (isAnnotationNull(from)) { + return; + } + to.setOperationId(mergeProperty(to.getOperationId(), from.operationId(), override)); + to.setSummary(mergeProperty(to.getSummary(), from.summary(), override)); + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathItemImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathItemImpl.java new file mode 100644 index 00000000000..5d0923c8944 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathItemImpl.java @@ -0,0 +1,312 @@ +package fish.payara.microprofile.openapi.impl.model; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.parameters.Parameter; +import org.eclipse.microprofile.openapi.models.servers.Server; + +public class PathItemImpl extends ExtensibleImpl implements PathItem { + + protected String ref; + protected String summary; + protected String description; + protected Operation get; + protected Operation put; + protected Operation post; + protected Operation delete; + protected Operation options; + protected Operation head; + protected Operation patch; + protected Operation trace; + protected List servers = new ArrayList<>(); + protected List parameters = new ArrayList<>(); + + @Override + public String getRef() { + return ref; + } + + @Override + public void setRef(String ref) { + this.ref = ref; + } + + @Override + public PathItem ref(String ref) { + setRef(ref); + return this; + } + + @Override + public String getSummary() { + return summary; + } + + @Override + public void setSummary(String summary) { + this.summary = summary; + } + + @Override + public PathItem summary(String summary) { + setSummary(summary); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public PathItem description(String description) { + setDescription(description); + return this; + } + + @Override + public Operation getGET() { + return get; + } + + @Override + public void setGET(Operation get) { + this.get = get; + } + + @Override + public PathItem GET(Operation get) { + setGET(get); + return this; + } + + @Override + public Operation getPUT() { + return put; + } + + @Override + public void setPUT(Operation put) { + this.put = put; + } + + @Override + public PathItem PUT(Operation put) { + setPUT(put); + return this; + } + + @Override + public Operation getPOST() { + return post; + } + + @Override + public void setPOST(Operation post) { + this.post = post; + } + + @Override + public PathItem POST(Operation post) { + setPOST(post); + return this; + } + + @Override + public Operation getDELETE() { + return delete; + } + + @Override + public void setDELETE(Operation delete) { + this.delete = delete; + } + + @Override + public PathItem DELETE(Operation delete) { + setDELETE(delete); + return this; + } + + @Override + public Operation getOPTIONS() { + return options; + } + + @Override + public void setOPTIONS(Operation options) { + this.options = options; + } + + @Override + public PathItem OPTIONS(Operation options) { + setOPTIONS(options); + return this; + } + + @Override + public Operation getHEAD() { + return head; + } + + @Override + public void setHEAD(Operation head) { + this.head = head; + } + + @Override + public PathItem HEAD(Operation head) { + setHEAD(head); + return this; + } + + @Override + public Operation getPATCH() { + return patch; + } + + @Override + public void setPATCH(Operation patch) { + this.patch = patch; + } + + @Override + public PathItem PATCH(Operation patch) { + setPATCH(patch); + return this; + } + + @Override + public Operation getTRACE() { + return trace; + } + + @Override + public void setTRACE(Operation trace) { + this.trace = trace; + } + + @Override + public PathItem TRACE(Operation trace) { + setTRACE(trace); + return this; + } + + @Override + public List readOperations() { + List allOperations = new ArrayList<>(); + if (this.get != null) { + allOperations.add(this.get); + } + if (this.put != null) { + allOperations.add(this.put); + } + if (this.head != null) { + allOperations.add(this.head); + } + if (this.post != null) { + allOperations.add(this.post); + } + if (this.delete != null) { + allOperations.add(this.delete); + } + if (this.patch != null) { + allOperations.add(this.patch); + } + if (this.options != null) { + allOperations.add(this.options); + } + if (this.trace != null) { + allOperations.add(this.trace); + } + + return allOperations; + } + + @Override + public Map readOperationsMap() { + Map result = new EnumMap<>(HttpMethod.class); + + if (this.get != null) { + result.put(HttpMethod.GET, this.get); + } + if (this.put != null) { + result.put(HttpMethod.PUT, this.put); + } + if (this.post != null) { + result.put(HttpMethod.POST, this.post); + } + if (this.delete != null) { + result.put(HttpMethod.DELETE, this.delete); + } + if (this.patch != null) { + result.put(HttpMethod.PATCH, this.patch); + } + if (this.head != null) { + result.put(HttpMethod.HEAD, this.head); + } + if (this.options != null) { + result.put(HttpMethod.OPTIONS, this.options); + } + if (this.trace != null) { + result.put(HttpMethod.TRACE, this.trace); + } + + return result; + } + + @Override + public List getServers() { + return servers; + } + + @Override + public void setServers(List servers) { + this.servers = servers; + } + + @Override + public PathItem servers(List servers) { + setServers(servers); + return this; + } + + @Override + public PathItem addServer(Server server) { + servers.add(server); + return this; + } + + @Override + public List getParameters() { + return parameters; + } + + @Override + public void setParameters(List parameters) { + this.parameters = parameters; + } + + @Override + public PathItem parameters(List parameters) { + setParameters(parameters); + return this; + } + + @Override + public PathItem addParameter(Parameter parameter) { + setParameters(parameters); + return this; + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathsImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathsImpl.java new file mode 100644 index 00000000000..af6f25ccf86 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathsImpl.java @@ -0,0 +1,52 @@ +package fish.payara.microprofile.openapi.impl.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.Paths; + +import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; + +public class PathsImpl extends TreeMap implements Paths { + + private static final long serialVersionUID = -3876996963579977405L; + + protected Map extensions = new HashMap<>(); + + @Override + public Paths addPathItem(String name, PathItem item) { + super.put(name, item); + return this; + } + + @Override + public Map getExtensions() { + return extensions; + } + + @Override + public void addExtension(String name, Object value) { + extensions.put(name, value); + } + + @Override + public void setExtensions(Map extensions) { + this.extensions = extensions; + } + + public static void merge(Paths from, Paths to, boolean override) { + if (from == null || to == null) { + return; + } + from.entrySet().forEach(entry -> { + if (!to.containsKey(entry.getKey())) { + to.addPathItem(entry.getKey(), entry.getValue()); + } else { + ModelUtils.merge(entry.getValue(), to.get(entry.getKey()), override); + } + }); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/callbacks/CallbackImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/callbacks/CallbackImpl.java new file mode 100644 index 00000000000..5890add1115 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/callbacks/CallbackImpl.java @@ -0,0 +1,97 @@ +package fish.payara.microprofile.openapi.impl.model.callbacks; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.annotations.callbacks.CallbackOperation; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.callbacks.Callback; + +import fish.payara.microprofile.openapi.impl.model.PathItemImpl; +import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; + +public class CallbackImpl extends LinkedHashMap implements Callback { + + private static final long serialVersionUID = 5549098533131353142L; + + protected String ref; + protected Map extensions = new HashMap<>(); + + @Override + public Callback addPathItem(String name, PathItem item) { + this.put(name, item); + return this; + } + + @Override + public String getRef() { + return this.ref; + } + + @Override + public void setRef(String ref) { + if (ref != null && !ref.contains(".") && !ref.contains("/")) { + ref = "#/components/callbacks/" + ref; + } + this.ref = ref; + } + + @Override + public Callback ref(String ref) { + setRef(ref); + return this; + } + + @Override + public Map getExtensions() { + return extensions; + } + + @Override + public void setExtensions(Map extensions) { + this.extensions = extensions; + } + + @Override + public void addExtension(String name, Object value) { + this.extensions.put(name, value); + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.callbacks.Callback from, Callback to, + boolean override) { + if (isAnnotationNull(from)) { + return; + } + if (from.ref() != null && !from.ref().isEmpty()) { + applyReference(to, from.ref()); + return; + } + if (!from.callbackUrlExpression().isEmpty()) { + PathItem pathItem = new PathItemImpl(); + to.addPathItem(from.callbackUrlExpression(), pathItem); + if (from.operations() != null) { + for (CallbackOperation callbackOperation : from.operations()) { + applyCallbackOperationAnnotation(pathItem, callbackOperation, override); + } + } + } + } + + private static void applyCallbackOperationAnnotation(PathItem pathItem, CallbackOperation annotation, + boolean override) { + HttpMethod method = HttpMethod.valueOf(annotation.method()); + if (method != null) { + Operation operation = ModelUtils.getOrCreateOperation(pathItem, method); + operation.setDescription(mergeProperty(operation.getDescription(), annotation.description(), override)); + operation.setSummary(mergeProperty(operation.getSummary(), annotation.summary(), override)); + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java new file mode 100644 index 00000000000..7c94a6667bc --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java @@ -0,0 +1,112 @@ +package fish.payara.microprofile.openapi.impl.model.examples; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; +import org.eclipse.microprofile.openapi.models.examples.Example; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class ExampleImpl extends ExtensibleImpl implements Example { + + protected String summary; + protected String description; + protected Object value; + protected String externalValue; + protected String ref; + + @Override + public String getSummary() { + return summary; + } + + @Override + public void setSummary(String summary) { + this.summary = summary; + } + + @Override + public Example summary(String summary) { + setSummary(summary); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public Example description(String description) { + setDescription(description); + return this; + } + + @Override + public Object getValue() { + return value; + } + + @Override + public void setValue(Object value) { + this.value = value; + } + + @Override + public Example value(Object value) { + setValue(value); + return this; + } + + @Override + public String getExternalValue() { + return externalValue; + } + + @Override + public void setExternalValue(String externalValue) { + this.externalValue = externalValue; + } + + @Override + public Example externalValue(String externalValue) { + setExternalValue(externalValue); + return this; + } + + @Override + public String getRef() { + return ref; + } + + @Override + public void setRef(String ref) { + if (ref != null && !ref.contains(".") && !ref.contains("/")) { + ref = "#/components/examples/" + ref; + } + this.ref = ref; + } + + @Override + public Example ref(String ref) { + setRef(ref); + return this; + } + + public static void merge(ExampleObject from, Example to, boolean override) { + if (isAnnotationNull(from)) { + return; + } + to.setSummary(mergeProperty(to.getSummary(), from.summary(), override)); + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + to.setValue(mergeProperty(to.getValue(), from.value(), override)); + to.setExternalValue(mergeProperty(to.getExternalValue(), from.externalValue(), override)); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java new file mode 100644 index 00000000000..0813a2a8870 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java @@ -0,0 +1,238 @@ +package fish.payara.microprofile.openapi.impl.model.headers; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.examples.Example; +import org.eclipse.microprofile.openapi.models.headers.Header; +import org.eclipse.microprofile.openapi.models.media.Content; +import org.eclipse.microprofile.openapi.models.media.Schema; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; +import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; + +public class HeaderImpl extends ExtensibleImpl implements Header { + + protected String ref; + protected String description; + protected Boolean required; + protected Boolean deprecated; + protected Boolean allowEmptyValue; + protected Style style; + protected Boolean explode; + protected Schema schema; + protected Map examples = new HashMap<>(); + protected Object example; + protected Content content; + + @Override + public String getRef() { + return ref; + } + + @Override + public void setRef(String ref) { + if (ref != null && !ref.contains(".") && !ref.contains("/")) { + ref = "#/components/headers/" + ref; + } + this.ref = ref; + } + + @Override + public Header ref(String ref) { + setRef(ref); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public Header description(String description) { + setDescription(description); + return this; + } + + @Override + public Boolean getRequired() { + return required; + } + + @Override + public void setRequired(Boolean required) { + this.required = required; + } + + @Override + public Header required(Boolean required) { + setRequired(required); + return this; + } + + @Override + public Boolean getDeprecated() { + return deprecated; + } + + @Override + public void setDeprecated(Boolean deprecated) { + this.deprecated = deprecated; + } + + @Override + public Header deprecated(Boolean deprecated) { + setDeprecated(deprecated); + return this; + } + + @Override + public Boolean getAllowEmptyValue() { + return allowEmptyValue; + } + + @Override + public void setAllowEmptyValue(Boolean allowEmptyValue) { + this.allowEmptyValue = allowEmptyValue; + } + + @Override + public Header allowEmptyValue(Boolean allowEmptyValue) { + setAllowEmptyValue(allowEmptyValue); + return this; + } + + @Override + public Style getStyle() { + return style; + } + + @Override + public void setStyle(Style style) { + this.style = style; + } + + @Override + public Header style(Style style) { + setStyle(style); + return this; + } + + @Override + public Boolean getExplode() { + return explode; + } + + @Override + public void setExplode(Boolean explode) { + this.explode = explode; + } + + @Override + public Header explode(Boolean explode) { + setExplode(explode); + return this; + } + + @Override + public Schema getSchema() { + return schema; + } + + @Override + public void setSchema(Schema schema) { + this.schema = schema; + } + + @Override + public Header schema(Schema schema) { + setSchema(schema); + return this; + } + + @Override + public Map getExamples() { + return examples; + } + + @Override + public void setExamples(Map examples) { + this.examples = examples; + } + + @Override + public Header examples(Map examples) { + setExamples(examples); + return this; + } + + @Override + public Header addExample(String key, Example examplesItem) { + this.examples.put(key, examplesItem); + return this; + } + + @Override + public Object getExample() { + return example; + } + + @Override + public void setExample(Object example) { + this.example = example; + } + + @Override + public Header example(Object example) { + setExample(example); + return this; + } + + @Override + public Content getContent() { + return content; + } + + @Override + public void setContent(Content content) { + this.content = content; + } + + @Override + public Header content(Content content) { + setContent(content); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.headers.Header from, Header to, + boolean override, Map currentSchemas) { + if (isAnnotationNull(from)) { + return; + } + if (from.ref() != null && !from.ref().isEmpty()) { + applyReference(to, from.ref()); + return; + } + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + to.setRequired(mergeProperty(to.getRequired(), from.required(), override)); + to.setAllowEmptyValue(mergeProperty(to.getAllowEmptyValue(), from.allowEmptyValue(), override)); + to.setDeprecated(mergeProperty(to.getDeprecated(), from.deprecated(), override)); + if (!isAnnotationNull(from.schema())) { + if (to.getSchema() == null) { + to.setSchema(new SchemaImpl()); + } + SchemaImpl.merge(from.schema(), to.getSchema(), override, currentSchemas); + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/ContactImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/ContactImpl.java new file mode 100644 index 00000000000..d203786f3e8 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/ContactImpl.java @@ -0,0 +1,74 @@ +package fish.payara.microprofile.openapi.impl.model.info; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import org.eclipse.microprofile.openapi.models.info.Contact; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class ContactImpl extends ExtensibleImpl implements Contact { + + protected String name; + protected String url; + protected String email; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public Contact name(String name) { + setName(name); + return this; + } + + @Override + public String getUrl() { + return url; + } + + @Override + public void setUrl(String url) { + this.url = url; + } + + @Override + public Contact url(String url) { + setUrl(url); + return this; + } + + @Override + public String getEmail() { + return email; + } + + @Override + public void setEmail(String email) { + this.email = email; + } + + @Override + public Contact email(String email) { + setEmail(email); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.info.Contact from, Contact to, + boolean override) { + if (isAnnotationNull(from)) { + return; + } + to.setName(mergeProperty(to.getName(), from.name(), override)); + to.setUrl(mergeProperty(to.getUrl(), from.url(), override)); + to.setEmail(mergeProperty(to.getEmail(), from.email(), override)); + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/InfoImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/InfoImpl.java new file mode 100644 index 00000000000..28055b0c38f --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/InfoImpl.java @@ -0,0 +1,139 @@ +package fish.payara.microprofile.openapi.impl.model.info; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import org.eclipse.microprofile.openapi.models.info.Contact; +import org.eclipse.microprofile.openapi.models.info.Info; +import org.eclipse.microprofile.openapi.models.info.License; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class InfoImpl extends ExtensibleImpl implements Info { + + protected String title; + protected String description; + protected String termsOfService; + protected Contact contact; + protected License license; + protected String version; + + @Override + public String getTitle() { + return title; + } + + @Override + public void setTitle(String title) { + this.title = title; + } + + @Override + public Info title(String title) { + setTitle(title); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public Info description(String description) { + setDescription(description); + return this; + } + + @Override + public String getTermsOfService() { + return termsOfService; + } + + @Override + public void setTermsOfService(String termsOfService) { + this.termsOfService = termsOfService; + } + + @Override + public Info termsOfService(String termsOfService) { + setTermsOfService(termsOfService); + return this; + } + + @Override + public Contact getContact() { + return contact; + } + + @Override + public void setContact(Contact contact) { + this.contact = contact; + } + + @Override + public Info contact(Contact contact) { + setContact(contact); + return this; + } + + @Override + public License getLicense() { + return license; + } + + @Override + public void setLicense(License license) { + this.license = license; + } + + @Override + public Info license(License license) { + setLicense(license); + return this; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public void setVersion(String version) { + this.version = version; + } + + @Override + public Info version(String version) { + setVersion(version); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.info.Info from, Info to, boolean override) { + if (isAnnotationNull(from)) { + return; + } + to.setTitle(mergeProperty(to.getTitle(), from.title(), override)); + to.setVersion(mergeProperty(to.getVersion(), from.version(), override)); + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + to.setTermsOfService(mergeProperty(to.getTermsOfService(), from.termsOfService(), override)); + if (!isAnnotationNull(from.license())) { + if (to.getLicense() == null) { + to.setLicense(new LicenseImpl()); + } + LicenseImpl.merge(from.license(), to.getLicense(), override); + } + if (!isAnnotationNull(from.contact())) { + if (to.getContact() == null) { + to.setContact(new ContactImpl()); + } + ContactImpl.merge(from.contact(), to.getContact(), override); + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/LicenseImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/LicenseImpl.java new file mode 100644 index 00000000000..eba9a4ca319 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/LicenseImpl.java @@ -0,0 +1,56 @@ +package fish.payara.microprofile.openapi.impl.model.info; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import org.eclipse.microprofile.openapi.models.info.License; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class LicenseImpl extends ExtensibleImpl implements License { + + protected String name; + protected String url; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public License name(String name) { + setName(name); + return this; + } + + @Override + public String getUrl() { + return url; + } + + @Override + public void setUrl(String url) { + this.url = url; + } + + @Override + public License url(String url) { + setUrl(url); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.info.License from, License to, + boolean override) { + if (isAnnotationNull(from)) { + return; + } + to.setName(mergeProperty(to.getName(), from.name(), override)); + to.setUrl(mergeProperty(to.getUrl(), from.url(), override)); + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java new file mode 100644 index 00000000000..514bc38ec34 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java @@ -0,0 +1,160 @@ +package fish.payara.microprofile.openapi.impl.model.links; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.links.Link; +import org.eclipse.microprofile.openapi.models.servers.Server; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class LinkImpl extends ExtensibleImpl implements Link { + + protected String operationRef; + protected String operationId; + protected Map parameters = new HashMap<>(); + protected Object requestBody; + protected String description; + protected String ref; + protected Server server; + + @Override + public Server getServer() { + return server; + } + + @Override + public void setServer(Server server) { + this.server = server; + } + + @Override + public Link server(Server server) { + setServer(server); + return this; + } + + @Override + public String getOperationRef() { + return operationRef; + } + + @Override + public void setOperationRef(String operationRef) { + this.operationRef = operationRef; + } + + @Override + public Link operationRef(String operationRef) { + setOperationRef(operationRef); + return this; + } + + @Override + public Object getRequestBody() { + return requestBody; + } + + @Override + public void setRequestBody(Object requestBody) { + this.requestBody = requestBody; + } + + @Override + public Link requestBody(Object requestBody) { + setRequestBody(requestBody); + return this; + } + + @Override + public String getOperationId() { + return operationId; + } + + @Override + public void setOperationId(String operationId) { + this.operationId = operationId; + } + + @Override + public Link operationId(String operationId) { + setOperationId(operationId); + return this; + } + + @Override + public Map getParameters() { + return parameters; + } + + @Override + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + @Override + public Link parameters(Map parameters) { + setParameters(parameters); + return this; + } + + @Override + public Link addParameter(String name, Object parameter) { + this.parameters.put(name, parameter); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public Link description(String description) { + setDescription(description); + return this; + } + + @Override + public String getRef() { + return ref; + } + + @Override + public void setRef(String ref) { + if (ref != null && !ref.contains(".") && !ref.contains("/")) { + ref = "#/components/links/" + ref; + } + this.ref = ref; + } + + @Override + public Link ref(String ref) { + setRef(ref); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.links.Link from, Link to, boolean override) { + if (isAnnotationNull(from)) { + return; + } + if (from.ref() != null && !from.ref().isEmpty()) { + applyReference(to, from.ref()); + return; + } + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + to.setOperationId(mergeProperty(to.getOperationId(), from.operationId(), override)); + to.setOperationRef(mergeProperty(to.getOperationRef(), from.operationRef(), override)); + to.setRequestBody(mergeProperty(to.getRequestBody(), from.requestBody(), override)); + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/ContentImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/ContentImpl.java new file mode 100644 index 00000000000..b48d515fd8a --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/ContentImpl.java @@ -0,0 +1,50 @@ +package fish.payara.microprofile.openapi.impl.model.media; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.media.Content; +import org.eclipse.microprofile.openapi.models.media.MediaType; +import org.eclipse.microprofile.openapi.models.media.Schema; + +public class ContentImpl extends LinkedHashMap implements Content { + + private static final long serialVersionUID = 1575356277308242221L; + + @Override + public Content addMediaType(String name, MediaType item) { + this.put(name, item); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.media.Content from, Content to, + boolean override, Map currentSchemas) { + if (from == null) { + return; + } + if (!isAnnotationNull(from.schema()) || from.encoding().length > 0) { + // Get the name of the media type + String typeName = from.mediaType(); + if (typeName == null || typeName.isEmpty()) { + typeName = javax.ws.rs.core.MediaType.WILDCARD; + } + + // Get the current mediaType, or create a new one + MediaType mediaType = to.getOrDefault(typeName, new MediaTypeImpl()); + to.put(typeName, mediaType); + + if (!isAnnotationNull(from.schema())) { + // Get the current schema, or create a new one + Schema schema = mediaType.getSchema(); + if (schema == null) { + schema = new SchemaImpl(); + mediaType.setSchema(schema); + } + SchemaImpl.merge(from.schema(), schema, override, currentSchemas); + } + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/DiscriminatorImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/DiscriminatorImpl.java new file mode 100644 index 00000000000..1c7796bd557 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/DiscriminatorImpl.java @@ -0,0 +1,51 @@ +package fish.payara.microprofile.openapi.impl.model.media; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.media.Discriminator; + +public class DiscriminatorImpl implements Discriminator { + + protected String propertyName; + protected Map mapping = new HashMap<>(); + + @Override + public String getPropertyName() { + return propertyName; + } + + @Override + public void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + @Override + public Discriminator propertyName(String propertyName) { + setPropertyName(propertyName); + return this; + } + + @Override + public Map getMapping() { + return mapping; + } + + @Override + public void setMapping(Map mapping) { + this.mapping = mapping; + } + + @Override + public Discriminator mapping(Map mapping) { + setMapping(mapping); + return this; + } + + @Override + public Discriminator addMapping(String name, String value) { + mapping.put(name, value); + return this; + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/EncodingImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/EncodingImpl.java new file mode 100644 index 00000000000..f623539a713 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/EncodingImpl.java @@ -0,0 +1,99 @@ +package fish.payara.microprofile.openapi.impl.model.media; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.headers.Header; +import org.eclipse.microprofile.openapi.models.media.Encoding; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class EncodingImpl extends ExtensibleImpl implements Encoding { + + protected String contentType; + protected Map headers = new HashMap<>(); + protected Style style; + protected Boolean explode; + protected Boolean allowReserved; + + @Override + public String getContentType() { + return contentType; + } + + @Override + public void setContentType(String contentType) { + this.contentType = contentType; + } + + @Override + public Encoding contentType(String contentType) { + setContentType(contentType); + return this; + } + + @Override + public Map getHeaders() { + return headers; + } + + @Override + public void setHeaders(Map headers) { + this.headers = headers; + } + + @Override + public Encoding headers(Map headers) { + setHeaders(headers); + return this; + } + + @Override + public Style getStyle() { + return style; + } + + @Override + public void setStyle(Style style) { + this.style = style; + } + + @Override + public Encoding style(Style style) { + setStyle(style); + return this; + } + + @Override + public Boolean getExplode() { + return explode; + } + + @Override + public void setExplode(Boolean explode) { + this.explode = explode; + } + + @Override + public Encoding explode(Boolean explode) { + setExplode(explode); + return this; + } + + @Override + public Boolean getAllowReserved() { + return allowReserved; + } + + @Override + public void setAllowReserved(Boolean allowReserved) { + this.allowReserved = allowReserved; + } + + @Override + public Encoding allowReserved(Boolean allowReserved) { + setAllowReserved(allowReserved); + return this; + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/MediaTypeImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/MediaTypeImpl.java new file mode 100644 index 00000000000..57f75ecedd9 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/MediaTypeImpl.java @@ -0,0 +1,96 @@ +package fish.payara.microprofile.openapi.impl.model.media; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.examples.Example; +import org.eclipse.microprofile.openapi.models.media.Encoding; +import org.eclipse.microprofile.openapi.models.media.MediaType; +import org.eclipse.microprofile.openapi.models.media.Schema; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class MediaTypeImpl extends ExtensibleImpl implements MediaType { + + protected Schema schema; + protected Map examples = new HashMap<>(); + protected Object example; + protected Map encoding = new HashMap<>(); + + @Override + public Schema getSchema() { + return schema; + } + + @Override + public void setSchema(Schema schema) { + this.schema = schema; + } + + @Override + public MediaType schema(Schema schema) { + setSchema(schema); + return this; + } + + @Override + public Map getExamples() { + return examples; + } + + @Override + public void setExamples(Map examples) { + this.examples = examples; + } + + @Override + public MediaType examples(Map examples) { + setExamples(examples); + return this; + } + + @Override + public MediaType addExample(String key, Example example) { + this.examples.put(key, example); + return this; + } + + @Override + public Object getExample() { + return example; + } + + @Override + public void setExample(Object example) { + this.example = example; + } + + @Override + public MediaType example(Object example) { + setExample(example); + return this; + } + + @Override + public Map getEncoding() { + return encoding; + } + + @Override + public void setEncoding(Map encoding) { + this.encoding = encoding; + } + + @Override + public MediaType encoding(Map encoding) { + setEncoding(encoding); + return this; + } + + @Override + public MediaType addEncoding(String key, Encoding encodingItem) { + this.encoding.put(key, encodingItem); + return this; + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/SchemaImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/SchemaImpl.java new file mode 100644 index 00000000000..9049b1c7c2d --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/SchemaImpl.java @@ -0,0 +1,843 @@ +package fish.payara.microprofile.openapi.impl.model.media; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.overwrite; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.microprofile.openapi.annotations.media.DiscriminatorMapping; +import org.eclipse.microprofile.openapi.models.ExternalDocumentation; +import org.eclipse.microprofile.openapi.models.media.Discriminator; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.media.XML; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; +import fish.payara.microprofile.openapi.impl.model.ExternalDocumentationImpl; + +public class SchemaImpl extends ExtensibleImpl implements Schema { + + protected Object defaultValue; + + protected String title; + protected BigDecimal multipleOf; + protected BigDecimal maximum; + protected Boolean exclusiveMaximum; + protected BigDecimal minimum; + protected Boolean exclusiveMinimum; + protected Integer maxLength; + protected Integer minLength; + protected String pattern; + protected Integer maxItems; + protected Integer minItems; + protected Boolean uniqueItems; + protected Integer maxProperties; + protected Integer minProperties; + protected List required = new ArrayList<>(); + protected SchemaType type; + protected Schema not; + protected Map properties = new HashMap<>(); + protected String description; + protected String format; + protected String ref; + protected Boolean nullable; + protected Boolean readOnly; + protected Boolean writeOnly; + protected Object example; + protected ExternalDocumentation externalDocs; + protected Boolean deprecated; + protected XML xml; + protected List enumeration = new ArrayList<>(); + protected Discriminator discriminator; + + protected List anyOf = new ArrayList<>(); + protected List allOf = new ArrayList<>(); + protected List oneOf = new ArrayList<>(); + + protected Object additionalProperties; + protected Schema items; + + @Override + public Discriminator getDiscriminator() { + return discriminator; + } + + @Override + public void setDiscriminator(Discriminator discriminator) { + this.discriminator = discriminator; + } + + @Override + public Schema discriminator(Discriminator discriminator) { + setDiscriminator(discriminator); + return this; + } + + @Override + public String getTitle() { + return title; + } + + @Override + public void setTitle(String title) { + this.title = title; + } + + @Override + public Schema title(String title) { + setTitle(title); + return this; + } + + @Override + public Object getDefaultValue() { + return defaultValue; + } + + @Override + public void setDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + } + + @Override + public Schema defaultValue(Object defaultValue) { + setDefaultValue(defaultValue); + return this; + } + + @Override + public List getEnumeration() { + return enumeration; + } + + @Override + public void setEnumeration(List enumeration) { + this.enumeration = enumeration; + } + + @Override + public Schema addEnumeration(Object enumerationItem) { + this.enumeration.add(enumerationItem); + return this; + } + + @Override + public BigDecimal getMultipleOf() { + return multipleOf; + } + + @Override + public void setMultipleOf(BigDecimal multipleOf) { + this.multipleOf = multipleOf; + } + + @Override + public Schema multipleOf(BigDecimal multipleOf) { + setMultipleOf(multipleOf); + return this; + } + + @Override + public BigDecimal getMaximum() { + return maximum; + } + + @Override + public void setMaximum(BigDecimal maximum) { + this.maximum = maximum; + } + + @Override + public Schema maximum(BigDecimal maximum) { + setMaximum(maximum); + return this; + } + + @Override + public Boolean getExclusiveMaximum() { + return exclusiveMaximum; + } + + @Override + public void setExclusiveMaximum(Boolean exclusiveMaximum) { + this.exclusiveMaximum = exclusiveMaximum; + } + + @Override + public Schema exclusiveMaximum(Boolean exclusiveMaximum) { + setExclusiveMaximum(exclusiveMaximum); + return this; + } + + @Override + public BigDecimal getMinimum() { + return minimum; + } + + @Override + public void setMinimum(BigDecimal minimum) { + this.minimum = minimum; + } + + @Override + public Schema minimum(BigDecimal minimum) { + setMinimum(minimum); + return this; + } + + @Override + public Boolean getExclusiveMinimum() { + return exclusiveMinimum; + } + + @Override + public void setExclusiveMinimum(Boolean exclusiveMinimum) { + this.exclusiveMinimum = exclusiveMinimum; + } + + @Override + public Schema exclusiveMinimum(Boolean exclusiveMinimum) { + setExclusiveMinimum(exclusiveMinimum); + return this; + } + + @Override + public Integer getMaxLength() { + return maxLength; + } + + @Override + public void setMaxLength(Integer maxLength) { + this.maxLength = maxLength; + } + + @Override + public Schema maxLength(Integer maxLength) { + setMaxLength(maxLength); + return this; + } + + @Override + public Integer getMinLength() { + return minLength; + } + + @Override + public void setMinLength(Integer minLength) { + this.minLength = minLength; + } + + @Override + public Schema minLength(Integer minLength) { + setMinLength(minLength); + return this; + } + + @Override + public String getPattern() { + return pattern; + } + + @Override + public void setPattern(String pattern) { + this.pattern = pattern; + } + + @Override + public Schema pattern(String pattern) { + setPattern(pattern); + return this; + } + + @Override + public Integer getMaxItems() { + return maxItems; + } + + @Override + public void setMaxItems(Integer maxItems) { + this.maxItems = maxItems; + } + + @Override + public Schema maxItems(Integer maxItems) { + setMaxItems(maxItems); + return this; + } + + @Override + public Integer getMinItems() { + return minItems; + } + + @Override + public void setMinItems(Integer minItems) { + this.minItems = minItems; + } + + @Override + public Schema minItems(Integer minItems) { + setMinItems(minItems); + return this; + } + + @Override + public Boolean getUniqueItems() { + return uniqueItems; + } + + @Override + public void setUniqueItems(Boolean uniqueItems) { + this.uniqueItems = uniqueItems; + } + + @Override + public Schema uniqueItems(Boolean uniqueItems) { + setUniqueItems(uniqueItems); + return this; + } + + @Override + public Integer getMaxProperties() { + return maxProperties; + } + + @Override + public void setMaxProperties(Integer maxProperties) { + this.maxProperties = maxProperties; + } + + @Override + public Schema maxProperties(Integer maxProperties) { + setMaxProperties(maxProperties); + return this; + } + + @Override + public Integer getMinProperties() { + return minProperties; + } + + @Override + public void setMinProperties(Integer minProperties) { + this.minProperties = minProperties; + } + + @Override + public Schema minProperties(Integer minProperties) { + setMinProperties(minProperties); + return this; + } + + @Override + public List getRequired() { + return required; + } + + @Override + public void setRequired(List required) { + this.required = required; + } + + @Override + public Schema required(List required) { + setRequired(required); + return this; + } + + @Override + public Schema addRequired(String requiredItem) { + this.required.add(requiredItem); + Collections.sort(required); + return this; + } + + @Override + public SchemaType getType() { + return type; + } + + @Override + public void setType(SchemaType type) { + this.type = type; + } + + @Override + public Schema type(SchemaType type) { + setType(type); + return this; + } + + @Override + public Schema getNot() { + return not; + } + + @Override + public void setNot(Schema not) { + this.not = not; + } + + @Override + public Schema not(Schema not) { + setNot(not); + return this; + } + + @Override + public Map getProperties() { + return properties; + } + + @Override + public void setProperties(Map properties) { + this.properties = properties; + } + + @Override + public Schema properties(Map properties) { + setProperties(properties); + return this; + } + + @Override + public Schema addProperty(String key, Schema propertiesItem) { + this.properties.put(key, propertiesItem); + return this; + } + + @Override + public Object getAdditionalProperties() { + return additionalProperties; + } + + @Override + public void setAdditionalProperties(Schema additionalProperties) { + this.additionalProperties = additionalProperties; + } + + @Override + public Schema additionalProperties(Schema additionalProperties) { + setAdditionalProperties(additionalProperties); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public Schema description(String description) { + setDescription(description); + return this; + } + + @Override + public String getFormat() { + return format; + } + + @Override + public void setFormat(String format) { + this.format = format; + } + + @Override + public Schema format(String format) { + setFormat(format); + return this; + } + + @Override + public String getRef() { + return ref; + } + + @Override + public void setRef(String ref) { + if (ref != null && !ref.contains(".") && !ref.contains("/")) { + ref = "#/components/schemas/" + ref; + } + this.ref = ref; + } + + @Override + public Schema ref(String ref) { + setRef(ref); + return this; + } + + @Override + public Boolean getNullable() { + return nullable; + } + + @Override + public void setNullable(Boolean nullable) { + this.nullable = nullable; + } + + @Override + public Schema nullable(Boolean nullable) { + setNullable(nullable); + return this; + } + + @Override + public Boolean getReadOnly() { + return readOnly; + } + + @Override + public void setReadOnly(Boolean readOnly) { + this.readOnly = readOnly; + } + + @Override + public Schema readOnly(Boolean readOnly) { + setReadOnly(readOnly); + return this; + } + + @Override + public Boolean getWriteOnly() { + return writeOnly; + } + + @Override + public void setWriteOnly(Boolean writeOnly) { + this.writeOnly = writeOnly; + } + + @Override + public Schema writeOnly(Boolean writeOnly) { + setWriteOnly(writeOnly); + return this; + } + + @Override + public Object getExample() { + return example; + } + + @Override + public void setExample(Object example) { + this.example = example; + } + + @Override + public Schema example(Object example) { + setExample(example); + return this; + } + + @Override + public ExternalDocumentation getExternalDocs() { + return externalDocs; + } + + @Override + public void setExternalDocs(ExternalDocumentation externalDocs) { + this.externalDocs = externalDocs; + } + + @Override + public Schema externalDocs(ExternalDocumentation externalDocs) { + setExternalDocs(externalDocs); + return this; + } + + @Override + public Boolean getDeprecated() { + return deprecated; + } + + @Override + public void setDeprecated(Boolean deprecated) { + this.deprecated = deprecated; + } + + @Override + public Schema deprecated(Boolean deprecated) { + setDeprecated(deprecated); + return this; + } + + @Override + public XML getXml() { + return xml; + } + + @Override + public void setXml(XML xml) { + this.xml = xml; + } + + @Override + public Schema xml(XML xml) { + setXml(xml); + return this; + } + + @Override + public Schema enumeration(List enumeration) { + setEnumeration(enumeration); + return this; + } + + @Override + public Schema getItems() { + return this.items; + } + + @Override + public void setItems(Schema items) { + this.items = items; + } + + @Override + public Schema items(Schema items) { + setItems(items); + return this; + } + + @Override + public List getAllOf() { + return allOf; + } + + @Override + public void setAllOf(List allOf) { + this.allOf = allOf; + } + + @Override + public Schema allOf(List allOf) { + setAllOf(allOf); + return this; + } + + @Override + public Schema addAllOf(Schema allOf) { + this.allOf.add(allOf); + return this; + } + + @Override + public List getAnyOf() { + return this.anyOf; + } + + @Override + public void setAnyOf(List anyOf) { + this.anyOf = anyOf; + } + + @Override + public Schema anyOf(List anyOf) { + setAnyOf(anyOf); + return this; + } + + @Override + public Schema addAnyOf(Schema anyOf) { + this.anyOf.add(anyOf); + return this; + } + + @Override + public List getOneOf() { + return this.oneOf; + } + + @Override + public void setOneOf(List oneOf) { + this.oneOf = oneOf; + } + + @Override + public Schema oneOf(List oneOf) { + setOneOf(oneOf); + return this; + } + + @Override + public Schema addOneOf(Schema oneOf) { + this.oneOf.add(oneOf); + return this; + } + + @Override + public void setAdditionalProperties(Boolean additionalProperties) { + this.additionalProperties = additionalProperties; + } + + @Override + public Schema additionalProperties(Boolean additionalProperties) { + setAdditionalProperties(additionalProperties); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.media.Schema from, Schema to, + boolean override, Map currentSchemas) { + if (isAnnotationNull(from)) { + return; + } + if (from.ref() != null && !from.ref().isEmpty()) { + applyReference(to, from.ref()); + return; + } + if (from.implementation() != null && currentSchemas != null) { + Class implementationClass = from.implementation(); + + // If the implementation class is a schema + if (implementationClass + .isAnnotationPresent(org.eclipse.microprofile.openapi.annotations.media.Schema.class)) { + + // Get the schema name + String schemaName = implementationClass + .getDeclaredAnnotation(org.eclipse.microprofile.openapi.annotations.media.Schema.class).name(); + if (schemaName == null) { + schemaName = implementationClass.getSimpleName(); + } + + // Get the schema reference, and copy it's values over to the new schema model + Schema copyFrom = currentSchemas.get(schemaName); + overwrite(copyFrom, to); + } + } + if (from.discriminatorMapping() != null && !from.discriminatorProperty().isEmpty()) { + if (to.getDiscriminator() == null) { + to.setDiscriminator(new DiscriminatorImpl()); + } + Discriminator discriminator = to.getDiscriminator(); + discriminator.setPropertyName( + mergeProperty(discriminator.getPropertyName(), from.discriminatorProperty(), override)); + for (DiscriminatorMapping mapping : from.discriminatorMapping()) { + discriminator.addMapping(mapping.value(), mapping.schema().getSimpleName()); + } + } + to.setTitle(mergeProperty(to.getTitle(), from.title(), override)); + to.setDefaultValue(mergeProperty(to.getDefaultValue(), from.defaultValue(), override)); + if (from.enumeration() != null && from.enumeration().length > 0) { + if (to.getEnumeration() == null) { + to.setEnumeration(new ArrayList<>()); + } + for (String value : from.enumeration()) { + if (!to.getEnumeration().contains(value)) { + to.addEnumeration(value); + } + } + } + if (from.multipleOf() > 0) { + to.setMultipleOf(mergeProperty(to.getMultipleOf(), BigDecimal.valueOf(from.multipleOf()), override)); + } + if (!from.maximum().isEmpty()) { + to.setMaximum(mergeProperty(to.getMaximum(), new BigDecimal(from.maximum()), override)); + } + if (!from.minimum().isEmpty()) { + to.setMinimum(mergeProperty(to.getMinimum(), new BigDecimal(from.minimum()), override)); + } + to.setExclusiveMaximum(mergeProperty(to.getExclusiveMaximum(), from.exclusiveMaximum(), override)); + to.setExclusiveMinimum(mergeProperty(to.getExclusiveMinimum(), from.exclusiveMinimum(), override)); + to.setMaxLength(mergeProperty(to.getMaxLength(), from.maxLength(), override)); + to.setMinLength(mergeProperty(to.getMinLength(), from.minLength(), override)); + to.setMaxItems(mergeProperty(to.getMaxItems(), from.maxItems(), override)); + to.setMinItems(mergeProperty(to.getMinItems(), from.minItems(), override)); + to.setMaxProperties(mergeProperty(to.getMaxProperties(), from.maxProperties(), override)); + to.setMinProperties(mergeProperty(to.getMinProperties(), from.minProperties(), override)); + to.setUniqueItems(mergeProperty(to.getUniqueItems(), from.uniqueItems(), override)); + to.setPattern(mergeProperty(to.getPattern(), from.pattern(), override)); + if (from.requiredProperties() != null && from.requiredProperties().length > 0) { + if (to.getRequired() == null) { + to.setRequired(new ArrayList<>()); + } + for (String value : from.requiredProperties()) { + if (!to.getRequired().contains(value)) { + to.addRequired(value); + } + } + } + if (from.type() != null + && from.type() != org.eclipse.microprofile.openapi.annotations.enums.SchemaType.DEFAULT) { + to.setType(mergeProperty(to.getType(), SchemaType.valueOf(from.type().name()), override)); + } + if (from.not() != null) { + if (Schema.class.isAssignableFrom(from.not())) { + try { + to.setNot((Schema) from.not().newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + to.setFormat(mergeProperty(to.getFormat(), from.format(), override)); + to.setNullable(mergeProperty(to.getNullable(), from.nullable(), override)); + to.setReadOnly(mergeProperty(to.getReadOnly(), from.readOnly(), override)); + to.setWriteOnly(mergeProperty(to.getWriteOnly(), from.writeOnly(), override)); + to.setExample(mergeProperty(to.getExample(), from.example(), override)); + if (!isAnnotationNull(from.externalDocs())) { + if (to.getExternalDocs() == null) { + to.setExternalDocs(new ExternalDocumentationImpl()); + } + ExternalDocumentationImpl.merge(from.externalDocs(), to.getExternalDocs(), override); + } + to.setDeprecated(mergeProperty(to.getDeprecated(), from.deprecated(), override)); + if (from.allOf() != null) { + for (Class allOfClass : from.allOf()) { + if (Schema.class.isAssignableFrom(allOfClass)) { + if (to.getAllOf() == null) { + to.setAllOf(new ArrayList<>()); + } + try { + to.addAllOf((Schema) allOfClass.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + if (from.anyOf() != null) { + for (Class anyOfClass : from.anyOf()) { + if (Schema.class.isAssignableFrom(anyOfClass)) { + if (to.getAnyOf() == null) { + to.setAnyOf(new ArrayList<>()); + } + try { + to.addAnyOf((Schema) anyOfClass.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + if (from.oneOf() != null) { + for (Class oneOfClass : from.oneOf()) { + if (Schema.class.isAssignableFrom(oneOfClass)) { + if (to.getOneOf() == null) { + to.setOneOf(new ArrayList<>()); + } + try { + to.addOneOf((Schema) oneOfClass.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + } + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/XMLImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/XMLImpl.java new file mode 100644 index 00000000000..185d4ea24ac --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/XMLImpl.java @@ -0,0 +1,95 @@ +package fish.payara.microprofile.openapi.impl.model.media; + +import org.eclipse.microprofile.openapi.models.media.XML; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class XMLImpl extends ExtensibleImpl implements XML { + + protected String name; + protected String namespace; + protected String prefix; + protected Boolean attribute; + protected Boolean wrapped; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public XML name(String name) { + setName(name); + return this; + } + + @Override + public String getNamespace() { + return namespace; + } + + @Override + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + @Override + public XML namespace(String namespace) { + setNamespace(namespace); + return this; + } + + @Override + public String getPrefix() { + return prefix; + } + + @Override + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + @Override + public XML prefix(String prefix) { + setPrefix(prefix); + return this; + } + + @Override + public Boolean getAttribute() { + return attribute; + } + + @Override + public void setAttribute(Boolean attribute) { + this.attribute = attribute; + } + + @Override + public XML attribute(Boolean attribute) { + setAttribute(attribute); + return this; + } + + @Override + public Boolean getWrapped() { + return wrapped; + } + + @Override + public void setWrapped(Boolean wrapped) { + this.wrapped = wrapped; + } + + @Override + public XML wrapped(Boolean wrapped) { + setWrapped(wrapped); + return this; + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/ParameterImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/ParameterImpl.java new file mode 100644 index 00000000000..8369ddf5b49 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/ParameterImpl.java @@ -0,0 +1,323 @@ +package fish.payara.microprofile.openapi.impl.model.parameters; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.annotations.enums.Explode; +import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn; +import org.eclipse.microprofile.openapi.annotations.enums.ParameterStyle; +import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; +import org.eclipse.microprofile.openapi.models.examples.Example; +import org.eclipse.microprofile.openapi.models.media.Content; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.parameters.Parameter; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; +import fish.payara.microprofile.openapi.impl.model.examples.ExampleImpl; +import fish.payara.microprofile.openapi.impl.model.media.ContentImpl; +import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; + +public class ParameterImpl extends ExtensibleImpl implements Parameter { + + protected String name; + protected In in; + protected String description; + protected Boolean required; + protected Boolean deprecated; + protected Boolean allowEmptyValue; + protected String ref; + + protected Style style; + protected Boolean explode; + protected Boolean allowReserved; + protected Schema schema; + protected Map examples = new HashMap<>(); + protected Object example; + protected Content content; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public Parameter name(String name) { + setName(name); + return this; + } + + @Override + public In getIn() { + return in; + } + + @Override + public void setIn(In in) { + this.in = in; + } + + @Override + public Parameter in(In in) { + setIn(in); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public Parameter description(String description) { + setDescription(description); + return this; + } + + @Override + public Boolean getRequired() { + return required; + } + + @Override + public void setRequired(Boolean required) { + this.required = required; + } + + @Override + public Parameter required(Boolean required) { + setRequired(required); + return this; + } + + @Override + public Boolean getDeprecated() { + return deprecated; + } + + @Override + public void setDeprecated(Boolean deprecated) { + this.deprecated = deprecated; + } + + @Override + public Parameter deprecated(Boolean deprecated) { + setDeprecated(deprecated); + return this; + } + + @Override + public Boolean getAllowEmptyValue() { + return allowEmptyValue; + } + + @Override + public void setAllowEmptyValue(Boolean allowEmptyValue) { + this.allowEmptyValue = allowEmptyValue; + } + + @Override + public Parameter allowEmptyValue(Boolean allowEmptyValue) { + setAllowEmptyValue(allowEmptyValue); + return this; + } + + @Override + public Style getStyle() { + return style; + } + + @Override + public void setStyle(Style style) { + this.style = style; + } + + @Override + public Parameter style(Style style) { + setStyle(style); + return this; + } + + @Override + public Boolean getExplode() { + return explode; + } + + @Override + public void setExplode(Boolean explode) { + this.explode = explode; + } + + @Override + public Parameter explode(Boolean explode) { + setExplode(explode); + return this; + } + + @Override + public Boolean getAllowReserved() { + return allowReserved; + } + + @Override + public void setAllowReserved(Boolean allowReserved) { + this.allowReserved = allowReserved; + } + + @Override + public Parameter allowReserved(Boolean allowReserved) { + setAllowReserved(allowReserved); + return this; + } + + @Override + public Schema getSchema() { + return schema; + } + + @Override + public void setSchema(Schema schema) { + this.schema = schema; + } + + @Override + public Parameter schema(Schema schema) { + setSchema(schema); + return this; + } + + @Override + public Map getExamples() { + return examples; + } + + @Override + public void setExamples(Map examples) { + this.examples = examples; + } + + @Override + public Parameter examples(Map examples) { + setExamples(examples); + return this; + } + + @Override + public Parameter addExample(String key, Example example) { + this.examples.put(key, example); + return this; + } + + @Override + public Object getExample() { + return example; + } + + @Override + public void setExample(Object example) { + this.example = example; + } + + @Override + public Parameter example(Object example) { + setExample(example); + return this; + } + + @Override + public Content getContent() { + return content; + } + + @Override + public void setContent(Content content) { + this.content = content; + } + + @Override + public Parameter content(Content content) { + setContent(content); + return this; + } + + @Override + public String getRef() { + return ref; + } + + @Override + public void setRef(String ref) { + if (ref != null && !ref.contains(".") && !ref.contains("/")) { + ref = "#/components/parameters/" + ref; + } + this.ref = ref; + } + + @Override + public Parameter ref(String ref) { + setRef(ref); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.parameters.Parameter from, Parameter to, + boolean override, Map currentSchemas) { + if (isAnnotationNull(from)) { + return; + } + if (from.ref() != null && !from.ref().isEmpty()) { + applyReference(to, from.ref()); + return; + } + to.setName(mergeProperty(to.getName(), from.name(), override)); + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + if (from.in() != null && from.in() != ParameterIn.DEFAULT) { + to.setIn(mergeProperty(to.getIn(), Parameter.In.valueOf(from.in().name()), override)); + } + to.setRequired(mergeProperty(to.getRequired(), from.required(), override)); + to.setDeprecated(mergeProperty(to.getDeprecated(), from.deprecated(), override)); + to.setAllowEmptyValue(mergeProperty(to.getAllowEmptyValue(), from.allowEmptyValue(), override)); + if (from.style() != null && from.style() != ParameterStyle.DEFAULT) { + to.setStyle(mergeProperty(to.getStyle(), Style.valueOf(from.style().name()), override)); + } + if (from.explode() != null && from.explode() != Explode.DEFAULT) { + to.setExplode(mergeProperty(to.getExplode(), false, override)); + } + to.setAllowReserved(mergeProperty(to.getAllowReserved(), from.allowReserved(), override)); + if (!isAnnotationNull(from.schema())) { + if (to.getSchema() == null) { + to.setSchema(new SchemaImpl()); + } + SchemaImpl.merge(from.schema(), to.getSchema(), override, currentSchemas); + } + to.setExample(mergeProperty(to.getExample(), from.example(), override)); + if (from.examples() != null) { + for (ExampleObject exampleObject : from.examples()) { + Example example = new ExampleImpl(); + ExampleImpl.merge(exampleObject, example, override); + to.addExample(exampleObject.name(), example); + } + } + if (from.content() != null) { + for (org.eclipse.microprofile.openapi.annotations.media.Content content : from.content()) { + if (to.getContent() == null) { + to.setContent(new ContentImpl()); + } + ContentImpl.merge(content, to.getContent(), override, null); + } + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/RequestBodyImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/RequestBodyImpl.java new file mode 100644 index 00000000000..64f74f3840b --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/RequestBodyImpl.java @@ -0,0 +1,112 @@ +package fish.payara.microprofile.openapi.impl.model.parameters; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.media.Content; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.parameters.RequestBody; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; +import fish.payara.microprofile.openapi.impl.model.media.ContentImpl; + +public class RequestBodyImpl extends ExtensibleImpl implements RequestBody { + + protected String description; + protected Content content = new ContentImpl(); + protected Boolean required; + protected String ref; + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public RequestBody description(String description) { + setDescription(description); + return this; + } + + @Override + public Content getContent() { + return content; + } + + @Override + public void setContent(Content content) { + this.content = content; + } + + @Override + public RequestBody content(Content content) { + setContent(content); + return this; + } + + @Override + public Boolean getRequired() { + return required; + } + + @Override + public void setRequired(Boolean required) { + this.required = required; + } + + @Override + public RequestBody required(Boolean required) { + setRequired(required); + return this; + } + + @Override + public String getRef() { + return ref; + } + + @Override + public void setRef(String ref) { + if (ref != null && !ref.contains(".") && !ref.contains("/")) { + ref = "#/components/requestBodies/" + ref; + } + this.ref = ref; + } + + @Override + public RequestBody ref(String ref) { + setRef(ref); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.parameters.RequestBody from, RequestBody to, + boolean override, Map currentSchemas) { + if (isAnnotationNull(from)) { + return; + } + if (from.ref() != null && !from.ref().isEmpty()) { + applyReference(to, from.ref()); + return; + } + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + to.setRequired(mergeProperty(to.getRequired(), from.required(), override)); + if (from.content() != null) { + for (org.eclipse.microprofile.openapi.annotations.media.Content content : from.content()) { + if (to.getContent() == null) { + to.setContent(new ContentImpl()); + } + ContentImpl.merge(content, to.getContent(), override, currentSchemas); + } + } + + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponseImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponseImpl.java new file mode 100644 index 00000000000..1113ff85857 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponseImpl.java @@ -0,0 +1,142 @@ +package fish.payara.microprofile.openapi.impl.model.responses; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.headers.Header; +import org.eclipse.microprofile.openapi.models.links.Link; +import org.eclipse.microprofile.openapi.models.media.Content; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.responses.APIResponse; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; +import fish.payara.microprofile.openapi.impl.model.media.ContentImpl; + +public class APIResponseImpl extends ExtensibleImpl implements APIResponse { + + protected String description; + protected Map headers = new HashMap<>(); + protected Content content = new ContentImpl(); + protected Map links = new HashMap<>(); + protected String ref; + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public APIResponse description(String description) { + setDescription(description); + return this; + } + + @Override + public Map getHeaders() { + return headers; + } + + @Override + public void setHeaders(Map headers) { + this.headers = headers; + } + + @Override + public APIResponse headers(Map headers) { + setHeaders(headers); + return this; + } + + @Override + public APIResponse addHeader(String name, Header header) { + headers.put(name, header); + return this; + } + + @Override + public Content getContent() { + return content; + } + + @Override + public void setContent(Content content) { + this.content = content; + } + + @Override + public APIResponse content(Content content) { + setContent(content); + return this; + } + + @Override + public Map getLinks() { + return links; + } + + @Override + public void setLinks(Map links) { + this.links = links; + } + + @Override + public APIResponse links(Map links) { + setLinks(links); + return this; + } + + @Override + public APIResponse addLink(String name, Link link) { + links.put(name, link); + return this; + } + + @Override + public String getRef() { + return ref; + } + + @Override + public void setRef(String ref) { + if (ref != null && !ref.contains(".") && !ref.contains("/")) { + ref = "#/components/responses/" + ref; + } + this.ref = ref; + } + + @Override + public APIResponse ref(String ref) { + setRef(ref); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.responses.APIResponse from, APIResponse to, + boolean override, Map currentSchemas) { + if (isAnnotationNull(from)) { + return; + } + if (from.ref() != null && !from.ref().isEmpty()) { + applyReference(to, from.ref()); + return; + } + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + if (from.content() != null) { + for (org.eclipse.microprofile.openapi.annotations.media.Content content : from.content()) { + if (to.getContent() == null) { + to.setContent(new ContentImpl()); + } + ContentImpl.merge(content, to.getContent(), override, currentSchemas); + } + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponsesImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponsesImpl.java new file mode 100644 index 00000000000..e5b99ef2d8e --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponsesImpl.java @@ -0,0 +1,34 @@ +package fish.payara.microprofile.openapi.impl.model.responses; + +import java.util.LinkedHashMap; + +import org.eclipse.microprofile.openapi.models.responses.APIResponse; +import org.eclipse.microprofile.openapi.models.responses.APIResponses; + +public class APIResponsesImpl extends LinkedHashMap implements APIResponses { + + private static final long serialVersionUID = 2811935761440110541L; + + @Override + public APIResponses addApiResponse(String name, APIResponse item) { + put(name, item); + return this; + } + + @Override + public APIResponse getDefault() { + return this.get(DEFAULT); + } + + @Override + public void setDefaultValue(APIResponse defaultValue) { + addApiResponse(DEFAULT, defaultValue); + } + + @Override + public APIResponses defaultValue(APIResponse defaultValue) { + setDefaultValue(defaultValue); + return this; + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowImpl.java new file mode 100644 index 00000000000..5dae52a7496 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowImpl.java @@ -0,0 +1,100 @@ +package fish.payara.microprofile.openapi.impl.model.security; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import org.eclipse.microprofile.openapi.annotations.security.OAuthScope; +import org.eclipse.microprofile.openapi.models.security.OAuthFlow; +import org.eclipse.microprofile.openapi.models.security.Scopes; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class OAuthFlowImpl extends ExtensibleImpl implements OAuthFlow { + + protected String authorizationUrl; + protected String tokenUrl; + protected String refreshUrl; + protected Scopes scopes; + + @Override + public String getAuthorizationUrl() { + return authorizationUrl; + } + + @Override + public void setAuthorizationUrl(String authorizationUrl) { + this.authorizationUrl = authorizationUrl; + } + + @Override + public OAuthFlow authorizationUrl(String authorizationUrl) { + setAuthorizationUrl(authorizationUrl); + return this; + } + + @Override + public String getTokenUrl() { + return tokenUrl; + } + + @Override + public void setTokenUrl(String tokenUrl) { + this.tokenUrl = tokenUrl; + } + + @Override + public OAuthFlow tokenUrl(String tokenUrl) { + setTokenUrl(tokenUrl); + return this; + } + + @Override + public String getRefreshUrl() { + return refreshUrl; + } + + @Override + public void setRefreshUrl(String refreshUrl) { + this.refreshUrl = refreshUrl; + } + + @Override + public OAuthFlow refreshUrl(String refreshUrl) { + setRefreshUrl(refreshUrl); + return this; + } + + @Override + public Scopes getScopes() { + return scopes; + } + + @Override + public void setScopes(Scopes scopes) { + this.scopes = scopes; + } + + @Override + public OAuthFlow scopes(Scopes scopes) { + setScopes(scopes); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.security.OAuthFlow from, OAuthFlow to, + boolean override) { + if (isAnnotationNull(from)) { + return; + } + to.setTokenUrl(mergeProperty(to.getTokenUrl(), from.tokenUrl(), override)); + if (from.scopes() != null) { + Scopes scopes = new ScopesImpl(); + for (OAuthScope scope : from.scopes()) { + scopes.addScope(scope.name(), scope.description()); + } + to.setScopes(mergeProperty(to.getScopes(), scopes, override)); + } + to.setRefreshUrl(mergeProperty(to.getRefreshUrl(), from.refreshUrl(), override)); + to.setAuthorizationUrl(mergeProperty(to.getAuthorizationUrl(), from.authorizationUrl(), override)); + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowsImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowsImpl.java new file mode 100644 index 00000000000..b132b90f819 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowsImpl.java @@ -0,0 +1,109 @@ +package fish.payara.microprofile.openapi.impl.model.security; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import org.eclipse.microprofile.openapi.models.security.OAuthFlow; +import org.eclipse.microprofile.openapi.models.security.OAuthFlows; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class OAuthFlowsImpl extends ExtensibleImpl implements OAuthFlows { + + protected OAuthFlow implicit; + protected OAuthFlow password; + protected OAuthFlow clientCredentials; + protected OAuthFlow authorizationCode; + + @Override + public OAuthFlow getImplicit() { + return implicit; + } + + @Override + public void setImplicit(OAuthFlow implicit) { + this.implicit = implicit; + } + + @Override + public OAuthFlows implicit(OAuthFlow implicit) { + setImplicit(implicit); + return this; + } + + @Override + public OAuthFlow getPassword() { + return password; + } + + @Override + public void setPassword(OAuthFlow password) { + this.password = password; + } + + @Override + public OAuthFlows password(OAuthFlow password) { + setPassword(password); + return this; + } + + @Override + public OAuthFlow getClientCredentials() { + return clientCredentials; + } + + @Override + public void setClientCredentials(OAuthFlow clientCredentials) { + this.clientCredentials = clientCredentials; + } + + @Override + public OAuthFlows clientCredentials(OAuthFlow clientCredentials) { + setClientCredentials(clientCredentials); + return this; + } + + @Override + public OAuthFlow getAuthorizationCode() { + return authorizationCode; + } + + @Override + public void setAuthorizationCode(OAuthFlow authorizationCode) { + this.authorizationCode = authorizationCode; + } + + @Override + public OAuthFlows authorizationCode(OAuthFlow authorizationCode) { + setAuthorizationCode(authorizationCode); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.security.OAuthFlows from, OAuthFlows to, + boolean override) { + if (from == null) { + return; + } + if (!isAnnotationNull(from.password())) { + OAuthFlow flow = new OAuthFlowImpl(); + OAuthFlowImpl.merge(from.password(), flow, override); + to.setPassword(mergeProperty(to.getPassword(), flow, override)); + } + if (!isAnnotationNull(from.authorizationCode())) { + OAuthFlow flow = new OAuthFlowImpl(); + OAuthFlowImpl.merge(from.authorizationCode(), flow, override); + to.setAuthorizationCode(mergeProperty(to.getAuthorizationCode(), flow, override)); + } + if (!isAnnotationNull(from.clientCredentials())) { + OAuthFlow flow = new OAuthFlowImpl(); + OAuthFlowImpl.merge(from.clientCredentials(), flow, override); + to.setClientCredentials(mergeProperty(to.getClientCredentials(), flow, override)); + } + if (!isAnnotationNull(from.implicit())) { + OAuthFlow flow = new OAuthFlowImpl(); + OAuthFlowImpl.merge(from.implicit(), flow, override); + to.setImplicit(mergeProperty(to.getImplicit(), flow, override)); + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/ScopesImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/ScopesImpl.java new file mode 100644 index 00000000000..e526491f111 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/ScopesImpl.java @@ -0,0 +1,36 @@ +package fish.payara.microprofile.openapi.impl.model.security; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.security.Scopes; + +public class ScopesImpl extends LinkedHashMap implements Scopes { + + private static final long serialVersionUID = -615440059031779085L; + + protected Map extensions = new HashMap<>(); + + @Override + public Scopes addScope(String name, String item) { + this.put(name, item); + return this; + } + + @Override + public Map getExtensions() { + return extensions; + } + + @Override + public void setExtensions(Map extensions) { + this.extensions = extensions; + } + + @Override + public void addExtension(String name, Object value) { + this.extensions.put(name, value); + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecurityRequirementImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecurityRequirementImpl.java new file mode 100644 index 00000000000..b5d9573bb6e --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecurityRequirementImpl.java @@ -0,0 +1,44 @@ +package fish.payara.microprofile.openapi.impl.model.security; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; + +import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; + +public class SecurityRequirementImpl extends LinkedHashMap> implements SecurityRequirement { + + private static final long serialVersionUID = -677783376083861245L; + + @Override + public SecurityRequirement addScheme(String name, String item) { + this.put(name, Arrays.asList(item)); + return this; + } + + @Override + public SecurityRequirement addScheme(String name, List item) { + this.put(name, item); + return this; + } + + @Override + public SecurityRequirement addScheme(String name) { + this.put(name, new ArrayList<>()); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement from, + SecurityRequirement to, boolean override) { + if (isAnnotationNull(from) || from.scopes().length == 0) { + return; + } + if (from.name() != null && !from.name().isEmpty()) { + to.addScheme(from.name(), Arrays.asList(from.scopes())); + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecuritySchemeImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecuritySchemeImpl.java new file mode 100644 index 00000000000..ba3e154c7f4 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecuritySchemeImpl.java @@ -0,0 +1,206 @@ +package fish.payara.microprofile.openapi.impl.model.security; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeIn; +import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType; +import org.eclipse.microprofile.openapi.models.security.OAuthFlows; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class SecuritySchemeImpl extends ExtensibleImpl implements SecurityScheme { + + protected SecurityScheme.Type type; + protected String description; + protected String name; + protected String schemeName; + protected String ref; + + protected SecurityScheme.In in; + protected String scheme; + protected String bearerFormat; + protected OAuthFlows flows; + protected String openIdConnectUrl; + + @Override + public SecurityScheme.Type getType() { + return type; + } + + @Override + public void setType(SecurityScheme.Type type) { + this.type = type; + } + + @Override + public SecurityScheme type(SecurityScheme.Type type) { + setType(type); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public SecurityScheme description(String description) { + setDescription(description); + return this; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public SecurityScheme name(String name) { + setName(name); + return this; + } + + @Override + public SecurityScheme.In getIn() { + return in; + } + + @Override + public void setIn(SecurityScheme.In in) { + this.in = in; + } + + @Override + public SecurityScheme in(SecurityScheme.In in) { + setIn(in); + return this; + } + + @Override + public String getScheme() { + return scheme; + } + + @Override + public void setScheme(String scheme) { + this.scheme = scheme; + } + + @Override + public SecurityScheme scheme(String scheme) { + setScheme(scheme); + return this; + } + + @Override + public String getBearerFormat() { + return bearerFormat; + } + + @Override + public void setBearerFormat(String bearerFormat) { + this.bearerFormat = bearerFormat; + } + + @Override + public SecurityScheme bearerFormat(String bearerFormat) { + setBearerFormat(bearerFormat); + return this; + } + + @Override + public OAuthFlows getFlows() { + return flows; + } + + @Override + public void setFlows(OAuthFlows flows) { + this.flows = flows; + } + + @Override + public SecurityScheme flows(OAuthFlows flows) { + setFlows(flows); + return this; + } + + @Override + public String getOpenIdConnectUrl() { + return openIdConnectUrl; + } + + @Override + public void setOpenIdConnectUrl(String openIdConnectUrl) { + this.openIdConnectUrl = openIdConnectUrl; + } + + @Override + public SecurityScheme openIdConnectUrl(String openIdConnectUrl) { + setOpenIdConnectUrl(openIdConnectUrl); + return this; + } + + @Override + public String getRef() { + return ref; + } + + @Override + public void setRef(String ref) { + if (ref != null && !ref.contains(".") && !ref.contains("/")) { + ref = "#/components/securitySchemes/" + ref; + } + this.ref = ref; + } + + @Override + public SecurityScheme ref(String ref) { + setRef(ref); + return this; + } + + @JsonIgnore + public String getSchemeName() { + return schemeName; + } + + public void setSchemeName(String schemeName) { + this.schemeName = schemeName; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.security.SecurityScheme from, + SecurityScheme to, boolean override) { + if (isAnnotationNull(from)) { + return; + } + to.setName(mergeProperty(to.getName(), from.apiKeyName(), override)); + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + to.setScheme(mergeProperty(to.getScheme(), from.scheme(), override)); + if (from.in() != null && from.in() != SecuritySchemeIn.DEFAULT) { + to.setIn(mergeProperty(to.getIn(), In.valueOf(from.in().name()), override)); + } + if (from.type() != null && from.type() != SecuritySchemeType.DEFAULT) { + to.setType(mergeProperty(to.getType(), Type.valueOf(from.type().name()), override)); + } + if (from.flows() != null) { + OAuthFlows flows = new OAuthFlowsImpl(); + OAuthFlowsImpl.merge(from.flows(), flows, override); + to.setFlows(mergeProperty(to.getFlows(), flows, override)); + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerImpl.java new file mode 100644 index 00000000000..7b91a4503be --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerImpl.java @@ -0,0 +1,83 @@ +package fish.payara.microprofile.openapi.impl.model.servers; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import org.eclipse.microprofile.openapi.annotations.servers.ServerVariable; +import org.eclipse.microprofile.openapi.models.servers.Server; +import org.eclipse.microprofile.openapi.models.servers.ServerVariables; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class ServerImpl extends ExtensibleImpl implements Server { + + protected String url; + protected String description; + protected ServerVariables variables; + + @Override + public String getUrl() { + return url; + } + + @Override + public void setUrl(String url) { + this.url = url; + } + + @Override + public Server url(String url) { + setUrl(url); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public Server description(String description) { + setDescription(description); + return this; + } + + @Override + public ServerVariables getVariables() { + return variables; + } + + @Override + public void setVariables(ServerVariables variables) { + this.variables = variables; + } + + @Override + public Server variables(ServerVariables variables) { + setVariables(variables); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.servers.Server from, Server to, + boolean override) { + if (isAnnotationNull(from)) { + return; + } + to.setUrl(mergeProperty(to.getUrl(), from.url(), override)); + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + if (from.variables() != null) { + if (to.getVariables() == null) { + to.setVariables(new ServerVariablesImpl()); + } + for (ServerVariable variable : from.variables()) { + ServerVariablesImpl.merge(variable, to.getVariables(), override); + } + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariableImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariableImpl.java new file mode 100644 index 00000000000..76b09e2de8b --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariableImpl.java @@ -0,0 +1,70 @@ +package fish.payara.microprofile.openapi.impl.model.servers; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.microprofile.openapi.models.servers.ServerVariable; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; + +public class ServerVariableImpl extends ExtensibleImpl implements ServerVariable { + + protected List enumeration = new ArrayList<>(); + protected String defaultValue; + protected String description; + + @Override + public List getEnumeration() { + return enumeration; + } + + @Override + public void setEnumeration(List enumeration) { + this.enumeration = enumeration; + } + + @Override + public ServerVariable enumeration(List enumeration) { + setEnumeration(enumeration); + return this; + } + + @Override + public ServerVariable addEnumeration(String enumeration) { + this.enumeration.add(enumeration); + return this; + } + + @Override + public String getDefaultValue() { + return defaultValue; + } + + @Override + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + @Override + public ServerVariable defaultValue(String defaultValue) { + setDefaultValue(defaultValue); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public ServerVariable description(String description) { + setDescription(description); + return this; + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariablesImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariablesImpl.java new file mode 100644 index 00000000000..02ba1577fd3 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariablesImpl.java @@ -0,0 +1,64 @@ +package fish.payara.microprofile.openapi.impl.model.servers; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.servers.ServerVariable; +import org.eclipse.microprofile.openapi.models.servers.ServerVariables; + +public class ServerVariablesImpl extends LinkedHashMap implements ServerVariables { + + private static final long serialVersionUID = 8869393484826870024L; + + protected Map extensions = new HashMap<>(); + + @Override + public ServerVariables addServerVariable(String name, ServerVariable item) { + this.put(name, item); + return this; + } + + @Override + public Map getExtensions() { + return extensions; + } + + @Override + public void setExtensions(Map extensions) { + this.extensions = extensions; + } + + @Override + public void addExtension(String name, Object value) { + this.extensions.put(name, value); + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.servers.ServerVariable from, + ServerVariables to, boolean override) { + if (isAnnotationNull(from)) { + return; + } + org.eclipse.microprofile.openapi.models.servers.ServerVariable variable = new ServerVariableImpl(); + variable.setDefaultValue(mergeProperty(variable.getDefaultValue(), from.defaultValue(), override)); + variable.setDescription(mergeProperty(variable.getDefaultValue(), from.description(), override)); + if (from.enumeration() != null && from.enumeration().length != 0) { + if (variable.getEnumeration() == null) { + variable.setEnumeration(new ArrayList<>()); + } + for (String value : from.enumeration()) { + if (!variable.getEnumeration().contains(value)) { + variable.addEnumeration(value); + } + } + } + if ((to.containsKey(from.name()) && override) || !to.containsKey(from.name())) { + to.addServerVariable(from.name(), variable); + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/tags/TagImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/tags/TagImpl.java new file mode 100644 index 00000000000..cdac0ab6ea1 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/tags/TagImpl.java @@ -0,0 +1,112 @@ +package fish.payara.microprofile.openapi.impl.model.tags; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + +import java.util.List; + +import org.eclipse.microprofile.openapi.models.ExternalDocumentation; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.tags.Tag; + +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; +import fish.payara.microprofile.openapi.impl.model.ExternalDocumentationImpl; + +public class TagImpl extends ExtensibleImpl implements Tag { + + protected String name; + protected String description; + protected ExternalDocumentation externalDocs; + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public Tag name(String name) { + setName(name); + return this; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public Tag description(String description) { + setDescription(description); + return this; + } + + @Override + public ExternalDocumentation getExternalDocs() { + return externalDocs; + } + + @Override + public void setExternalDocs(ExternalDocumentation externalDocs) { + this.externalDocs = externalDocs; + } + + @Override + public Tag externalDocs(ExternalDocumentation externalDocs) { + setExternalDocs(externalDocs); + return this; + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.tags.Tag from, Tag to, boolean override) { + if (isAnnotationNull(from)) { + return; + } + to.setName(mergeProperty(to.getName(), from.name(), override)); + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + if (!isAnnotationNull(from.externalDocs())) { + if (to.getExternalDocs() == null) { + to.setExternalDocs(new ExternalDocumentationImpl()); + } + ExternalDocumentationImpl.merge(from.externalDocs(), to.getExternalDocs(), override); + } + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.tags.Tag from, Operation to, boolean override, + List apiTags) { + if (isAnnotationNull(from)) { + return; + } + + String tagName = from.name(); + + Tag tag = getExistingTag(from.ref(), apiTags); + if (tag == null) { + tag = new TagImpl(); + apiTags.add(tag); + } else { + tagName = tag.getName(); + } + merge(from, tag, override); + to.addTag(tagName); + } + + private static Tag getExistingTag(String name, List apiTags) { + Tag foundTag = null; + for (Tag tag : apiTags) { + if (tag.getName().equals(name)) { + foundTag = tag; + } + } + return foundTag; + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtils.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtils.java new file mode 100644 index 00000000000..93a54320068 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtils.java @@ -0,0 +1,393 @@ +package fish.payara.microprofile.openapi.impl.model.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Collection; + +import javax.inject.Inject; +import javax.ws.rs.BeanParam; +import javax.ws.rs.CookieParam; +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.MatrixParam; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.PATCH; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.ext.Provider; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; + +import fish.payara.microprofile.openapi.impl.model.OperationImpl; + +public final class ModelUtils { + + /** + * Private constructor to hide public one. + */ + private ModelUtils() { + } + + /** + * @param method the method to analyse. + * @return the {@link HttpMethod} applied to this method, or null if there is + * none. + */ + public static HttpMethod getHttpMethod(Method method) { + if (method.isAnnotationPresent(GET.class)) { + return HttpMethod.GET; + } + if (method.isAnnotationPresent(POST.class)) { + return HttpMethod.POST; + } + if (method.isAnnotationPresent(PUT.class)) { + return HttpMethod.PUT; + } + if (method.isAnnotationPresent(DELETE.class)) { + return HttpMethod.DELETE; + } + if (method.isAnnotationPresent(HEAD.class)) { + return HttpMethod.HEAD; + } + if (method.isAnnotationPresent(OPTIONS.class)) { + return HttpMethod.OPTIONS; + } + if (method.isAnnotationPresent(PATCH.class)) { + return HttpMethod.PATCH; + } + return null; + } + + /** + * Creates a new {@link Operation}, and inserts it into the {@link PathItem}. + * + * @param pathItem the {@link PathItem} to add the {@link Operation} to. + * @param httpMethod the HTTP method of the operation to add. + * @return the newly created {@link Operation}, or the existing operation if + * available. + */ + public static Operation getOrCreateOperation(PathItem pathItem, HttpMethod httpMethod) { + Operation operation = new OperationImpl(); + if (pathItem.readOperationsMap().get(httpMethod) != null) { + return pathItem.readOperationsMap().get(httpMethod); + } + switch (httpMethod) { + case GET: + pathItem.setGET(operation); + break; + case POST: + pathItem.setPOST(operation); + break; + case PUT: + pathItem.setPUT(operation); + break; + case DELETE: + pathItem.setDELETE(operation); + break; + case HEAD: + pathItem.setHEAD(operation); + break; + case OPTIONS: + pathItem.setOPTIONS(operation); + break; + case PATCH: + pathItem.setPATCH(operation); + break; + case TRACE: + pathItem.setTRACE(operation); + break; + default: + throw new IllegalArgumentException("HTTP method not recognised."); + } + return operation; + } + + public static Operation findOperation(OpenAPI api, Method method, String path) { + Operation foundOperation = null; + try { + return api.getPaths().get(path).readOperationsMap().get(getHttpMethod(method)); + } catch (NullPointerException ex) { + // Operation not found + } + return foundOperation; + } + + public static void removeOperation(PathItem pathItem, Operation operation) { + if (operation == null) { + return; + } + if (operation.equals(pathItem.getGET())) { + pathItem.setGET(null); + } + if (operation.equals(pathItem.getPOST())) { + pathItem.setPOST(null); + } + if (operation.equals(pathItem.getPUT())) { + pathItem.setPUT(null); + } + if (operation.equals(pathItem.getDELETE())) { + pathItem.setDELETE(null); + } + if (operation.equals(pathItem.getHEAD())) { + pathItem.setHEAD(null); + } + if (operation.equals(pathItem.getPATCH())) { + pathItem.setPATCH(null); + } + if (operation.equals(pathItem.getOPTIONS())) { + pathItem.setOPTIONS(null); + } + if (operation.equals(pathItem.getTRACE())) { + pathItem.setTRACE(null); + } + } + + /** + * Finds the {@link SchemaType} that corresponds to a given class. + * + * @param type the class to map. + * @return the schema type the class corresponds to. + */ + public static SchemaType getSchemaType(Class type) { + if (Boolean.class.isAssignableFrom(type) || boolean.class.isAssignableFrom(type)) { + return SchemaType.BOOLEAN; + } + if (Integer.class.isAssignableFrom(type) || int.class.isAssignableFrom(type)) { + return SchemaType.INTEGER; + } + if (type == short.class || type == Short.class || type == long.class || type == Long.class + || type == float.class || type == Float.class || type == double.class || type == Double.class) { + return SchemaType.NUMBER; + } + if (type.isArray()) { + return SchemaType.ARRAY; + } + if (String.class.isAssignableFrom(type)) { + return SchemaType.STRING; + } + return SchemaType.OBJECT; + } + + /** + * Finds a {@link SchemaType} that can represent both of the given types. If one + * of the input values are null, this function returns the other. If both are + * null, this function returns null. + * + * @param type1 the first schema type. + * @param type2 the second schema type. + * @return a {@link SchemaType} that can represent both. + */ + public static SchemaType getParentSchemaType(SchemaType type1, SchemaType type2) { + if (type1 == null && type2 == null) { + return null; + } + if (type1 == null) { + return type2; + } + if (type2 == null) { + return type1; + } + if (type1 == SchemaType.OBJECT || type2 == SchemaType.OBJECT) { + return SchemaType.OBJECT; + } + if (type1 == SchemaType.STRING || type2 == SchemaType.STRING) { + return SchemaType.STRING; + } + if (type1 != type2) { + return SchemaType.STRING; + } + return type1; + } + + public static boolean isRequestBody(Parameter parameter) { + if (parameter.getDeclaredAnnotations().length == 0) { + return true; + } + if (parameter.isAnnotationPresent(FormParam.class)) { + return false; + } + if (parameter.isAnnotationPresent(QueryParam.class)) { + return false; + } + if (parameter.isAnnotationPresent(MatrixParam.class)) { + return false; + } + if (parameter.isAnnotationPresent(BeanParam.class)) { + return false; + } + if (parameter.isAnnotationPresent(HeaderParam.class)) { + return false; + } + if (parameter.isAnnotationPresent(PathParam.class)) { + return false; + } + if (parameter.isAnnotationPresent(CookieParam.class)) { + return false; + } + if (parameter.isAnnotationPresent(Context.class)) { + return false; + } + if (parameter.isAnnotationPresent(Inject.class)) { + return false; + } + if (parameter.isAnnotationPresent(Provider.class)) { + return false; + } + return true; + } + + public static In getParameterType(Parameter parameter) { + if (parameter.isAnnotationPresent(PathParam.class)) { + return In.PATH; + } + if (parameter.isAnnotationPresent(QueryParam.class)) { + return In.QUERY; + } + if (parameter.isAnnotationPresent(HeaderParam.class)) { + return In.HEADER; + } + if (parameter.isAnnotationPresent(CookieParam.class)) { + return In.COOKIE; + } + return null; + } + + public static String getParameterName(Parameter parameter) { + if (parameter.isAnnotationPresent(PathParam.class)) { + return parameter.getDeclaredAnnotation(PathParam.class).value(); + } else if (parameter.isAnnotationPresent(QueryParam.class)) { + return parameter.getDeclaredAnnotation(QueryParam.class).value(); + } else if (parameter.isAnnotationPresent(HeaderParam.class)) { + return parameter.getDeclaredAnnotation(HeaderParam.class).value(); + } else if (parameter.isAnnotationPresent(CookieParam.class)) { + return parameter.getDeclaredAnnotation(CookieParam.class).value(); + } + return null; + } + + public static boolean isAnnotationNull(Annotation annotation) { + if (annotation == null) { + return true; + } + boolean allNull = true; + for (Method m : annotation.annotationType().getDeclaredMethods()) { + if (m.getParameterCount() == 0) { + try { + Object value = m.invoke(annotation); + if (value != null) { + if (value.getClass().isArray() && Array.getLength(value) > 0) { + allNull = false; + } else if (String.class.isAssignableFrom(value.getClass()) && !value.toString().isEmpty()) { + allNull = false; + } + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + e.printStackTrace(); + } + } + } + return allNull; + } + + public static E mergeProperty(E current, E offer, boolean override) { + // Treat empty strings as null + if (offer instanceof String && offer.toString().isEmpty()) { + offer = null; + } + E resolve = current; + if (offer != null) { + if (override) { + resolve = offer; + } else { + if (current == null) { + resolve = offer; + } + } + } + return resolve; + } + + /** + * Set the reference property of an object, and clear every other field. + */ + public static void applyReference(org.eclipse.microprofile.openapi.models.Reference referee, String reference) { + // Set the reference + referee.setRef(reference); + + // For every field except the reference + for (Field field : referee.getClass().getDeclaredFields()) { + // Make the field accessible + boolean accessible = field.isAccessible(); + field.setAccessible(true); + try { + Object currentValue = field.get(referee); + // If the field is not equal to the reference + if (currentValue != null && !field.getName().equals("ref")) { + // If the field is a collection, clear it + if (Collection.class.isAssignableFrom(field.getType())) { + Collection.class.cast(field.get(referee)).clear(); + continue; + } + // If the field is an array, clear it + if (field.getType().isArray()) { + field.set(referee, field.getType().getClass().cast(new Object[0])); + continue; + } + // Otherwise, set it to null + field.set(referee, null); + } + } catch (Exception ex) { + continue; + } finally { + field.setAccessible(accessible); + } + } + } + + public static void overwrite(T from, T to) { + if (to != null) { + for (Field f : to.getClass().getDeclaredFields()) { + boolean accessible = f.isAccessible(); + f.setAccessible(true); + try { + f.set(to, f.get(from)); + } catch (IllegalArgumentException | IllegalAccessException e) { + // Ignore errors + } finally { + f.setAccessible(accessible); + } + } + } + } + + public static void merge(T from, T to, boolean override) { + if (to != null) { + for (Field f : to.getClass().getDeclaredFields()) { + boolean accessible = f.isAccessible(); + f.setAccessible(true); + try { + f.set(to, mergeProperty(f.get(to), f.get(from), override)); + } catch (IllegalArgumentException | IllegalAccessException e) { + // Ignore errors + } finally { + f.setAccessible(accessible); + } + } + } + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/appserver/payara-appserver-modules/microprofile/openapi/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer new file mode 100644 index 00000000000..c498f09ab8b --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +fish.payara.microprofile.openapi.impl.rest.init.OpenApiServletContainerInitializer \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/pom.xml b/appserver/payara-appserver-modules/microprofile/pom.xml index 1a8245dce64..0742457a5eb 100644 --- a/appserver/payara-appserver-modules/microprofile/pom.xml +++ b/appserver/payara-appserver-modules/microprofile/pom.xml @@ -53,9 +53,10 @@ holder. config - fault-tolerance - healthcheck - jwt-auth + fault-tolerance + healthcheck + jwt-auth metrics + openapi diff --git a/appserver/pom.xml b/appserver/pom.xml index 3ac06333ece..c234b203e6d 100644 --- a/appserver/pom.xml +++ b/appserver/pom.xml @@ -217,6 +217,7 @@ 1.0.payara-p1 1.0.payara-p1 1.1.payara-p1 + 1.0 5.4 1.2.payara-p1 diff --git a/nucleus/packager/nucleus-jersey/pom.xml b/nucleus/packager/nucleus-jersey/pom.xml index 61a9712c2d8..5fb97a83a3a 100644 --- a/nucleus/packager/nucleus-jersey/pom.xml +++ b/nucleus/packager/nucleus-jersey/pom.xml @@ -138,6 +138,14 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml + + org.yaml + snakeyaml + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + org.glassfish javax.json diff --git a/nucleus/pom.xml b/nucleus/pom.xml index 03d690a25b1..5ba2a63b9a2 100644 --- a/nucleus/pom.xml +++ b/nucleus/pom.xml @@ -302,7 +302,8 @@ 2.9.5 - + + 1.18 1.1 @@ -1127,6 +1128,16 @@ Parent is ${project.parent} jackson-dataformat-xml ${jackson.version} + + org.yaml + snakeyaml + ${snakeyaml.version} + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${jackson.version} + org.jvnet.mimepull From 1e007173492e482f325cf01ea084501d8688f5eb Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Fri, 11 May 2018 10:34:13 +0100 Subject: [PATCH 02/20] Base Processor model. --- .../openapi/api/processor/OASProcessor.java | 11 +++ .../openapi/impl/processor/BaseProcessor.java | 70 +++++++++++++++++++ .../impl/processor/utils/ProcessorUtils.java | 41 +++++++++++ 3 files changed, 122 insertions(+) create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/processor/OASProcessor.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/utils/ProcessorUtils.java diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/processor/OASProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/processor/OASProcessor.java new file mode 100644 index 00000000000..42e49cc9050 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/processor/OASProcessor.java @@ -0,0 +1,11 @@ +package fish.payara.microprofile.openapi.api.processor; + +import org.eclipse.microprofile.openapi.models.OpenAPI; + +import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; + +public interface OASProcessor { + + void process(OpenAPI api, OpenApiConfiguration config); + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java new file mode 100644 index 00000000000..5d9683f3706 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java @@ -0,0 +1,70 @@ +package fish.payara.microprofile.openapi.impl.processor; + +import java.util.Map.Entry; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; + +import fish.payara.microprofile.openapi.api.processor.OASProcessor; +import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; +import fish.payara.microprofile.openapi.impl.model.PathItemImpl; +import fish.payara.microprofile.openapi.impl.model.info.InfoImpl; +import fish.payara.microprofile.openapi.impl.model.servers.ServerImpl; + +public class BaseProcessor implements OASProcessor { + + private final String applicationPath; + + public BaseProcessor(String applicationPath) { + this.applicationPath = applicationPath; + } + + @Override + public void process(OpenAPI api, OpenApiConfiguration config) { + + // Set the OpenAPI version if it hasn't been set + if (api.getOpenapi() == null) { + api.setOpenapi("3.0.0"); + } + + // Set the info if it hasn't been set + if (api.getInfo() == null) { + api.setInfo(new InfoImpl().title("Deployed Resources").version("1.0.0")); + } + + // Add the default server + api.addServer(new ServerImpl().url("http://localhost:8080" + applicationPath)); + + // Add the config specified servers + if (config != null && config.getServers().size() > 0) { + config.getServers().forEach(url -> api.addServer(new ServerImpl().url(url))); + } + + // Add the path servers + if (config != null && !config.getPathServerMap().isEmpty()) { + for (Entry entry : config.getPathServerMap().entrySet()) { + if (!api.getPaths().containsKey(entry.getKey())) { + api.getPaths().addPathItem(entry.getKey(), new PathItemImpl()); + } + api.getPaths().get(entry.getKey()).addServer(new ServerImpl().url(entry.getValue())); + } + } + + // Add the operation servers + if (config != null && !config.getOperationServerMap().isEmpty()) { + for (Entry entry : config.getOperationServerMap().entrySet()) { + + // For each operation in the tree, add the server if the operation id matches + for (PathItem pathItem : api.getPaths().values()) { + for (Operation operation : pathItem.readOperations()) { + if (operation.getOperationId().equals(entry.getKey())) { + operation.addServer(new ServerImpl().url(entry.getValue())); + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/utils/ProcessorUtils.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/utils/ProcessorUtils.java new file mode 100644 index 00000000000..17d31cc6bbc --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/utils/ProcessorUtils.java @@ -0,0 +1,41 @@ +package fish.payara.microprofile.openapi.impl.processor.utils; + +import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.Set; +import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; + +public final class ProcessorUtils { + + /** + * Private constructor to hide implicit public one. + */ + private ProcessorUtils() { + + } + + private static final Logger LOGGER = Logger.getLogger(ProcessorUtils.class.getName()); + + /** + * Gets the set of classes contained within a {@link ClassLoader}. The set + * returned will not be null, but could be empty. + * + * @param classLoader the classloader to get the classes from. + * @return the set of classes managed by the classloader. + */ + @SuppressWarnings("unchecked") + public static Set> getClassesFromLoader(ClassLoader classLoader) { + Set> classes = new HashSet<>(); + try { + Field classesField = ClassLoader.class.getDeclaredField("classes"); + classesField.setAccessible(true); + classes = new HashSet<>((Vector>) classesField.get(classLoader)); + classesField.setAccessible(false); + } catch (Exception ex) { + LOGGER.log(Level.WARNING, "Unable to get classes from classloader.", ex); + } + return classes; + } +} \ No newline at end of file From 1cc9dfb79b5d2393204b31847b10258185236fbf Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Fri, 11 May 2018 10:35:05 +0100 Subject: [PATCH 03/20] Added base REST service. --- .../impl/model/util/ExtensionsMixin.java | 31 ++++ .../impl/rest/app/OpenApiApplication.java | 29 ++++ .../app/provider/ObjectMapperFactory.java | 159 ++++++++++++++++++ .../rest/app/provider/QueryFormatFilter.java | 50 ++++++ .../app/provider/writer/AbstractWriter.java | 37 ++++ .../rest/app/provider/writer/JsonWriter.java | 21 +++ .../rest/app/provider/writer/YamlWriter.java | 21 +++ .../rest/app/service/OpenApiResource.java | 22 +++ .../OpenApiServletContainerInitializer.java | 44 +++++ 9 files changed, 414 insertions(+) create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ExtensionsMixin.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/OpenApiApplication.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/QueryFormatFilter.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/JsonWriter.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/YamlWriter.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/init/OpenApiServletContainerInitializer.java diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ExtensionsMixin.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ExtensionsMixin.java new file mode 100644 index 00000000000..9dc193917d4 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ExtensionsMixin.java @@ -0,0 +1,31 @@ +package fish.payara.microprofile.openapi.impl.model.util; + +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +public abstract class ExtensionsMixin { + + @JsonAnyGetter + public abstract Map getExtensions(); + + @JsonAnySetter + public abstract void addExtension(String name, Object value); + + @JsonProperty("enum") + public abstract void getEnumeration(); + + @JsonProperty("default") + public abstract void getDefaultValue(); + + @JsonProperty("$ref") + public abstract void getRef(); + + // TODO: Fix ignored additional properties + @JsonIgnore + public abstract void setAdditionalProperties(Boolean additionalProperties); + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/OpenApiApplication.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/OpenApiApplication.java new file mode 100644 index 00000000000..4f0c4fd7d76 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/OpenApiApplication.java @@ -0,0 +1,29 @@ +package fish.payara.microprofile.openapi.impl.rest.app; + +import static fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication.OPEN_API_APPLICATION_PATH; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN; + +import javax.ws.rs.ApplicationPath; + +import org.glassfish.jersey.server.ResourceConfig; + +import fish.payara.microprofile.openapi.impl.rest.app.provider.QueryFormatFilter; +import fish.payara.microprofile.openapi.impl.rest.app.provider.writer.JsonWriter; +import fish.payara.microprofile.openapi.impl.rest.app.provider.writer.YamlWriter; +import fish.payara.microprofile.openapi.impl.rest.app.service.OpenApiResource; + +@ApplicationPath(OPEN_API_APPLICATION_PATH) +public class OpenApiApplication extends ResourceConfig { + + public static final String OPEN_API_APPLICATION_PATH = "/openapi"; + public static final String APPLICATION_YAML = TEXT_PLAIN; + + public OpenApiApplication() { + register(OpenApiResource.class); + register(QueryFormatFilter.class); + register(YamlWriter.class); + register(JsonWriter.class); + property("payara-internal", "true"); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java new file mode 100644 index 00000000000..9d4bb359912 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java @@ -0,0 +1,159 @@ +package fish.payara.microprofile.openapi.impl.rest.app.provider; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; + +import org.eclipse.microprofile.openapi.models.Components; +import org.eclipse.microprofile.openapi.models.ExternalDocumentation; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.Paths; +import org.eclipse.microprofile.openapi.models.callbacks.Callback; +import org.eclipse.microprofile.openapi.models.examples.Example; +import org.eclipse.microprofile.openapi.models.headers.Header; +import org.eclipse.microprofile.openapi.models.info.Contact; +import org.eclipse.microprofile.openapi.models.info.Info; +import org.eclipse.microprofile.openapi.models.info.License; +import org.eclipse.microprofile.openapi.models.links.Link; +import org.eclipse.microprofile.openapi.models.media.Content; +import org.eclipse.microprofile.openapi.models.media.Discriminator; +import org.eclipse.microprofile.openapi.models.media.Encoding; +import org.eclipse.microprofile.openapi.models.media.MediaType; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.media.XML; +import org.eclipse.microprofile.openapi.models.parameters.Parameter; +import org.eclipse.microprofile.openapi.models.parameters.RequestBody; +import org.eclipse.microprofile.openapi.models.responses.APIResponse; +import org.eclipse.microprofile.openapi.models.responses.APIResponses; +import org.eclipse.microprofile.openapi.models.security.OAuthFlow; +import org.eclipse.microprofile.openapi.models.security.OAuthFlows; +import org.eclipse.microprofile.openapi.models.security.Scopes; +import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; +import org.eclipse.microprofile.openapi.models.servers.Server; +import org.eclipse.microprofile.openapi.models.servers.ServerVariable; +import org.eclipse.microprofile.openapi.models.servers.ServerVariables; +import org.eclipse.microprofile.openapi.models.tags.Tag; + +import fish.payara.microprofile.openapi.impl.model.ComponentsImpl; +import fish.payara.microprofile.openapi.impl.model.ExternalDocumentationImpl; +import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; +import fish.payara.microprofile.openapi.impl.model.OperationImpl; +import fish.payara.microprofile.openapi.impl.model.PathItemImpl; +import fish.payara.microprofile.openapi.impl.model.PathsImpl; +import fish.payara.microprofile.openapi.impl.model.callbacks.CallbackImpl; +import fish.payara.microprofile.openapi.impl.model.examples.ExampleImpl; +import fish.payara.microprofile.openapi.impl.model.headers.HeaderImpl; +import fish.payara.microprofile.openapi.impl.model.info.ContactImpl; +import fish.payara.microprofile.openapi.impl.model.info.InfoImpl; +import fish.payara.microprofile.openapi.impl.model.info.LicenseImpl; +import fish.payara.microprofile.openapi.impl.model.links.LinkImpl; +import fish.payara.microprofile.openapi.impl.model.media.ContentImpl; +import fish.payara.microprofile.openapi.impl.model.media.DiscriminatorImpl; +import fish.payara.microprofile.openapi.impl.model.media.EncodingImpl; +import fish.payara.microprofile.openapi.impl.model.media.MediaTypeImpl; +import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; +import fish.payara.microprofile.openapi.impl.model.media.XMLImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.ParameterImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.RequestBodyImpl; +import fish.payara.microprofile.openapi.impl.model.responses.APIResponseImpl; +import fish.payara.microprofile.openapi.impl.model.responses.APIResponsesImpl; +import fish.payara.microprofile.openapi.impl.model.security.OAuthFlowImpl; +import fish.payara.microprofile.openapi.impl.model.security.OAuthFlowsImpl; +import fish.payara.microprofile.openapi.impl.model.security.ScopesImpl; +import fish.payara.microprofile.openapi.impl.model.security.SecurityRequirementImpl; +import fish.payara.microprofile.openapi.impl.model.security.SecuritySchemeImpl; +import fish.payara.microprofile.openapi.impl.model.servers.ServerImpl; +import fish.payara.microprofile.openapi.impl.model.servers.ServerVariableImpl; +import fish.payara.microprofile.openapi.impl.model.servers.ServerVariablesImpl; +import fish.payara.microprofile.openapi.impl.model.tags.TagImpl; +import fish.payara.microprofile.openapi.impl.model.util.ExtensionsMixin; + +public final class ObjectMapperFactory { + + public static ObjectMapper createJson() { + return create(new JsonFactory()); + } + + public static ObjectMapper createYaml() { + YAMLFactory factory = new YAMLFactory(); + factory.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER); + factory.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES); + factory.enable(YAMLGenerator.Feature.SPLIT_LINES); + factory.enable(YAMLGenerator.Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS); + return create(factory); + } + + public static ObjectMapper create(JsonFactory factory) { + ObjectMapper mapper = new ObjectMapper(factory); + mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); + mapper.setSerializationInclusion(Include.NON_DEFAULT); + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + // Create mapping module + SimpleModule module = new SimpleModule(); + + module.addAbstractTypeMapping(Callback.class, CallbackImpl.class); + module.addAbstractTypeMapping(Example.class, ExampleImpl.class); + module.addAbstractTypeMapping(Header.class, HeaderImpl.class); + module.addAbstractTypeMapping(Info.class, InfoImpl.class); + module.addAbstractTypeMapping(Contact.class, ContactImpl.class); + module.addAbstractTypeMapping(License.class, LicenseImpl.class); + module.addAbstractTypeMapping(Link.class, LinkImpl.class); + module.addAbstractTypeMapping(Content.class, ContentImpl.class); + module.addAbstractTypeMapping(Discriminator.class, DiscriminatorImpl.class); + module.addAbstractTypeMapping(Encoding.class, EncodingImpl.class); + module.addAbstractTypeMapping(MediaType.class, MediaTypeImpl.class); + module.addAbstractTypeMapping(Schema.class, SchemaImpl.class); + module.addAbstractTypeMapping(XML.class, XMLImpl.class); + module.addAbstractTypeMapping(Parameter.class, ParameterImpl.class); + module.addAbstractTypeMapping(RequestBody.class, RequestBodyImpl.class); + module.addAbstractTypeMapping(APIResponse.class, APIResponseImpl.class); + module.addAbstractTypeMapping(APIResponses.class, APIResponsesImpl.class); + module.addAbstractTypeMapping(OAuthFlow.class, OAuthFlowImpl.class); + module.addAbstractTypeMapping(OAuthFlows.class, OAuthFlowsImpl.class); + module.addAbstractTypeMapping(Scopes.class, ScopesImpl.class); + module.addAbstractTypeMapping(SecurityRequirement.class, SecurityRequirementImpl.class); + module.addAbstractTypeMapping(SecurityScheme.class, SecuritySchemeImpl.class); + module.addAbstractTypeMapping(Server.class, ServerImpl.class); + module.addAbstractTypeMapping(ServerVariable.class, ServerVariableImpl.class); + module.addAbstractTypeMapping(ServerVariables.class, ServerVariablesImpl.class); + module.addAbstractTypeMapping(Tag.class, TagImpl.class); + module.addAbstractTypeMapping(Components.class, ComponentsImpl.class); + module.addAbstractTypeMapping(ExternalDocumentation.class, ExternalDocumentationImpl.class); + module.addAbstractTypeMapping(OpenAPI.class, OpenAPIImpl.class); + module.addAbstractTypeMapping(Operation.class, OperationImpl.class); + module.addAbstractTypeMapping(PathItem.class, PathItemImpl.class); + module.addAbstractTypeMapping(Paths.class, PathsImpl.class); + + List> mixinTargets = Arrays.asList(APIResponse.class, Callback.class, Components.class, Contact.class, + Encoding.class, Example.class, ExternalDocumentation.class, Header.class, Info.class, License.class, + Link.class, MediaType.class, OAuthFlow.class, OAuthFlows.class, OpenAPI.class, Operation.class, + Parameter.class, PathItem.class, Paths.class, RequestBody.class, Scopes.class, SecurityScheme.class, + Server.class, ServerVariable.class, ServerVariables.class, Tag.class, XML.class, Schema.class); + mapper.setMixIns( + mixinTargets.stream().collect(Collectors.toMap(Function.identity(), c -> ExtensionsMixin.class))); + + mapper.registerModule(module); + + return mapper; + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/QueryFormatFilter.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/QueryFormatFilter.java new file mode 100644 index 00000000000..f355b7f5273 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/QueryFormatFilter.java @@ -0,0 +1,50 @@ +package fish.payara.microprofile.openapi.impl.rest.app.provider; + +import static fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication.APPLICATION_YAML; +import static javax.ws.rs.core.HttpHeaders.ACCEPT; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.ext.Provider; + +/** + * A filter that attempts to change the Accept header if the + * format query parameter is provided. + */ +@Provider +@PreMatching +public class QueryFormatFilter implements ContainerRequestFilter { + + /** + * A map of recognised media types that can be specified in a + * format query parameter. + */ + private static final Map mappings; + + static { + Map map = new HashMap<>(); + map.put("yaml", APPLICATION_YAML); + map.put("json", APPLICATION_JSON); + mappings = Collections.unmodifiableMap(map); + } + + /** + * Filters incoming requests to change the Accept header based on + * the format query parameter. + */ + @Override + public void filter(ContainerRequestContext request) throws IOException { + String format = request.getUriInfo().getQueryParameters().getFirst("format"); + if (format != null && mappings.containsKey(format)) { + request.getHeaders().putSingle(ACCEPT, mappings.get(format)); + } + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java new file mode 100644 index 00000000000..f40ca10d488 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java @@ -0,0 +1,37 @@ +package fish.payara.microprofile.openapi.impl.rest.app.provider.writer; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.eclipse.microprofile.openapi.models.OpenAPI; + +public abstract class AbstractWriter implements MessageBodyWriter { + + protected final ObjectMapper mapper; + + public AbstractWriter(ObjectMapper mapper) { + this.mapper = mapper; + } + + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return type != null && OpenAPI.class.isAssignableFrom(type); + } + + @Override + public void writeTo(OpenAPI t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) + throws IOException, WebApplicationException { + entityStream.write(mapper.writeValueAsBytes(t)); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/JsonWriter.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/JsonWriter.java new file mode 100644 index 00000000000..59550d30c8a --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/JsonWriter.java @@ -0,0 +1,21 @@ +package fish.payara.microprofile.openapi.impl.rest.app.provider.writer; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import javax.ws.rs.Produces; +import javax.ws.rs.ext.Provider; + +import fish.payara.microprofile.openapi.impl.rest.app.provider.ObjectMapperFactory; + +/** + * Writes the JSON response to the stream. + */ +@Provider +@Produces(APPLICATION_JSON) +public class JsonWriter extends AbstractWriter { + + public JsonWriter() { + super(ObjectMapperFactory.createJson()); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/YamlWriter.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/YamlWriter.java new file mode 100644 index 00000000000..4b8c39bcc4c --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/YamlWriter.java @@ -0,0 +1,21 @@ +package fish.payara.microprofile.openapi.impl.rest.app.provider.writer; + +import static fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication.APPLICATION_YAML; + +import javax.ws.rs.Produces; +import javax.ws.rs.ext.Provider; + +import fish.payara.microprofile.openapi.impl.rest.app.provider.ObjectMapperFactory; + +/** + * Writes the YAML response to the stream. + */ +@Provider +@Produces(APPLICATION_YAML) +public class YamlWriter extends AbstractWriter { + + public YamlWriter() { + super(ObjectMapperFactory.createYaml()); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java new file mode 100644 index 00000000000..91900acdc9d --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java @@ -0,0 +1,22 @@ +package fish.payara.microprofile.openapi.impl.rest.app.service; + +import static fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication.APPLICATION_YAML; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import org.eclipse.microprofile.openapi.models.OpenAPI; + +import fish.payara.microprofile.openapi.impl.OpenApiService; + +@Path("/") +public class OpenApiResource { + + @GET + @Produces({ APPLICATION_YAML, APPLICATION_JSON }) + public OpenAPI getResponse() { + return OpenApiService.getInstance().getDocument(); + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/init/OpenApiServletContainerInitializer.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/init/OpenApiServletContainerInitializer.java new file mode 100644 index 00000000000..9ed37de7f31 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/init/OpenApiServletContainerInitializer.java @@ -0,0 +1,44 @@ +package fish.payara.microprofile.openapi.impl.rest.init; + +import static fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication.OPEN_API_APPLICATION_PATH; +import static java.util.Arrays.asList; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; + +import org.glassfish.jersey.servlet.init.JerseyServletContainerInitializer; + +import fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication; + +/** + * Deploys the OpenAPI application to each listener when an application is deployed. + */ +public class OpenApiServletContainerInitializer implements ServletContainerInitializer { + + @Override + public void onStartup(Set> c, ServletContext ctx) throws ServletException { + + // Only deploy to app root + if (!"".equals(ctx.getContextPath())) { + return; + } + + // Check if there is already an endpoint for OpenAPI + Map registrations = ctx.getServletRegistrations(); + for (ServletRegistration reg : registrations.values()) { + if (reg.getMappings().contains(OPEN_API_APPLICATION_PATH)) { + return; + } + } + + // Start the OpenAPI application + new JerseyServletContainerInitializer().onStartup(new HashSet<>(asList(OpenApiApplication.class)), ctx); + } + +} \ No newline at end of file From 0b3f2c85019cc14cc18b826de220801f87d51562 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Fri, 11 May 2018 10:35:33 +0100 Subject: [PATCH 04/20] Added tests for processors. --- .../impl/model/util/ModelUtilsTest.java | 29 ++ .../classloader/ApplicationClassLoader.java | 73 +++ .../rule/AnnotationProcessedDocument.java | 30 ++ .../rule/ApplicationProcessedDocument.java | 24 + .../resource/rule/BaseProcessedDocument.java | 12 + .../resource/rule/ProcessedDocument.java | 7 + .../openapi/resource/util/TestUtils.java | 480 ++++++++++++++++++ .../openapi/test/app/TestApplication.java | 98 ++++ .../test/app/annotation/APIResponseTest.java | 75 +++ .../test/app/annotation/CallbackTest.java | 63 +++ .../test/app/annotation/ExtensionTest.java | 52 ++ .../annotation/ExternalDocumentationTest.java | 73 +++ .../app/annotation/OpenAPIDefinitionTest.java | 420 +++++++++++++++ .../test/app/annotation/OperationTest.java | 56 ++ .../annotation/ParameterAnnotationTest.java | 95 ++++ .../test/app/annotation/RequestBodyTest.java | 89 ++++ .../test/app/annotation/SchemaTest.java | 121 +++++ .../annotation/SecurityRequirementTest.java | 69 +++ .../app/annotation/SecuritySchemeTest.java | 103 ++++ .../test/app/annotation/ServerTest.java | 90 ++++ .../openapi/test/app/annotation/TagTest.java | 83 +++ .../test/app/application/FormParamTest.java | 68 +++ .../test/app/application/MethodMergeTest.java | 69 +++ .../test/app/application/MethodTest.java | 102 ++++ .../test/app/application/ParameterTest.java | 149 ++++++ .../test/app/application/PathTest.java | 48 ++ .../test/app/application/RequestTest.java | 90 ++++ .../test/app/application/ResponseTest.java | 101 ++++ .../test/app/application/RootPathTest.java | 41 ++ .../openapi/test/app/data/ComponentTest.java | 94 ++++ .../test/app/data/SchemaComponentTest.java | 62 +++ 31 files changed, 2966 insertions(+) create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtilsTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/AnnotationProcessedDocument.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/BaseProcessedDocument.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ProcessedDocument.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/util/TestUtils.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/TestApplication.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/APIResponseTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/CallbackTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExtensionTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExternalDocumentationTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OpenAPIDefinitionTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OperationTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ParameterAnnotationTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/RequestBodyTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SchemaTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecurityRequirementTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecuritySchemeTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ServerTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/TagTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/FormParamTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodMergeTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ParameterTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/PathTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RequestTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ResponseTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RootPathTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/ComponentTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/SchemaComponentTest.java diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtilsTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtilsTest.java new file mode 100644 index 00000000000..eaf2d798aa6 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtilsTest.java @@ -0,0 +1,29 @@ +package fish.payara.microprofile.openapi.impl.model.util; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class ModelUtilsTest { + + private static final String CURRENT_VALUE = "whatever"; + private static final String NEW_VALUE = "something else"; + private static final String NULL = null; + + /** + * Tests that the function overrides properties correctly. + */ + @Test + public void mergePropertyTest() { + assertEquals(NEW_VALUE, mergeProperty(CURRENT_VALUE, NEW_VALUE, true)); + assertEquals(CURRENT_VALUE, mergeProperty(CURRENT_VALUE, NEW_VALUE, false)); + assertEquals(CURRENT_VALUE, mergeProperty(CURRENT_VALUE, NULL, true)); + assertEquals(CURRENT_VALUE, mergeProperty(CURRENT_VALUE, NULL, false)); + assertEquals(NEW_VALUE, mergeProperty(NULL, NEW_VALUE, true)); + assertEquals(NEW_VALUE, mergeProperty(NULL, NEW_VALUE, false)); + assertEquals(NULL, mergeProperty(NULL, NULL, true)); + assertEquals(NULL, mergeProperty(NULL, NULL, false)); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java new file mode 100644 index 00000000000..3d09ca4f2e3 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java @@ -0,0 +1,73 @@ +package fish.payara.microprofile.openapi.resource.classloader; + +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.core.Application; + +public class ApplicationClassLoader extends ClassLoader { + + private Set> appClasses; + + public ApplicationClassLoader(Application app, Set> extraClasses) { + super(); + appClasses = new HashSet<>(); + appClasses.add(app.getClass()); + appClasses.addAll(app.getClasses()); + if (extraClasses != null) { + appClasses.addAll(extraClasses); + } + for (Class clazz : appClasses) { + try { + loadClass(clazz.getName()); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + } + + public ApplicationClassLoader(Application app) { + this(app, null); + } + + private Class getClass(String name) throws ClassNotFoundException { + String file = name.replace('.', File.separatorChar) + ".class"; + byte[] b = null; + try { + b = loadClassData(file); + Class c = defineClass(name, b, 0, b.length); + resolveClass(c); + return c; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + for (Class clazz : appClasses) { + if (clazz.getName().equals(name)) { + return getClass(name); + } + } + return super.loadClass(name); + } + + private byte[] loadClassData(String name) throws IOException { + // Opening the file + InputStream stream = getClass().getClassLoader().getResourceAsStream(name); + int size = stream.available(); + byte buff[] = new byte[size]; + DataInputStream in = new DataInputStream(stream); + // Reading the binary data + in.readFully(buff); + in.close(); + return buff; + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/AnnotationProcessedDocument.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/AnnotationProcessedDocument.java new file mode 100644 index 00000000000..7800e3d03b3 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/AnnotationProcessedDocument.java @@ -0,0 +1,30 @@ +package fish.payara.microprofile.openapi.resource.rule; + +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toSet; + +import fish.payara.microprofile.openapi.impl.processor.AnnotationProcessor; +import fish.payara.microprofile.openapi.impl.processor.ApplicationProcessor; +import fish.payara.microprofile.openapi.impl.processor.BaseProcessor; +import fish.payara.microprofile.openapi.resource.classloader.ApplicationClassLoader; +import fish.payara.microprofile.openapi.test.app.TestApplication; +import fish.payara.microprofile.openapi.test.app.annotation.OpenAPIDefinitionTest; +import fish.payara.microprofile.openapi.test.app.data.SchemaComponentTest; + +public class AnnotationProcessedDocument extends ProcessedDocument { + + public AnnotationProcessedDocument() { + // Apply base processor + new BaseProcessor("/testlocation_123").process(this, null); + + ClassLoader appClassLoader = new ApplicationClassLoader(new TestApplication(), + asList(SchemaComponentTest.class, OpenAPIDefinitionTest.class).stream().collect(toSet())); + + // Apply application processor + new ApplicationProcessor(appClassLoader).process(this, null); + + // Apply annotation processor + new AnnotationProcessor(appClassLoader).process(this, null); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java new file mode 100644 index 00000000000..d2440e20994 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java @@ -0,0 +1,24 @@ +package fish.payara.microprofile.openapi.resource.rule; + +import static java.util.Collections.singleton; + +import fish.payara.microprofile.openapi.impl.processor.ApplicationProcessor; +import fish.payara.microprofile.openapi.impl.processor.BaseProcessor; +import fish.payara.microprofile.openapi.resource.classloader.ApplicationClassLoader; +import fish.payara.microprofile.openapi.test.app.TestApplication; +import fish.payara.microprofile.openapi.test.app.data.SchemaComponentTest; + +public class ApplicationProcessedDocument extends ProcessedDocument { + + public ApplicationProcessedDocument() { + // Apply base processor + new BaseProcessor("/testlocation_123").process(this, null); + + ClassLoader appClassLoader = new ApplicationClassLoader(new TestApplication(), + singleton(SchemaComponentTest.class)); + + // Apply application processor + new ApplicationProcessor(appClassLoader).process(this, null); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/BaseProcessedDocument.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/BaseProcessedDocument.java new file mode 100644 index 00000000000..8eb12df5a05 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/BaseProcessedDocument.java @@ -0,0 +1,12 @@ +package fish.payara.microprofile.openapi.resource.rule; + +import fish.payara.microprofile.openapi.impl.processor.BaseProcessor; + +public class BaseProcessedDocument extends ProcessedDocument { + + public BaseProcessedDocument() { + // Apply base processor + new BaseProcessor("/testlocation_123").process(this, null); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ProcessedDocument.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ProcessedDocument.java new file mode 100644 index 00000000000..c4f844d7e75 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ProcessedDocument.java @@ -0,0 +1,7 @@ +package fish.payara.microprofile.openapi.resource.rule; + +import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; + +public abstract class ProcessedDocument extends OpenAPIImpl { + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/util/TestUtils.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/util/TestUtils.java new file mode 100644 index 00000000000..0a678160cf6 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/util/TestUtils.java @@ -0,0 +1,480 @@ +package fish.payara.microprofile.openapi.resource.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.callbacks.Callback; +import org.eclipse.microprofile.openapi.models.media.Content; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.eclipse.microprofile.openapi.models.parameters.Parameter; +import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; +import org.eclipse.microprofile.openapi.models.parameters.RequestBody; +import org.eclipse.microprofile.openapi.models.responses.APIResponses; +import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.models.servers.Server; +import org.eclipse.microprofile.openapi.models.servers.ServerVariable; +import org.eclipse.microprofile.openapi.models.tags.Tag; + +public final class TestUtils { + + private TestUtils() { + } + + /** + * Tests that a given operation exists in the document. + * + * @param document the OpenAPI document to scan for the operation. + * @param endpointPath the path of the operation. + * @param httpMethod the name of the method mapped to the operation. If this + * value is null, it will not be checked. + * @param method the HTTP Method of the operation. + * + * @throws AssertionError if the operation isn't found. + */ + public static void testOperation(OpenAPI document, String endpointPath, String httpMethod, HttpMethod method) { + assertNotNull(endpointPath + " doesn't exist.", document.getPaths().get(endpointPath)); + assertNotNull(endpointPath + " has no operations.", document.getPaths().get(endpointPath).readOperationsMap()); + assertNotNull(endpointPath + " has no " + method.toString() + ".", + document.getPaths().get(endpointPath).readOperationsMap().get(method)); + if (httpMethod != null) { + assertEquals(endpointPath + " has the wrong method name.", + document.getPaths().get(endpointPath).readOperationsMap().get(method).getOperationId(), httpMethod); + } + } + + /** + * Tests that a named parameter with the given type exists for an endpoint in + * the document. + * + * @param document the OpenAPI document to scan for the operation. + * @param endpointPath the path of the operation. + * @param httpMethod the name of the method mapped to the operation. If this + * value is null, it will not be checked. + * @param parameterMap A map mapping the name of a parameter to search for to + * the parameter type. + * + * @throws AssertionError if the parameter or operation aren't found. + */ + public static void testParameter(OpenAPI document, String endpointPath, HttpMethod httpMethod, + Map parameterMap) { + testOperation(document, endpointPath, null, httpMethod); + Operation operation = document.getPaths().get(endpointPath).readOperationsMap().get(httpMethod); + + // If the parameter map is null, check there are no parameters + if (parameterMap == null) { + assertTrue(endpointPath + " has parameters.", + operation.getParameters() == null || operation.getParameters().isEmpty()); + return; + } + + List parameters = operation.getParameters(); + for (Entry entry : parameterMap.entrySet()) { + String parameterName = entry.getKey(); + In parameterType = entry.getValue(); + + assertTrue(endpointPath + " has no parameter with name " + parameterName, + parameters.stream().anyMatch(param -> param.getName().equals(parameterName))); + assertEquals( + endpointPath + " parameter " + parameterName + " is the wrong type.", parameters.stream() + .filter(param -> param.getName().equals(parameterName)).findFirst().get().getIn(), + parameterType); + } + } + + /** + * Tests that a named content type with the given schema type exists in the + * request body of an endpoint in the document. + * + * @param document the OpenAPI document to scan for the operation. + * @param endpointPath the path of the operation. + * @param httpMethod the name of the method mapped to the operation. If this + * value is null, it will not be checked. + * @param requestMap A map mapping the content type to search for to the + * schema type or reference. A value of type + * {@link SchemaType} will attempt to match a content type. + * A value of type {@link String} will attempt to match a + * reference instead. If this map is null, the operation + * will be tested for no request body. + * + * @return the request body found. + * + * @throws AssertionError if the content type or operation aren't found. + */ + public static RequestBody testRequestBody(OpenAPI document, String endpointPath, HttpMethod httpMethod, + Map requestMap) { + testOperation(document, endpointPath, null, httpMethod); + Operation operation = document.getPaths().get(endpointPath).readOperationsMap().get(httpMethod); + if (requestMap == null) { + assertNull(endpointPath + " has a requestBody.", operation.getRequestBody()); + return null; + } + assertNotNull(endpointPath + " has no requestBody.", operation.getRequestBody()); + assertNotNull(endpointPath + " has no content.", operation.getRequestBody().getContent()); + + Content content = operation.getRequestBody().getContent(); + try { + testContent(content, requestMap); + } catch (AssertionError ex) { + fail(endpointPath + " -> " + ex.getMessage()); + } + return operation.getRequestBody(); + } + + /** + * Tests that a named content type with the given schema type exists in a named + * response of an endpoint in the document. + * + * @param document the OpenAPI document to scan for the operation. + * @param endpointPath the path of the operation. + * @param httpMethod the name of the method mapped to the operation. If this + * value is null, it will not be checked. + * @param responseCode the name of the response. + * @param responseMap A map mapping the content type to search for to the + * schema type or reference. A value of type + * {@link SchemaType} will attempt to match a content type. + * A value of type {@link String} will attempt to match a + * reference instead. If this map is null, the operation + * will be tested for no response with the given name. + * + * @return the found response. + * + * @throws AssertionError if the content type or operation aren't found. + */ + public static org.eclipse.microprofile.openapi.models.responses.APIResponse testResponse(OpenAPI document, + String endpointPath, HttpMethod httpMethod, String responseCode, Map responseMap) { + assertNotNull(endpointPath + " doesn't exist.", document.getPaths().get(endpointPath)); + Operation operation = document.getPaths().get(endpointPath).readOperationsMap().get(httpMethod); + assertNotNull("Operation not found.", operation); + assertNotNull("No responses found.", operation.getResponses()); + assertNotNull("Response not found.", operation.getResponses().get(responseCode)); + + Content content = operation.getResponses().get(responseCode).getContent(); + try { + testContent(content, responseMap); + } catch (AssertionError ex) { + fail(endpointPath + " -> " + ex.getMessage()); + } + return operation.getResponses().get(responseCode); + } + + /** + * Tests that a named content type with the given schema type exists in the + * default response of an endpoint in the document. + * + * @param document the OpenAPI document to scan for the operation. + * @param endpointPath the path of the operation. + * @param httpMethod the name of the method mapped to the operation. If this + * value is null, it will not be checked. + * @param responseMap A map mapping the content type to search for to the + * schema type or reference. A value of type + * {@link SchemaType} will attempt to match a content type. + * A value of type {@link String} will attempt to match a + * reference instead. + * + * @return the found response. + * + * @throws AssertionError if the content type or operation aren't found. + */ + public static org.eclipse.microprofile.openapi.models.responses.APIResponse testResponse(OpenAPI document, + String endpointPath, HttpMethod httpMethod, Map responseMap) { + return testResponse(document, endpointPath, httpMethod, APIResponses.DEFAULT, responseMap); + } + + private static void testContent(Content content, Map typeMap) { + assertNotNull("The found content was null.", content); + for (Entry entry : typeMap.entrySet()) { + String contentName = entry.getKey(); + assertTrue("No content type found with name " + contentName, content.keySet().contains(contentName)); + if (entry.getValue() instanceof SchemaType) { + SchemaType schemaType = (SchemaType) entry.getValue(); + assertNull("The found schema should not contain a reference.", + content.get(contentName).getSchema().getRef()); + assertEquals("The found schema had the wrong type.", content.get(contentName).getSchema().getType(), + schemaType); + } else if (entry.getValue() instanceof String) { + String refName = (String) entry.getValue(); + assertEquals("The found schema had the wrong reference.", refName, + content.get(contentName).getSchema().getRef()); + assertNull("The found schema should not contain a type when it has a reference.", + content.get(contentName).getSchema().getType()); + } + } + } + + /** + * Tests that a component with the specified name exists in the document. Will + * also test that the found component contains a property with the given name + * and type. + * + * @param document the OpenAPI document to search in. + * @param componentName the name of the component to search for. + * @param propertyName the name of the property to search for. + * @param propertyType the type of the propetry to search for. If more than one + * type is specified, the sub item type will be checked + * recursively. For example, the types "array, array, int", + * will check for a multidimensional array of ints. + * + * @throws AssertionError if the component or property aren't found. + */ + public static void testComponentProperty(OpenAPI document, String componentName, String propertyName, + SchemaType... propertyTypes) { + // Check the property exists + assertNotNull("The component property " + propertyName + " wasn't found.", + document.getComponents().getSchemas().get(componentName).getProperties().get(propertyName)); + // Check the property and each sub property has the correct type + Schema schema = document.getComponents().getSchemas().get(componentName).getProperties().get(propertyName); + for (SchemaType propertyType : propertyTypes) { + assertNotNull("The schema had no sub items.", schema); + // Check the property has the correct type + assertEquals("The component property " + propertyName + " wasn't the correct type.", schema.getType(), + propertyType); + try { + schema = schema.getItems(); + } catch (NullPointerException ex) { + // Ignore + } + } + } + + /** + * Tests that a server with the given values exists at the given operation. If + * url is null, then the server is searched for at the document + * root. + * + * @param document the OpenAPI document to search in. + * @param url the url of the path item to test. + * @param method the {@link HttpMethod} of the operation. + * @param serverUrl the url of the server to expect. + * @param serverDescription the expected description of the found server. + * + * @return the found server. + */ + public static Server testServer(OpenAPI document, String url, HttpMethod method, String serverUrl, + String serverDescription) { + List servers = null; + if (url == null) { + servers = document.getServers(); + } else { + PathItem pathItem = document.getPaths().get(url); + assertNotNull("No Path item found for url: " + url, pathItem); + Operation operation = pathItem.readOperationsMap().get(method); + assertNotNull("No operation found for method: " + method + " at url: " + url, pathItem); + servers = operation.getServers(); + } + assertFalse("There were no servers found for element", servers == null || servers.isEmpty()); + Optional optional = servers.stream().filter(s -> s.getUrl().equals(serverUrl)).findAny(); + assertTrue("No server found with url: " + serverUrl, optional.isPresent()); + Server server = optional.get(); + assertEquals("Server with url: " + server.getUrl() + " has the wrong description.", server.getDescription(), + serverDescription); + return server; + } + + /** + * Tests that a server with the given values doesn't exist at the given + * operation. If url is null, then the server is searched for at + * the document root. + * + * @param document the OpenAPI document to search in. + * @param url the url of the path item to test. + * @param method the {@link HttpMethod} of the operation. + * @param serverUrl the url of the server to expect. + * + * @throws AssertionError if the server isn't found. + */ + public static void testNotServer(OpenAPI document, String url, HttpMethod method, String serverUrl) { + List servers = null; + if (url == null) { + servers = document.getServers(); + } else { + PathItem pathItem = document.getPaths().get(url); + assertNotNull("No Path item found for url: " + url, pathItem); + Operation operation = pathItem.readOperationsMap().get(method); + assertNotNull("No operation found for method: " + method + " at url: " + url, pathItem); + servers = operation.getServers(); + } + if (servers == null || servers.isEmpty()) { + return; + } + Optional optional = document.getServers().stream().filter(s -> s.getUrl().equals(serverUrl)).findAny(); + assertFalse("No server found with url: " + url, optional.isPresent()); + } + + /** + * Tests that a server with the given url contains a variable with the given + * values. + * + * @param server the server to test. + * @param variableName the name of the variable. + * @param variableDescription the description of the variable. + * @param defaultValue the default value of the variable. + * @param enumValue the enum value of the variable. + * + * @throws AssertionError if the server variable isn't found. + */ + public static void testServerContainsVariable(Server server, String variableName, String variableDescription, + String defaultValue, String enumValue) { + assertNotNull(server.getUrl() + " has no variables.", server.getVariables()); + ServerVariable variable = server.getVariables().get(variableName); + assertNotNull(variableName + " has no variable called: " + variableName, variable); + assertEquals(variableName + " has the wrong description.", variable.getDescription(), variableDescription); + assertEquals(variableName + " has the wrong default value.", variable.getDefaultValue(), defaultValue); + if (enumValue == null) { + assertTrue(variableName + " contains enum values.", + variable.getEnumeration() == null || variable.getEnumeration().isEmpty()); + } else { + assertFalse(variableName + " contains no enum values.", + variable.getEnumeration() == null || variable.getEnumeration().isEmpty()); + } + } + + /** + * Tests that a tag with the given values exists at the given operation. If + * url is null, then the tag is searched for at the document root. + * + * @param document the OpenAPI document to search in. + * @param url the url of the path item to test. + * @param method the {@link HttpMethod} of the operation. + * @param tagName the name of the tag to expect. + * @param tagDescription the expected description of the found tag. + * + * @throws AssertionError if the tag isn't found. + */ + public static void testTag(OpenAPI document, String url, HttpMethod method, String tagName, String tagDescription) { + List tags = document.getTags(); + if (url != null) { + PathItem pathItem = document.getPaths().get(url); + assertNotNull("No Path item found for url: " + url, pathItem); + Operation operation = pathItem.readOperationsMap().get(method); + assertNotNull("No operation found for method: " + method + " at url: " + url, pathItem); + List operationTags = operation.getTags(); + assertNotNull("The operation: " + method + " at url: " + url + " contains no tags.", operationTags); + assertTrue("There were no tags with the name: " + tagName, operationTags.contains(tagName)); + } + assertFalse("There were no tags found.", tags == null || tags.isEmpty()); + Optional optional = tags.stream().filter(t -> t.getName().equals(tagName)).findAny(); + assertTrue("There were no tags found with name: " + tagName, optional.isPresent()); + Tag tag = optional.get(); + assertEquals("The tag " + tagName + " had the wrong description.", tag.getDescription(), tagDescription); + } + + /** + * Tests that a tag with the given values does not exist at the given operation. + * If url is null, then the tag is searched for at the document + * root. + * + * @param document the OpenAPI document to search in. + * @param url the url of the path item to test. + * @param method the {@link HttpMethod} of the operation. + * @param tagName the name of the tag to expect. + * + * @throws AssertionError if the tag is found. + */ + public static void testNotTag(OpenAPI document, String url, HttpMethod method, String tagName) { + List tags = document.getTags(); + if (url != null) { + PathItem pathItem = document.getPaths().get(url); + assertNotNull("No Path item found for url: " + url, pathItem); + Operation operation = pathItem.readOperationsMap().get(method); + assertNotNull("No operation found for method: " + method + " at url: " + url, pathItem); + List operationTags = operation.getTags(); + assertTrue("The operation: " + method + " at url: " + url + " contains no tags.", + operationTags == null || !operationTags.contains(tagName)); + } else { + boolean found = tags.stream().anyMatch(t -> t.getName().equals(tagName)); + assertFalse("There was a tag found with name: " + tagName, found); + } + } + + /** + * Tests that a security configuration with the specified name exists at the + * given operation. + * + * @param document the OpenAPI document to search in. + * @param url the url of the path item to test. + * @param httpMethod the {@link HttpMethod} of the operation. + * @param schemeName the name of the expected scheme. + * @param scope the name of the scope to expect in the found scheme. + */ + public static void testSecurityRequirement(OpenAPI document, String url, HttpMethod httpMethod, String schemeName, String scope) { + testOperation(document, url, null, httpMethod); + Operation operation = document.getPaths().get(url).readOperationsMap().get(httpMethod); + List operationRequirements = operation.getSecurity(); + assertNotNull("No security requirements found at operation.", operationRequirements); + for (SecurityRequirement requirement : operationRequirements) { + if (requirement.containsKey(schemeName)) { + assertTrue("Scope not found.", requirement.get(schemeName).contains(scope)); + return; + } + } + fail("Security requirement not found."); + } + + /** + * Tests that a security configuration with the specified name doesn't exist at + * the given operation. + * + * @param document the OpenAPI document to search in. + * @param url the url of the path item to test. + * @param httpMethod the {@link HttpMethod} of the operation. + * @param schemeName the name of the scheme to test for. + */ + public static void testNotSecurityRequirement(OpenAPI document, String url, HttpMethod httpMethod, String schemeName) { + testOperation(document, url, null, httpMethod); + Operation operation = document.getPaths().get(url).readOperationsMap().get(httpMethod); + List operationRequirements = operation.getSecurity(); + for (SecurityRequirement requirement : operationRequirements) { + if (requirement.containsKey(schemeName)) { + fail("Security requirement found."); + return; + } + } + } + + /** + * Tests that a named callback operation with the given values exists for an + * endpoint in the document. + * + * @param document the OpenAPI document to scan for the operation. + * @param endpointPath the path of the operation. + * @param httpMethod the name of the method mapped to the operation. If + * this value is null, it will not be checked. + * @param callbackName the name of the callback to test for. + * @param callbackUrl the url of the callback to test for. + * @param callbackOperation the method of the callback operation to test for. + * @param callbackDescription the description of the callback operation, or null + * to not test. + * + * @throws AssertionError if the callback or callback operation aren't found. + */ + public static void testCallback(OpenAPI document, String endpointPath, HttpMethod httpMethod, String callbackName, + String callbackUrl, HttpMethod callbackOperation, String callbackDescription) { + testOperation(document, endpointPath, null, httpMethod); + Operation operation = document.getPaths().get(endpointPath).readOperationsMap().get(httpMethod); + assertNotNull("No callbacks found.", operation.getCallbacks()); + Callback callback = operation.getCallbacks().get(callbackName); + assertNotNull("Callback " + callbackName + " not found.", callback); + PathItem callbackPath = callback.get(callbackUrl); + assertNotNull("Callback url expression " + callbackUrl + " not found.", callbackPath); + operation = callbackPath.readOperationsMap().get(callbackOperation); + assertNotNull("Callback operation " + callbackOperation + " not found.", operation); + if (callbackDescription != null) { + assertEquals("Wrong callback operation description.", callbackDescription, operation.getDescription()); + } + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/TestApplication.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/TestApplication.java new file mode 100644 index 00000000000..9e343728fae --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/TestApplication.java @@ -0,0 +1,98 @@ +package fish.payara.microprofile.openapi.test.app; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.BaseProcessedDocument; +import fish.payara.microprofile.openapi.test.app.application.FormParamTest; +import fish.payara.microprofile.openapi.test.app.application.MethodMergeTest; +import fish.payara.microprofile.openapi.test.app.application.MethodTest; +import fish.payara.microprofile.openapi.test.app.application.ParameterTest; +import fish.payara.microprofile.openapi.test.app.application.PathTest; +import fish.payara.microprofile.openapi.test.app.application.RequestTest; +import fish.payara.microprofile.openapi.test.app.application.ResponseTest; +import fish.payara.microprofile.openapi.test.app.application.RootPathTest; +import fish.payara.microprofile.openapi.test.app.annotation.APIResponseTest; +import fish.payara.microprofile.openapi.test.app.annotation.CallbackTest; +import fish.payara.microprofile.openapi.test.app.annotation.ExtensionTest; +import fish.payara.microprofile.openapi.test.app.annotation.ExternalDocumentationTest; +import fish.payara.microprofile.openapi.test.app.annotation.OpenAPIDefinitionTest; +import fish.payara.microprofile.openapi.test.app.annotation.OperationTest; +import fish.payara.microprofile.openapi.test.app.annotation.ParameterAnnotationTest; +import fish.payara.microprofile.openapi.test.app.annotation.RequestBodyTest; +import fish.payara.microprofile.openapi.test.app.annotation.SchemaTest; +import fish.payara.microprofile.openapi.test.app.annotation.SecurityRequirementTest; +import fish.payara.microprofile.openapi.test.app.annotation.SecuritySchemeTest; +import fish.payara.microprofile.openapi.test.app.annotation.ServerTest; +import fish.payara.microprofile.openapi.test.app.annotation.TagTest; + + +@ApplicationPath("/test") +public class TestApplication extends Application { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new BaseProcessedDocument(); + } + + @Override + public Set> getClasses() { + Set> classes = new HashSet<>(); + // All JAX-RS classes + classes.add(RootPathTest.class); + classes.add(PathTest.class); + classes.add(MethodTest.class); + classes.add(ParameterTest.class); + classes.add(RequestTest.class); + classes.add(ResponseTest.class); + classes.add(MethodMergeTest.class); + classes.add(FormParamTest.class); + + // All OpenAPI classes + classes.add(OpenAPIDefinitionTest.class); + classes.add(ServerTest.class); + classes.add(TagTest.class); + classes.add(RequestBodyTest.class); + classes.add(APIResponseTest.class); + classes.add(SchemaTest.class); + classes.add(SecuritySchemeTest.class); + classes.add(SecurityRequirementTest.class); + classes.add(ParameterAnnotationTest.class); + classes.add(OperationTest.class); + classes.add(CallbackTest.class); + classes.add(ExternalDocumentationTest.class); + classes.add(ExtensionTest.class); + return classes; + } + + @Test + public void testBase() { + assertEquals("The document has the wrong version.", "3.0.0", document.getOpenapi()); + assertEquals("The document has the wrong title.", "Deployed Resources", document.getInfo().getTitle()); + assertEquals("The document has the wrong info version.", "1.0.0", document.getInfo().getVersion()); + assertEquals("The document has the wrong server list.", 1, document.getServers().size()); + assertEquals("The document has the wrong server URL.", "http://localhost:8080/testlocation_123", + document.getServers().get(0).getUrl()); + assertNotNull("The document paths should be an empty array if empty.", document.getPaths()); + assertTrue("The document shouldn't have any paths.", document.getPaths().isEmpty()); + assertTrue("The document shouldn't have any extensions.", document.getExtensions().isEmpty()); + assertNull("The document shouldn't have any external docs.", document.getExternalDocs()); + assertTrue("The document shouldn't have any security properties.", document.getSecurity().isEmpty()); + assertTrue("The document shouldn't have any tags.", document.getTags().isEmpty()); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/APIResponseTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/APIResponseTest.java new file mode 100644 index 00000000000..3bca380b0b4 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/APIResponseTest.java @@ -0,0 +1,75 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import static java.util.Collections.singletonMap; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.MediaType.APPLICATION_XML; +import static org.junit.Assert.assertEquals; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test that the @APIResponse annotation is handled correctly. + */ +@Path("/apiresponse") +public class APIResponseTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @GET + @Path("/default") + @Produces(APPLICATION_XML) + @APIResponse(description = "specified by an annotation.") + public String defaultResponse() { + return null; + } + + @Test + public void testDefaultResponse() { + TestUtils.testOperation(document, "/test/apiresponse/default", "defaultResponse", HttpMethod.GET); + org.eclipse.microprofile.openapi.models.responses.APIResponse response = TestUtils.testResponse(document, + "/test/apiresponse/default", HttpMethod.GET, singletonMap(APPLICATION_XML, SchemaType.STRING)); + assertEquals("/test/apiresponse/default response had the wrong description.", "specified by an annotation.", + response.getDescription()); + } + + @GET + @Path("/specified") + @Produces(APPLICATION_JSON) + @APIResponse(responseCode = "200", description = "specified by an annotation.") + public String specifiedResponse() { + return null; + } + + @Test + public void testSpecifiedResponse() { + TestUtils.testOperation(document, "/test/apiresponse/specified", "specifiedResponse", HttpMethod.GET); + + // Check the default response is still in tact + org.eclipse.microprofile.openapi.models.responses.APIResponse defaultResponse = TestUtils.testResponse(document, + "/test/apiresponse/specified", HttpMethod.GET, singletonMap(APPLICATION_JSON, SchemaType.STRING)); + assertEquals("Description was incorrect.", "Default Response.", defaultResponse.getDescription()); + + // Check the new response is created + org.eclipse.microprofile.openapi.models.responses.APIResponse successResponse = document.getPaths() + .get("/test/apiresponse/specified").getGET().getResponses().get("200"); + assertEquals("Description was incorrect.", "specified by an annotation.", successResponse.getDescription()); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/CallbackTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/CallbackTest.java new file mode 100644 index 00000000000..8e93a9ba4c2 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/CallbackTest.java @@ -0,0 +1,63 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.annotations.callbacks.Callback; +import org.eclipse.microprofile.openapi.annotations.callbacks.CallbackOperation; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +@Path("/callback") +public class CallbackTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @GET + @Path("/specified") + @Callback( + name = "testCallback", + callbackUrlExpression = "api/callbackUrl", + operations = { + @CallbackOperation(method = "POST", description = "The callback operation.") + } + ) + @Callback( + name = "testCallback", + callbackUrlExpression = "api/callbackUrl2", + operations = { + @CallbackOperation(method = "POST", description = "The callback2 operation.") + } + ) + @Callback( + name = "testCallback2", + callbackUrlExpression = "whatever", + operations = { + @CallbackOperation(method = "OPTIONS", description = "The second callback operation.") + } + ) + public String specifiedCallback() { + return null; + } + + @Test + public void specifiedCallbackTest() { + TestUtils.testCallback(document, "/test/callback/specified", HttpMethod.GET, "testCallback", "api/callbackUrl", + HttpMethod.POST, "The callback operation."); + TestUtils.testCallback(document, "/test/callback/specified", HttpMethod.GET, "testCallback", "api/callbackUrl2", + HttpMethod.POST, "The callback2 operation."); + TestUtils.testCallback(document, "/test/callback/specified", HttpMethod.GET, "testCallback2", "whatever", + HttpMethod.OPTIONS, "The second callback operation."); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExtensionTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExtensionTest.java new file mode 100644 index 00000000000..c8256668fea --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExtensionTest.java @@ -0,0 +1,52 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Map; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.annotations.extensions.Extension; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; + +@Path("/extension") +@Extension(name = "class-extension", value = "http://extension") +public class ExtensionTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @Test + public void classExtensionTest() { + Map extensions = document.getExtensions(); + assertNotNull("No extensions were found.", extensions); + assertTrue("Extension not found.", extensions.containsKey("class-extension")); + assertEquals("Extension had wrong value.", extensions.get("class-extension"), "http://extension"); + } + + @GET + @Path("/specified") + @Extension(name = "method-extension", value = "http://extension2") + public String methodExtension() { + return null; + } + + @Test + public void methodExtensionTest() { + Map extensions = document.getPaths().get("/test/extension/specified").getGET().getExtensions(); + assertNotNull("No extensions were found.", extensions); + assertTrue("Extension not found.", extensions.containsKey("method-extension")); + assertEquals("Extension had wrong value.", extensions.get("method-extension"), "http://extension2"); + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExternalDocumentationTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExternalDocumentationTest.java new file mode 100644 index 00000000000..643b06ca225 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExternalDocumentationTest.java @@ -0,0 +1,73 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.annotations.ExternalDocumentation; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; + +@Path("/externaldocs") +@ExternalDocumentation(description = "Inherited external documentation.", url = "http://inherited") +public class ExternalDocumentationTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @GET + @Path("/specified") + @ExternalDocumentation(description = "Specified external documentation.", url = "http://external-docs") + public String specifiedExternalDocs() { + return null; + } + + @Test + public void specifiedExternalDocsTest() { + org.eclipse.microprofile.openapi.models.ExternalDocumentation externalDocs = document.getPaths() + .get("/test/externaldocs/specified").getGET().getExternalDocs(); + assertNotNull("No external docs found.", externalDocs); + assertEquals("Incorrect description.", "Specified external documentation.", externalDocs.getDescription()); + assertEquals("Incorrect url.", "http://external-docs", externalDocs.getUrl()); + } + + @GET + @Path("/inherited") + public String inheritedExternalDocs() { + return null; + } + + @Test + public void inheritedExternalDocsTest() { + org.eclipse.microprofile.openapi.models.ExternalDocumentation externalDocs = document.getPaths() + .get("/test/externaldocs/inherited").getGET().getExternalDocs(); + assertNotNull("No external docs found.", externalDocs); + assertEquals("Incorrect description.", "Inherited external documentation.", externalDocs.getDescription()); + assertEquals("Incorrect url.", "http://inherited", externalDocs.getUrl()); + } + + @GET + @Path("/ignored") + @ExternalDocumentation + public String ignoredExternalDocs() { + return null; + } + + @Test + public void ignoredExternalDocsTest() { + org.eclipse.microprofile.openapi.models.ExternalDocumentation externalDocs = document.getPaths() + .get("/test/externaldocs/ignored").getGET().getExternalDocs(); + assertNull("External docs found.", externalDocs); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OpenAPIDefinitionTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OpenAPIDefinitionTest.java new file mode 100644 index 00000000000..7a7a30c949c --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OpenAPIDefinitionTest.java @@ -0,0 +1,420 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.microprofile.openapi.annotations.Components; +import org.eclipse.microprofile.openapi.annotations.ExternalDocumentation; +import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; +import org.eclipse.microprofile.openapi.annotations.callbacks.Callback; +import org.eclipse.microprofile.openapi.annotations.callbacks.CallbackOperation; +import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn; +import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType; +import org.eclipse.microprofile.openapi.annotations.extensions.Extension; +import org.eclipse.microprofile.openapi.annotations.headers.Header; +import org.eclipse.microprofile.openapi.annotations.info.Contact; +import org.eclipse.microprofile.openapi.annotations.info.Info; +import org.eclipse.microprofile.openapi.annotations.info.License; +import org.eclipse.microprofile.openapi.annotations.links.Link; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.security.OAuthFlow; +import org.eclipse.microprofile.openapi.annotations.security.OAuthFlows; +import org.eclipse.microprofile.openapi.annotations.security.OAuthScope; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; +import org.eclipse.microprofile.openapi.annotations.servers.Server; +import org.eclipse.microprofile.openapi.annotations.servers.ServerVariable; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.examples.Example; +import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme.Type; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +@OpenAPIDefinition( + info = @Info( + title = "Test Application", + version = "1.2.3", + description = "Application to test OpenAPI implementation.", + termsOfService = "OpenAPI terms of service.", + contact = @Contact( + name = "test-person", + email = "openapi-test@payara.fish", + url = "http://payara.fish" + ), + license = @License( + name = "test-license", + url = "http://payara.fish/openapi-test-license" + ) + ), + servers = { + @Server( + url = "http://server1", + description = "the first server.", + variables = { + @ServerVariable( + name = "serverVariable1", + description = "the first server variable for server1.", + defaultValue = "null" + ) + } + ) + }, + externalDocs = @ExternalDocumentation( + description = "the first external docs.", + url = "http://external-docs" + ), + security = { + @SecurityRequirement( + name = "securityRequirement1", + scopes = {"scope1", "scope2"} + ), + @SecurityRequirement( + name = "securityRequirement2", + scopes = {"lion", "tiger"} + ) + }, + tags = { + @Tag( + name = "tag1", + description = "the first tag.", + externalDocs = @ExternalDocumentation( + description = "the first external docs for tag1.", + url = "http://external-docs/tag1" + ) + ), + @Tag( + name = "tag2", + description = "the second tag.", + externalDocs = @ExternalDocumentation( + description = "the first external docs for tag2.", + url = "http://external-docs/tag2" + ) + ) + }, + components = @Components( + schemas = { + @Schema( + name = "schema1", + title = "the first schema.", + description = "An integer that is divisible by 2.3.", + deprecated = false, + type = SchemaType.INTEGER, + multipleOf = 2.3, + defaultValue = "23" + ) + }, + callbacks = { + @Callback( + name = "callback1", + callbackUrlExpression = "http://callback1.org", + operations = { + @CallbackOperation( + description = "callback1 operation1", + method = "OPTIONS", + summary = "The first operation of callback1.", + extensions = { + @Extension(name = "extension1", value = "extension2") + } + ) + } + ) + }, + examples = { + @ExampleObject( + name = "exampleObject1", + summary = "The first example object.", + description = "longer description of the same thing", + value = "test content", + externalValue = "http://test-content" + ) + }, + headers = { + @Header( + name = "header1", + description = "the first header.", + required = false + ) + }, + links = { + @Link( + name = "link1", + description = "the first link.", + requestBody = "request body content." + ) + }, + parameters = { + @Parameter( + name = "parameter1", + description = "the first parameter.", + in = ParameterIn.PATH + ) + }, + requestBodies = { + @RequestBody( + name = "requestBody1", + description = "the first request body.", + content = { + @Content( + mediaType = "app/test", + schema = @Schema( + ref = "schema1" + ) + ) + } + ) + }, + responses = { + @APIResponse( + name = "response1", + description = "the first response.", + responseCode = "200", + content = { + @Content( + mediaType = "app/test2", + schema = @Schema( + ref = "schema1" + ) + ) + } + ) + }, + securitySchemes = { + @SecurityScheme( + securitySchemeName = "securityScheme1", + description = "the first security scheme.", + scheme = "BASIC", + type = SecuritySchemeType.HTTP, + flows = @OAuthFlows( + password = @OAuthFlow( + authorizationUrl = "http://auth", + scopes = { + @OAuthScope( + name = "oauthScope1", + description = "the first OAuth scope." + ) + } + ) + ) + ) + } + ) +) +public class OpenAPIDefinitionTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + + @Test + public void infoTest() { + org.eclipse.microprofile.openapi.models.info.Info info = document.getInfo(); + assertNotNull("The document has no info.", info); + assertEquals("The Info object has the wrong title.", "Test Application", info.getTitle()); + assertEquals("The Info object has the wrong version.", "1.2.3", info.getVersion()); + assertEquals("The Info object has the wrong description.", "Application to test OpenAPI implementation.", + info.getDescription()); + assertEquals("The Info object has the wrong terms of service.", "OpenAPI terms of service.", + info.getTermsOfService()); + } + + @Test + public void contactTest() { + org.eclipse.microprofile.openapi.models.info.Contact contact = document.getInfo().getContact(); + assertNotNull("The document has no contacts.", contact); + assertEquals("The Contacts object has the wrong name.", "test-person", contact.getName()); + assertEquals("The Contacts object has the wrong email.", "openapi-test@payara.fish", contact.getEmail()); + assertEquals("The Contacts object has the wrong url.", "http://payara.fish", contact.getUrl()); + } + + @Test + public void licenseTest() { + org.eclipse.microprofile.openapi.models.info.License license = document.getInfo().getLicense(); + assertNotNull("The document has no license.", license); + assertEquals("The document has the wrong license name.", "test-license", license.getName()); + assertEquals("The document has the wrong license url.", "http://payara.fish/openapi-test-license", + license.getUrl()); + } + + @Test + public void serversTest() { + org.eclipse.microprofile.openapi.models.servers.Server server = TestUtils.testServer(document, null, + null, "http://server1", "the first server."); + TestUtils.testServerContainsVariable(server, "serverVariable1", "the first server variable for server1.", + "null", null); + } + + @Test + public void externalDocsTest() { + org.eclipse.microprofile.openapi.models.ExternalDocumentation externalDocs = document + .getExternalDocs(); + assertNotNull("The document has no external docs.", externalDocs); + assertEquals("The external docs has the wrong description.", "the first external docs.", + externalDocs.getDescription()); + assertEquals("The external docs has the wrong url.", "http://external-docs", externalDocs.getUrl()); + } + + @Test + public void securityTest() { + List requirements = document + .getSecurity(); + assertNotNull("The document has no security requirements.", requirements); + + Optional optional = requirements.stream() + .filter(r -> r.containsKey("securityRequirement1")).findAny(); + assertTrue("securityRequirement1 wasn't found.", optional.isPresent()); + org.eclipse.microprofile.openapi.models.security.SecurityRequirement requirement = optional.get(); + assertTrue("securityRequirement1 didn't contain scope1.", + requirement.get("securityRequirement1").contains("scope1")); + assertTrue("securityRequirement1 didn't contain scope2.", + requirement.get("securityRequirement1").contains("scope2")); + + optional = requirements.stream().filter(r -> r.containsKey("securityRequirement2")).findAny(); + assertTrue("securityRequirement2 wasn't found.", optional.isPresent()); + requirement = optional.get(); + assertTrue("securityRequirement2 didn't contain scope1.", + requirement.get("securityRequirement2").contains("lion")); + assertTrue("securityRequirement2 didn't contain scope2.", + requirement.get("securityRequirement2").contains("tiger")); + } + @Test + public void tagsTest() { + // Test that the base tags were created + TestUtils.testTag(document, null, null, "tag1", "the first tag."); + TestUtils.testTag(document, null, null, "tag2", "the second tag."); + } + + @Test + public void schemaTest() { + assertNotNull("The document components were null.", document.getComponents()); + Map schemas = document.getComponents() + .getSchemas(); + assertFalse("The document contained no schemas.", schemas == null || schemas.isEmpty()); + } + + @Test + public void componentsTest() { + org.eclipse.microprofile.openapi.models.Components components = document.getComponents(); + assertNotNull("The document has no components.", components); + + // Test the schema components + Map schemas = components.getSchemas(); + assertTrue("schema1 wasn't found.", schemas.containsKey("schema1")); + assertEquals("schema1 has the wrong title.", "the first schema.", schemas.get("schema1").getTitle()); + assertEquals("schema1 has the wrong deprecated value.", false, schemas.get("schema1").getDeprecated()); + + // Test the callback components + Map callbacks = components.getCallbacks(); + assertTrue("callback1 wasn't found.", callbacks.containsKey("callback1")); + assertTrue("callback1 has the wrong url.", callbacks.get("callback1").containsKey("http://callback1.org")); + Operation callbackOperation = callbacks.get("callback1").get("http://callback1.org").getOPTIONS(); + assertNotNull("callback1 operation has the wrong HTTP method.", callbackOperation); + assertEquals("callback1 operation has the wrong description.", "callback1 operation1", + callbackOperation.getDescription()); + assertEquals("callback1 operation has the wrong summary.", "The first operation of callback1.", + callbackOperation.getSummary()); + + // Test the example components + Map examples = components.getExamples(); + assertTrue("exampleObject1 wasn't found.", examples.containsKey("exampleObject1")); + assertEquals("exampleObject1 has the wrong summary.", "The first example object.", + examples.get("exampleObject1").getSummary()); + assertEquals("exampleObject1 has the wrong description.", "longer description of the same thing", + examples.get("exampleObject1").getDescription()); + assertEquals("exampleObject1 has the wrong value.", "test content", + examples.get("exampleObject1").getValue().toString()); + assertEquals("exampleObject1 has the wrong externalValue.", "http://test-content", + examples.get("exampleObject1").getExternalValue()); + + // Test the header components + Map headers = components.getHeaders(); + assertTrue("header1 wasn't found.", headers.containsKey("header1")); + assertEquals("header1 has the wrong description.", "the first header.", + headers.get("header1").getDescription()); + assertEquals("header1 has the wrong required value.", false, headers.get("header1").getRequired()); + + // Test the link components + Map links = components.getLinks(); + assertTrue("link1 wasn't found.", links.containsKey("link1")); + assertEquals("link1 has the wrong description.", "the first link.", links.get("link1").getDescription()); + assertEquals("link1 has the wrong request body.", "request body content.", links.get("link1").getRequestBody()); + + // Test the parameter components + Map parameters = components + .getParameters(); + assertTrue("parameter1 wasn't found.", parameters.containsKey("parameter1")); + assertEquals("parameter1 has the wrong description.", "the first parameter.", + parameters.get("parameter1").getDescription()); + assertEquals("parameter1 has the wrong request body.", In.PATH, parameters.get("parameter1").getIn()); + + // Test the request body components + Map requestBodies = components + .getRequestBodies(); + assertTrue("requestBody1 wasn't found.", requestBodies.containsKey("requestBody1")); + assertEquals("requestBody1 has the wrong description.", "the first request body.", + requestBodies.get("requestBody1").getDescription()); + assertNotNull("requestBody1 has the wrong content type.", + requestBodies.get("requestBody1").getContent().get("app/test")); + assertNotNull("requestBody1 has the wrong schema.", + requestBodies.get("requestBody1").getContent().get("app/test").getSchema()); + assertNotNull("requestBody1 has no schema ref.", + requestBodies.get("requestBody1").getContent().get("app/test").getSchema().getRef()); + assertEquals("requestBody1 has the wrong schema ref.", "#/components/schemas/schema1", + requestBodies.get("requestBody1").getContent().get("app/test").getSchema().getRef()); + + // Test the response components + Map responses = components + .getResponses(); + assertTrue("response1 wasn't found.", responses.containsKey("response1")); + assertEquals("response1 has the wrong description.", "the first response.", + responses.get("response1").getDescription()); + assertNotNull("response1 has the wrong content type.", + responses.get("response1").getContent().get("app/test2")); + assertNotNull("response1 has the wrong schema.", + responses.get("response1").getContent().get("app/test2").getSchema()); + assertNotNull("response1 has no schema ref.", + responses.get("response1").getContent().get("app/test2").getSchema().getRef()); + assertEquals("response1 has the wrong schema ref.", "#/components/schemas/schema1", + responses.get("response1").getContent().get("app/test2").getSchema().getRef()); + + // Test the security scheme components + Map security = components + .getSecuritySchemes(); + assertTrue("securityScheme1 wasn't found.", security.containsKey("securityScheme1")); + assertEquals("securityScheme1 has the wrong description.", "the first security scheme.", + security.get("securityScheme1").getDescription()); + assertEquals("securityScheme1 has the wrong scheme.", "BASIC", security.get("securityScheme1").getScheme()); + assertEquals("securityScheme1 has the wrong type.", Type.HTTP, security.get("securityScheme1").getType()); + assertNotNull("securityScheme1 has no flows.", security.get("securityScheme1").getFlows()); + assertNotNull("securityScheme1 has no flow password.", + security.get("securityScheme1").getFlows().getPassword()); + assertEquals("securityScheme1 has the wrong auth url.", "http://auth", + security.get("securityScheme1").getFlows().getPassword().getAuthorizationUrl()); + assertNotNull("securityScheme1 has no scope.", + security.get("securityScheme1").getFlows().getPassword().getScopes().get("oauthScope1")); + assertEquals("securityScheme1 has the wrong auth url.", "the first OAuth scope.", + security.get("securityScheme1").getFlows().getPassword().getScopes().get("oauthScope1")); + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OperationTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OperationTest.java new file mode 100644 index 00000000000..f07a12fc4be --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OperationTest.java @@ -0,0 +1,56 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; + +@Path("/operation") +public class OperationTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @GET + @Path("/specified") + @Operation(description = "A GET operation.", operationId = "getOperation", summary = "GET operation summary.") + public String specifiedOperation() { + return null; + } + + @Test + public void specifiedOperationTest() { + org.eclipse.microprofile.openapi.models.Operation operation = document.getPaths() + .get("/test/operation/specified").getGET(); + assertEquals("Operation had the wrong description.", "A GET operation.", operation.getDescription()); + assertEquals("Operation had the wrong operation id.", "getOperation", operation.getOperationId()); + assertEquals("Operation had the wrong summary.", "GET operation summary.", operation.getSummary()); + } + + @GET + @Path("/hidden") + @Operation(hidden = true) + public String hiddenOperation() { + return null; + } + + @Test + public void hiddenOperationTest() { + org.eclipse.microprofile.openapi.models.Operation operation = document.getPaths() + .get("/test/operation/hidden").getGET(); + assertNull("Operation was not hidden.", operation); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ParameterAnnotationTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ParameterAnnotationTest.java new file mode 100644 index 00000000000..0a6f46f1760 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ParameterAnnotationTest.java @@ -0,0 +1,95 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.List; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn; +import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; + +@Path("/parameter/openapi/") +public class ParameterAnnotationTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @GET + @Path("/inline") + public String inlineQueryAnnotation( + @QueryParam("format") + @Parameter( + description = "The format of the output.", + in = ParameterIn.COOKIE, + schema = @Schema( + type = SchemaType.ARRAY, + description = "format parameter schema." + ) + ) + String formatVar) { + return null; + } + + @Test + public void inlineQueryAnnotationTest() { + List parameters = document.getPaths() + .get("/test/parameter/openapi/inline").getGET().getParameters(); + for (org.eclipse.microprofile.openapi.models.parameters.Parameter parameter : parameters) { + if (parameter.getName().equals("format")) { + // Test that the description has been set + assertEquals("The parameter has the wrong description.", "The format of the output.", + parameter.getDescription()); + // Test that the input type hasn't been changed + assertEquals("The parameter has the wrong location.", In.QUERY, parameter.getIn()); + // Test that the schema type hasn't been changed + assertEquals("The parameter has the wrong schema type.", + org.eclipse.microprofile.openapi.models.media.Schema.SchemaType.STRING, + parameter.getSchema().getType()); + // Test that the schema description has filtered through + assertEquals("The parameter has the wrong schema description.", "format parameter schema.", + parameter.getSchema().getDescription()); + return; + } + } + fail("Parameter not found."); + } + + @GET + @Path("/method") + @Parameter(name = "format", in = ParameterIn.QUERY, description = "The format of the output.") + public String methodQueryAnnotation(@QueryParam("format") String formatVar) { + return null; + } + + @Test + public void methodQueryAnnotationTest() { + List parameters = document.getPaths() + .get("/test/parameter/openapi/method").getGET().getParameters(); + for (org.eclipse.microprofile.openapi.models.parameters.Parameter parameter : parameters) { + if (parameter.getName().equals("format")) { + // Test that the description has been set + assertEquals("The parameter has the wrong description.", "The format of the output.", + parameter.getDescription()); + return; + } + } + fail("Parameter not found."); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/RequestBodyTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/RequestBodyTest.java new file mode 100644 index 00000000000..6118d02e22d --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/RequestBodyTest.java @@ -0,0 +1,89 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import static java.util.Collections.singletonMap; +import static javax.ws.rs.core.MediaType.APPLICATION_XML; +import static javax.ws.rs.core.MediaType.WILDCARD; +import static org.junit.Assert.assertEquals; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test that the @RequestBody annotation is handled correctly. + */ +@Path("/requestbody") +public class RequestBodyTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @GET + @Path("/empty") + @RequestBody + public String emptyRequestBody() { + return null; + } + + @Test + public void testEmptyRequestBody() { + TestUtils.testOperation(document, "/test/requestbody/empty", "emptyRequestBody", HttpMethod.GET); + TestUtils.testRequestBody(document, "/test/requestbody/empty", HttpMethod.GET, null); + } + + @GET + @Path("/specified") + @Consumes(APPLICATION_XML) + @RequestBody(description = "specified by an annotation.", required = true) + public String specifiedRequestBody(String test, @Context UriInfo info) { + return null; + } + + @Test + public void testSpecifiedRequestBody() { + TestUtils.testOperation(document, "/test/requestbody/specified", "specifiedRequestBody", HttpMethod.GET); + org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = TestUtils.testRequestBody(document, + "/test/requestbody/specified", HttpMethod.GET, singletonMap(APPLICATION_XML, SchemaType.STRING)); + assertEquals("/test/requestbody/specified requestBody had the wrong description.", + "specified by an annotation.", requestBody.getDescription()); + assertEquals("/test/requestbody/specified requestBody had the wrong required value.", true, + requestBody.getRequired()); + } + + @POST + @Path("/override") + @RequestBody(name = "ignored", description = "ignore me", required = true) + public String overrideRequestBody( + @RequestBody(name = "override", description = "overrides method.", required = false) String input) { + return null; + } + + @Test + public void testOverridenRequestBody() { + TestUtils.testOperation(document, "/test/requestbody/override", "overrideRequestBody", HttpMethod.POST); + org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = TestUtils.testRequestBody(document, + "/test/requestbody/override", HttpMethod.POST, singletonMap(WILDCARD, SchemaType.STRING)); + assertEquals("/test/requestbody/override requestBody had the wrong description.", "overrides method.", + requestBody.getDescription()); + assertEquals("/test/requestbody/override requestBody had the wrong required value.", false, + requestBody.getRequired()); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SchemaTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SchemaTest.java new file mode 100644 index 00000000000..7c60be69204 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SchemaTest.java @@ -0,0 +1,121 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.core.MediaType; + +import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.responses.APIResponses; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; +import fish.payara.microprofile.openapi.test.app.data.SchemaComponentTest; + +@Path("/schema") +public class SchemaTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @GET + @Path("/schema1") + @APIResponse(content = @Content(schema = @Schema(ref = "Schema1"))) + public Object getSchema1Response() { + return null; + } + + @Test + public void responseSchemaTest() { + org.eclipse.microprofile.openapi.models.responses.APIResponse response = document.getPaths() + .get("/test/schema/schema1").getGET().getResponses().get(APIResponses.DEFAULT); + assertNotNull("No default response found.", response); + assertNotNull("No content found.", response.getContent()); + assertNotNull("No mediatype found.", response.getContent().get("*/*")); + org.eclipse.microprofile.openapi.models.media.Schema schema = response.getContent().get("*/*").getSchema(); + assertNotNull("No content schema found.", schema); + assertEquals("Incorrect reference.", "#/components/schemas/Schema1", schema.getRef()); + assertNull("Incorrect type.", schema.getType()); + } + + @POST + @Path("/array") + @RequestBody(content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(description = "An array of strings.", type = SchemaType.ARRAY, format = "[item1,item2,item3]"))) + @Consumes(MediaType.APPLICATION_JSON) + public String postStringArray(String[] value) { + return null; + } + + @Test + public void newRequestSchemaTest() { + org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = document.getPaths() + .get("/test/schema/array").getPOST().getRequestBody(); + assertNotNull("No request body found.", requestBody); + assertNotNull("No content found.", requestBody.getContent()); + assertNotNull("No mediatype found.", requestBody.getContent().get(MediaType.APPLICATION_JSON)); + org.eclipse.microprofile.openapi.models.media.Schema schema = requestBody.getContent() + .get(MediaType.APPLICATION_JSON).getSchema(); + assertNotNull("No content schema found.", schema); + assertEquals("Incorrect schema description.", "An array of strings.", schema.getDescription()); + assertEquals("Incorrect schema schema type.", + org.eclipse.microprofile.openapi.models.media.Schema.SchemaType.ARRAY, schema.getType()); + assertEquals("Incorrect schema format.", "[item1,item2,item3]", schema.getFormat()); + } + + @POST + @Path("/component") + public String addSchemaComponent(@Schema(ref = "TestComponent") Object component) { + return null; + } + + @Test + public void parameterSchemaTest() { + org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = document.getPaths() + .get("/test/schema/component").getPOST().getRequestBody(); + assertNotNull("No request body found.", requestBody); + assertNotNull("No content found.", requestBody.getContent()); + assertNotNull("No mediatype found.", requestBody.getContent().get("*/*")); + org.eclipse.microprofile.openapi.models.media.Schema schema = requestBody.getContent().get("*/*").getSchema(); + assertNotNull("No content schema found.", schema); + assertEquals("Incorrect reference.", "#/components/schemas/TestComponent", schema.getRef()); + assertNull("Incorrect type.", schema.getType()); + } + + @POST + @Path("/component/override") + @RequestBody(content = @Content(schema = @Schema(title = "Schema Component RequestBody", implementation = SchemaComponentTest.class))) + public String addOverridenSchemaComponent(SchemaComponentTest component) { + return null; + } + + @Test + public void overridenSchemaTest() { + org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = document.getPaths() + .get("/test/schema/component/override").getPOST().getRequestBody(); + assertNotNull("No request body found.", requestBody); + assertNotNull("No content found.", requestBody.getContent()); + assertNotNull("No mediatype found.", requestBody.getContent().get("*/*")); + org.eclipse.microprofile.openapi.models.media.Schema schema = requestBody.getContent().get("*/*").getSchema(); + assertNotNull("No content schema found.", schema); + assertNull("Incorrect reference.", schema.getRef()); + assertEquals("Incorrect schema title.", "Schema Component RequestBody", schema.getTitle()); + assertEquals("Incorrect schema description.", "A component for testing the @Schema annotation.", + schema.getDescription()); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecurityRequirementTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecurityRequirementTest.java new file mode 100644 index 00000000000..e9a8abce39b --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecurityRequirementTest.java @@ -0,0 +1,69 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirements; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +@SecurityRequirement(name = "securityScheme1", scopes = { "oauthScope1" }) +@SecurityRequirements({ + @SecurityRequirement(name = "methodDeclaredApiKey", scopes = { "read" }) +}) +@Path("/security/requirement") +public class SecurityRequirementTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @GET + @Path("/specified") + @SecurityRequirement(name = "classDeclaredOAuth", scopes = { "write" }) + @SecurityRequirements({ + @SecurityRequirement(name = "notDeclared", scopes = { "whatever" }) + }) + public String specifiedRequirement() { + return null; + } + + @Test + public void specifiedRequirementTest() { + TestUtils.testSecurityRequirement(document, "/test/security/requirement/specified", HttpMethod.GET, + "classDeclaredOAuth", "write"); + TestUtils.testSecurityRequirement(document, "/test/security/requirement/specified", HttpMethod.GET, + "notDeclared", "whatever"); + TestUtils.testNotSecurityRequirement(document, "/test/security/requirement/specified", HttpMethod.GET, + "securityScheme1"); + TestUtils.testNotSecurityRequirement(document, "/test/security/requirement/specified", HttpMethod.GET, + "methodDeclaredApiKey"); + } + + @GET + @Path("/inherited") + public String inheritedRequirement() { + return null; + } + + @Test + public void inheritedRequirementTest() { + TestUtils.testSecurityRequirement(document, "/test/security/requirement/inherited", HttpMethod.GET, + "securityScheme1", "oauthScope1"); + TestUtils.testSecurityRequirement(document, "/test/security/requirement/inherited", HttpMethod.GET, + "methodDeclaredApiKey", "read"); + TestUtils.testNotSecurityRequirement(document, "/test/security/requirement/inherited", HttpMethod.GET, + "classDeclaredOAuth"); + TestUtils.testNotSecurityRequirement(document, "/test/security/requirement/inherited", HttpMethod.GET, + "notDeclared"); + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecuritySchemeTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecuritySchemeTest.java new file mode 100644 index 00000000000..f24c887dfdc --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecuritySchemeTest.java @@ -0,0 +1,103 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeIn; +import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType; +import org.eclipse.microprofile.openapi.annotations.security.OAuthFlow; +import org.eclipse.microprofile.openapi.annotations.security.OAuthFlows; +import org.eclipse.microprofile.openapi.annotations.security.OAuthScope; +import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme.In; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme.Type; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; + +/** + * A resource to test that the @SecurityScheme annotation is handled correctly. + */ +@SecurityScheme( + securitySchemeName = "classDeclaredOAuth", + type = SecuritySchemeType.OAUTH2, + description = "OAuth key.", + flows = @OAuthFlows( + authorizationCode = @OAuthFlow( + tokenUrl = "/api/auth", + scopes = { + @OAuthScope(name = "read", description = "Permission to read."), + @OAuthScope(name = "write", description = "Permission to write.") + } + ) + ) +) +@Path("/security/scheme") +public class SecuritySchemeTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @Test + public void testClassDeclaredOauth() { + org.eclipse.microprofile.openapi.models.security.SecurityScheme scheme = document.getComponents() + .getSecuritySchemes().get("classDeclaredOAuth"); + assertNotNull("The scheme wasn't found.", scheme); + // Test the type + assertEquals("The scheme had the wrong type.", Type.OAUTH2, scheme.getType()); + // Test the description + assertEquals("The scheme had the wrong description.", "OAuth key.", scheme.getDescription()); + // Test the OAuth Flows + assertNotNull("The scheme had no flows.", scheme.getFlows()); + assertNotNull("The scheme had no authorization flow.", scheme.getFlows().getAuthorizationCode()); + // Test the OAuth Flow + assertEquals("The authorization flow had the wrong token url.", "/api/auth", + scheme.getFlows().getAuthorizationCode().getTokenUrl()); + // Test the OAuth Scopes + assertNotNull("The authorization flow had no scopes.", scheme.getFlows().getAuthorizationCode().getScopes()); + // Test the OAuth READ scope + assertTrue("The authorization flow had no read scope.", + scheme.getFlows().getAuthorizationCode().getScopes().containsKey("read")); + assertEquals("The write scope has the wrong description.", "Permission to read.", + scheme.getFlows().getAuthorizationCode().getScopes().get("read")); + // Test the OAuth WRITE scope + assertTrue("The authorization flow had no write scope.", + scheme.getFlows().getAuthorizationCode().getScopes().containsKey("write")); + assertEquals("The write scope has the wrong description.", "Permission to write.", + scheme.getFlows().getAuthorizationCode().getScopes().get("write")); + } + + @GET + @SecurityScheme( + securitySchemeName = "methodDeclaredApiKey", + type = SecuritySchemeType.APIKEY, + in = SecuritySchemeIn.QUERY, + apiKeyName = "key", + description = "Insecure key." + ) + public String specifiedScheme(@QueryParam(value = "key") String authKey) { + return null; + } + + @Test + public void testMethodDeclaredApiKey() { + org.eclipse.microprofile.openapi.models.security.SecurityScheme scheme = document.getComponents() + .getSecuritySchemes().get("methodDeclaredApiKey"); + assertNotNull("The scheme wasn't found.", scheme); + assertEquals("The scheme had the wrong type.", Type.APIKEY, scheme.getType()); + assertEquals("The scheme had the wrong location.", In.QUERY, scheme.getIn()); + assertEquals("The scheme had the wrong apiKeyName.", "key", scheme.getName()); + assertEquals("The scheme had the wrong description.", "Insecure key.", scheme.getDescription()); + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ServerTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ServerTest.java new file mode 100644 index 00000000000..2d87294cedc --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ServerTest.java @@ -0,0 +1,90 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.annotations.servers.Server; +import org.eclipse.microprofile.openapi.annotations.servers.ServerVariable; +import org.eclipse.microprofile.openapi.annotations.servers.Servers; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test that the @Server and @Servers annotations are handled + * correctly. + */ +@Path("/servers") +@Server(description = "override me", url = "http://override-me") +@Servers(value = { @Server(description = "also override me", url = "http://also-override-me") }) +public class ServerTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @GET + @Path("/override") + @Server(description = "overridden", url = "http://overriden", variables = { + @ServerVariable(name = "name", description = "description", defaultValue = "value", enumeration = { "TEST", + "ENUM" }) }) + public String getOverriden() { + return null; + } + + @Test + public void overridenServerTest() { + org.eclipse.microprofile.openapi.models.servers.Server server = TestUtils.testServer(document, + "/test/servers/override", HttpMethod.GET, "http://overriden", "overridden"); + TestUtils.testNotServer(document, "/test/servers/override", HttpMethod.GET, "http://override-me"); + TestUtils.testNotServer(document, "/test/servers/override", HttpMethod.GET, "http://also-override-me"); + TestUtils.testServerContainsVariable(server, "name", "description", "value", "TEST"); + TestUtils.testServerContainsVariable(server, "name", "description", "value", "ENUM"); + } + + @GET + @Path("/inherit") + public String getInherited() { + return null; + } + + @Test + public void inheritedServerTest() { + TestUtils.testNotServer(document, "/test/servers/inherit", HttpMethod.GET, "http://overriden"); + TestUtils.testServer(document, "/test/servers/inherit", HttpMethod.GET, "http://override-me", "override me"); + TestUtils.testServer(document, "/test/servers/inherit", HttpMethod.GET, "http://also-override-me", + "also override me"); + } + + @GET + @Path("/ignore") + @Server + public String getIgnored() { + return null; + } + + @GET + @Path("/ignore2") + @Servers + public String getIgnored2() { + return null; + } + + @Test + public void ignoredServerTest() { + TestUtils.testNotServer(document, "/test/servers/ignore", HttpMethod.GET, "http://overriden"); + TestUtils.testNotServer(document, "/test/servers/ignore", HttpMethod.GET, "http://override-me"); + TestUtils.testNotServer(document, "/test/servers/ignore", HttpMethod.GET, "http://also-override-me"); + TestUtils.testNotServer(document, "/test/servers/ignore2", HttpMethod.GET, "http://overriden"); + TestUtils.testNotServer(document, "/test/servers/ignore2", HttpMethod.GET, "http://override-me"); + TestUtils.testNotServer(document, "/test/servers/ignore2", HttpMethod.GET, "http://also-override-me"); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/TagTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/TagTest.java new file mode 100644 index 00000000000..0f95e4b9e58 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/TagTest.java @@ -0,0 +1,83 @@ +package fish.payara.microprofile.openapi.test.app.annotation; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.eclipse.microprofile.openapi.annotations.tags.Tags; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test that the @Tag and @Tags annotations are handled correctly. + */ +@Path("/tags") +@Tag(name = "new", description = "Created from the class.") +@Tags(refs = { "tag1" }) +public class TagTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @GET + @Path("/override") + @Tag(name = "override", description = "Overriden.") + @Tag(ref = "tag2") + public String getOverriden() { + return null; + } + + @Test + public void overridenTagTest() { + TestUtils.testTag(document, "/test/tags/override", HttpMethod.GET, "override", "Overriden."); + TestUtils.testTag(document, "/test/tags/override", HttpMethod.GET, "tag2", "the second tag."); + TestUtils.testNotTag(document, "/test/tags/override", HttpMethod.GET, "tag1"); + TestUtils.testNotTag(document, "/test/tags/override", HttpMethod.GET, "new"); + } + + @GET + @Path("/inherit") + public String getInherited() { + return null; + } + + @Test + public void inheritedTagTest() { + TestUtils.testTag(document, "/test/tags/inherit", HttpMethod.GET, "new", "Created from the class."); + TestUtils.testTag(document, "/test/tags/inherit", HttpMethod.GET, "tag1", "the first tag."); + } + + @GET + @Path("/ignore") + @Tag + public String getIgnored() { + return null; + } + + @GET + @Path("/ignore2") + @Tags + public String getIgnored2() { + return null; + } + + @Test + public void ignoredTagsTest() { + TestUtils.testNotTag(document, "/test/tags/ignore", HttpMethod.GET, "tag1"); + TestUtils.testNotTag(document, "/test/tags/ignore", HttpMethod.GET, "tag2"); + TestUtils.testNotTag(document, "/test/tags/ignore", HttpMethod.GET, "new"); + TestUtils.testNotTag(document, "/test/tags/ignore2", HttpMethod.GET, "tag1"); + TestUtils.testNotTag(document, "/test/tags/ignore2", HttpMethod.GET, "tag2"); + TestUtils.testNotTag(document, "/test/tags/ignore2", HttpMethod.GET, "new"); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/FormParamTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/FormParamTest.java new file mode 100644 index 00000000000..926dce31be7 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/FormParamTest.java @@ -0,0 +1,68 @@ +package fish.payara.microprofile.openapi.test.app.application; + +import static java.util.Collections.singletonMap; + +import javax.ws.rs.FormParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test that various types of form parameters are successfully + * generalised into one data type. + */ +@Path("/form") +public class FormParamTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new ApplicationProcessedDocument(); + } + + @POST + @Path("/stringint") + public String getStringAndInt(@FormParam("stringParam") String stringParam, @FormParam("intParam") int intParam) { + return null; + } + + @Test + public void stringIntTest() { + TestUtils.testRequestBody(document, "/test/form/stringint", HttpMethod.POST, + singletonMap("*/*", SchemaType.STRING)); + } + + @POST + @Path("/intstring") + public String getIntAndString(@FormParam("intParam") int intParam, @FormParam("stringParam") String stringParam) { + return null; + } + + @Test + public void intStringTest() { + TestUtils.testRequestBody(document, "/test/form/intstring", HttpMethod.POST, + singletonMap("*/*", SchemaType.STRING)); + } + + @POST + @Path("/intint") + public String getIntAndInt(@FormParam("intParam") int intParam, @FormParam("stringParam") int intParam2) { + return null; + } + + @Test + public void intIntTest() { + TestUtils.testRequestBody(document, "/test/form/intint", HttpMethod.POST, + singletonMap("*/*", SchemaType.INTEGER)); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodMergeTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodMergeTest.java new file mode 100644 index 00000000000..bac1d79e298 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodMergeTest.java @@ -0,0 +1,69 @@ +package fish.payara.microprofile.openapi.test.app.application; + +import static java.util.Collections.singletonMap; +import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.MediaType.APPLICATION_XML; + +import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test the results of multiple methods sharing the same endpoint. + */ +@Path("/merge") +public class MethodMergeTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new ApplicationProcessedDocument(); + } + + @POST + @Consumes(APPLICATION_JSON) + @Produces(APPLICATION_XML) + public Object jsonToXml(Object data) { + return data; + } + + @POST + @Consumes(APPLICATION_FORM_URLENCODED) + @Produces(APPLICATION_JSON) + public Object formToJson(@FormParam("paramName") String name, @FormParam("paramValue") int value) { + return null; + } + + @Test + public void testMergedEndpoint() { + TestUtils.testOperation(document, "/test/merge", null, HttpMethod.POST); + Operation operation = document.getPaths().get("/test/merge").getPOST(); + if ("jsonToXml".equals(operation.getOperationId())) { + TestUtils.testRequestBody(document, "/test/merge", HttpMethod.POST, + singletonMap(APPLICATION_JSON, SchemaType.OBJECT)); + TestUtils.testResponse(document, "/test/merge", HttpMethod.POST, + singletonMap(APPLICATION_XML, SchemaType.OBJECT)); + } + if ("formToJson".equals(operation.getOperationId())) { + TestUtils.testRequestBody(document, "/test/merge", HttpMethod.POST, + singletonMap(APPLICATION_FORM_URLENCODED, SchemaType.STRING)); + TestUtils.testResponse(document, "/test/merge", HttpMethod.POST, + singletonMap(APPLICATION_JSON, SchemaType.OBJECT)); + } + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodTest.java new file mode 100644 index 00000000000..124a23102e7 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodTest.java @@ -0,0 +1,102 @@ +package fish.payara.microprofile.openapi.test.app.application; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.PATCH; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test each of the JAX-RS methods. + */ +@Path("/method") +public class MethodTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new ApplicationProcessedDocument(); + } + + @GET + public String getResource() { + return null; + } + + @Test + public void testGet() { + TestUtils.testOperation(document, "/test/method", "getResource", HttpMethod.GET); + } + + @POST + public String postResource() { + return null; + } + + @Test + public void testPost() { + TestUtils.testOperation(document, "/test/method", "postResource", HttpMethod.POST); + } + + @PUT + public String putResource() { + return null; + } + + @Test + public void testPut() { + TestUtils.testOperation(document, "/test/method", "putResource", HttpMethod.PUT); + } + + @DELETE + public String deleteResource() { + return null; + } + + @Test + public void testDelete() { + TestUtils.testOperation(document, "/test/method", "deleteResource", HttpMethod.DELETE); + } + + @HEAD + public String headResource() { + return null; + } + + @Test + public void testHead() { + TestUtils.testOperation(document, "/test/method", "headResource", HttpMethod.HEAD); + } + + @OPTIONS + public String optionsResource() { + return null; + } + + @Test + public void testOptions() { + TestUtils.testOperation(document, "/test/method", "optionsResource", HttpMethod.OPTIONS); + } + + @PATCH + public String patchResource() { + return null; + } + + @Test + public void testPatch() { + TestUtils.testOperation(document, "/test/method", "patchResource", HttpMethod.PATCH); + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ParameterTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ParameterTest.java new file mode 100644 index 00000000000..34bc8c6fdac --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ParameterTest.java @@ -0,0 +1,149 @@ +package fish.payara.microprofile.openapi.test.app.application; + +import java.util.Collections; + +import javax.ws.rs.CookieParam; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.MatrixParam; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriInfo; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test various types of parameters. + */ +@Path("/parameter") +public class ParameterTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new ApplicationProcessedDocument(); + } + + // Recognise parameter name + + @GET + @Path("/name") + public String name(@QueryParam("name1") String blarg, @QueryParam("name2") int glarg) { + return null; + } + + @Test + public void testParameterName() { + TestUtils.testParameter(document, "/test/parameter/name", HttpMethod.GET, + Collections.singletonMap("name1", In.QUERY)); + TestUtils.testParameter(document, "/test/parameter/name", HttpMethod.GET, + Collections.singletonMap("name2", In.QUERY)); + } + + // Recognise different types of parameters + + @GET + @Path("/query") + public String query(@QueryParam("param") String param) { + return null; + } + + @Test + public void testQueryParameter() { + TestUtils.testParameter(document, "/test/parameter/query", HttpMethod.GET, + Collections.singletonMap("param", In.QUERY)); + } + + @GET + @Path("/path/{param}") + public String path(@PathParam("param") String param) { + return null; + } + + @Test + public void testPathParameter() { + TestUtils.testParameter(document, "/test/parameter/path/{param}", HttpMethod.GET, + Collections.singletonMap("param", In.PATH)); + } + + @GET + @Path("/cookie") + public String cookie(@CookieParam("param") String param) { + return null; + } + + @Test + public void testCookieParameter() { + TestUtils.testParameter(document, "/test/parameter/cookie", HttpMethod.GET, + Collections.singletonMap("param", In.COOKIE)); + } + + @GET + @Path("/header") + public String header(@HeaderParam("param") String param) { + return null; + } + + @Test + public void testHeaderParameter() { + TestUtils.testParameter(document, "/test/parameter/header", HttpMethod.GET, + Collections.singletonMap("param", In.HEADER)); + } + + // PARAMETERS EXPECTED TO BE IGNORED + + @GET + @Path("/fake") + public String fake(/* Not a parameter */ String param) { + return null; + } + + @Test + public void testFakeParameter() { + TestUtils.testParameter(document, "/test/parameter/fake", HttpMethod.GET, null); + } + + @GET + @Path("/form") + public String form(@FormParam("param") String param) { + return null; + } + + @Test + public void testFormParameter() { + TestUtils.testParameter(document, "/test/parameter/form", HttpMethod.GET, null); + } + + @GET + @Path("/matrix") + public String matrix(@MatrixParam("param") String param) { + return null; + } + + @Test + public void testMatrixParameter() { + TestUtils.testParameter(document, "/test/parameter/matrix", HttpMethod.GET, null); + } + + @GET + @Path("/context") + public String context(@Context UriInfo uriInfo) { + return null; + } + + @Test + public void testContextParameter() { + TestUtils.testParameter(document, "/test/parameter/context", HttpMethod.GET, null); + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/PathTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/PathTest.java new file mode 100644 index 00000000000..12861dcb9a6 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/PathTest.java @@ -0,0 +1,48 @@ +package fish.payara.microprofile.openapi.test.app.application; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test that various paths are created correctly. + */ +@Path("/path") +public class PathTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new ApplicationProcessedDocument(); + } + + @GET + public String getRootResource() { + return null; + } + + @Test + public void testRootResource() { + TestUtils.testOperation(document, "/test/path", "getRootResource", HttpMethod.GET); + } + + @GET + @Path("/1") + public String getResource() { + return null; + } + + @Test + public void testResource() { + TestUtils.testOperation(document, "/test/path/1", "getResource", HttpMethod.GET); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RequestTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RequestTest.java new file mode 100644 index 00000000000..8638a6719a1 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RequestTest.java @@ -0,0 +1,90 @@ +package fish.payara.microprofile.openapi.test.app.application; + +import static java.util.Collections.singletonMap; +import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.MediaType.APPLICATION_XML; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN; + +import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test that various types of request body are created properly. + */ +@Path("/request") +public class RequestTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new ApplicationProcessedDocument(); + } + + @GET + @Path("/none") + public String untyped(Object data, @QueryParam("ignoreme") String param) { + return null; + } + + @Test + public void testUntyped() { + TestUtils.testRequestBody(document, "/test/request/none", HttpMethod.GET, + singletonMap("*/*", SchemaType.OBJECT)); + } + + @POST + @Consumes(APPLICATION_FORM_URLENCODED) + @Path("/form") + public String form(@FormParam("test") String test, @QueryParam("ignoreme") String param) { + return null; + } + + @Test + public void testForm() { + TestUtils.testRequestBody(document, "/test/request/form", HttpMethod.POST, + singletonMap(APPLICATION_FORM_URLENCODED, SchemaType.STRING)); + } + + @GET + @Consumes(APPLICATION_JSON) + @Path("/json") + public String json(String[] data) { + return null; + } + + @Test + public void testArray() { + TestUtils.testRequestBody(document, "/test/request/json", HttpMethod.GET, + singletonMap(APPLICATION_JSON, SchemaType.ARRAY)); + } + + @GET + @Consumes({ APPLICATION_XML, TEXT_PLAIN }) + @Path("/multiple") + public String multiple(Boolean data) { + return null; + } + + @Test + public void testMultiple() { + TestUtils.testRequestBody(document, "/test/request/multiple", HttpMethod.GET, + singletonMap(APPLICATION_XML, SchemaType.BOOLEAN)); + TestUtils.testRequestBody(document, "/test/request/multiple", HttpMethod.GET, + singletonMap(TEXT_PLAIN, SchemaType.BOOLEAN)); + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ResponseTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ResponseTest.java new file mode 100644 index 00000000000..8792bd640ea --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ResponseTest.java @@ -0,0 +1,101 @@ +package fish.payara.microprofile.openapi.test.app.application; + +import static java.util.Collections.singletonMap; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.MediaType.APPLICATION_XML; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN; +import static javax.ws.rs.core.MediaType.TEXT_XML; +import static javax.ws.rs.core.MediaType.WILDCARD; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; +import fish.payara.microprofile.openapi.test.app.data.ComponentTest; + +/** + * A resource to test that various response types are mapped properly. + */ +@Path("/response") +public class ResponseTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new ApplicationProcessedDocument(); + } + + @GET + @Path("/none") + public String none() { + return null; + } + + @Test + public void noResponseTest() { + TestUtils.testResponse(document, "/test/response/none", HttpMethod.GET, + singletonMap(WILDCARD, SchemaType.STRING)); + } + + @GET + @Path("/component") + public ComponentTest component() { + return null; + } + + @Test + public void componentResponseTest() { + TestUtils.testResponse(document, "/test/response/component", HttpMethod.GET, + singletonMap(WILDCARD, "#/components/schemas/ComponentTest")); + } + + @GET + @Produces(APPLICATION_JSON) + @Path("/json") + public Object json() { + return null; + } + + @Test + public void jsonResponseTest() { + TestUtils.testResponse(document, "/test/response/json", HttpMethod.GET, + singletonMap(APPLICATION_JSON, SchemaType.OBJECT)); + } + + @GET + @Produces(TEXT_PLAIN) + @Path("/text") + public Float text() { + return null; + } + + @Test + public void floatResponseTest() { + TestUtils.testResponse(document, "/test/response/text", HttpMethod.GET, + singletonMap(TEXT_PLAIN, SchemaType.NUMBER)); + } + + @GET + @Produces({ APPLICATION_XML, TEXT_XML }) + @Path("/multiple") + public Integer multiple() { + return null; + } + + @Test + public void multipleResponsesTest() { + TestUtils.testResponse(document, "/test/response/multiple", HttpMethod.GET, + singletonMap(APPLICATION_XML, SchemaType.INTEGER)); + TestUtils.testResponse(document, "/test/response/multiple", HttpMethod.GET, + singletonMap(TEXT_XML, SchemaType.INTEGER)); + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RootPathTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RootPathTest.java new file mode 100644 index 00000000000..05054dbd94a --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RootPathTest.java @@ -0,0 +1,41 @@ +package fish.payara.microprofile.openapi.test.app.application; + +import java.util.Collections; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A resource to test that a resource at the context root is mapped correctly. + */ +@Path("/") +public class RootPathTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new ApplicationProcessedDocument(); + } + + @GET + public String getRoot() { + return null; + } + + @Test + public void testRoot() { + TestUtils.testOperation(document, "/test", "getRoot", HttpMethod.GET); + TestUtils.testResponse(document, "/test", HttpMethod.GET, Collections.singletonMap("*/*", SchemaType.STRING)); + TestUtils.testParameter(document, "/test", HttpMethod.GET, null); + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/ComponentTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/ComponentTest.java new file mode 100644 index 00000000000..f91ba8c9ed2 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/ComponentTest.java @@ -0,0 +1,94 @@ +package fish.payara.microprofile.openapi.test.app.data; + +import static org.junit.Assert.assertNull; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; +import fish.payara.microprofile.openapi.resource.util.TestUtils; + +/** + * A test component to be added to the model. + */ +@SuppressWarnings("unused") +public class ComponentTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new ApplicationProcessedDocument(); + } + + @Test + public void ignoreTransientFieldTest() { + assertNull(document.getComponents().getSchemas().get("ComponentTest").getProperties().get("document")); + } + + // Should be INTEGER type + private int id; + private Integer id2; + + @Test + public void testIntegers() { + TestUtils.testComponentProperty(document, "ComponentTest", "id", SchemaType.INTEGER); + TestUtils.testComponentProperty(document, "ComponentTest", "id2", SchemaType.INTEGER); + } + + // Should be NUMBER type + private float $ref; + private Float $ref2; + private double $ref3; + private Double $ref4; + + @Test + public void testNumbers() { + TestUtils.testComponentProperty(document, "ComponentTest", "$ref", SchemaType.NUMBER); + TestUtils.testComponentProperty(document, "ComponentTest", "$ref2", SchemaType.NUMBER); + TestUtils.testComponentProperty(document, "ComponentTest", "$ref3", SchemaType.NUMBER); + TestUtils.testComponentProperty(document, "ComponentTest", "$ref4", SchemaType.NUMBER); + } + + // Should be String type + private String this$0; + + @Test + public void testStrings() { + TestUtils.testComponentProperty(document, "ComponentTest", "this$0", SchemaType.STRING); + } + + // Should be BOOLEAN type + private boolean bool; + private Boolean bool2; + + @Test + public void testBooleans() { + TestUtils.testComponentProperty(document, "ComponentTest", "bool", SchemaType.BOOLEAN); + TestUtils.testComponentProperty(document, "ComponentTest", "bool2", SchemaType.BOOLEAN); + } + + // Should be OBJECT type + private Object _var_; + + @Test + public void testObjects() { + TestUtils.testComponentProperty(document, "ComponentTest", "_var_", SchemaType.OBJECT); + } + + // Should be ARRAY type + private String[] array; + private Object[] array2; + private int[][] array3; + + @Test + public void testArrays() { + TestUtils.testComponentProperty(document, "ComponentTest", "array", SchemaType.ARRAY, SchemaType.STRING); + TestUtils.testComponentProperty(document, "ComponentTest", "array2", SchemaType.ARRAY, SchemaType.OBJECT); + TestUtils.testComponentProperty(document, "ComponentTest", "array3", SchemaType.ARRAY, SchemaType.ARRAY, + SchemaType.INTEGER); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/SchemaComponentTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/SchemaComponentTest.java new file mode 100644 index 00000000000..8bdd3eb134b --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/SchemaComponentTest.java @@ -0,0 +1,62 @@ +package fish.payara.microprofile.openapi.test.app.data; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; + +@Schema(name = "TestComponent", title = "Test Component", description = "A component for testing the @Schema annotation.") +public class SchemaComponentTest { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + document = new AnnotationProcessedDocument(); + } + + @Test + public void schemaCreationTest() { + org.eclipse.microprofile.openapi.models.media.Schema component = document.getComponents().getSchemas() + .get("TestComponent"); + assertNotNull("TestComponent was not found.", component); + assertEquals("TestComponent had the wrong title.", "Test Component", component.getTitle()); + assertEquals("TestComponent had the wrong description.", "A component for testing the @Schema annotation.", + component.getDescription()); + } + + @Schema(name = "componentHeight", title = "The height of the component.", example = "50m") + public int height; + + @Test + public void heightCreationTest() { + org.eclipse.microprofile.openapi.models.media.Schema component = document.getComponents().getSchemas() + .get("TestComponent").getProperties().get("componentHeight"); + assertNotNull("TestComponent had no height property.", component); + assertEquals("TestComponent height had the wrong type.", + org.eclipse.microprofile.openapi.models.media.Schema.SchemaType.INTEGER, component.getType()); + assertEquals("TestComponent height had the wrong example.", "50m", component.getExample()); + assertEquals("TestComponent height had the wrong title.", "The height of the component.", component.getTitle()); + } + + @Schema(title = "An ignored reference to Schema1", ref = "Schema1") + public Object reference; + + @Test + public void referenceCreationTest() { + org.eclipse.microprofile.openapi.models.media.Schema component = document.getComponents().getSchemas() + .get("TestComponent").getProperties().get("reference"); + assertNotNull("TestComponent had no reference property.", component); + assertNull("TestComponent reference had a type.", component.getType()); + assertNull("TestComponent title should be empty.", component.getTitle()); + assertEquals("TestComponent reference had the wrong reference.", "#/components/schemas/Schema1", + component.getRef()); + } + +} \ No newline at end of file From cfc01d9697c2450f873f0f3bfd6bcb57d9cb75b4 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Fri, 11 May 2018 10:36:02 +0100 Subject: [PATCH 05/20] Added application scanner and visitor model. --- .../openapi/api/visitor/ApiContext.java | 24 + .../openapi/api/visitor/ApiVisitor.java | 35 ++ .../openapi/api/visitor/ApiWalker.java | 14 + .../impl/processor/ApplicationProcessor.java | 31 ++ .../impl/visitor/AnnotationVisitor.java | 498 ++++++++++++++++++ .../impl/visitor/ApplicationVisitor.java | 279 ++++++++++ .../openapi/impl/visitor/OpenApiContext.java | 50 ++ .../openapi/impl/visitor/OpenApiWalker.java | 152 ++++++ 8 files changed, 1083 insertions(+) create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiContext.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiVisitor.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiWalker.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/AnnotationVisitor.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/ApplicationVisitor.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiContext.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiContext.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiContext.java new file mode 100644 index 00000000000..3c306d1bd0f --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiContext.java @@ -0,0 +1,24 @@ +package fish.payara.microprofile.openapi.api.visitor; + +import org.eclipse.microprofile.openapi.models.OpenAPI; + +/** + * The context in which a class object is being visited. + * For example, if a method is being visited, the context + * will contain the current state of the {@link OpenAPI}, + * and the current path in the API. + */ +public interface ApiContext { + + /** + * The current {@link OpenAPI} object being operated on. + */ + OpenAPI getApi(); + + /** + * The path of the object currently being visited. + * If the path is null, the object has no context + * (e.g a POJO). + */ + String getPath(); +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiVisitor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiVisitor.java new file mode 100644 index 00000000000..a2d87480ff6 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiVisitor.java @@ -0,0 +1,35 @@ +package fish.payara.microprofile.openapi.api.visitor; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + +/** + * Represents an object that can visit components of an API. + */ +public interface ApiVisitor { + + /** + * @param clazz the class to visit. + * @param context the current context of the API. + */ + void visitClass(Class clazz, ApiContext context); + + /** + * @param method the method to visit. + * @param context the current context of the API. + */ + void visitMethod(Method method, ApiContext context); + + /** + * @param field the field to visit. + * @param context the current context of the API. + */ + void visitField(Field field, ApiContext context); + + /** + * @param parameter the parameter to visit. + * @param context the current context of the API. + */ + void visitParameter(Parameter parameter, ApiContext context); +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiWalker.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiWalker.java new file mode 100644 index 00000000000..88d77eaef98 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiWalker.java @@ -0,0 +1,14 @@ +package fish.payara.microprofile.openapi.api.visitor; + +/** + * Represents an object that can traverse an API + * by passing each element to the given {@link ApiVisitor}. + */ +public interface ApiWalker { + + /** + * Traverse the API, passing each element to the visitor. + * @param visitor the visitor to pass each element to. + */ + void accept(ApiVisitor visitor); +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java new file mode 100644 index 00000000000..9cfc5e6da73 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -0,0 +1,31 @@ +package fish.payara.microprofile.openapi.impl.processor; + +import static fish.payara.microprofile.openapi.impl.processor.utils.ProcessorUtils.getClassesFromLoader; + +import java.util.Set; + +import org.eclipse.microprofile.openapi.models.OpenAPI; + +import fish.payara.microprofile.openapi.api.processor.OASProcessor; +import fish.payara.microprofile.openapi.api.visitor.ApiVisitor; +import fish.payara.microprofile.openapi.api.visitor.ApiWalker; +import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; +import fish.payara.microprofile.openapi.impl.visitor.ApplicationVisitor; +import fish.payara.microprofile.openapi.impl.visitor.OpenApiWalker; + +public class ApplicationProcessor implements OASProcessor { + + private final Set> classes; + + public ApplicationProcessor(ClassLoader appClassLoader) { + this.classes = getClassesFromLoader(appClassLoader); + } + + @Override + public void process(OpenAPI api, OpenApiConfiguration config) { + ApiWalker apiWalker = new OpenApiWalker(api, (config == null) ? classes : config.getValidClasses(classes)); + ApiVisitor apiVisitor = new ApplicationVisitor(); + apiWalker.accept(apiVisitor); + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/AnnotationVisitor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/AnnotationVisitor.java new file mode 100644 index 00000000000..109f2a9ed72 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/AnnotationVisitor.java @@ -0,0 +1,498 @@ +package fish.payara.microprofile.openapi.impl.visitor; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.microprofile.openapi.annotations.ExternalDocumentation; +import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.callbacks.Callback; +import org.eclipse.microprofile.openapi.annotations.callbacks.Callbacks; +import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn; +import org.eclipse.microprofile.openapi.annotations.extensions.Extension; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirements; +import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; +import org.eclipse.microprofile.openapi.annotations.servers.Server; +import org.eclipse.microprofile.openapi.annotations.servers.Servers; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.eclipse.microprofile.openapi.annotations.tags.Tags; +import org.eclipse.microprofile.openapi.models.Components; +import org.eclipse.microprofile.openapi.models.Extensible; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.media.MediaType; +import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; +import org.eclipse.microprofile.openapi.models.responses.APIResponses; + +import fish.payara.microprofile.openapi.api.visitor.ApiContext; +import fish.payara.microprofile.openapi.api.visitor.ApiVisitor; +import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; +import fish.payara.microprofile.openapi.impl.model.ExternalDocumentationImpl; +import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; +import fish.payara.microprofile.openapi.impl.model.OperationImpl; +import fish.payara.microprofile.openapi.impl.model.callbacks.CallbackImpl; +import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.ParameterImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.RequestBodyImpl; +import fish.payara.microprofile.openapi.impl.model.responses.APIResponseImpl; +import fish.payara.microprofile.openapi.impl.model.security.SecurityRequirementImpl; +import fish.payara.microprofile.openapi.impl.model.security.SecuritySchemeImpl; +import fish.payara.microprofile.openapi.impl.model.servers.ServerImpl; +import fish.payara.microprofile.openapi.impl.model.tags.TagImpl; +import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; + +public class AnnotationVisitor implements ApiVisitor { + + @Override + public void visitClass(Class clazz, ApiContext context) { + OpenAPI api = context.getApi(); + // Handle @OpenApiDefinition + handleOpenAPIAnnotation(api, clazz); + // Handle @SecurityScheme + handleSecuritySchemeAnnotation(clazz, api.getComponents()); + // Handle @Extension + handleExtensionAnnotation(clazz, context.getApi()); + } + + @Override + public void visitMethod(Method method, ApiContext context) { + if (context.getPath() == null) { + return; + } + org.eclipse.microprofile.openapi.models.Operation operation = ModelUtils.findOperation(context.getApi(), method, + context.getPath()); + + // Handle @SecurityScheme + handleSecuritySchemeAnnotation(method, context.getApi().getComponents()); + + if (operation != null) { + // Handle @Operation + handleOperationAnnotation(method, operation, context.getApi().getPaths().get(context.getPath())); + // Handle @ExternalDocumentation + handleExternalDocumentationAnnotation(method, operation); + // Handle @Servers + handleServersAnnotation(method, operation); + // Handle @Tags + handleTagsAnnotation(method, operation, context.getApi()); + // Handle @APIResponse + handleAPIResponseAnnotation(method, operation, context.getApi().getComponents().getSchemas()); + // Handle @SecurityRequirement + handleSecurityRequirementAnnotation(method, operation); + // Handle @Parameter + handleParameterAnnotation(method, operation); + // Handle @Callback + handleCallbacksAnnotation(method, operation); + // Handle @Extension + handleExtensionAnnotation(method, operation); + } + } + + @Override + public void visitField(Field field, ApiContext context) { + // Handle @Schema + handleSchemaAnnotation(field, context.getApi()); + } + + @Override + public void visitParameter(Parameter parameter, ApiContext context) { + org.eclipse.microprofile.openapi.models.Operation operation = ModelUtils.findOperation(context.getApi(), + (Method) parameter.getDeclaringExecutable(), context.getPath()); + + if (operation != null) { + // Handle @RequestBody + handleRequestBodyAnnotation(parameter, operation, context.getApi().getComponents().getSchemas()); + // Handle @Schema + handleSchemaAnnotation(parameter, operation, context.getApi().getComponents().getSchemas()); + // Handle @Parameter + handleParameterAnnotation(parameter, operation); + } + } + + private void handleParameterAnnotation(Parameter parameter, + org.eclipse.microprofile.openapi.models.Operation operation) { + if (parameter.isAnnotationPresent(org.eclipse.microprofile.openapi.annotations.parameters.Parameter.class)) { + org.eclipse.microprofile.openapi.annotations.parameters.Parameter annotation = parameter + .getDeclaredAnnotation(org.eclipse.microprofile.openapi.annotations.parameters.Parameter.class); + for (org.eclipse.microprofile.openapi.models.parameters.Parameter param : operation.getParameters()) { + if (param.getName().equals(ModelUtils.getParameterName(parameter))) { + ParameterImpl.merge(annotation, param, false, null); + } + } + } + } + + private void handleParameterAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation) { + if (method.isAnnotationPresent(org.eclipse.microprofile.openapi.annotations.parameters.Parameter.class)) { + org.eclipse.microprofile.openapi.annotations.parameters.Parameter annotation = method + .getDeclaredAnnotation(org.eclipse.microprofile.openapi.annotations.parameters.Parameter.class); + // If the parameter reference is valid + if (annotation.name() != null && !annotation.name().isEmpty()) { + // Get all parameters with the same name + List matchingMethodParameters = Arrays.asList(method.getParameters()).stream() + .filter(x -> ModelUtils.getParameterName(x).equals(annotation.name())) + .collect(Collectors.toList()); + // If there is more than one match, filter it further + if (matchingMethodParameters.size() > 1 && annotation.in() != null + && annotation.in() != ParameterIn.DEFAULT) { + // Remove all parameters of the wrong input type + matchingMethodParameters + .removeIf(x -> ModelUtils.getParameterType(x) != In.valueOf(annotation.in().name())); + } + // If there's only one matching parameter, handle it immediately + Parameter matchingMethodParam = matchingMethodParameters.get(0); + // Find the matching operation parameter + for (org.eclipse.microprofile.openapi.models.parameters.Parameter operationParam : operation + .getParameters()) { + if (operationParam.getName().equals(ModelUtils.getParameterName(matchingMethodParam))) { + ParameterImpl.merge(annotation, operationParam, false, null); + } + } + } + } + } + + private void handleSchemaAnnotation(Field field, OpenAPI api) { + Class clazz = field.getDeclaringClass(); + if (clazz.isAnnotationPresent(Schema.class)) { + Schema annotation = clazz.getDeclaredAnnotation(Schema.class); + + // Get the actual schema name + String schemaName = annotation.name(); + if (schemaName == null || schemaName.isEmpty()) { + schemaName = clazz.getSimpleName(); + } + + // Find and correct the name of the correct schema + updateSchemaName(api.getComponents(), clazz.getSimpleName(), schemaName); + org.eclipse.microprofile.openapi.models.media.Schema model = api.getComponents().getSchemas() + .get(schemaName); + if (model == null) { + model = new SchemaImpl(); + api.getComponents().addSchema(schemaName, model); + } + SchemaImpl.merge(annotation, model, true, api.getComponents().getSchemas()); + + // Start parsing the field in the same manner + if (field.isAnnotationPresent(Schema.class) && !Modifier.isTransient(field.getModifiers())) { + annotation = field.getDeclaredAnnotation(Schema.class); + + org.eclipse.microprofile.openapi.models.media.Schema property = new SchemaImpl(); + SchemaImpl.merge(annotation, property, true, api.getComponents().getSchemas()); + if (property.getRef() == null) { + property.setType(ModelUtils.getSchemaType(field.getType())); + } + + schemaName = annotation.name(); + if (schemaName == null || schemaName.isEmpty()) { + schemaName = field.getName(); + } + + model.addProperty(schemaName, property); + } + } + } + + private void handleSchemaAnnotation(Parameter parameter, + org.eclipse.microprofile.openapi.models.Operation operation, + Map currentSchemas) { + if (parameter.isAnnotationPresent(Schema.class)) { + Schema annotation = parameter.getDeclaredAnnotation(Schema.class); + // Check if it's a request body + if (ModelUtils.isRequestBody(parameter)) { + // Insert the schema to every request body media type + for (MediaType mediaType : operation.getRequestBody().getContent().values()) { + SchemaImpl.merge(annotation, mediaType.getSchema(), true, currentSchemas); + if (annotation.ref() != null && !annotation.ref().isEmpty()) { + mediaType.setSchema(new SchemaImpl().ref(annotation.ref())); + } + } + } else if (ModelUtils.getParameterType(parameter) != null) { + for (org.eclipse.microprofile.openapi.models.parameters.Parameter param : operation.getParameters()) { + if (param.getName().equals(ModelUtils.getParameterName(parameter))) { + SchemaImpl.merge(annotation, param.getSchema(), true, currentSchemas); + if (annotation.ref() != null && !annotation.ref().isEmpty()) { + param.setSchema(new SchemaImpl().ref(annotation.ref())); + } + } + } + } + } + } + + private void updateSchemaName(Components components, String oldName, String newName) { + if (oldName.equals(newName)) { + return; + } + org.eclipse.microprofile.openapi.models.media.Schema schema = components.getSchemas().get(oldName); + if (schema == null) { + return; + } + components.getSchemas().remove(oldName); + components.addSchema(newName, schema); + } + + private void handleOpenAPIAnnotation(OpenAPI api, Class clazz) { + if (clazz.isAnnotationPresent(OpenAPIDefinition.class)) { + OpenAPIDefinition annotation = clazz.getDeclaredAnnotation(OpenAPIDefinition.class); + OpenAPIImpl.merge(annotation, api, true); + } + } + + private void handleOperationAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation, + PathItem pathItem) { + if (method.isAnnotationPresent(Operation.class)) { + Operation annotation = method.getDeclaredAnnotation(Operation.class); + OperationImpl.merge(annotation, operation, true); + if (annotation.hidden()) { + ModelUtils.removeOperation(pathItem, operation); + } + } + } + + private void handleExternalDocumentationAnnotation(Method method, + org.eclipse.microprofile.openapi.models.Operation operation) { + ExternalDocumentation annotation = null; + if (method.isAnnotationPresent(ExternalDocumentation.class)) { + annotation = method.getDeclaredAnnotation(ExternalDocumentation.class); + } else if (method.getDeclaringClass().isAnnotationPresent(ExternalDocumentation.class)) { + annotation = method.getDeclaringClass().getDeclaredAnnotation(ExternalDocumentation.class); + } else { + return; + } + operation.setExternalDocs(new ExternalDocumentationImpl()); + ExternalDocumentationImpl.merge(annotation, operation.getExternalDocs(), true); + if (operation.getExternalDocs().getUrl() == null) { + operation.setExternalDocs(null); + } + } + + private void handleServersAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation) { + List declaredServers = new ArrayList<>(); + if (method.isAnnotationPresent(Server.class) || method.isAnnotationPresent(Servers.class)) { + if (method.isAnnotationPresent(Server.class)) { + Server annotation = method.getDeclaredAnnotation(Server.class); + declaredServers.add(annotation); + } + if (method.isAnnotationPresent(Servers.class)) { + Servers annotation = method.getDeclaredAnnotation(Servers.class); + for (Server server : annotation.value()) { + declaredServers.add(server); + } + } + } else if (method.getDeclaringClass().isAnnotationPresent(Server.class) + || method.getDeclaringClass().isAnnotationPresent(Servers.class)) { + if (method.getDeclaringClass().isAnnotationPresent(Server.class)) { + Server annotation = method.getDeclaringClass().getDeclaredAnnotation(Server.class); + declaredServers.add(annotation); + } + if (method.getDeclaringClass().isAnnotationPresent(Servers.class)) { + Servers annotation = method.getDeclaringClass().getDeclaredAnnotation(Servers.class); + for (Server server : annotation.value()) { + declaredServers.add(server); + } + } + } + + for (Server annotation : declaredServers) { + org.eclipse.microprofile.openapi.models.servers.Server server = new ServerImpl(); + ServerImpl.merge(annotation, server, true); + operation.addServer(server); + } + } + + private void handleCallbacksAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation) { + List declaredCallbacks = new ArrayList<>(); + if (method.isAnnotationPresent(Callback.class) || method.isAnnotationPresent(Callbacks.class)) { + if (method.isAnnotationPresent(Callback.class)) { + Callback annotation = method.getDeclaredAnnotation(Callback.class); + declaredCallbacks.add(annotation); + } + if (method.isAnnotationPresent(Callbacks.class)) { + Callbacks annotation = method.getDeclaredAnnotation(Callbacks.class); + for (Callback callback : annotation.value()) { + declaredCallbacks.add(callback); + } + } + } else if (method.getDeclaringClass().isAnnotationPresent(Callback.class) + || method.getDeclaringClass().isAnnotationPresent(Callbacks.class)) { + if (method.getDeclaringClass().isAnnotationPresent(Callback.class)) { + Callback annotation = method.getDeclaringClass().getDeclaredAnnotation(Callback.class); + declaredCallbacks.add(annotation); + } + if (method.getDeclaringClass().isAnnotationPresent(Callbacks.class)) { + Callbacks annotation = method.getDeclaringClass().getDeclaredAnnotation(Callbacks.class); + for (Callback callback : annotation.value()) { + declaredCallbacks.add(callback); + } + } + } + + for (Callback annotation : declaredCallbacks) { + String callbackName = annotation.name(); + if (callbackName != null && !callbackName.isEmpty()) { + org.eclipse.microprofile.openapi.models.callbacks.Callback model = operation.getCallbacks() + .getOrDefault(callbackName, new CallbackImpl()); + CallbackImpl.merge(annotation, model, true); + operation.getCallbacks().put(callbackName, model); + } + } + } + + private void handleTagsAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation, + OpenAPI api) { + List annotations = new ArrayList<>(); + if (method.isAnnotationPresent(Tag.class) || method.isAnnotationPresent(Tags.class)) { + if (method.isAnnotationPresent(Tag.class)) { + Tag annotation = method.getDeclaredAnnotation(Tag.class); + annotations.add(annotation); + } + if (method.isAnnotationPresent(Tags.class)) { + Tags annotation = method.getDeclaredAnnotation(Tags.class); + for (Tag tag : annotation.value()) { + annotations.add(tag); + } + for (String ref : annotation.refs()) { + if (ref != null && !ref.isEmpty()) { + operation.addTag(ref); + } + } + } + } else if (method.getDeclaringClass().isAnnotationPresent(Tag.class) + || method.getDeclaringClass().isAnnotationPresent(Tags.class)) { + if (method.getDeclaringClass().isAnnotationPresent(Tag.class)) { + Tag annotation = method.getDeclaringClass().getDeclaredAnnotation(Tag.class); + annotations.add(annotation); + } + if (method.getDeclaringClass().isAnnotationPresent(Tags.class)) { + Tags annotation = method.getDeclaringClass().getDeclaredAnnotation(Tags.class); + for (Tag tag : annotation.value()) { + annotations.add(tag); + } + for (String ref : annotation.refs()) { + if (ref != null && !ref.isEmpty()) { + operation.addTag(ref); + } + } + } + } + + for (Tag annotation : annotations) { + TagImpl.merge(annotation, operation, true, api.getTags()); + } + } + + private void handleRequestBodyAnnotation(Parameter parameter, + org.eclipse.microprofile.openapi.models.Operation operation, + Map currentSchemas) { + if (parameter.isAnnotationPresent(RequestBody.class)) { + RequestBody annotation = parameter.getDeclaredAnnotation(RequestBody.class); + if (operation.getRequestBody() == null) { + operation.setRequestBody(new RequestBodyImpl()); + } + RequestBodyImpl.merge(annotation, operation.getRequestBody(), true, currentSchemas); + } else if (parameter.getDeclaringExecutable().isAnnotationPresent(RequestBody.class)) { + RequestBody annotation = parameter.getDeclaringExecutable().getDeclaredAnnotation(RequestBody.class); + if (operation.getRequestBody() == null) { + operation.setRequestBody(new RequestBodyImpl()); + } + RequestBodyImpl.merge(annotation, operation.getRequestBody(), true, currentSchemas); + } + } + + private void handleAPIResponseAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation, + Map currentSchemas) { + if (method.isAnnotationPresent(APIResponse.class)) { + APIResponse annotation = method.getDeclaredAnnotation(APIResponse.class); + + // Get the response name + String responseName = annotation.responseCode(); + if (responseName == null || responseName.isEmpty()) { + responseName = APIResponses.DEFAULT; + } + + org.eclipse.microprofile.openapi.models.responses.APIResponse response = operation.getResponses() + .getOrDefault(responseName, new APIResponseImpl()); + APIResponseImpl.merge(annotation, response, true, currentSchemas); + + operation.getResponses().addApiResponse(responseName, response); + } + } + + private void handleSecuritySchemeAnnotation(AnnotatedElement element, Components components) { + if (element.isAnnotationPresent(SecurityScheme.class)) { + SecurityScheme annotation = element.getDeclaredAnnotation(SecurityScheme.class); + + org.eclipse.microprofile.openapi.models.security.SecurityScheme model = new SecuritySchemeImpl(); + SecuritySchemeImpl.merge(annotation, model, true); + + if (annotation.securitySchemeName() != null && !annotation.securitySchemeName().isEmpty()) { + components.addSecurityScheme(annotation.securitySchemeName(), model); + } + } + } + + private void handleSecurityRequirementAnnotation(Method method, + org.eclipse.microprofile.openapi.models.Operation operation) { + List requirements = new ArrayList<>(); + + // If the method is annotated + if (method.isAnnotationPresent(SecurityRequirement.class) + || method.isAnnotationPresent(SecurityRequirements.class)) { + if (method.isAnnotationPresent(SecurityRequirement.class)) { + SecurityRequirement annotation = method.getDeclaredAnnotation(SecurityRequirement.class); + requirements.add(annotation); + } + if (method.isAnnotationPresent(SecurityRequirements.class)) { + SecurityRequirements annotation = method.getDeclaredAnnotation(SecurityRequirements.class); + for (SecurityRequirement requirement : annotation.value()) { + requirements.add(requirement); + } + } + // Else if the class is annotated, inherit it + } else if (method.getDeclaringClass().isAnnotationPresent(SecurityRequirement.class) + || method.getDeclaringClass().isAnnotationPresent(SecurityRequirements.class)) { + if (method.getDeclaringClass().isAnnotationPresent(SecurityRequirement.class)) { + SecurityRequirement annotation = method.getDeclaringClass() + .getDeclaredAnnotation(SecurityRequirement.class); + requirements.add(annotation); + } + if (method.getDeclaringClass().isAnnotationPresent(SecurityRequirements.class)) { + SecurityRequirements annotation = method.getDeclaringClass() + .getDeclaredAnnotation(SecurityRequirements.class); + for (SecurityRequirement requirement : annotation.value()) { + requirements.add(requirement); + } + } + } + + // Process all the elements to be added + for (SecurityRequirement annotation : requirements) { + org.eclipse.microprofile.openapi.models.security.SecurityRequirement model = new SecurityRequirementImpl(); + SecurityRequirementImpl.merge(annotation, model, true); + + if (annotation.name() != null && !annotation.name().isEmpty()) { + operation.addSecurityRequirement(model); + } + } + } + + private void handleExtensionAnnotation(AnnotatedElement element, Extensible extensible) { + if (element.isAnnotationPresent(Extension.class)) { + Extension annotation = element.getDeclaredAnnotation(Extension.class); + ExtensibleImpl.merge(annotation, extensible, true); + } + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/ApplicationVisitor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/ApplicationVisitor.java new file mode 100644 index 00000000000..fd7fa474ab0 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/ApplicationVisitor.java @@ -0,0 +1,279 @@ +package fish.payara.microprofile.openapi.impl.visitor; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.CookieParam; +import javax.ws.rs.FormParam; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.Paths; +import org.eclipse.microprofile.openapi.models.Reference; +import org.eclipse.microprofile.openapi.models.media.MediaType; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.eclipse.microprofile.openapi.models.parameters.Parameter; +import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; +import org.eclipse.microprofile.openapi.models.parameters.RequestBody; +import org.eclipse.microprofile.openapi.models.responses.APIResponse; +import org.eclipse.microprofile.openapi.models.responses.APIResponses; + +import fish.payara.microprofile.openapi.api.visitor.ApiContext; +import fish.payara.microprofile.openapi.api.visitor.ApiVisitor; +import fish.payara.microprofile.openapi.impl.model.PathItemImpl; +import fish.payara.microprofile.openapi.impl.model.PathsImpl; +import fish.payara.microprofile.openapi.impl.model.media.MediaTypeImpl; +import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.ParameterImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.RequestBodyImpl; +import fish.payara.microprofile.openapi.impl.model.responses.APIResponseImpl; +import fish.payara.microprofile.openapi.impl.model.responses.APIResponsesImpl; +import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; + +public class ApplicationVisitor implements ApiVisitor { + + @Override + public void visitClass(Class clazz, ApiContext context) { + } + + @Override + public void visitMethod(Method method, ApiContext context) { + // The application visitor doesn't care about non contextual objects + if (context.getPath() == null) { + return; + } + // Create the paths if they're not already created + Paths paths = context.getApi().getPaths(); + if (paths == null) { + paths = new PathsImpl(); + context.getApi().setPaths(paths); + } + + // If the path hasn't been added to the model, do so now + if (!paths.containsKey(context.getPath())) { + paths.put(context.getPath(), new PathItemImpl()); + } + PathItem pathItem = paths.get(context.getPath()); + + // Set the HTTP method type + Operation operation = ModelUtils.getOrCreateOperation(pathItem, ModelUtils.getHttpMethod(method)); + operation.setOperationId(method.getName()); + + // Add the default response + APIResponses responses = operation.getResponses(); + if (responses == null) { + responses = new APIResponsesImpl(); + operation.setResponses(responses); + } + insertDefaultResponse(context.getApi(), responses, method); + + insertDefaultRequestBody(context.getApi(), operation, method); + } + + @Override + public void visitField(Field field, ApiContext context) { + // Ignore fields + } + + @Override + public void visitParameter(java.lang.reflect.Parameter parameter, ApiContext context) { + // The application visitor doesn't care about non contextual objects + if (context.getPath() == null) { + return; + } + if (ModelUtils.getParameterType(parameter) == null) { + return; + } + // Create a jersey parameter modelling the method parameter + + Parameter newParam = new ParameterImpl(); + String sourceName = null; + if (parameter.isAnnotationPresent(PathParam.class)) { + newParam.setIn(In.PATH); + newParam.setRequired(true); + sourceName = parameter.getDeclaredAnnotation(PathParam.class).value(); + } else if (parameter.isAnnotationPresent(QueryParam.class)) { + newParam.setIn(In.QUERY); + newParam.setRequired(false); + sourceName = parameter.getDeclaredAnnotation(QueryParam.class).value(); + } else if (parameter.isAnnotationPresent(HeaderParam.class)) { + newParam.setIn(In.HEADER); + newParam.setRequired(false); + sourceName = parameter.getDeclaredAnnotation(HeaderParam.class).value(); + } else if (parameter.isAnnotationPresent(CookieParam.class)) { + newParam.setIn(In.COOKIE); + newParam.setRequired(false); + sourceName = parameter.getDeclaredAnnotation(CookieParam.class).value(); + } + newParam.setName(sourceName); + newParam.setSchema(new SchemaImpl().type(ModelUtils.getSchemaType(parameter.getType()))); + Operation operation = ModelUtils.findOperation(context.getApi(), (Method) parameter.getDeclaringExecutable(), + context.getPath()); + operation.addParameter(newParam); + } + + protected RequestBody insertDefaultRequestBody(OpenAPI api, Operation operation, Method method) { + RequestBody requestBody = new RequestBodyImpl(); + + // Get the return type of the variable + Class returnType = null; + // If form parameters are provided, this stores the general schema type for all + // of them + SchemaType formSchemaType = null; + for (java.lang.reflect.Parameter methodParam : method.getParameters()) { + if (ModelUtils.isRequestBody(methodParam)) { + returnType = methodParam.getType(); + break; + } + if (methodParam.isAnnotationPresent(FormParam.class)) { + returnType = methodParam.getType(); + formSchemaType = ModelUtils.getParentSchemaType(formSchemaType, + ModelUtils.getSchemaType(methodParam.getType())); + } + } + if (returnType == null) { + return null; + } + + // If there is an @Consumes, create a media type for each + if (method.isAnnotationPresent(Consumes.class)) { + String[] value = method.getDeclaredAnnotation(Consumes.class).value(); + for (String produceType : value) { + MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, returnType)); + requestBody.getContent().addMediaType(getContentType(produceType), mediaType); + } + } else { + // No @Consumes, create a wildcard + MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, returnType)); + requestBody.getContent().addMediaType(getContentType("*/*"), mediaType); + } + + // If there are form parameters, reconfigure the types + if (formSchemaType != null) { + for (MediaType mediaType : requestBody.getContent().values()) { + mediaType.getSchema().setType(formSchemaType); + } + } + + operation.setRequestBody(requestBody); + return requestBody; + } + + /** + * Creates a new {@link APIResponse} to model the default response of a + * {@link Method}, and inserts it into the {@link APIResponses}. + * + * @param responses the {@link APIResponses} to add the default response to. + * @param method the {@link Method} to model the default response on. + * @return the newly created {@link APIResponse}. + */ + protected APIResponse insertDefaultResponse(OpenAPI api, APIResponses responses, Method method) { + APIResponse defaultResponse = new APIResponseImpl(); + defaultResponse.setDescription("Default Response."); + + // Check if there are produce types specified + if (method.isAnnotationPresent(Produces.class)) { + // If there is an @Produces, get the value + String[] value = method.getDeclaredAnnotation(Produces.class).value(); + for (String produceType : value) { + // For each @Produces type, create a media type and add it to the response + // content + MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, method.getReturnType())); + defaultResponse.getContent().addMediaType(getContentType(produceType), mediaType); + } + } else { + // No @Produces, so create a wildcard response + MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, method.getReturnType())); + defaultResponse.getContent().addMediaType(getContentType("*/*"), mediaType); + } + + // Add the default response + responses.addApiResponse(APIResponses.DEFAULT, defaultResponse); + return defaultResponse; + } + + protected String getContentType(String name) { + try { + javax.ws.rs.core.MediaType mediaType = javax.ws.rs.core.MediaType.valueOf(name); + if (mediaType != null) { + return mediaType.toString(); + } + } catch (IllegalArgumentException ex) { + } + return javax.ws.rs.core.MediaType.WILDCARD; + } + + protected Schema createSchema(OpenAPI api, Class type) { + Schema schema = new SchemaImpl(); + SchemaType schemaType = ModelUtils.getSchemaType(type); + schema.setType(schemaType); + + // Set the subtype if it's an array (for example an array of ints) + if (schemaType == SchemaType.ARRAY) { + Class subType = type.getComponentType(); + Schema subSchema = schema; + while (subType != null) { + subSchema.setItems(new SchemaImpl().type(ModelUtils.getSchemaType(subType))); + subSchema = schema.getItems(); + subType = subType.getComponentType(); + } + } + + if (schemaType == SchemaType.OBJECT) { + if (insertObjectReference(api, schema, type)) { + schema.setType(null); + schema.setItems(null); + } + } + return schema; + } + + /** + * Replace the object in the referee with a reference, and create the reference + * in the API. + * + * @param api the OpenAPI object. + * @param referee the object containing the reference. + * @param referenceClass the class of the object being referenced. + * @return if the reference has been created. + */ + protected boolean insertObjectReference(OpenAPI api, Reference referee, Class referenceClass) { + + // If the object is java.lang.Object, exit + if (referenceClass.equals(Object.class)) { + return false; + } + + // Get the schemas + Map schemas = api.getComponents().getSchemas(); + + // Set the reference name + referee.setRef(referenceClass.getSimpleName()); + + if (!schemas.containsKey(referenceClass.getSimpleName())) { + // If the schema type doesn't already exist, create it + Schema schema = new SchemaImpl(); + schemas.put(referenceClass.getSimpleName(), schema); + schema.setType(SchemaType.OBJECT); + Map fields = new LinkedHashMap<>(); + for (Field field : referenceClass.getDeclaredFields()) { + if (!Modifier.isTransient(field.getModifiers())) { + fields.put(field.getName(), createSchema(api, field.getType())); + } + } + schema.setProperties(fields); + } + + return true; + } +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiContext.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiContext.java new file mode 100644 index 00000000000..3ba976239bf --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiContext.java @@ -0,0 +1,50 @@ +package fish.payara.microprofile.openapi.impl.visitor; + +import org.eclipse.microprofile.openapi.models.OpenAPI; + +import fish.payara.microprofile.openapi.api.visitor.ApiContext; + +public class OpenApiContext implements ApiContext { + + private final OpenAPI api; + private final String path; + + public OpenApiContext(OpenAPI api, String path) { + this.api = api; + this.path = normaliseUrl(path); + } + + @Override + public OpenAPI getApi() { + return api; + } + + @Override + public String getPath() { + return path; + } + + /** + * Normalises a path string. A normalised path has: + *
    + *
  • no multiple slashes.
  • + *
  • no trailing slash.
  • + *
+ * + * @param path the path to be normalised. + */ + private String normaliseUrl(String path) { + if (path == null) { + return null; + } + // Remove multiple slashes + path = path.replaceAll("/+", "/"); + + // Remove trailing slash + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + return path; + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java new file mode 100644 index 00000000000..e3ca5a0aa58 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java @@ -0,0 +1,152 @@ +package fish.payara.microprofile.openapi.impl.visitor; + +import java.lang.reflect.Field; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.PATCH; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.core.Application; + +import org.eclipse.microprofile.openapi.models.OpenAPI; + +import fish.payara.microprofile.openapi.api.visitor.ApiVisitor; +import fish.payara.microprofile.openapi.api.visitor.ApiWalker; + +public class OpenApiWalker implements ApiWalker { + + private final OpenAPI api; + private final Set> classes; + private final Map>> resourceMapping; + + public OpenApiWalker(OpenAPI api, Set> classes) { + this.api = api; + + this.resourceMapping = new HashMap<>(); + generateResourceMapping(classes); + + this.classes = new TreeSet<>(new Comparator>() { + @Override + public int compare(Class class1, Class class2) { + if (class1.equals(class2)) { + return 0; + } + // Non contextual objects at the start + if (!class1.isAnnotationPresent(ApplicationPath.class) && !class1.isAnnotationPresent(Path.class)) { + return -1; + } + // Followed by applications + if (class1.isAnnotationPresent(ApplicationPath.class)) { + return -1; + } + // Followed by everything else + return 1; + } + }); + this.classes.addAll(classes); + } + + @Override + public void accept(ApiVisitor visitor) { + for (Class clazz : classes) { + + // Visit each class + String path = getResourcePath(clazz); + visitor.visitClass(clazz, new OpenApiContext(api, path)); + + for (Method method : clazz.getDeclaredMethods()) { + + // Visit each method + path = getResourcePath(method); + method.setAccessible(true); + visitor.visitMethod(method, new OpenApiContext(api, path)); + + // Visit each parameter + for (Parameter parameter : method.getParameters()) { + visitor.visitParameter(parameter, new OpenApiContext(api, path)); + } + } + + // Visit each field + for (Field field : clazz.getDeclaredFields()) { + + field.setAccessible(true); + visitor.visitField(field, new OpenApiContext(api, path)); + } + } + } + + private String getResourcePath(GenericDeclaration declaration) { + if (declaration instanceof Method) { + Method method = (Method) declaration; + + // If the method is a valid resource + if (method.isAnnotationPresent(GET.class) || method.isAnnotationPresent(POST.class) + || method.isAnnotationPresent(PUT.class) || method.isAnnotationPresent(DELETE.class) + || method.isAnnotationPresent(HEAD.class) || method.isAnnotationPresent(OPTIONS.class) + || method.isAnnotationPresent(PATCH.class)) { + if (method.isAnnotationPresent(Path.class)) { + return getResourcePath(method.getDeclaringClass()) + + method.getDeclaredAnnotation(Path.class).value(); + } else { + return getResourcePath(method.getDeclaringClass()); + } + } + } + if (declaration instanceof Class) { + Class clazz = (Class) declaration; + + // If the class is a resource and contains a mapping + if (clazz.isAnnotationPresent(Path.class)) { + for (String key : resourceMapping.keySet()) { + if (resourceMapping.get(key).contains(clazz)) { + return key + clazz.getDeclaredAnnotation(Path.class).value(); + } + } + } + } + return null; + } + + private void generateResourceMapping(Set> classList) { + for (Class clazz : classList) { + if (clazz.isAnnotationPresent(ApplicationPath.class) && Application.class.isAssignableFrom(clazz)) { + // Produce the mapping + String key = clazz.getDeclaredAnnotation(ApplicationPath.class).value(); + Set> resourceClasses = new HashSet<>(); + resourceMapping.put(key, resourceClasses); + + try { + Application app = (Application) clazz.newInstance(); + // Add all classes contained in the application + resourceClasses.addAll(app.getClasses()); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + // If there is one application and it's empty, add all classes + if (resourceMapping.keySet().size() == 1) { + Set> classes = resourceMapping.values().iterator().next(); + if (classes.isEmpty()) { + classes.addAll(classList); + } + } + } + +} \ No newline at end of file From bc50a7c2b04f1bb3b5ef5d6dd5abe8a81f589d26 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Fri, 11 May 2018 10:36:17 +0100 Subject: [PATCH 06/20] Added annotation processing. --- .../impl/processor/AnnotationProcessor.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/AnnotationProcessor.java diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/AnnotationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/AnnotationProcessor.java new file mode 100644 index 00000000000..8f5505f9d05 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/AnnotationProcessor.java @@ -0,0 +1,33 @@ +package fish.payara.microprofile.openapi.impl.processor; + +import static fish.payara.microprofile.openapi.impl.processor.utils.ProcessorUtils.getClassesFromLoader; + +import java.util.Set; + +import org.eclipse.microprofile.openapi.models.OpenAPI; + +import fish.payara.microprofile.openapi.api.processor.OASProcessor; +import fish.payara.microprofile.openapi.api.visitor.ApiVisitor; +import fish.payara.microprofile.openapi.api.visitor.ApiWalker; +import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; +import fish.payara.microprofile.openapi.impl.visitor.AnnotationVisitor; +import fish.payara.microprofile.openapi.impl.visitor.OpenApiWalker; + +public class AnnotationProcessor implements OASProcessor { + + private final Set> classes; + + public AnnotationProcessor(ClassLoader appClassLoader) { + this.classes = getClassesFromLoader(appClassLoader); + } + + @Override + public void process(OpenAPI api, OpenApiConfiguration config) { + ApiWalker apiWalker = new OpenApiWalker(api, (config == null) ? classes : config.getValidClasses(classes)); + ApiVisitor apiVisitor = new AnnotationVisitor(); + if (config == null || !config.getScanDisable()) { + apiWalker.accept(apiVisitor); + } + } + +} \ No newline at end of file From f59cc727887832ba400f99d5deb35b610ed9f3f5 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Fri, 11 May 2018 10:37:40 +0100 Subject: [PATCH 07/20] Added static file parsing. --- .../openapi/impl/processor/FileProcessor.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java new file mode 100644 index 00000000000..02acd99da67 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java @@ -0,0 +1,53 @@ +package fish.payara.microprofile.openapi.impl.processor; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.merge; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.eclipse.microprofile.openapi.models.OpenAPI; + +import fish.payara.microprofile.openapi.api.processor.OASProcessor; +import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; +import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; +import fish.payara.microprofile.openapi.impl.rest.app.provider.ObjectMapperFactory; + +public class FileProcessor implements OASProcessor { + + private File file; + private ObjectMapper mapper; + + public FileProcessor(ClassLoader appClassLoader) { + try { + if (appClassLoader.getResource("META-INF/openapi.json") != null) { + file = new File(appClassLoader.getResource("META-INF/openapi.json").toURI()); + mapper = ObjectMapperFactory.createJson(); + } else if (appClassLoader.getResource("META-INF/openapi.yaml") != null) { + file = new File(appClassLoader.getResource("META-INF/openapi.yaml").toURI()); + mapper = ObjectMapperFactory.createYaml(); + } else if (appClassLoader.getResource("META-INF/openapi.yml") != null) { + file = new File(appClassLoader.getResource("META-INF/openapi.yml").toURI()); + mapper = ObjectMapperFactory.createYaml(); + } + } catch (URISyntaxException e) { + e.printStackTrace(); + } + } + + @Override + public void process(OpenAPI api, OpenApiConfiguration config) { + if (file != null) { + OpenAPI readResult = null; + try { + readResult = mapper.readValue(file, OpenAPIImpl.class); + } catch (IOException e) { + e.printStackTrace(); + } + merge(readResult, api, false); + } + } + +} \ No newline at end of file From 29c430f79e6c41b8ab1d08417ca24da4ce1b6510 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Fri, 11 May 2018 10:38:00 +0100 Subject: [PATCH 08/20] Added filter and model reader processors. --- .../impl/processor/FilterProcessor.java | 130 ++++++++++++++++++ .../impl/processor/ModelReaderProcessor.java | 30 ++++ 2 files changed, 160 insertions(+) create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java new file mode 100644 index 00000000000..15fa18889dc --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java @@ -0,0 +1,130 @@ +package fish.payara.microprofile.openapi.impl.processor; + +import java.lang.reflect.Field; +import java.util.Map; + +import org.eclipse.microprofile.openapi.OASFilter; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.callbacks.Callback; +import org.eclipse.microprofile.openapi.models.headers.Header; +import org.eclipse.microprofile.openapi.models.links.Link; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.parameters.Parameter; +import org.eclipse.microprofile.openapi.models.parameters.RequestBody; +import org.eclipse.microprofile.openapi.models.responses.APIResponse; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; +import org.eclipse.microprofile.openapi.models.servers.Server; +import org.eclipse.microprofile.openapi.models.tags.Tag; + +import fish.payara.microprofile.openapi.api.processor.OASProcessor; +import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; +import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; + +public class FilterProcessor implements OASProcessor { + + private OASFilter filter; + + @Override + public void process(OpenAPI api, OpenApiConfiguration config) { + try { + if (config.getFilter() != null) { + filter = config.getFilter().newInstance(); + } + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + if (filter != null) { + filterObject(api); + } + } + + private Object filterObject(Object object) { + if (object != null) { + + // If the object is a map + if (object instanceof Map) { + for (Object item : Map.class.cast(object).values()) { + filterObject(item); + } + } + + // If the object is iterable + if (object instanceof Iterable) { + for (Object item : Iterable.class.cast(object)) { + filterObject(item); + } + } + + // If the object is a model item + if (object.getClass().getPackage().getName().startsWith(OpenAPIImpl.class.getPackage().getName())) { + + // Visit each field + for (Field field : object.getClass().getDeclaredFields()) { + boolean accessible = field.isAccessible(); + try { + field.setAccessible(true); + Object fieldValue = field.get(object); + filterObject(fieldValue); + } catch (IllegalArgumentException | IllegalAccessException e) { + } finally { + field.setAccessible(accessible); + } + } + + // Visit the object + visitObject(object); + } + + return object; + } + return null; + } + + private Object visitObject(Object object) { + if (object != null) { + if (PathItem.class.isAssignableFrom(object.getClass())) { + return filter.filterPathItem((PathItem) object); + } + if (Operation.class.isAssignableFrom(object.getClass())) { + return filter.filterOperation((Operation) object); + } + if (Parameter.class.isAssignableFrom(object.getClass())) { + return filter.filterParameter((Parameter) object); + } + if (Header.class.isAssignableFrom(object.getClass())) { + return filter.filterHeader((Header) object); + } + if (RequestBody.class.isAssignableFrom(object.getClass())) { + return filter.filterRequestBody((RequestBody) object); + } + if (APIResponse.class.isAssignableFrom(object.getClass())) { + return filter.filterAPIResponse((APIResponse) object); + } + if (Schema.class.isAssignableFrom(object.getClass())) { + return filter.filterSchema((Schema) object); + } + if (SecurityScheme.class.isAssignableFrom(object.getClass())) { + return filter.filterSecurityScheme((SecurityScheme) object); + } + if (Server.class.isAssignableFrom(object.getClass())) { + return filter.filterServer((Server) object); + } + if (Tag.class.isAssignableFrom(object.getClass())) { + return filter.filterTag((Tag) object); + } + if (Link.class.isAssignableFrom(object.getClass())) { + return filter.filterLink((Link) object); + } + if (Callback.class.isAssignableFrom(object.getClass())) { + return filter.filterCallback((Callback) object); + } + if (OpenAPI.class.isAssignableFrom(object.getClass())) { + filter.filterOpenAPI((OpenAPI) object); + } + } + return null; + } + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java new file mode 100644 index 00000000000..025dc3be44d --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java @@ -0,0 +1,30 @@ +package fish.payara.microprofile.openapi.impl.processor; + +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.merge; + +import org.eclipse.microprofile.openapi.OASModelReader; +import org.eclipse.microprofile.openapi.models.OpenAPI; + +import fish.payara.microprofile.openapi.api.processor.OASProcessor; +import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; + +public class ModelReaderProcessor implements OASProcessor { + + private OASModelReader reader; + + @Override + public void process(OpenAPI api, OpenApiConfiguration config) { + try { + if (config.getModelReader() != null) { + reader = config.getModelReader().newInstance(); + } + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + if (reader != null) { + OpenAPI model = reader.buildModel(); + merge(model, api, true); + } + } + +} \ No newline at end of file From 6169f3f052997d31525bca06652928780cacceeb Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Fri, 11 May 2018 10:38:29 +0100 Subject: [PATCH 09/20] Patched config API to work for null invocation contexts. --- .../spi/ConfigProviderResolverImpl.java | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java b/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java index d3e8419479f..9e0f30df4c3 100644 --- a/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java +++ b/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java @@ -226,7 +226,7 @@ Config getConfig(ApplicationInfo appInfo) { initialiseApplicationConfig(appInfo); LinkedList sources = new LinkedList<>(); LinkedList converters = new LinkedList<>(); - sources.addAll(getDefaultSources()); + sources.addAll(getDefaultSources(appInfo)); sources.addAll(getDiscoveredSources(appInfo)); converters.addAll(getDefaultConverters()); converters.addAll(getDiscoveredConverters(appInfo)); @@ -260,22 +260,8 @@ Config getNamedConfig(String applicationName) { return result; } - List getDefaultSources() { + private List getDefaultSources(String appName, String moduleName) { LinkedList sources = new LinkedList<>(); - String appName = null; - String moduleName = null; - ComponentInvocation currentInvocation = invocationManager.getCurrentInvocation(); - if (currentInvocation == null) { - ApplicationInfo info = getAppInfo(Thread.currentThread().getContextClassLoader()); - if (info != null) { - appName = info.getName(); - moduleName = appName; - } - } else { - appName = currentInvocation.getAppName(); - moduleName = currentInvocation.getModuleName(); - } - String serverName = context.getInstanceName(); String configName = context.getConfigBean().getConfig().getName(); sources.add(new DomainConfigSource()); @@ -293,9 +279,32 @@ List getDefaultSources() { sources.add(new ModuleConfigSource(appName, moduleName)); for (Properties props : getDeployedApplicationProperties(appName)) { sources.add(new PropertiesConfigSource(props, appName)); - } } + } + } return sources; } + List getDefaultSources() { + return getDefaultSources(null); + } + + List getDefaultSources(ApplicationInfo appInfo) { + String appName = null; + String moduleName = null; + ComponentInvocation currentInvocation = invocationManager.getCurrentInvocation(); + if (currentInvocation == null) { + if (appInfo == null) { + appInfo = getAppInfo(Thread.currentThread().getContextClassLoader()); + } + if (appInfo != null) { + appName = appInfo.getName(); + moduleName = appName; + } + } else { + appName = currentInvocation.getAppName(); + moduleName = currentInvocation.getModuleName(); + } + return getDefaultSources(appName, moduleName); + } @Override public void registerConfig(Config config, ClassLoader classLoader) { From 499f13d388b87bf09306da267c7e21fb6cba201d Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Fri, 18 May 2018 17:28:41 +0100 Subject: [PATCH 10/20] Fixed TCK tests, and added copyright headers. --- .../openapi/api/processor/OASProcessor.java | 52 +- .../openapi/api/visitor/ApiContext.java | 57 +- .../openapi/api/visitor/ApiVisitor.java | 174 +++- .../openapi/api/visitor/ApiWalker.java | 39 + .../openapi/impl/OpenApiService.java | 95 +- .../admin/GetOpenApiConfigurationCommand.java | 115 +++ .../admin/OpenApiServiceConfiguration.java | 63 ++ .../admin/SetOpenApiConfigurationCommand.java | 117 +++ .../impl/config/OpenApiConfiguration.java | 152 ++- .../openapi/impl/model/ComponentsImpl.java | 41 +- .../openapi/impl/model/ExtensibleImpl.java | 39 + .../impl/model/ExternalDocumentationImpl.java | 39 + .../impl/model/OASFactoryResolverImpl.java | 168 ++++ .../openapi/impl/model/OpenAPIImpl.java | 51 + .../openapi/impl/model/OperationImpl.java | 82 ++ .../openapi/impl/model/PathItemImpl.java | 41 +- .../openapi/impl/model/PathsImpl.java | 39 + .../impl/model/callbacks/CallbackImpl.java | 62 +- .../impl/model/examples/ExampleImpl.java | 66 ++ .../impl/model/headers/HeaderImpl.java | 66 ++ .../openapi/impl/model/info/ContactImpl.java | 39 + .../openapi/impl/model/info/InfoImpl.java | 39 + .../openapi/impl/model/info/LicenseImpl.java | 39 + .../openapi/impl/model/links/LinkImpl.java | 83 ++ .../openapi/impl/model/media/ContentImpl.java | 87 +- .../impl/model/media/DiscriminatorImpl.java | 39 + .../impl/model/media/EncodingImpl.java | 74 ++ .../impl/model/media/MediaTypeImpl.java | 39 + .../openapi/impl/model/media/SchemaImpl.java | 78 +- .../openapi/impl/model/media/XMLImpl.java | 39 + .../impl/model/parameters/ParameterImpl.java | 39 + .../model/parameters/RequestBodyImpl.java | 39 + .../impl/model/responses/APIResponseImpl.java | 51 + .../model/responses/APIResponsesImpl.java | 61 ++ .../impl/model/security/OAuthFlowImpl.java | 39 + .../impl/model/security/OAuthFlowsImpl.java | 39 + .../impl/model/security/ScopesImpl.java | 39 + .../security/SecurityRequirementImpl.java | 41 +- .../model/security/SecuritySchemeImpl.java | 60 +- .../impl/model/servers/ServerImpl.java | 39 + .../model/servers/ServerVariableImpl.java | 80 +- .../model/servers/ServerVariablesImpl.java | 43 +- .../openapi/impl/model/tags/TagImpl.java | 69 +- .../impl/model/util/ExtensionsMixin.java | 31 - .../openapi/impl/model/util/ModelUtils.java | 162 +++- .../impl/processor/AnnotationProcessor.java | 33 - .../impl/processor/ApplicationProcessor.java | 907 +++++++++++++++++- .../openapi/impl/processor/BaseProcessor.java | 96 +- .../openapi/impl/processor/FileProcessor.java | 96 +- .../impl/processor/FilterProcessor.java | 118 ++- .../impl/processor/ModelReaderProcessor.java | 67 +- .../impl/processor/utils/ProcessorUtils.java | 41 - .../impl/rest/app/OpenApiApplication.java | 39 + .../app/provider/ObjectMapperFactory.java | 163 ++-- .../rest/app/provider/QueryFormatFilter.java | 39 + .../app/provider/mixin/ExtensionsMixin.java | 72 ++ .../app/provider/writer/AbstractWriter.java | 39 + .../rest/app/provider/writer/JsonWriter.java | 39 + .../rest/app/provider/writer/YamlWriter.java | 39 + .../rest/app/service/OpenApiResource.java | 70 +- .../OpenApiServletContainerInitializer.java | 39 + .../impl/visitor/AnnotationVisitor.java | 498 ---------- .../impl/visitor/ApplicationVisitor.java | 279 ------ .../openapi/impl/visitor/OpenApiContext.java | 74 +- .../openapi/impl/visitor/OpenApiWalker.java | 251 +++-- ...icroprofile.openapi.spi.OASFactoryResolver | 1 + .../impl/model/util/ModelUtilsTest.java | 39 + .../classloader/ApplicationClassLoader.java | 39 + .../rule/AnnotationProcessedDocument.java | 30 - .../rule/ApplicationProcessedDocument.java | 47 +- .../resource/rule/BaseProcessedDocument.java | 12 - .../resource/rule/ProcessedDocument.java | 7 - .../openapi/resource/util/TestUtils.java | 480 --------- .../openapi/test/app/TestApplication.java | 114 +-- .../test/app/annotation/APIResponseTest.java | 75 -- .../test/app/annotation/CallbackTest.java | 63 -- .../test/app/annotation/ExtensionTest.java | 52 - .../annotation/ExternalDocumentationTest.java | 73 -- .../app/annotation/OpenAPIDefinitionTest.java | 420 -------- .../test/app/annotation/OperationTest.java | 56 -- .../annotation/ParameterAnnotationTest.java | 95 -- .../test/app/annotation/RequestBodyTest.java | 89 -- .../test/app/annotation/SchemaTest.java | 121 --- .../annotation/SecurityRequirementTest.java | 69 -- .../app/annotation/SecuritySchemeTest.java | 103 -- .../test/app/annotation/ServerTest.java | 90 -- .../openapi/test/app/annotation/TagTest.java | 83 -- .../test/app/application/FormParamTest.java | 68 -- .../test/app/application/MethodMergeTest.java | 69 -- .../test/app/application/MethodTest.java | 102 -- .../test/app/application/ParameterTest.java | 149 --- .../test/app/application/PathTest.java | 48 - .../test/app/application/RequestTest.java | 90 -- .../test/app/application/ResponseTest.java | 138 +-- .../test/app/application/RootPathTest.java | 61 +- .../openapi/test/app/data/ComponentTest.java | 94 -- .../test/app/data/SchemaComponentTest.java | 62 -- .../openapi/test/app/data/TestComponent.java | 78 ++ nucleus/packager/nucleus-jersey/pom.xml | 10 +- .../converters/StringArrayConverter.java | 63 ++ .../spi/ConfigProviderResolverImpl.java | 63 +- 101 files changed, 5090 insertions(+), 4108 deletions(-) create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/GetOpenApiConfigurationCommand.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/OpenApiServiceConfiguration.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OASFactoryResolverImpl.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ExtensionsMixin.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/AnnotationProcessor.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/utils/ProcessorUtils.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/mixin/ExtensionsMixin.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/AnnotationVisitor.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/ApplicationVisitor.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/main/resources/META-INF/services/org.eclipse.microprofile.openapi.spi.OASFactoryResolver delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/AnnotationProcessedDocument.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/BaseProcessedDocument.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ProcessedDocument.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/util/TestUtils.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/APIResponseTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/CallbackTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExtensionTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExternalDocumentationTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OpenAPIDefinitionTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OperationTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ParameterAnnotationTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/RequestBodyTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SchemaTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecurityRequirementTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecuritySchemeTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ServerTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/TagTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/FormParamTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodMergeTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ParameterTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/PathTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RequestTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/ComponentTest.java delete mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/SchemaComponentTest.java create mode 100644 appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/TestComponent.java create mode 100644 nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/converters/StringArrayConverter.java diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/processor/OASProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/processor/OASProcessor.java index 42e49cc9050..e27bbc2c69a 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/processor/OASProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/processor/OASProcessor.java @@ -1,11 +1,61 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.api.processor; import org.eclipse.microprofile.openapi.models.OpenAPI; import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; +/** + * A processor accepts an {@link OpenAPI} object, and returns a new model after + * applying a transformation. + */ public interface OASProcessor { - void process(OpenAPI api, OpenApiConfiguration config); + /** + * Processes a model. + * + * @param api the OpenAPI model to process. + * @param config the configuration to use. + * @return a processed model. + */ + OpenAPI process(OpenAPI api, OpenApiConfiguration config); } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiContext.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiContext.java index 3c306d1bd0f..082f985c498 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiContext.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiContext.java @@ -1,12 +1,51 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.api.visitor; import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; /** - * The context in which a class object is being visited. - * For example, if a method is being visited, the context - * will contain the current state of the {@link OpenAPI}, - * and the current path in the API. + * The context in which a class object is being visited. For example, if a + * method is being visited, the context will contain the current state of the + * {@link OpenAPI}, and the current path in the API. */ public interface ApiContext { @@ -16,9 +55,13 @@ public interface ApiContext { OpenAPI getApi(); /** - * The path of the object currently being visited. - * If the path is null, the object has no context - * (e.g a POJO). + * The path of the object currently being visited. If the path is null, the + * object has no context (e.g a POJO). */ String getPath(); + + /** + * The created operation currently being worked on. + */ + Operation getWorkingOperation(); } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiVisitor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiVisitor.java index a2d87480ff6..d65b96b40b9 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiVisitor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiVisitor.java @@ -1,35 +1,163 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.api.visitor; -import java.lang.reflect.Field; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -/** - * Represents an object that can visit components of an API. - */ +import javax.ws.rs.Consumes; +import javax.ws.rs.CookieParam; +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.PATCH; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; + +import org.eclipse.microprofile.openapi.annotations.ExternalDocumentation; +import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.callbacks.Callback; +import org.eclipse.microprofile.openapi.annotations.callbacks.Callbacks; +import org.eclipse.microprofile.openapi.annotations.extensions.Extension; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirements; +import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; +import org.eclipse.microprofile.openapi.annotations.security.SecuritySchemes; +import org.eclipse.microprofile.openapi.annotations.servers.Server; +import org.eclipse.microprofile.openapi.annotations.servers.Servers; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.eclipse.microprofile.openapi.annotations.tags.Tags; + public interface ApiVisitor { /** - * @param clazz the class to visit. - * @param context the current context of the API. + * Generic representation of each of these functions. */ - void visitClass(Class clazz, ApiContext context); + @FunctionalInterface + interface VisitorFunction { + void apply(A annotation, E element, ApiContext context); + } - /** - * @param method the method to visit. - * @param context the current context of the API. - */ - void visitMethod(Method method, ApiContext context); + // JAX-RS annotations - /** - * @param field the field to visit. - * @param context the current context of the API. - */ - void visitField(Field field, ApiContext context); + void visitGET(GET get, Method element, ApiContext context); + + void visitPOST(POST post, Method element, ApiContext context); + + void visitPUT(PUT put, Method element, ApiContext context); + + void visitDELETE(DELETE delete, Method element, ApiContext context); + + void visitHEAD(HEAD head, Method element, ApiContext context); + + void visitOPTIONS(OPTIONS options, Method element, ApiContext context); + + void visitPATCH(PATCH patch, Method element, ApiContext context); + + void visitProduces(Produces produces, AnnotatedElement element, ApiContext context); + + void visitConsumes(Consumes produces, AnnotatedElement element, ApiContext context); + + void visitQueryParam(QueryParam param, java.lang.reflect.Parameter element, ApiContext context); + + void visitPathParam(PathParam param, java.lang.reflect.Parameter element, ApiContext context); + + void visitFormParam(FormParam param, java.lang.reflect.Parameter element, ApiContext context); + + void visitHeaderParam(HeaderParam param, java.lang.reflect.Parameter element, ApiContext context); + + void visitCookieParam(CookieParam param, java.lang.reflect.Parameter element, ApiContext context); + + // OpenAPI annotations + + void visitOpenAPI(OpenAPIDefinition definition, AnnotatedElement element, ApiContext context); + + void visitSchema(Schema schema, AnnotatedElement element, ApiContext context); + + void visitExtension(Extension extension, AnnotatedElement element, ApiContext context); + + void visitOperation(Operation operation, AnnotatedElement element, ApiContext context); + + void visitCallback(Callback callback, AnnotatedElement element, ApiContext context); + + void visitCallbacks(Callbacks callbacks, AnnotatedElement element, ApiContext context); + + void visitRequestBody(RequestBody requestBody, AnnotatedElement element, ApiContext context); + + void visitAPIResponse(APIResponse apiResponse, AnnotatedElement element, ApiContext context); + + void visitAPIResponses(APIResponses apiResponses, AnnotatedElement element, ApiContext context); + + void visitParameter(Parameter parameter, AnnotatedElement element, ApiContext context); + + void visitExternalDocumentation(ExternalDocumentation externalDocs, AnnotatedElement element, ApiContext context); + + void visitServer(Server server, AnnotatedElement element, ApiContext context); + + void visitServers(Servers servers, AnnotatedElement element, ApiContext context); + + void visitTag(Tag tag, AnnotatedElement element, ApiContext context); + + void visitTags(Tags tags, AnnotatedElement element, ApiContext context); + + void visitSecurityScheme(SecurityScheme securityScheme, AnnotatedElement element, ApiContext context); + + void visitSecuritySchemes(SecuritySchemes securitySchemes, AnnotatedElement element, ApiContext context); + + void visitSecurityRequirement(SecurityRequirement securityRequirement, AnnotatedElement element, + ApiContext context); + + void visitSecurityRequirements(SecurityRequirements securityRequirements, AnnotatedElement element, + ApiContext context); - /** - * @param parameter the parameter to visit. - * @param context the current context of the API. - */ - void visitParameter(Parameter parameter, ApiContext context); } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiWalker.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiWalker.java index 88d77eaef98..d0b89cfeea3 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiWalker.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiWalker.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.api.visitor; /** diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java index 997c69a1442..7a254d480d2 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java @@ -1,7 +1,49 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl; -import java.util.LinkedHashMap; +import java.util.Collections; +import java.util.Deque; import java.util.Map; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.logging.Logger; import javax.inject.Inject; @@ -18,9 +60,9 @@ import org.glassfish.web.deployment.descriptor.WebBundleDescriptorImpl; import org.jvnet.hk2.annotations.Service; +import fish.payara.microprofile.openapi.impl.admin.OpenApiServiceConfiguration; import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; -import fish.payara.microprofile.openapi.impl.processor.AnnotationProcessor; import fish.payara.microprofile.openapi.impl.processor.ApplicationProcessor; import fish.payara.microprofile.openapi.impl.processor.BaseProcessor; import fish.payara.microprofile.openapi.impl.processor.FileProcessor; @@ -29,16 +71,21 @@ @Service(name = "microprofile-openapi-service") @RunLevel(StartupRunLevel.VAL) -public class OpenApiService implements EventListener, PostConstruct, PreDestroy { +public class OpenApiService implements PostConstruct, PreDestroy, EventListener { - private Map models; + private static final Logger LOGGER = Logger.getLogger(OpenApiService.class.getName()); + + private Deque> models; @Inject private Events events; + @Inject + private OpenApiServiceConfiguration config; + @Override public void postConstruct() { - models = new LinkedHashMap<>(); + models = new ConcurrentLinkedDeque<>(); events.register(this); } @@ -47,6 +94,10 @@ public void preDestroy() { events.unregister(this); } + public boolean isEnabled() { + return Boolean.parseBoolean(config.getEnabled()); + } + @Override public void event(Event event) { if (event.is(Deployment.APPLICATION_STARTED)) { @@ -54,11 +105,20 @@ public void event(Event event) { ApplicationInfo appInfo = (ApplicationInfo) event.hook(); // Create all the relevant resources - OpenApiConfiguration appConfig = new OpenApiConfiguration(appInfo.getAppClassLoader()); - models.put(appInfo, createOpenApiDocument(appInfo.getAppClassLoader(), getContextRoot(appInfo), appConfig)); + if (isEnabled()) { + OpenApiConfiguration appConfig = new OpenApiConfiguration(appInfo.getAppClassLoader()); + Map map = Collections.singletonMap(appInfo, + createOpenApiDocument(appInfo.getAppClassLoader(), getContextRoot(appInfo), appConfig)); + models.add(map); + } } else if (event.is(Deployment.APPLICATION_UNLOADED)) { ApplicationInfo appInfo = (ApplicationInfo) event.hook(); - models.remove(appInfo); + for (Map map : models) { + if (map.keySet().toArray()[0].equals(appInfo)) { + models.remove(map); + break; + } + } } } @@ -73,20 +133,19 @@ public OpenAPI getDocument() { if (models.isEmpty()) { return null; } - ApplicationInfo lastInfo = null; - for (ApplicationInfo info : models.keySet()) - lastInfo = info; - return models.get(lastInfo); + OpenAPI lastDocument = null; + for (Map model : models) + lastDocument = (OpenAPI) model.values().toArray()[0]; + return lastDocument; } private OpenAPI createOpenApiDocument(ClassLoader appClassLoader, String contextRoot, OpenApiConfiguration config) { OpenAPI document = new OpenAPIImpl(); - new ModelReaderProcessor().process(document, config); - new FileProcessor(appClassLoader).process(document, config); - new BaseProcessor(contextRoot).process(document, config); - new ApplicationProcessor(appClassLoader).process(document, config); - new AnnotationProcessor(appClassLoader).process(document, config); - new FilterProcessor().process(document, config); + document = new ModelReaderProcessor().process(document, config); + document = new FileProcessor(appClassLoader).process(document, config); + document = new ApplicationProcessor(appClassLoader).process(document, config); + document = new BaseProcessor(contextRoot).process(document, config); + document = new FilterProcessor().process(document, config); return document; } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/GetOpenApiConfigurationCommand.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/GetOpenApiConfigurationCommand.java new file mode 100644 index 00000000000..238441141dd --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/GetOpenApiConfigurationCommand.java @@ -0,0 +1,115 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.openapi.impl.admin; + +import static org.glassfish.api.admin.RestEndpoint.OpType.GET; +import static org.glassfish.config.support.CommandTarget.CLUSTER; +import static org.glassfish.config.support.CommandTarget.CLUSTERED_INSTANCE; +import static org.glassfish.config.support.CommandTarget.CONFIG; +import static org.glassfish.config.support.CommandTarget.DAS; +import static org.glassfish.config.support.CommandTarget.DEPLOYMENT_GROUP; +import static org.glassfish.config.support.CommandTarget.STANDALONE_INSTANCE; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import javax.inject.Inject; + +import com.sun.enterprise.config.serverbeans.Config; +import com.sun.enterprise.util.ColumnFormatter; + +import org.glassfish.api.ActionReport; +import org.glassfish.api.Param; +import org.glassfish.api.admin.AdminCommand; +import org.glassfish.api.admin.AdminCommandContext; +import org.glassfish.api.admin.ExecuteOn; +import org.glassfish.api.admin.RestEndpoint; +import org.glassfish.api.admin.RestEndpoints; +import org.glassfish.api.admin.RuntimeType; +import org.glassfish.config.support.TargetType; +import org.glassfish.hk2.api.PerLookup; +import org.glassfish.internal.api.Target; +import org.jvnet.hk2.annotations.Service; + +@Service(name = "get-openapi-configuration") +@PerLookup +@ExecuteOn({ RuntimeType.DAS }) +@TargetType({ DAS, DEPLOYMENT_GROUP, STANDALONE_INSTANCE, CLUSTER, CLUSTERED_INSTANCE, CONFIG }) +@RestEndpoints({ + @RestEndpoint(configBean = OpenApiServiceConfiguration.class, + opType = GET, + path = "get-openapi-configuration", + description = "Gets the OpenAPI Configuration") +}) +public class GetOpenApiConfigurationCommand implements AdminCommand { + + @Inject + private Target targetUtil; + + @Param(optional = true, defaultValue = "server-config") + private String target; + + @Override + public void execute(AdminCommandContext adminCommandContext) { + Config targetConfig = targetUtil.getConfig(target); + + if (targetConfig == null) { + adminCommandContext.getActionReport().setMessage("No such config name: " + targetUtil); + adminCommandContext.getActionReport().setActionExitCode(ActionReport.ExitCode.FAILURE); + return; + } + + OpenApiServiceConfiguration openApiConfig = targetConfig.getExtensionByType(OpenApiServiceConfiguration.class); + + ColumnFormatter columnFormatter = new ColumnFormatter(new String[] { "Enabled" }); + Object[] outputValues = { openApiConfig.getEnabled() }; + columnFormatter.addRow(outputValues); + + adminCommandContext.getActionReport().appendMessage(columnFormatter.toString()); + + Map extraPropertiesMap = new HashMap<>(); + extraPropertiesMap.put("enabled", openApiConfig.getEnabled()); + + Properties extraProperties = new Properties(); + extraProperties.put("openApiConfiguration", extraPropertiesMap); + adminCommandContext.getActionReport().setExtraProperties(extraProperties); + } +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/OpenApiServiceConfiguration.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/OpenApiServiceConfiguration.java new file mode 100644 index 00000000000..9fb5dbbcbc4 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/OpenApiServiceConfiguration.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.openapi.impl.admin; + +import java.beans.PropertyVetoException; + +import org.glassfish.api.admin.config.ConfigExtension; +import org.jvnet.hk2.config.Attribute; +import org.jvnet.hk2.config.ConfigBeanProxy; +import org.jvnet.hk2.config.Configured; + +/** + * Configuration for the OpenAPI Service. + */ +@Configured(name = "microprofile-openapi-configuration") +public interface OpenApiServiceConfiguration extends ConfigBeanProxy, ConfigExtension { + + /** + * @return whether the service is enabled or not. + */ + @Attribute(defaultValue = "true", dataType = Boolean.class) + String getEnabled(); + + void setEnabled(String value) throws PropertyVetoException; + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java new file mode 100644 index 00000000000..fa48c8e4634 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java @@ -0,0 +1,117 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.openapi.impl.admin; + +import static org.glassfish.api.admin.RestEndpoint.OpType.POST; +import static org.glassfish.config.support.CommandTarget.CLUSTER; +import static org.glassfish.config.support.CommandTarget.CLUSTERED_INSTANCE; +import static org.glassfish.config.support.CommandTarget.CONFIG; +import static org.glassfish.config.support.CommandTarget.DAS; +import static org.glassfish.config.support.CommandTarget.DEPLOYMENT_GROUP; +import static org.glassfish.config.support.CommandTarget.STANDALONE_INSTANCE; + +import java.util.logging.Logger; + +import javax.inject.Inject; + +import com.sun.enterprise.config.serverbeans.Config; + +import org.glassfish.api.Param; +import org.glassfish.api.admin.AdminCommand; +import org.glassfish.api.admin.AdminCommandContext; +import org.glassfish.api.admin.ExecuteOn; +import org.glassfish.api.admin.RestEndpoint; +import org.glassfish.api.admin.RestEndpoints; +import org.glassfish.api.admin.RuntimeType; +import org.glassfish.config.support.TargetType; +import org.glassfish.hk2.api.PerLookup; +import org.glassfish.internal.api.Target; +import org.jvnet.hk2.annotations.Service; +import org.jvnet.hk2.config.ConfigSupport; +import org.jvnet.hk2.config.TransactionFailure; + +@Service(name = "set-openapi-configuration") +@PerLookup +@ExecuteOn({ RuntimeType.DAS }) +@TargetType({ DAS, DEPLOYMENT_GROUP, STANDALONE_INSTANCE, CLUSTER, CLUSTERED_INSTANCE, CONFIG }) +@RestEndpoints({ + @RestEndpoint(configBean = OpenApiServiceConfiguration.class, + opType = POST, + path = "set-openapi-configuration", + description = "Sets the OpenAPI Configuration") +}) +public class SetOpenApiConfigurationCommand implements AdminCommand { + + private static final Logger LOGGER = Logger.getLogger(SetOpenApiConfigurationCommand.class.getName()); + + @Inject + private Target targetUtil; + + @Param(name = "enabled", optional = true) + private Boolean enabled; + + @Param(optional = true, defaultValue = "server-config") + private String target; + + @Override + public void execute(AdminCommandContext adminCommandContext) { + Config targetConfig = targetUtil.getConfig(target); + OpenApiServiceConfiguration openApiConfig = targetConfig.getExtensionByType(OpenApiServiceConfiguration.class); + + try { + ConfigSupport.apply(configProxy -> { + if (enabled != null) { + configProxy.setEnabled(Boolean.toString(enabled)); + } + return configProxy; + }, openApiConfig); + } catch (TransactionFailure ex) { + adminCommandContext.getActionReport().failure(LOGGER, "Failed to update OpenAPI configuration", ex); + return; + } + + if (enabled) { + LOGGER.info("OpenAPIService enabled."); + } else { + LOGGER.info("OpenAPIService disabled."); + } + } + +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java index 350f64389a3..3b25f5e4336 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java @@ -1,9 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.config; import static java.util.stream.Collectors.toSet; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -28,8 +69,8 @@ public class OpenApiConfiguration { private static final String SCAN_EXCLUDE_PACKAGES_KEY = "mp.openapi.scan.exclude.packages"; private static final String SCAN_EXCLUDE_CLASSES_KEY = "mp.openapi.scan.exclude.classes"; private static final String SERVERS_KEY = "mp.openapi.servers"; - private static final String PATH_PREFIX_KEY = "mp.openapi.path."; - private static final String OPERATION_PREFIX_KEY = "mp.openapi.operation."; + private static final String PATH_PREFIX_KEY = "mp.openapi.servers.path."; + private static final String OPERATION_PREFIX_KEY = "mp.openapi.servers.operation."; private Class modelReader; private Class filter; @@ -39,8 +80,8 @@ public class OpenApiConfiguration { private List excludePackages = new ArrayList<>(); private List> excludeClasses = new ArrayList<>(); private List servers = new ArrayList<>(); - private Map pathServerMap = new HashMap<>(); - private Map operationServerMap = new HashMap<>(); + private Map> pathServerMap = new HashMap<>(); + private Map> operationServerMap = new HashMap<>(); public OpenApiConfiguration(ClassLoader applicationClassLoader) { // Find the correct configuration instance @@ -62,73 +103,99 @@ public OpenApiConfiguration(ClassLoader applicationClassLoader) { this.operationServerMap = findOperationServerMap(config); } + /** + * @return the {@link OASModelReader} class provided by the application. + */ public Class getModelReader() { return modelReader; } + /** + * @return the {@link OASFilter} class provided by the application. + */ public Class getFilter() { return filter; } + /** + * @return whether to disable application scanning. + */ public boolean getScanDisable() { return scanDisable; } + /** + * @return a whitelist of packages to scan in the application. + */ public List getScanPackages() { return scanPackages; } + /** + * @return a whitelist of classes to scan in the application. + */ public List> getScanClasses() { return scanClasses; } + /** + * @return a blacklist of packages to not scan in the application. + */ public List getExcludePackages() { return excludePackages; } + /** + * @return a blacklist of classes to not scan in the application. + */ public List> getExcludeClasses() { return excludeClasses; } + /** + * @return a list of servers to add to the root document. + */ public List getServers() { return servers; } - public Map getPathServerMap() { + /** + * @return a map of paths to the servers it contains. + */ + public Map> getPathServerMap() { return pathServerMap; } - public Map getOperationServerMap() { + /** + * @return a map of operation ids to the servers it contains. + */ + public Map> getOperationServerMap() { return operationServerMap; } + /** + * @param classes the list of classes to filter. + * @return a filtered list of classes, using {@link #getScanClasses()}, + * {@link #getExcludeClasses()}, {@link #getScanPackages()} and + * {@link #getExcludePackages()}. + */ public Set> getValidClasses(Set> classes) { return classes.stream() // If scan classes are specified, check that the class is in the list .filter(clazz -> scanClasses.isEmpty() || scanClasses.contains(clazz)) // If exclude classes are specified, check that the class is not the list .filter(clazz -> excludeClasses.isEmpty() || !excludeClasses.contains(clazz)) - // If scan packages are specified, check that the class package starts with one in the list - .filter(clazz -> scanPackages.isEmpty() || scanPackages.stream().anyMatch(pkg -> clazz.getPackage().getName().startsWith(pkg))) - // If exclude packages are specified, check that the class package doesn't start with any in the list - .filter(clazz -> excludePackages.isEmpty() || !excludePackages.stream().anyMatch(pkg -> clazz.getPackage().getName().startsWith(pkg))) + // If scan packages are specified, check that the class package starts with one + // in the list + .filter(clazz -> scanPackages.isEmpty() + || scanPackages.stream().anyMatch(pkg -> clazz.getPackage().getName().startsWith(pkg))) + // If exclude packages are specified, check that the class package doesn't start + // with any in the list + .filter(clazz -> excludePackages.isEmpty() + || !excludePackages.stream().anyMatch(pkg -> clazz.getPackage().getName().startsWith(pkg))) .collect(toSet()); } - private Map findOperationServerMap(Config config) { - Map map = new HashMap<>(); - try { - for (String propertyName : config.getPropertyNames()) { - if (propertyName.startsWith(OPERATION_PREFIX_KEY)) { - operationServerMap.put(propertyName, config.getValue(propertyName, String.class)); - } - } - } catch (NoSuchElementException ex) { - // Ignore - } - return map; - } - @SuppressWarnings("unchecked") private Class findModelReaderFromConfig(Config config, ClassLoader classLoader) { try { @@ -166,22 +233,20 @@ private boolean findScanDisableFromConfig(Config config) { return false; } - @SuppressWarnings("unchecked") private List findScanPackagesFromConfig(Config config) { List packages = new ArrayList<>(); try { - packages.addAll(config.getValue(SCAN_PACKAGES_KEY, List.class)); + packages.addAll(Arrays.asList(config.getValue(SCAN_PACKAGES_KEY, String[].class))); } catch (NoSuchElementException ex) { // Ignore } return packages; } - @SuppressWarnings("unchecked") private List> findScanClassesFromConfig(Config config, ClassLoader classLoader) { List> classes = new ArrayList<>(); try { - List classNames = (List) config.getValue(SCAN_CLASSES_KEY, List.class); + List classNames = Arrays.asList(config.getValue(SCAN_CLASSES_KEY, String[].class)); for (String className : classNames) { Class clazz = getClassFromName(className, classLoader); if (clazz != null) { @@ -194,22 +259,20 @@ private List> findScanClassesFromConfig(Config config, ClassLoader clas return classes; } - @SuppressWarnings("unchecked") private List findExcludePackages(Config config) { List packages = new ArrayList<>(); try { - packages.addAll(config.getValue(SCAN_EXCLUDE_PACKAGES_KEY, List.class)); + packages.addAll(Arrays.asList(config.getValue(SCAN_EXCLUDE_PACKAGES_KEY, String[].class))); } catch (NoSuchElementException ex) { // Ignore } return packages; } - @SuppressWarnings("unchecked") private List> findExcludeClasses(Config config, ClassLoader classLoader) { List> classes = new ArrayList<>(); try { - List classNames = config.getValue(SCAN_EXCLUDE_CLASSES_KEY, List.class); + List classNames = Arrays.asList(config.getValue(SCAN_EXCLUDE_CLASSES_KEY, String[].class)); for (String className : classNames) { Class clazz = getClassFromName(className, classLoader); if (clazz != null) { @@ -222,23 +285,38 @@ private List> findExcludeClasses(Config config, ClassLoader classLoader return classes; } - @SuppressWarnings("unchecked") private List findServers(Config config) { List serverList = new ArrayList<>(); try { - serverList.addAll(config.getValue(SERVERS_KEY, List.class)); + serverList.addAll(Arrays.asList(config.getValue(SERVERS_KEY, String[].class))); } catch (NoSuchElementException ex) { // Ignore } return serverList; } - private Map findPathServerMap(Config config) { - Map map = new HashMap<>(); + private Map> findPathServerMap(Config config) { + Map> map = new HashMap<>(); try { for (String propertyName : config.getPropertyNames()) { if (propertyName.startsWith(PATH_PREFIX_KEY)) { - map.put(propertyName, config.getValue(propertyName, String.class)); + map.put(propertyName.replaceFirst(PATH_PREFIX_KEY, ""), + new HashSet<>(Arrays.asList(config.getValue(propertyName, String[].class)))); + } + } + } catch (NoSuchElementException ex) { + // Ignore + } + return map; + } + + private Map> findOperationServerMap(Config config) { + Map> map = new HashMap<>(); + try { + for (String propertyName : config.getPropertyNames()) { + if (propertyName.startsWith(OPERATION_PREFIX_KEY)) { + map.put(propertyName.replaceFirst(OPERATION_PREFIX_KEY, ""), + new HashSet<>(Arrays.asList(config.getValue(propertyName, String[].class)))); } } } catch (NoSuchElementException ex) { diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java index faeddde4157..0b668c9b0fa 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model; import java.util.Map; @@ -256,7 +295,7 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.Components if (callback != null) { if (callback.name() != null) { Callback newCallback = new CallbackImpl(); - CallbackImpl.merge(callback, newCallback, override); + CallbackImpl.merge(callback, newCallback, override, currentSchemas); to.addCallback(callback.name(), newCallback); } } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExtensibleImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExtensibleImpl.java index 73123919f44..96555b12abd 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExtensibleImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExtensibleImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExternalDocumentationImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExternalDocumentationImpl.java index 20dac9062a6..ae7b6aa08bf 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExternalDocumentationImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExternalDocumentationImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OASFactoryResolverImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OASFactoryResolverImpl.java new file mode 100644 index 00000000000..aab1b130548 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OASFactoryResolverImpl.java @@ -0,0 +1,168 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.openapi.impl.model; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.Components; +import org.eclipse.microprofile.openapi.models.Constructible; +import org.eclipse.microprofile.openapi.models.ExternalDocumentation; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.Paths; +import org.eclipse.microprofile.openapi.models.callbacks.Callback; +import org.eclipse.microprofile.openapi.models.examples.Example; +import org.eclipse.microprofile.openapi.models.headers.Header; +import org.eclipse.microprofile.openapi.models.info.Contact; +import org.eclipse.microprofile.openapi.models.info.Info; +import org.eclipse.microprofile.openapi.models.info.License; +import org.eclipse.microprofile.openapi.models.links.Link; +import org.eclipse.microprofile.openapi.models.media.Content; +import org.eclipse.microprofile.openapi.models.media.Discriminator; +import org.eclipse.microprofile.openapi.models.media.Encoding; +import org.eclipse.microprofile.openapi.models.media.MediaType; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.media.XML; +import org.eclipse.microprofile.openapi.models.parameters.Parameter; +import org.eclipse.microprofile.openapi.models.parameters.RequestBody; +import org.eclipse.microprofile.openapi.models.responses.APIResponse; +import org.eclipse.microprofile.openapi.models.responses.APIResponses; +import org.eclipse.microprofile.openapi.models.security.OAuthFlow; +import org.eclipse.microprofile.openapi.models.security.OAuthFlows; +import org.eclipse.microprofile.openapi.models.security.Scopes; +import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; +import org.eclipse.microprofile.openapi.models.servers.Server; +import org.eclipse.microprofile.openapi.models.servers.ServerVariable; +import org.eclipse.microprofile.openapi.models.servers.ServerVariables; +import org.eclipse.microprofile.openapi.models.tags.Tag; +import org.eclipse.microprofile.openapi.spi.OASFactoryResolver; + +import fish.payara.microprofile.openapi.impl.model.callbacks.CallbackImpl; +import fish.payara.microprofile.openapi.impl.model.examples.ExampleImpl; +import fish.payara.microprofile.openapi.impl.model.headers.HeaderImpl; +import fish.payara.microprofile.openapi.impl.model.info.ContactImpl; +import fish.payara.microprofile.openapi.impl.model.info.InfoImpl; +import fish.payara.microprofile.openapi.impl.model.info.LicenseImpl; +import fish.payara.microprofile.openapi.impl.model.links.LinkImpl; +import fish.payara.microprofile.openapi.impl.model.media.ContentImpl; +import fish.payara.microprofile.openapi.impl.model.media.DiscriminatorImpl; +import fish.payara.microprofile.openapi.impl.model.media.EncodingImpl; +import fish.payara.microprofile.openapi.impl.model.media.MediaTypeImpl; +import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; +import fish.payara.microprofile.openapi.impl.model.media.XMLImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.ParameterImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.RequestBodyImpl; +import fish.payara.microprofile.openapi.impl.model.responses.APIResponseImpl; +import fish.payara.microprofile.openapi.impl.model.responses.APIResponsesImpl; +import fish.payara.microprofile.openapi.impl.model.security.OAuthFlowImpl; +import fish.payara.microprofile.openapi.impl.model.security.OAuthFlowsImpl; +import fish.payara.microprofile.openapi.impl.model.security.ScopesImpl; +import fish.payara.microprofile.openapi.impl.model.security.SecurityRequirementImpl; +import fish.payara.microprofile.openapi.impl.model.security.SecuritySchemeImpl; +import fish.payara.microprofile.openapi.impl.model.servers.ServerImpl; +import fish.payara.microprofile.openapi.impl.model.servers.ServerVariableImpl; +import fish.payara.microprofile.openapi.impl.model.servers.ServerVariablesImpl; +import fish.payara.microprofile.openapi.impl.model.tags.TagImpl; + +public class OASFactoryResolverImpl extends OASFactoryResolver { + + public static final Map, Class> MODEL_MAP = new HashMap<>(); + static { + MODEL_MAP.put(Components.class, ComponentsImpl.class); + MODEL_MAP.put(ExternalDocumentation.class, ExternalDocumentationImpl.class); + MODEL_MAP.put(OpenAPI.class, OpenAPIImpl.class); + MODEL_MAP.put(Operation.class, OperationImpl.class); + MODEL_MAP.put(PathItem.class, PathItemImpl.class); + MODEL_MAP.put(Paths.class, PathsImpl.class); + MODEL_MAP.put(Callback.class, CallbackImpl.class); + MODEL_MAP.put(Example.class, ExampleImpl.class); + MODEL_MAP.put(Header.class, HeaderImpl.class); + MODEL_MAP.put(Contact.class, ContactImpl.class); + MODEL_MAP.put(Info.class, InfoImpl.class); + MODEL_MAP.put(License.class, LicenseImpl.class); + MODEL_MAP.put(Link.class, LinkImpl.class); + MODEL_MAP.put(Content.class, ContentImpl.class); + MODEL_MAP.put(Discriminator.class, DiscriminatorImpl.class); + MODEL_MAP.put(Encoding.class, EncodingImpl.class); + MODEL_MAP.put(MediaType.class, MediaTypeImpl.class); + MODEL_MAP.put(Schema.class, SchemaImpl.class); + MODEL_MAP.put(XML.class, XMLImpl.class); + MODEL_MAP.put(Parameter.class, ParameterImpl.class); + MODEL_MAP.put(RequestBody.class, RequestBodyImpl.class); + MODEL_MAP.put(APIResponse.class, APIResponseImpl.class); + MODEL_MAP.put(APIResponses.class, APIResponsesImpl.class); + MODEL_MAP.put(OAuthFlow.class, OAuthFlowImpl.class); + MODEL_MAP.put(OAuthFlows.class, OAuthFlowsImpl.class); + MODEL_MAP.put(Scopes.class, ScopesImpl.class); + MODEL_MAP.put(SecurityRequirement.class, SecurityRequirementImpl.class); + MODEL_MAP.put(SecurityScheme.class, SecuritySchemeImpl.class); + MODEL_MAP.put(Server.class, ServerImpl.class); + MODEL_MAP.put(ServerVariable.class, ServerVariableImpl.class); + MODEL_MAP.put(ServerVariables.class, ServerVariablesImpl.class); + MODEL_MAP.put(Tag.class, TagImpl.class); + } + + @SuppressWarnings("unchecked") + @Override + public T createObject(Class clazz) { + // Throw a null pointer exception if the class is null + if (clazz == null) { + throw new NullPointerException("Cannot create an object from a null class."); + } + + // Get the implementation class + Class implClass = MODEL_MAP.get(clazz); + + // If there is no implementation, throw an exception + if (implClass == null) { + throw new IllegalArgumentException(clazz.getName()); + } + + // Return a new instance. Shouldn't throw an exception. + try { + return (T) implClass.newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java index 42ae9db3486..096eeac49ee 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; @@ -20,6 +59,7 @@ import fish.payara.microprofile.openapi.impl.model.security.SecurityRequirementImpl; import fish.payara.microprofile.openapi.impl.model.servers.ServerImpl; import fish.payara.microprofile.openapi.impl.model.tags.TagImpl; +import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; public class OpenAPIImpl extends ExtensibleImpl implements OpenAPI { @@ -98,6 +138,17 @@ public OpenAPI servers(List servers) { @Override public OpenAPI addServer(Server server) { + if (server.getUrl() != null) { + for (Server existingServer : getServers()) { + // If a server with the same URL is found, merge them + if (server.getUrl().equals(existingServer.getUrl())) { + ModelUtils.merge(server, existingServer, true); + return this; + } + } + } + + // If a server with the same URL doesn't exist, create it servers.add(server); return this; } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OperationImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OperationImpl.java index df1f23aa73c..5e4cb8817dc 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OperationImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OperationImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; @@ -8,15 +47,21 @@ import java.util.List; import java.util.Map; +import org.eclipse.microprofile.openapi.annotations.callbacks.CallbackOperation; +import org.eclipse.microprofile.openapi.annotations.extensions.Extension; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.models.ExternalDocumentation; import org.eclipse.microprofile.openapi.models.Operation; import org.eclipse.microprofile.openapi.models.callbacks.Callback; +import org.eclipse.microprofile.openapi.models.media.Schema; import org.eclipse.microprofile.openapi.models.parameters.Parameter; import org.eclipse.microprofile.openapi.models.parameters.RequestBody; import org.eclipse.microprofile.openapi.models.responses.APIResponses; import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; import org.eclipse.microprofile.openapi.models.servers.Server; +import fish.payara.microprofile.openapi.impl.model.parameters.ParameterImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.RequestBodyImpl; import fish.payara.microprofile.openapi.impl.model.responses.APIResponsesImpl; public class OperationImpl extends ExtensibleImpl implements Operation { @@ -260,4 +305,41 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.Operation to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); } + public static void merge(CallbackOperation from, Operation to, + boolean override, Map currentSchemas) { + if (isAnnotationNull(from)) { + return; + } + to.setSummary(mergeProperty(to.getSummary(), from.summary(), override)); + to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); + if (from.extensions() != null) { + for (Extension extension : from.extensions()) { + ExtensibleImpl.merge(extension, to, override); + } + } + if (!isAnnotationNull(from.externalDocs())) { + if (to.getExternalDocs() == null) { + to.setExternalDocs(new ExternalDocumentationImpl()); + } + ExternalDocumentationImpl.merge(from.externalDocs(), to.getExternalDocs(), override); + } + if (from.parameters() != null) { + for (org.eclipse.microprofile.openapi.annotations.parameters.Parameter parameter : from.parameters()) { + Parameter newParameter = new ParameterImpl(); + ParameterImpl.merge(parameter, newParameter, override, currentSchemas); + } + } + if (!isAnnotationNull(from.requestBody())) { + if (to.getRequestBody() == null) { + to.setRequestBody(new RequestBodyImpl()); + } + RequestBodyImpl.merge(from.requestBody(), to.getRequestBody(), override, currentSchemas); + } + if (from.responses() != null) { + for (APIResponse response : from.responses()) { + APIResponsesImpl.merge(response, to.getResponses(), override, currentSchemas); + } + } + } + } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathItemImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathItemImpl.java index 5d0923c8944..92244103bcb 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathItemImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathItemImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model; import java.util.ArrayList; @@ -305,7 +344,7 @@ public PathItem parameters(List parameters) { @Override public PathItem addParameter(Parameter parameter) { - setParameters(parameters); + parameters.add(parameter); return this; } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathsImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathsImpl.java index af6f25ccf86..fb6b666a2aa 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathsImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathsImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model; import java.util.HashMap; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/callbacks/CallbackImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/callbacks/CallbackImpl.java index 5890add1115..a38e6313b7f 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/callbacks/CallbackImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/callbacks/CallbackImpl.java @@ -1,8 +1,48 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.callbacks; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.getHttpMethod; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.getOrCreateOperation; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; -import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; import java.util.HashMap; import java.util.LinkedHashMap; @@ -13,9 +53,10 @@ import org.eclipse.microprofile.openapi.models.PathItem; import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; import org.eclipse.microprofile.openapi.models.callbacks.Callback; +import org.eclipse.microprofile.openapi.models.media.Schema; +import fish.payara.microprofile.openapi.impl.model.OperationImpl; import fish.payara.microprofile.openapi.impl.model.PathItemImpl; -import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; public class CallbackImpl extends LinkedHashMap implements Callback { @@ -65,7 +106,7 @@ public void addExtension(String name, Object value) { } public static void merge(org.eclipse.microprofile.openapi.annotations.callbacks.Callback from, Callback to, - boolean override) { + boolean override, Map currentSchemas) { if (isAnnotationNull(from)) { return; } @@ -74,23 +115,22 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.callbacks. return; } if (!from.callbackUrlExpression().isEmpty()) { - PathItem pathItem = new PathItemImpl(); - to.addPathItem(from.callbackUrlExpression(), pathItem); + PathItem pathItem = to.getOrDefault(from.callbackUrlExpression(), new PathItemImpl()); + to.put(from.callbackUrlExpression(), pathItem); if (from.operations() != null) { for (CallbackOperation callbackOperation : from.operations()) { - applyCallbackOperationAnnotation(pathItem, callbackOperation, override); + applyCallbackOperationAnnotation(pathItem, callbackOperation, override, currentSchemas); } } } } private static void applyCallbackOperationAnnotation(PathItem pathItem, CallbackOperation annotation, - boolean override) { - HttpMethod method = HttpMethod.valueOf(annotation.method()); + boolean override, Map currentSchemas) { + HttpMethod method = getHttpMethod(annotation.method()); if (method != null) { - Operation operation = ModelUtils.getOrCreateOperation(pathItem, method); - operation.setDescription(mergeProperty(operation.getDescription(), annotation.description(), override)); - operation.setSummary(mergeProperty(operation.getSummary(), annotation.summary(), override)); + Operation operation = getOrCreateOperation(pathItem, method); + OperationImpl.merge(annotation, operation, override, currentSchemas); } } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java index 7c94a6667bc..0d5f1e83775 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java @@ -1,8 +1,49 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.examples; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; +import java.util.Map; + import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; import org.eclipse.microprofile.openapi.models.examples.Example; @@ -109,4 +150,29 @@ public static void merge(ExampleObject from, Example to, boolean override) { to.setExternalValue(mergeProperty(to.getExternalValue(), from.externalValue(), override)); } + public static void merge(ExampleObject example, Map examples, boolean override) { + if (isAnnotationNull(example)) { + return; + } + + // Get the example name + String exampleName = example.name(); + if (example.name() == null || example.name().isEmpty()) { + exampleName = "?"; + } + + // Get or create the example + Example model = examples.getOrDefault(exampleName, new ExampleImpl()); + examples.put(exampleName, model); + + // Merge the annotation + merge(example, model, override); + + // If the merged annotation has a reference, set the name to the reference + if (model.getRef() != null) { + examples.remove(exampleName); + examples.put(model.getRef().split("/")[3], model); + } + } + } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java index 0813a2a8870..5de0243bd9c 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.headers; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; @@ -233,6 +272,33 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.headers.He } SchemaImpl.merge(from.schema(), to.getSchema(), override, currentSchemas); } + to.setStyle(Style.SIMPLE); + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.headers.Header header, + Map headers, boolean override, Map currentSchemas) { + if (isAnnotationNull(header)) { + return; + } + + // Get the header name + String headerName = header.name(); + if (header.name() == null || header.name().isEmpty()) { + headerName = "?"; + } + + // Get or create the header + Header model = headers.getOrDefault(headerName, new HeaderImpl()); + headers.put(headerName, model); + + // Merge the annotation + merge(header, model, override, currentSchemas); + + // If the merged annotation has a reference, set the name to the reference + if (model.getRef() != null) { + headers.remove(headerName); + headers.put(model.getRef().split("/")[3], model); + } } } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/ContactImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/ContactImpl.java index d203786f3e8..169d07090c6 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/ContactImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/ContactImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.info; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/InfoImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/InfoImpl.java index 28055b0c38f..5405b4c12b5 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/InfoImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/InfoImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.info; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/LicenseImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/LicenseImpl.java index eba9a4ca319..773cf073d9c 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/LicenseImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/LicenseImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.info; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java index 514bc38ec34..336f86b7e5d 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.links; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; @@ -7,6 +46,7 @@ import java.util.HashMap; import java.util.Map; +import org.eclipse.microprofile.openapi.annotations.links.LinkParameter; import org.eclipse.microprofile.openapi.models.links.Link; import org.eclipse.microprofile.openapi.models.servers.Server; @@ -155,6 +195,49 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.links.Link to.setOperationId(mergeProperty(to.getOperationId(), from.operationId(), override)); to.setOperationRef(mergeProperty(to.getOperationRef(), from.operationRef(), override)); to.setRequestBody(mergeProperty(to.getRequestBody(), from.requestBody(), override)); + for (LinkParameter parameter : from.parameters()) { + applyLinkParameter(parameter, to.getParameters()); + } + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.links.Link link, Map links, + boolean override) { + if (isAnnotationNull(link)) { + return; + } + + // Get the link name + String linkName = link.name(); + if (link.name() == null || link.name().isEmpty()) { + linkName = "?"; + } + + // Get or create the link + Link model = links.getOrDefault(linkName, new LinkImpl()); + links.put(linkName, model); + + // Merge the annotation + merge(link, model, override); + + // If the merged annotation has a reference, set the name to the reference + if (model.getRef() != null) { + links.remove(linkName); + links.put(model.getRef().split("/")[3], model); + } + } + + private static void applyLinkParameter(LinkParameter parameter, Map linkParameters) { + + // Get the parameter name + String parameterName = parameter.name(); + if (parameterName == null || parameterName.isEmpty()) { + parameterName = "?"; + } + + // Create the object + Object model = linkParameters.get(parameterName); + model = mergeProperty(model, parameter.expression(), true); + linkParameters.put(parameterName, model); } } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/ContentImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/ContentImpl.java index b48d515fd8a..cbe163eb295 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/ContentImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/ContentImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.media; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; @@ -5,10 +44,14 @@ import java.util.LinkedHashMap; import java.util.Map; +import org.eclipse.microprofile.openapi.annotations.media.Encoding; +import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; import org.eclipse.microprofile.openapi.models.media.Content; import org.eclipse.microprofile.openapi.models.media.MediaType; import org.eclipse.microprofile.openapi.models.media.Schema; +import fish.payara.microprofile.openapi.impl.model.examples.ExampleImpl; + public class ContentImpl extends LinkedHashMap implements Content { private static final long serialVersionUID = 1575356277308242221L; @@ -24,26 +67,34 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.media.Cont if (from == null) { return; } - if (!isAnnotationNull(from.schema()) || from.encoding().length > 0) { - // Get the name of the media type - String typeName = from.mediaType(); - if (typeName == null || typeName.isEmpty()) { - typeName = javax.ws.rs.core.MediaType.WILDCARD; - } - // Get the current mediaType, or create a new one - MediaType mediaType = to.getOrDefault(typeName, new MediaTypeImpl()); - to.put(typeName, mediaType); - - if (!isAnnotationNull(from.schema())) { - // Get the current schema, or create a new one - Schema schema = mediaType.getSchema(); - if (schema == null) { - schema = new SchemaImpl(); - mediaType.setSchema(schema); - } - SchemaImpl.merge(from.schema(), schema, override, currentSchemas); + // Get the name of the media type + String typeName = from.mediaType(); + if (typeName == null || typeName.isEmpty()) { + typeName = javax.ws.rs.core.MediaType.WILDCARD; + } + + // Get or create the corresponding media type + MediaType mediaType = to.getOrDefault(typeName, new MediaTypeImpl()); + to.put(typeName, mediaType); + + // Merge encoding + for (Encoding encoding : from.encoding()) { + EncodingImpl.merge(encoding, to.get(typeName).getEncoding(), override, currentSchemas); + } + + // Merge examples + for (ExampleObject example : from.examples()) { + ExampleImpl.merge(example, to.get(typeName).getExamples(), override); + } + + // Merge schema + if (!isAnnotationNull(from.schema())) { + if (mediaType.getSchema() == null) { + mediaType.setSchema(new SchemaImpl()); } + Schema schema = mediaType.getSchema(); + SchemaImpl.merge(from.schema(), schema, true, currentSchemas); } } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/DiscriminatorImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/DiscriminatorImpl.java index 1c7796bd557..c71cba9bf15 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/DiscriminatorImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/DiscriminatorImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.media; import java.util.HashMap; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/EncodingImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/EncodingImpl.java index f623539a713..4436050fdaa 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/EncodingImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/EncodingImpl.java @@ -1,12 +1,56 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.media; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; + import java.util.HashMap; import java.util.Map; import org.eclipse.microprofile.openapi.models.headers.Header; import org.eclipse.microprofile.openapi.models.media.Encoding; +import org.eclipse.microprofile.openapi.models.media.Schema; import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; +import fish.payara.microprofile.openapi.impl.model.headers.HeaderImpl; public class EncodingImpl extends ExtensibleImpl implements Encoding { @@ -96,4 +140,34 @@ public Encoding allowReserved(Boolean allowReserved) { return this; } + public static void merge(org.eclipse.microprofile.openapi.annotations.media.Encoding from, Encoding to, + boolean override, Map currentSchemas) { + if (isAnnotationNull(from)) { + return; + } + to.setContentType(mergeProperty(to.getContentType(), from.contentType(), override)); + to.setStyle(mergeProperty(to.getStyle(), Style.valueOf(from.style().toUpperCase()), override)); + to.setExplode(mergeProperty(to.getExplode(), from.explode(), override)); + to.setAllowReserved(mergeProperty(to.getAllowReserved(), from.allowReserved(), override)); + for (org.eclipse.microprofile.openapi.annotations.headers.Header header : from.headers()) { + HeaderImpl.merge(header, to.getHeaders(), override, currentSchemas); + } + } + + public static void merge(org.eclipse.microprofile.openapi.annotations.media.Encoding encoding, + Map encodings, boolean override, Map currentSchemas) { + if (isAnnotationNull(encoding)) { + return; + } + + if (encoding.name() != null && !encoding.name().isEmpty()) { + // Get or create the encoding + Encoding model = encodings.getOrDefault(encoding.name(), new EncodingImpl()); + encodings.put(encoding.name(), model); + + // Merge the annotation + merge(encoding, model, override, currentSchemas); + } + } + } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/MediaTypeImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/MediaTypeImpl.java index 57f75ecedd9..f24e84c62d9 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/MediaTypeImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/MediaTypeImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.media; import java.util.HashMap; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/SchemaImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/SchemaImpl.java index 9049b1c7c2d..9147de3508b 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/SchemaImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/SchemaImpl.java @@ -1,9 +1,47 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.media; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; -import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.overwrite; import java.math.BigDecimal; import java.util.ArrayList; @@ -20,6 +58,7 @@ import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; import fish.payara.microprofile.openapi.impl.model.ExternalDocumentationImpl; +import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; public class SchemaImpl extends ExtensibleImpl implements Schema { @@ -699,23 +738,35 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.media.Sche applyReference(to, from.ref()); return; } + if (from.type() != null + && from.type() != org.eclipse.microprofile.openapi.annotations.enums.SchemaType.DEFAULT) { + to.setType(mergeProperty(to.getType(), SchemaType.valueOf(from.type().name()), override)); + } if (from.implementation() != null && currentSchemas != null) { Class implementationClass = from.implementation(); - - // If the implementation class is a schema - if (implementationClass - .isAnnotationPresent(org.eclipse.microprofile.openapi.annotations.media.Schema.class)) { - + if (!implementationClass.getTypeName().equals("java.lang.Void")) { + org.eclipse.microprofile.openapi.annotations.media.Schema annotation = implementationClass + .getDeclaredAnnotation(org.eclipse.microprofile.openapi.annotations.media.Schema.class); // Get the schema name - String schemaName = implementationClass - .getDeclaredAnnotation(org.eclipse.microprofile.openapi.annotations.media.Schema.class).name(); - if (schemaName == null) { + String schemaName = null; + if (annotation == null || annotation.name() == null || annotation.name().isEmpty()) { schemaName = implementationClass.getSimpleName(); + } else { + schemaName = annotation.name(); } // Get the schema reference, and copy it's values over to the new schema model Schema copyFrom = currentSchemas.get(schemaName); - overwrite(copyFrom, to); + if (implementationClass.equals(String.class)) { + copyFrom = new SchemaImpl().type(SchemaType.STRING); + } + if (to.getType() == SchemaType.ARRAY) { + to.setItems(new SchemaImpl()); + ModelUtils.merge(copyFrom, to.getItems(), true); + } else { + ModelUtils.merge(copyFrom, to, true); + } + to.setRef(null); } } if (from.discriminatorMapping() != null && !from.discriminatorProperty().isEmpty()) { @@ -742,7 +793,8 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.media.Sche } } if (from.multipleOf() > 0) { - to.setMultipleOf(mergeProperty(to.getMultipleOf(), BigDecimal.valueOf(from.multipleOf()), override)); + to.setMultipleOf(mergeProperty(to.getMultipleOf(), + BigDecimal.valueOf(from.multipleOf()).stripTrailingZeros(), override)); } if (!from.maximum().isEmpty()) { to.setMaximum(mergeProperty(to.getMaximum(), new BigDecimal(from.maximum()), override)); @@ -770,10 +822,6 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.media.Sche } } } - if (from.type() != null - && from.type() != org.eclipse.microprofile.openapi.annotations.enums.SchemaType.DEFAULT) { - to.setType(mergeProperty(to.getType(), SchemaType.valueOf(from.type().name()), override)); - } if (from.not() != null) { if (Schema.class.isAssignableFrom(from.not())) { try { diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/XMLImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/XMLImpl.java index 185d4ea24ac..479d480bd26 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/XMLImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/XMLImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.media; import org.eclipse.microprofile.openapi.models.media.XML; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/ParameterImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/ParameterImpl.java index 8369ddf5b49..786381a65bd 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/ParameterImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/ParameterImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.parameters; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/RequestBodyImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/RequestBodyImpl.java index 64f74f3840b..6843fea8aaa 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/RequestBodyImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/RequestBodyImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.parameters; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponseImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponseImpl.java index 1113ff85857..feb2f7ea4e2 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponseImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponseImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.responses; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; @@ -14,6 +53,8 @@ import org.eclipse.microprofile.openapi.models.responses.APIResponse; import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; +import fish.payara.microprofile.openapi.impl.model.headers.HeaderImpl; +import fish.payara.microprofile.openapi.impl.model.links.LinkImpl; import fish.payara.microprofile.openapi.impl.model.media.ContentImpl; public class APIResponseImpl extends ExtensibleImpl implements APIResponse { @@ -137,6 +178,16 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.responses. ContentImpl.merge(content, to.getContent(), override, currentSchemas); } } + if (from.headers() != null) { + for (org.eclipse.microprofile.openapi.annotations.headers.Header header : from.headers()) { + HeaderImpl.merge(header, to.getHeaders(), override, currentSchemas); + } + } + if (from.links() != null) { + for (org.eclipse.microprofile.openapi.annotations.links.Link link : from.links()) { + LinkImpl.merge(link, to.getLinks(), override); + } + } } } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponsesImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponsesImpl.java index e5b99ef2d8e..19ae86f59a4 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponsesImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponsesImpl.java @@ -1,7 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.responses; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; + import java.util.LinkedHashMap; +import java.util.Map; +import org.eclipse.microprofile.openapi.models.media.Schema; import org.eclipse.microprofile.openapi.models.responses.APIResponse; import org.eclipse.microprofile.openapi.models.responses.APIResponses; @@ -31,4 +74,22 @@ public APIResponses defaultValue(APIResponse defaultValue) { return this; } + public static void merge(org.eclipse.microprofile.openapi.annotations.responses.APIResponse from, APIResponses to, + boolean override, Map currentSchemas) { + if (isAnnotationNull(from)) { + return; + } + // Get the response name + String responseName = from.responseCode(); + if (responseName == null || responseName.isEmpty()) { + responseName = org.eclipse.microprofile.openapi.models.responses.APIResponses.DEFAULT; + } + + org.eclipse.microprofile.openapi.models.responses.APIResponse response = to + .getOrDefault(responseName, new APIResponseImpl()); + to.addApiResponse(responseName, response); + + APIResponseImpl.merge(from, response, override, currentSchemas); + } + } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowImpl.java index 5dae52a7496..74f3d2ebaf9 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.security; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowsImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowsImpl.java index b132b90f819..bd46c9afa3e 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowsImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowsImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.security; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/ScopesImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/ScopesImpl.java index e526491f111..20f58d4b0bb 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/ScopesImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/ScopesImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.security; import java.util.HashMap; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecurityRequirementImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecurityRequirementImpl.java index b5d9573bb6e..adb1d166165 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecurityRequirementImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecurityRequirementImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.security; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; @@ -33,7 +72,7 @@ public SecurityRequirement addScheme(String name) { public static void merge(org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement from, SecurityRequirement to, boolean override) { - if (isAnnotationNull(from) || from.scopes().length == 0) { + if (isAnnotationNull(from)) { return; } if (from.name() != null && !from.name().isEmpty()) { diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecuritySchemeImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecuritySchemeImpl.java index ba3e154c7f4..1f710ee2dc6 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecuritySchemeImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecuritySchemeImpl.java @@ -1,10 +1,48 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.security; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; -import com.fasterxml.jackson.annotation.JsonIgnore; - import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeIn; import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType; import org.eclipse.microprofile.openapi.models.security.OAuthFlows; @@ -17,7 +55,6 @@ public class SecuritySchemeImpl extends ExtensibleImpl implements SecurityScheme protected SecurityScheme.Type type; protected String description; protected String name; - protected String schemeName; protected String ref; protected SecurityScheme.In in; @@ -173,23 +210,22 @@ public SecurityScheme ref(String ref) { return this; } - @JsonIgnore - public String getSchemeName() { - return schemeName; - } - - public void setSchemeName(String schemeName) { - this.schemeName = schemeName; - } - public static void merge(org.eclipse.microprofile.openapi.annotations.security.SecurityScheme from, SecurityScheme to, boolean override) { if (isAnnotationNull(from)) { return; } + + if (from.ref() != null && !from.ref().isEmpty()) { + applyReference(to, from.ref()); + return; + } + to.setName(mergeProperty(to.getName(), from.apiKeyName(), override)); to.setDescription(mergeProperty(to.getDescription(), from.description(), override)); to.setScheme(mergeProperty(to.getScheme(), from.scheme(), override)); + to.setBearerFormat(mergeProperty(to.getBearerFormat(), from.bearerFormat(), override)); + to.setOpenIdConnectUrl(mergeProperty(to.getOpenIdConnectUrl(), from.openIdConnectUrl(), override)); if (from.in() != null && from.in() != SecuritySchemeIn.DEFAULT) { to.setIn(mergeProperty(to.getIn(), In.valueOf(from.in().name()), override)); } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerImpl.java index 7b91a4503be..7147050d097 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.servers; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariableImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariableImpl.java index 76b09e2de8b..188a649505e 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariableImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariableImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.servers; import java.util.ArrayList; @@ -9,29 +48,24 @@ public class ServerVariableImpl extends ExtensibleImpl implements ServerVariable { - protected List enumeration = new ArrayList<>(); - protected String defaultValue; protected String description; + protected String defaultValue; - @Override - public List getEnumeration() { - return enumeration; - } + protected List enumeration = new ArrayList<>(); @Override - public void setEnumeration(List enumeration) { - this.enumeration = enumeration; + public String getDescription() { + return description; } @Override - public ServerVariable enumeration(List enumeration) { - setEnumeration(enumeration); - return this; + public void setDescription(String description) { + this.description = description; } @Override - public ServerVariable addEnumeration(String enumeration) { - this.enumeration.add(enumeration); + public ServerVariable description(String description) { + setDescription(description); return this; } @@ -52,18 +86,26 @@ public ServerVariable defaultValue(String defaultValue) { } @Override - public String getDescription() { - return description; + public List getEnumeration() { + return enumeration; } @Override - public void setDescription(String description) { - this.description = description; + public void setEnumeration(List enumeration) { + this.enumeration = enumeration; } @Override - public ServerVariable description(String description) { - setDescription(description); + public ServerVariable enumeration(List enumeration) { + setEnumeration(enumeration); + return this; + } + + @Override + public ServerVariable addEnumeration(String enumeration) { + if (!this.enumeration.contains(enumeration)) { + this.enumeration.add(enumeration); + } return this; } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariablesImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariablesImpl.java index 02ba1577fd3..c0fec9e06bc 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariablesImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariablesImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.servers; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; @@ -51,9 +90,7 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.servers.Se variable.setEnumeration(new ArrayList<>()); } for (String value : from.enumeration()) { - if (!variable.getEnumeration().contains(value)) { - variable.addEnumeration(value); - } + variable.addEnumeration(value); } } if ((to.containsKey(from.name()) && override) || !to.containsKey(from.name())) { diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/tags/TagImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/tags/TagImpl.java index cdac0ab6ea1..8b9c56e7c11 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/tags/TagImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/tags/TagImpl.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.tags; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; @@ -86,27 +125,21 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.tags.Tag f return; } - String tagName = from.name(); - - Tag tag = getExistingTag(from.ref(), apiTags); - if (tag == null) { - tag = new TagImpl(); - apiTags.add(tag); - } else { - tagName = tag.getName(); + // If there is a reference, add the reference and return + if (from.ref() != null && !from.ref().isEmpty()) { + to.addTag(from.ref()); + return; } - merge(from, tag, override); - to.addTag(tagName); - } - private static Tag getExistingTag(String name, List apiTags) { - Tag foundTag = null; - for (Tag tag : apiTags) { - if (tag.getName().equals(name)) { - foundTag = tag; - } + if (from.name() != null && !from.name().isEmpty()) { + // Create the new tag + Tag newTag = new TagImpl(); + merge(from, newTag, true); + apiTags.add(newTag); + + // Reference the new tag + to.addTag(newTag.getName()); } - return foundTag; } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ExtensionsMixin.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ExtensionsMixin.java deleted file mode 100644 index 9dc193917d4..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ExtensionsMixin.java +++ /dev/null @@ -1,31 +0,0 @@ -package fish.payara.microprofile.openapi.impl.model.util; - -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; - -public abstract class ExtensionsMixin { - - @JsonAnyGetter - public abstract Map getExtensions(); - - @JsonAnySetter - public abstract void addExtension(String name, Object value); - - @JsonProperty("enum") - public abstract void getEnumeration(); - - @JsonProperty("default") - public abstract void getDefaultValue(); - - @JsonProperty("$ref") - public abstract void getRef(); - - // TODO: Fix ignored additional properties - @JsonIgnore - public abstract void setAdditionalProperties(Boolean additionalProperties); - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtils.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtils.java index 93a54320068..6772ebbf5c6 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtils.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtils.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.util; import java.lang.annotation.Annotation; @@ -7,6 +46,9 @@ import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import javax.inject.Inject; import javax.ws.rs.BeanParam; @@ -26,6 +68,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.ext.Provider; +import org.eclipse.microprofile.openapi.models.Constructible; import org.eclipse.microprofile.openapi.models.OpenAPI; import org.eclipse.microprofile.openapi.models.Operation; import org.eclipse.microprofile.openapi.models.PathItem; @@ -43,6 +86,36 @@ public final class ModelUtils { private ModelUtils() { } + + + /** + * Normalises a path string. A normalised path has: + *
    + *
  • no multiple slashes.
  • + *
  • no trailing slash.
  • + *
+ * + * @param path the path to be normalised. + */ + public static String normaliseUrl(String path) { + if (path == null) { + return null; + } + // Remove multiple slashes + path = path.replaceAll("/+", "/"); + + // Remove trailing slash + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + + // Assure that there is one slash + if (path.isEmpty()) { + path = "/"; + } + return path; + } + /** * @param method the method to analyse. * @return the {@link HttpMethod} applied to this method, or null if there is @@ -73,6 +146,31 @@ public static HttpMethod getHttpMethod(Method method) { return null; } + public static HttpMethod getHttpMethod(String method) { + if (method.equalsIgnoreCase("GET")) { + return HttpMethod.GET; + } + if (method.equalsIgnoreCase("POST")) { + return HttpMethod.POST; + } + if (method.equalsIgnoreCase("PUT")) { + return HttpMethod.PUT; + } + if (method.equalsIgnoreCase("DELETE")) { + return HttpMethod.DELETE; + } + if (method.equalsIgnoreCase("HEAD")) { + return HttpMethod.HEAD; + } + if (method.equalsIgnoreCase("OPTIONS")) { + return HttpMethod.OPTIONS; + } + if (method.equalsIgnoreCase("PATCH")) { + return HttpMethod.PATCH; + } + return null; + } + /** * Creates a new {@link Operation}, and inserts it into the {@link PathItem}. * @@ -291,9 +389,21 @@ public static boolean isAnnotationNull(Annotation anno Object value = m.invoke(annotation); if (value != null) { if (value.getClass().isArray() && Array.getLength(value) > 0) { - allNull = false; + return false; + } else if (value instanceof Collection && Collection.class.cast(value).size() > 0) { + return false; + } else if (value instanceof Boolean && Boolean.class.cast(value)) { + return false; + } else if (value.getClass().equals(Class.class) + && !Class.class.cast(value).getTypeName().equals("java.lang.Void")) { + return false; + } else if (value.getClass().isEnum() && !Enum.class.cast(value).name().isEmpty() + && !Enum.class.cast(value).name().equalsIgnoreCase("DEFAULT")) { + return false; } else if (String.class.isAssignableFrom(value.getClass()) && !value.toString().isEmpty()) { - allNull = false; + return false; + } else if (value instanceof Annotation) { + allNull = isAnnotationNull((Annotation) value); } } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { @@ -309,6 +419,10 @@ public static E mergeProperty(E current, E offer, boolean override) { if (offer instanceof String && offer.toString().isEmpty()) { offer = null; } + // Treat max or min integers as null + if (offer instanceof Integer && (offer.equals(Integer.MAX_VALUE) || offer.equals(Integer.MIN_VALUE))) { + offer = null; + } E resolve = current; if (offer != null) { if (override) { @@ -360,32 +474,56 @@ public static void applyReference(org.eclipse.microprofile.openapi.models.Refere } public static void overwrite(T from, T to) { - if (to != null) { + if (to != null && from != null) { for (Field f : to.getClass().getDeclaredFields()) { - boolean accessible = f.isAccessible(); f.setAccessible(true); try { f.set(to, f.get(from)); } catch (IllegalArgumentException | IllegalAccessException e) { // Ignore errors - } finally { - f.setAccessible(accessible); } } } } public static void merge(T from, T to, boolean override) { - if (to != null) { + if (from != null && to != null) { for (Field f : to.getClass().getDeclaredFields()) { - boolean accessible = f.isAccessible(); f.setAccessible(true); try { - f.set(to, mergeProperty(f.get(to), f.get(from), override)); - } catch (IllegalArgumentException | IllegalAccessException e) { + // Get the new and old value + Object newValue = f.get(from); + Object currentValue = f.get(to); + if (newValue != null) { + if (newValue instanceof Map) { + for (Entry entry : (Set>) Map.class.cast(newValue) + .entrySet()) { + if (!Map.class.cast(currentValue).containsKey(entry.getKey())) { + Map.class.cast(currentValue).put(entry.getKey(), entry.getValue()); + } else { + merge(entry.getValue(), Map.class.cast(currentValue).get(entry.getKey()), override); + } + } + } + if (newValue instanceof Collection) { + for (Object o : Collection.class.cast(newValue)) { + if (!Collection.class.cast(currentValue).contains(o)) { + Collection.class.cast(currentValue).add(o); + } + } + } + if (newValue instanceof Constructible) { + if (currentValue == null) { + f.set(to, newValue.getClass().newInstance()); + currentValue = f.get(to); + } + merge(newValue, currentValue, override); + } else { + f.set(to, mergeProperty(f.get(to), f.get(from), override)); + } + } + } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { // Ignore errors - } finally { - f.setAccessible(accessible); } } } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/AnnotationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/AnnotationProcessor.java deleted file mode 100644 index 8f5505f9d05..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/AnnotationProcessor.java +++ /dev/null @@ -1,33 +0,0 @@ -package fish.payara.microprofile.openapi.impl.processor; - -import static fish.payara.microprofile.openapi.impl.processor.utils.ProcessorUtils.getClassesFromLoader; - -import java.util.Set; - -import org.eclipse.microprofile.openapi.models.OpenAPI; - -import fish.payara.microprofile.openapi.api.processor.OASProcessor; -import fish.payara.microprofile.openapi.api.visitor.ApiVisitor; -import fish.payara.microprofile.openapi.api.visitor.ApiWalker; -import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; -import fish.payara.microprofile.openapi.impl.visitor.AnnotationVisitor; -import fish.payara.microprofile.openapi.impl.visitor.OpenApiWalker; - -public class AnnotationProcessor implements OASProcessor { - - private final Set> classes; - - public AnnotationProcessor(ClassLoader appClassLoader) { - this.classes = getClassesFromLoader(appClassLoader); - } - - @Override - public void process(OpenAPI api, OpenApiConfiguration config) { - ApiWalker apiWalker = new OpenApiWalker(api, (config == null) ? classes : config.getValidClasses(classes)); - ApiVisitor apiVisitor = new AnnotationVisitor(); - if (config == null || !config.getScanDisable()) { - apiWalker.accept(apiVisitor); - } - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java index 9cfc5e6da73..596cb47a1c8 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -1,31 +1,922 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.processor; -import static fish.payara.microprofile.openapi.impl.processor.utils.ProcessorUtils.getClassesFromLoader; - +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.Consumes; +import javax.ws.rs.CookieParam; +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.OPTIONS; +import javax.ws.rs.PATCH; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Application; +import org.eclipse.microprofile.openapi.annotations.ExternalDocumentation; +import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.callbacks.Callback; +import org.eclipse.microprofile.openapi.annotations.callbacks.Callbacks; +import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn; +import org.eclipse.microprofile.openapi.annotations.extensions.Extension; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirements; +import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; +import org.eclipse.microprofile.openapi.annotations.security.SecuritySchemes; +import org.eclipse.microprofile.openapi.annotations.servers.Server; +import org.eclipse.microprofile.openapi.annotations.servers.Servers; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.eclipse.microprofile.openapi.annotations.tags.Tags; import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.Reference; +import org.eclipse.microprofile.openapi.models.media.MediaType; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; +import org.eclipse.microprofile.openapi.models.parameters.Parameter.Style; import fish.payara.microprofile.openapi.api.processor.OASProcessor; +import fish.payara.microprofile.openapi.api.visitor.ApiContext; import fish.payara.microprofile.openapi.api.visitor.ApiVisitor; import fish.payara.microprofile.openapi.api.visitor.ApiWalker; import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; -import fish.payara.microprofile.openapi.impl.visitor.ApplicationVisitor; +import fish.payara.microprofile.openapi.impl.model.ExternalDocumentationImpl; +import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; +import fish.payara.microprofile.openapi.impl.model.OperationImpl; +import fish.payara.microprofile.openapi.impl.model.PathItemImpl; +import fish.payara.microprofile.openapi.impl.model.callbacks.CallbackImpl; +import fish.payara.microprofile.openapi.impl.model.media.ContentImpl; +import fish.payara.microprofile.openapi.impl.model.media.MediaTypeImpl; +import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.ParameterImpl; +import fish.payara.microprofile.openapi.impl.model.parameters.RequestBodyImpl; +import fish.payara.microprofile.openapi.impl.model.responses.APIResponseImpl; +import fish.payara.microprofile.openapi.impl.model.responses.APIResponsesImpl; +import fish.payara.microprofile.openapi.impl.model.security.SecurityRequirementImpl; +import fish.payara.microprofile.openapi.impl.model.security.SecuritySchemeImpl; +import fish.payara.microprofile.openapi.impl.model.servers.ServerImpl; +import fish.payara.microprofile.openapi.impl.model.tags.TagImpl; +import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; import fish.payara.microprofile.openapi.impl.visitor.OpenApiWalker; -public class ApplicationProcessor implements OASProcessor { +/** + * A processor to parse the application for annotations, to add to the OpenAPI + * model. + */ +public class ApplicationProcessor implements OASProcessor, ApiVisitor { + + private static final Logger LOGGER = Logger.getLogger(ApplicationProcessor.class.getName()); + /** + * A list of all classes in the given application. + */ private final Set> classes; + /** + * @param appClassLoader the class loader for the application. + */ public ApplicationProcessor(ClassLoader appClassLoader) { this.classes = getClassesFromLoader(appClassLoader); } @Override - public void process(OpenAPI api, OpenApiConfiguration config) { - ApiWalker apiWalker = new OpenApiWalker(api, (config == null) ? classes : config.getValidClasses(classes)); - ApiVisitor apiVisitor = new ApplicationVisitor(); - apiWalker.accept(apiVisitor); + public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { + ApiWalker apiWalker = null; + if (config == null) { + apiWalker = new OpenApiWalker(api, classes, generateResourceMapping(classes)); + } else { + apiWalker = new OpenApiWalker(api, config.getValidClasses(classes), generateResourceMapping(classes)); + } + if (config == null || !config.getScanDisable()) { + apiWalker.accept(this); + } + return api; + } + + // JAX-RS method handlers + + @Override + public void visitGET(GET get, Method element, ApiContext context) { + if (context.getPath() == null) { + return; + } + + // Get or create the path item + PathItem pathItem = context.getApi().getPaths().getOrDefault(context.getPath(), new PathItemImpl()); + context.getApi().getPaths().addPathItem(context.getPath(), pathItem); + + org.eclipse.microprofile.openapi.models.Operation operation = new OperationImpl(); + pathItem.setGET(operation); + operation.setOperationId(element.getName()); + + // Add the default request + insertDefaultRequestBody(context.getApi(), operation, element); + + // Add the default response + insertDefaultResponse(context.getApi(), operation, element); + } + + @Override + public void visitPOST(POST post, Method element, ApiContext context) { + if (context.getPath() == null) { + return; + } + + // Get or create the path item + PathItem pathItem = context.getApi().getPaths().getOrDefault(context.getPath(), new PathItemImpl()); + context.getApi().getPaths().addPathItem(context.getPath(), pathItem); + + org.eclipse.microprofile.openapi.models.Operation operation = new OperationImpl(); + pathItem.setPOST(operation); + operation.setOperationId(element.getName()); + + // Add the default request + insertDefaultRequestBody(context.getApi(), operation, element); + + // Add the default response + insertDefaultResponse(context.getApi(), operation, element); + } + + @Override + public void visitPUT(PUT put, Method element, ApiContext context) { + if (context.getPath() == null) { + return; + } + + // Get or create the path item + PathItem pathItem = context.getApi().getPaths().getOrDefault(context.getPath(), new PathItemImpl()); + context.getApi().getPaths().addPathItem(context.getPath(), pathItem); + + org.eclipse.microprofile.openapi.models.Operation operation = new OperationImpl(); + pathItem.setPUT(operation); + operation.setOperationId(element.getName()); + + // Add the default request + insertDefaultRequestBody(context.getApi(), operation, element); + + // Add the default response + insertDefaultResponse(context.getApi(), operation, element); + } + + @Override + public void visitDELETE(DELETE delete, Method element, ApiContext context) { + if (context.getPath() == null) { + return; + } + + // Get or create the path item + PathItem pathItem = context.getApi().getPaths().getOrDefault(context.getPath(), new PathItemImpl()); + context.getApi().getPaths().addPathItem(context.getPath(), pathItem); + + org.eclipse.microprofile.openapi.models.Operation operation = new OperationImpl(); + pathItem.setDELETE(operation); + operation.setOperationId(element.getName()); + + // Add the default request + insertDefaultRequestBody(context.getApi(), operation, element); + + // Add the default response + insertDefaultResponse(context.getApi(), operation, element); + } + + @Override + public void visitHEAD(HEAD head, Method element, ApiContext context) { + if (context.getPath() == null) { + return; + } + + // Get or create the path item + PathItem pathItem = context.getApi().getPaths().getOrDefault(context.getPath(), new PathItemImpl()); + context.getApi().getPaths().addPathItem(context.getPath(), pathItem); + + org.eclipse.microprofile.openapi.models.Operation operation = new OperationImpl(); + pathItem.setHEAD(operation); + operation.setOperationId(element.getName()); + + // Add the default request + insertDefaultRequestBody(context.getApi(), operation, element); + + // Add the default response + insertDefaultResponse(context.getApi(), operation, element); + } + + @Override + public void visitOPTIONS(OPTIONS options, Method element, ApiContext context) { + if (context.getPath() == null) { + return; + } + + // Get or create the path item + PathItem pathItem = context.getApi().getPaths().getOrDefault(context.getPath(), new PathItemImpl()); + context.getApi().getPaths().addPathItem(context.getPath(), pathItem); + + org.eclipse.microprofile.openapi.models.Operation operation = new OperationImpl(); + pathItem.setOPTIONS(operation); + operation.setOperationId(element.getName()); + + // Add the default request + insertDefaultRequestBody(context.getApi(), operation, element); + + // Add the default response + insertDefaultResponse(context.getApi(), operation, element); + } + + @Override + public void visitPATCH(PATCH patch, Method element, ApiContext context) { + if (context.getPath() == null) { + return; + } + + // Get or create the path item + PathItem pathItem = context.getApi().getPaths().getOrDefault(context.getPath(), new PathItemImpl()); + context.getApi().getPaths().addPathItem(context.getPath(), pathItem); + + org.eclipse.microprofile.openapi.models.Operation operation = new OperationImpl(); + pathItem.setPATCH(operation); + operation.setOperationId(element.getName()); + + // Add the default request + insertDefaultRequestBody(context.getApi(), operation, element); + + // Add the default response + insertDefaultResponse(context.getApi(), operation, element); + } + + @Override + public void visitProduces(Produces produces, AnnotatedElement element, ApiContext context) { + if (element instanceof Method) { + for (org.eclipse.microprofile.openapi.models.responses.APIResponse response : context.getWorkingOperation() + .getResponses().values()) { + + if (response != null) { + // Find the wildcard return type + if (response.getContent() != null + && response.getContent().get(javax.ws.rs.core.MediaType.WILDCARD) != null) { + MediaType wildcardMedia = response.getContent().get(javax.ws.rs.core.MediaType.WILDCARD); + + // Copy the wildcard return type to the valid response types + for (String mediaType : produces.value()) { + response.getContent().put(getContentType(mediaType), wildcardMedia); + } + // If there is an @Produces, remove the wildcard + response.getContent().remove(javax.ws.rs.core.MediaType.WILDCARD); + } + } + } + } + } + + @Override + public void visitConsumes(Consumes consumes, AnnotatedElement element, ApiContext context) { + if (element instanceof Method) { + org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = context.getWorkingOperation() + .getRequestBody(); + + if (requestBody != null) { + // Find the wildcard return type + if (requestBody.getContent() != null + && requestBody.getContent().get(javax.ws.rs.core.MediaType.WILDCARD) != null) { + MediaType wildcardMedia = requestBody.getContent().get(javax.ws.rs.core.MediaType.WILDCARD); + + // Copy the wildcard return type to the valid request body types + for (String mediaType : consumes.value()) { + requestBody.getContent().put(getContentType(mediaType), wildcardMedia); + } + // If there is an @Consumes, remove the wildcard + requestBody.getContent().remove(javax.ws.rs.core.MediaType.WILDCARD); + } + } + } + } + + @Override + public void visitQueryParam(QueryParam param, java.lang.reflect.Parameter element, ApiContext context) { + org.eclipse.microprofile.openapi.models.parameters.Parameter newParameter = new ParameterImpl(); + newParameter.setName(param.value()); + newParameter.setIn(In.QUERY); + newParameter.setStyle(Style.SIMPLE); + newParameter.setSchema(new SchemaImpl().type(ModelUtils.getSchemaType(element.getType()))); + context.getWorkingOperation().addParameter(newParameter); + } + + @Override + public void visitPathParam(PathParam param, java.lang.reflect.Parameter element, ApiContext context) { + org.eclipse.microprofile.openapi.models.parameters.Parameter newParameter = new ParameterImpl(); + newParameter.setName(param.value()); + newParameter.setRequired(true); + newParameter.setIn(In.PATH); + newParameter.setStyle(Style.SIMPLE); + newParameter.setSchema(new SchemaImpl().type(ModelUtils.getSchemaType(element.getType()))); + context.getWorkingOperation().addParameter(newParameter); + } + + @Override + public void visitFormParam(FormParam param, java.lang.reflect.Parameter element, ApiContext context) { + // Find the aggregate schema type of all the parameters + SchemaType formSchemaType = null; + for (java.lang.reflect.Parameter methodParam : element.getDeclaringExecutable().getParameters()) { + if (methodParam.isAnnotationPresent(FormParam.class)) { + formSchemaType = ModelUtils.getParentSchemaType(formSchemaType, + ModelUtils.getSchemaType(methodParam.getType())); + } + } + + // If there's no request body, fill out a new one right down to the schema + if (context.getWorkingOperation().getRequestBody() == null) { + context.getWorkingOperation().setRequestBody(new RequestBodyImpl().content(new ContentImpl() + .addMediaType(javax.ws.rs.core.MediaType.WILDCARD, new MediaTypeImpl().schema(new SchemaImpl())))); + } + + // Set the request body type accordingly. + context.getWorkingOperation().getRequestBody().getContent().get(javax.ws.rs.core.MediaType.WILDCARD).getSchema().setType(formSchemaType); + } + + @Override + public void visitHeaderParam(HeaderParam param, java.lang.reflect.Parameter element, ApiContext context) { + org.eclipse.microprofile.openapi.models.parameters.Parameter newParameter = new ParameterImpl(); + newParameter.setName(param.value()); + newParameter.setIn(In.HEADER); + newParameter.setStyle(Style.SIMPLE); + newParameter.setSchema(new SchemaImpl().type(ModelUtils.getSchemaType(element.getType()))); + context.getWorkingOperation().addParameter(newParameter); + } + + @Override + public void visitCookieParam(CookieParam param, java.lang.reflect.Parameter element, ApiContext context) { + org.eclipse.microprofile.openapi.models.parameters.Parameter newParameter = new ParameterImpl(); + newParameter.setName(param.value()); + newParameter.setIn(In.COOKIE); + newParameter.setStyle(Style.SIMPLE); + newParameter.setSchema(new SchemaImpl().type(ModelUtils.getSchemaType(element.getType()))); + context.getWorkingOperation().addParameter(newParameter); + } + + @Override + public void visitOpenAPI(OpenAPIDefinition definition, AnnotatedElement element, ApiContext context) { + OpenAPIImpl.merge(definition, context.getApi(), true); + } + + @Override + public void visitSchema(Schema schema, AnnotatedElement element, ApiContext context) { + if (element instanceof Class) { + + // Get the schema object name + String schemaName = schema.name(); + if (schemaName == null || schemaName.isEmpty()) { + schemaName = Class.class.cast(element).getSimpleName(); + } + + // Add the new schema + org.eclipse.microprofile.openapi.models.media.Schema newSchema = new SchemaImpl(); + context.getApi().getComponents().addSchema(schemaName, newSchema); + SchemaImpl.merge(schema, newSchema, true, context.getApi().getComponents().getSchemas()); + } + if (element instanceof Field) { + + // Get the schema object name + String schemaName = schema.name(); + if (schemaName == null || schemaName.isEmpty()) { + schemaName = Field.class.cast(element).getName(); + } + + // Get the parent schema object name + String parentName = null; + try { + parentName = Field.class.cast(element).getDeclaringClass().getDeclaredAnnotation(Schema.class).name(); + } catch (NullPointerException ex) { + } + if (parentName == null || parentName.isEmpty()) { + parentName = Field.class.cast(element).getDeclaringClass().getSimpleName(); + } + + // Get or create the parent schema object + org.eclipse.microprofile.openapi.models.media.Schema parent = context.getApi().getComponents().getSchemas() + .getOrDefault(parentName, new SchemaImpl()); + context.getApi().getComponents().getSchemas().put(parentName, parent); + + org.eclipse.microprofile.openapi.models.media.Schema property = new SchemaImpl(); + parent.addProperty(schemaName, property); + property.setType(ModelUtils.getSchemaType(Field.class.cast(element).getType())); + SchemaImpl.merge(schema, property, true, context.getApi().getComponents().getSchemas()); + } + if (element instanceof java.lang.reflect.Parameter) { + + // If this is being parsed at the start, ignore it as the path doesn't exist + if (context.getWorkingOperation() == null) { + return; + } + + java.lang.reflect.Parameter parameter = (java.lang.reflect.Parameter) element; + // Check if it's a request body + if (ModelUtils.isRequestBody(parameter)) { + if (context.getWorkingOperation().getRequestBody() == null) { + context.getWorkingOperation().setRequestBody(new RequestBodyImpl()); + } + // Insert the schema to the request body media type + MediaType mediaType = context.getWorkingOperation().getRequestBody().getContent().get(javax.ws.rs.core.MediaType.WILDCARD); + SchemaImpl.merge(schema, mediaType.getSchema(), true, context.getApi().getComponents().getSchemas()); + if (schema.ref() != null && !schema.ref().isEmpty()) { + mediaType.setSchema(new SchemaImpl().ref(schema.ref())); + } + } else if (ModelUtils.getParameterType(parameter) != null) { + for (org.eclipse.microprofile.openapi.models.parameters.Parameter param : context.getWorkingOperation() + .getParameters()) { + if (param.getName().equals(ModelUtils.getParameterName(parameter))) { + SchemaImpl.merge(schema, param.getSchema(), true, + context.getApi().getComponents().getSchemas()); + if (schema.ref() != null && !schema.ref().isEmpty()) { + param.setSchema(new SchemaImpl().ref(schema.ref())); + } + } + } + } + } + } + + @Override + public void visitExtension(Extension extension, AnnotatedElement element, ApiContext context) { + if (extension.name() != null && !extension.name().isEmpty() && extension.value() != null + && !extension.value().isEmpty()) { + if (element instanceof Method) { + context.getWorkingOperation().addExtension(extension.name(), extension.value()); + } else { + context.getApi().addExtension(extension.name(), extension.value()); + } + } + } + + @Override + public void visitOperation(Operation operation, AnnotatedElement element, ApiContext context) { + OperationImpl.merge(operation, context.getWorkingOperation(), true); + // If the operation should be hidden, remove it + if (operation.hidden()) { + ModelUtils.removeOperation(context.getApi().getPaths().get(context.getPath()), + context.getWorkingOperation()); + } + } + + @Override + public void visitCallback(Callback callback, AnnotatedElement element, ApiContext context) { + if (element instanceof Method) { + org.eclipse.microprofile.openapi.models.callbacks.Callback callbackModel = context.getWorkingOperation() + .getCallbacks().getOrDefault(callback.name(), new CallbackImpl()); + context.getWorkingOperation().getCallbacks().put(callback.name(), callbackModel); + CallbackImpl.merge(callback, callbackModel, true, context.getApi().getComponents().getSchemas()); + } + } + + @Override + public void visitCallbacks(Callbacks callbacks, AnnotatedElement element, ApiContext context) { + for (Callback callback : callbacks.value()) { + visitCallback(callback, element, context); + } + } + + @Override + public void visitRequestBody(RequestBody requestBody, AnnotatedElement element, ApiContext context) { + if (element instanceof Method) { + if (context.getWorkingOperation().getRequestBody() != null) { + RequestBodyImpl.merge(requestBody, context.getWorkingOperation().getRequestBody(), true, + context.getApi().getComponents().getSchemas()); + } + } + if (element instanceof java.lang.reflect.Parameter) { + if (context.getWorkingOperation().getRequestBody() != null) { + RequestBodyImpl.merge(requestBody, context.getWorkingOperation().getRequestBody(), true, + context.getApi().getComponents().getSchemas()); + } + } + } + + @Override + public void visitAPIResponse(APIResponse apiResponse, AnnotatedElement element, ApiContext context) { + APIResponsesImpl.merge(apiResponse, context.getWorkingOperation().getResponses(), true, + context.getApi().getComponents().getSchemas()); + + // If an APIResponse has been processed that isn't the default + if (apiResponse.responseCode() != null && !apiResponse.responseCode().isEmpty() && !apiResponse.responseCode() + .equals(org.eclipse.microprofile.openapi.models.responses.APIResponses.DEFAULT)) { + // If the element doesn't also contain a response mapping to the default + if (!Arrays.asList(element.getDeclaredAnnotationsByType(APIResponse.class)).stream() + .anyMatch(a -> a.responseCode() == null || a.responseCode().isEmpty() || a.responseCode() + .equals(org.eclipse.microprofile.openapi.models.responses.APIResponses.DEFAULT))) { + // Then remove the default response + context.getWorkingOperation().getResponses() + .remove(org.eclipse.microprofile.openapi.models.responses.APIResponses.DEFAULT); + } + } + } + + @Override + public void visitAPIResponses(APIResponses apiResponses, AnnotatedElement element, ApiContext context) { + for (APIResponse response : apiResponses.value()) { + visitAPIResponse(response, element, context); + } + } + + @Override + public void visitParameter(Parameter parameter, AnnotatedElement element, ApiContext context) { + org.eclipse.microprofile.openapi.models.parameters.Parameter matchedParam = null; + + if (element instanceof java.lang.reflect.Parameter) { + // Find the matching parameter, and match it + for (org.eclipse.microprofile.openapi.models.parameters.Parameter param : context.getWorkingOperation() + .getParameters()) { + if (param.getName() != null + && param.getName().equals(ModelUtils.getParameterName((java.lang.reflect.Parameter) element))) { + matchedParam = param; + } + } + } + if (element instanceof Method) { + // If the parameter reference is valid + if (parameter.name() != null && !parameter.name().isEmpty()) { + // Get all parameters with the same name + List matchingMethodParameters = Arrays + .asList(Method.class.cast(element).getParameters()).stream() + .filter(x -> ModelUtils.getParameterName(x).equals(parameter.name())) + .collect(Collectors.toList()); + // If there is more than one match, filter it further + if (matchingMethodParameters.size() > 1 && parameter.in() != null + && parameter.in() != ParameterIn.DEFAULT) { + // Remove all parameters of the wrong input type + matchingMethodParameters + .removeIf(x -> ModelUtils.getParameterType(x) != In.valueOf(parameter.in().name())); + } + // If there's only one matching parameter, handle it immediately + String matchingMethodParamName = ModelUtils.getParameterName(matchingMethodParameters.get(0)); + // Find the matching operation parameter + for (org.eclipse.microprofile.openapi.models.parameters.Parameter operationParam : context + .getWorkingOperation().getParameters()) { + if (operationParam.getName().equals(matchingMethodParamName)) { + matchedParam = operationParam; + } + } + } + } + + if (matchedParam != null) { + ParameterImpl.merge(parameter, matchedParam, true, context.getApi().getComponents().getSchemas()); + + // If a content was added, and a schema type exists, reconfigure the schema type + if (matchedParam.getContent() != null && matchedParam.getSchema() != null + && matchedParam.getSchema().getType() != null) { + SchemaType type = matchedParam.getSchema().getType(); + matchedParam.setSchema(null); + + for (MediaType mediaType : matchedParam.getContent().values()) { + if (mediaType.getSchema() == null) { + mediaType.setSchema(new SchemaImpl()); + } + mediaType.getSchema() + .setType(ModelUtils.mergeProperty(mediaType.getSchema().getType(), type, false)); + } + } + } + } + + @Override + public void visitExternalDocumentation(ExternalDocumentation externalDocs, AnnotatedElement element, + ApiContext context) { + if (element instanceof Method) { + org.eclipse.microprofile.openapi.models.ExternalDocumentation newExternalDocs = new ExternalDocumentationImpl(); + ExternalDocumentationImpl.merge(externalDocs, newExternalDocs, true); + if (newExternalDocs.getUrl() != null && !newExternalDocs.getUrl().isEmpty()) { + context.getWorkingOperation().setExternalDocs(newExternalDocs); + } + } + } + + @Override + public void visitServer(Server server, AnnotatedElement element, ApiContext context) { + if (element instanceof Method) { + org.eclipse.microprofile.openapi.models.servers.Server newServer = new ServerImpl(); + context.getWorkingOperation().addServer(newServer); + ServerImpl.merge(server, newServer, true); + } + } + + @Override + public void visitServers(Servers servers, AnnotatedElement element, ApiContext context) { + for (Server server : servers.value()) { + visitServer(server, element, context); + } + } + + @Override + public void visitTag(Tag tag, AnnotatedElement element, ApiContext context) { + if (element instanceof Method) { + TagImpl.merge(tag, context.getWorkingOperation(), true, context.getApi().getTags()); + } else { + org.eclipse.microprofile.openapi.models.tags.Tag newTag = new TagImpl(); + TagImpl.merge(tag, newTag, true); + if (newTag.getName() != null && !newTag.getName().isEmpty()) { + context.getApi().getTags().add(newTag); + } + } + } + + @Override + public void visitTags(Tags tags, AnnotatedElement element, ApiContext context) { + if (element instanceof Method) { + for (Tag tag : tags.value()) { + visitTag(tag, element, context); + } + for (String ref : tags.refs()) { + if (ref != null && !ref.isEmpty()) { + context.getWorkingOperation().addTag(ref); + } + } + } + } + + @Override + public void visitSecurityScheme(SecurityScheme securityScheme, AnnotatedElement element, ApiContext context) { + if (securityScheme.securitySchemeName() != null && !securityScheme.securitySchemeName().isEmpty()) { + org.eclipse.microprofile.openapi.models.security.SecurityScheme newScheme = context.getApi().getComponents() + .getSecuritySchemes().getOrDefault(securityScheme.securitySchemeName(), new SecuritySchemeImpl()); + context.getApi().getComponents().addSecurityScheme(securityScheme.securitySchemeName(), newScheme); + SecuritySchemeImpl.merge(securityScheme, newScheme, true); + } + } + + @Override + public void visitSecuritySchemes(SecuritySchemes securitySchemes, AnnotatedElement element, ApiContext context) { + for (SecurityScheme securityScheme : securitySchemes.value()) { + visitSecurityScheme(securityScheme, element, context); + } + } + + @Override + public void visitSecurityRequirement(SecurityRequirement securityRequirement, AnnotatedElement element, + ApiContext context) { + if (element instanceof Method) { + if (securityRequirement.name() != null && !securityRequirement.name().isEmpty()) { + org.eclipse.microprofile.openapi.models.security.SecurityRequirement model = new SecurityRequirementImpl(); + SecurityRequirementImpl.merge(securityRequirement, model, true); + context.getWorkingOperation().addSecurityRequirement(model); + } + } + } + + @Override + public void visitSecurityRequirements(SecurityRequirements securityRequirements, AnnotatedElement element, + ApiContext context) { + for (SecurityRequirement requirement : securityRequirements.value()) { + visitSecurityRequirement(requirement, element, context); + } + } + + // PRIVATE METHODS + + /** + * Gets the set of classes contained within a {@link ClassLoader}. The set + * returned will not be null, but could be empty. + * + * @param classLoader the classloader to get the classes from. + * @return the set of classes managed by the classloader. + */ + @SuppressWarnings("unchecked") + private Set> getClassesFromLoader(ClassLoader classLoader) { + Set> classes = new HashSet<>(); + try { + Field classesField = ClassLoader.class.getDeclaredField("classes"); + classesField.setAccessible(true); + classes = new HashSet<>((Vector>) classesField.get(classLoader)); + classesField.setAccessible(false); + } catch (Exception ex) { + LOGGER.log(Level.WARNING, "Unable to get classes from classloader.", ex); + } + return classes; + } + + /** + * Generates a map listing the location each resource class is mapped to. + */ + private Map>> generateResourceMapping(Set> classList) { + Map>> resourceMapping = new HashMap<>(); + for (Class clazz : classList) { + if (clazz.isAnnotationPresent(ApplicationPath.class) && Application.class.isAssignableFrom(clazz)) { + // Produce the mapping + String key = clazz.getDeclaredAnnotation(ApplicationPath.class).value(); + Set> resourceClasses = new HashSet<>(); + resourceMapping.put(key, resourceClasses); + + try { + Application app = (Application) clazz.newInstance(); + // Add all classes contained in the application + resourceClasses.addAll(app.getClasses()); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + // If there is one application and it's empty, add all classes + if (resourceMapping.keySet().size() == 1) { + Set> classes = resourceMapping.values().iterator().next(); + if (classes.isEmpty()) { + classes.addAll(classList); + } + } + + // If there is no application, add all classes to the context root. + // TODO: parse the web xml to find the correct mapping in this case + if (resourceMapping.isEmpty()) { + resourceMapping.put("/", classList); + } + + return resourceMapping; + } + + private org.eclipse.microprofile.openapi.models.parameters.RequestBody insertDefaultRequestBody(OpenAPI api, + org.eclipse.microprofile.openapi.models.Operation operation, Method method) { + org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = new RequestBodyImpl(); + + // Get the request body type of the method + Class bodyType = null; + for (java.lang.reflect.Parameter methodParam : method.getParameters()) { + if (ModelUtils.isRequestBody(methodParam)) { + bodyType = methodParam.getType(); + break; + } + } + if (bodyType == null) { + return null; + } + + // Create the default request body with a wildcard mediatype + MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, bodyType)); + requestBody.getContent().addMediaType(javax.ws.rs.core.MediaType.WILDCARD, mediaType); + + operation.setRequestBody(requestBody); + return requestBody; + } + + /** + * Creates a new {@link APIResponse} to model the default response of a + * {@link Method}, and inserts it into the {@link APIResponses}. + * + * @param responses the {@link APIResponses} to add the default response to. + * @param method the {@link Method} to model the default response on. + * @return the newly created {@link APIResponse}. + */ + private org.eclipse.microprofile.openapi.models.responses.APIResponse insertDefaultResponse(OpenAPI api, + org.eclipse.microprofile.openapi.models.Operation operation, Method method) { + org.eclipse.microprofile.openapi.models.responses.APIResponse defaultResponse = new APIResponseImpl(); + defaultResponse.setDescription("Default Response."); + + // Create the default response with a wildcard mediatype + MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, method.getReturnType())); + defaultResponse.getContent().addMediaType(javax.ws.rs.core.MediaType.WILDCARD, mediaType); + + // Add the default response + operation.setResponses(new APIResponsesImpl().addApiResponse( + org.eclipse.microprofile.openapi.models.responses.APIResponses.DEFAULT, defaultResponse)); + return defaultResponse; + } + + /** + * @return the {@link javax.ws.rs.core.MediaType} with the given name. Defaults to WILDCARD. + */ + private String getContentType(String name) { + try { + javax.ws.rs.core.MediaType mediaType = javax.ws.rs.core.MediaType.valueOf(name); + if (mediaType != null) { + return mediaType.toString(); + } + } catch (IllegalArgumentException ex) { + } + return javax.ws.rs.core.MediaType.WILDCARD; + } + + private org.eclipse.microprofile.openapi.models.media.Schema createSchema(OpenAPI api, Class type) { + org.eclipse.microprofile.openapi.models.media.Schema schema = new SchemaImpl(); + SchemaType schemaType = ModelUtils.getSchemaType(type); + schema.setType(schemaType); + + // Set the subtype if it's an array (for example an array of ints) + if (schemaType == SchemaType.ARRAY) { + Class subType = type.getComponentType(); + org.eclipse.microprofile.openapi.models.media.Schema subSchema = schema; + while (subType != null) { + subSchema.setItems(new SchemaImpl().type(ModelUtils.getSchemaType(subType))); + subSchema = schema.getItems(); + subType = subType.getComponentType(); + } + } + + if (schemaType == SchemaType.OBJECT) { + if (insertObjectReference(api, schema, type)) { + schema.setType(null); + schema.setItems(null); + } + } + return schema; + } + + /** + * Replace the object in the referee with a reference, and create the reference + * in the API. + * + * @param api the OpenAPI object. + * @param referee the object containing the reference. + * @param referenceClass the class of the object being referenced. + * @return if the reference has been created. + */ + private boolean insertObjectReference(OpenAPI api, Reference referee, Class referenceClass) { + + // If the object is java.lang.Object, exit + if (referenceClass.equals(Object.class)) { + return false; + } + + // Get the schemas + Map schemas = api.getComponents().getSchemas(); + + // Set the reference name + referee.setRef(referenceClass.getSimpleName()); + + if (!schemas.containsKey(referenceClass.getSimpleName())) { + // If the schema type doesn't already exist, create it + org.eclipse.microprofile.openapi.models.media.Schema schema = new SchemaImpl(); + schemas.put(referenceClass.getSimpleName(), schema); + schema.setType(SchemaType.OBJECT); + Map fields = new LinkedHashMap<>(); + for (Field field : referenceClass.getDeclaredFields()) { + if (!Modifier.isTransient(field.getModifiers())) { + fields.put(field.getName(), createSchema(api, field.getType())); + } + } + schema.setProperties(fields); + } + + return true; } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java index 5d9683f3706..04f840b784f 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java @@ -1,6 +1,48 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.processor; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.normaliseUrl; + import java.util.Map.Entry; +import java.util.Set; import org.eclipse.microprofile.openapi.models.OpenAPI; import org.eclipse.microprofile.openapi.models.Operation; @@ -12,6 +54,10 @@ import fish.payara.microprofile.openapi.impl.model.info.InfoImpl; import fish.payara.microprofile.openapi.impl.model.servers.ServerImpl; +/** + * A processor to apply any configuration options to the model, and fill any + * required but currently empty values. + */ public class BaseProcessor implements OASProcessor { private final String applicationPath; @@ -21,7 +67,7 @@ public BaseProcessor(String applicationPath) { } @Override - public void process(OpenAPI api, OpenApiConfiguration config) { + public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { // Set the OpenAPI version if it hasn't been set if (api.getOpenapi() == null) { @@ -33,38 +79,64 @@ public void process(OpenAPI api, OpenApiConfiguration config) { api.setInfo(new InfoImpl().title("Deployed Resources").version("1.0.0")); } - // Add the default server - api.addServer(new ServerImpl().url("http://localhost:8080" + applicationPath)); - // Add the config specified servers if (config != null && config.getServers().size() > 0) { - config.getServers().forEach(url -> api.addServer(new ServerImpl().url(url))); + // Clear all the other servers + api.getServers().clear(); + // Add all the specified ones + config.getServers().forEach(serverUrl -> api.addServer(new ServerImpl().url(serverUrl))); + } + + // Add the default server if there are none + if (api.getServers().size() == 0) { + api.addServer( + new ServerImpl().url("http://localhost:8080" + applicationPath).description("Default Server.")); } // Add the path servers if (config != null && !config.getPathServerMap().isEmpty()) { - for (Entry entry : config.getPathServerMap().entrySet()) { - if (!api.getPaths().containsKey(entry.getKey())) { - api.getPaths().addPathItem(entry.getKey(), new PathItemImpl()); + for (Entry> entry : config.getPathServerMap().entrySet()) { + // Get the normalised path + String path = normaliseUrl(entry.getKey()); + + // If the path doesn't exist, create it + if (!api.getPaths().containsKey(path)) { + api.getPaths().addPathItem(path, new PathItemImpl()); + } + + // Clear the current list of servers + api.getPaths().get(path).getServers().clear(); + + // Add each url + for (String serverUrl : entry.getValue()) { + api.getPaths().get(path).addServer(new ServerImpl().url(serverUrl)); } - api.getPaths().get(entry.getKey()).addServer(new ServerImpl().url(entry.getValue())); } } // Add the operation servers if (config != null && !config.getOperationServerMap().isEmpty()) { - for (Entry entry : config.getOperationServerMap().entrySet()) { + for (Entry> entry : config.getOperationServerMap().entrySet()) { - // For each operation in the tree, add the server if the operation id matches + // Find the matching operation for (PathItem pathItem : api.getPaths().values()) { for (Operation operation : pathItem.readOperations()) { if (operation.getOperationId().equals(entry.getKey())) { - operation.addServer(new ServerImpl().url(entry.getValue())); + + // Clear the current list of servers + operation.getServers().clear(); + + // Add each server url to the operation + for (String serverUrl : entry.getValue()) { + operation.addServer(new ServerImpl().url(serverUrl)); + } } } } } } + + return api; } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java index 02acd99da67..32b3a002f38 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.processor; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.merge; @@ -5,6 +44,9 @@ import java.io.File; import java.io.IOException; import java.net.URISyntaxException; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; import com.fasterxml.jackson.databind.ObjectMapper; @@ -15,39 +57,63 @@ import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; import fish.payara.microprofile.openapi.impl.rest.app.provider.ObjectMapperFactory; +/** + * A processor to process a static document in the META-INF + * directory of the application, and merge it into the provided model. + */ public class FileProcessor implements OASProcessor { + private static final Logger LOGGER = Logger.getLogger(FileProcessor.class.getName()); + + /** + * The static file provided by the application. + */ private File file; + + /** + * A mapper object to create an OpenAPI model from the document. + */ private ObjectMapper mapper; public FileProcessor(ClassLoader appClassLoader) { try { - if (appClassLoader.getResource("META-INF/openapi.json") != null) { - file = new File(appClassLoader.getResource("META-INF/openapi.json").toURI()); - mapper = ObjectMapperFactory.createJson(); - } else if (appClassLoader.getResource("META-INF/openapi.yaml") != null) { - file = new File(appClassLoader.getResource("META-INF/openapi.yaml").toURI()); - mapper = ObjectMapperFactory.createYaml(); - } else if (appClassLoader.getResource("META-INF/openapi.yml") != null) { - file = new File(appClassLoader.getResource("META-INF/openapi.yml").toURI()); - mapper = ObjectMapperFactory.createYaml(); + // Search for a the correct file + URL fileUrl = appClassLoader.getResource("META-INF/openapi.json"); + if (fileUrl == null) { + fileUrl = appClassLoader.getResource("META-INF/openapi.yaml"); + if (fileUrl == null) { + fileUrl = appClassLoader.getResource("META-INF/openapi.yml"); + } } - } catch (URISyntaxException e) { - e.printStackTrace(); + if (fileUrl != null) { + file = new File(fileUrl.toURI()); + if (file.getPath().endsWith(".json")) { + mapper = ObjectMapperFactory.createJson(); + } else { + mapper = ObjectMapperFactory.createYaml(); + } + } else { + LOGGER.fine("No static OpenAPI document provided."); + } + } catch (URISyntaxException ex) { + LOGGER.log(Level.WARNING, "Invalid URI syntax", ex); } } @Override - public void process(OpenAPI api, OpenApiConfiguration config) { + public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { if (file != null) { OpenAPI readResult = null; try { readResult = mapper.readValue(file, OpenAPIImpl.class); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ex) { + LOGGER.log(Level.WARNING, "Error when reading static OpenAPI document.", ex); + } + if (readResult != null) { + merge(readResult, api, true); } - merge(readResult, api, false); } + return api; } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java index 15fa18889dc..f86651e6a6c 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java @@ -1,7 +1,51 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.processor; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import org.eclipse.microprofile.openapi.OASFilter; import org.eclipse.microprofile.openapi.models.OpenAPI; @@ -22,38 +66,79 @@ import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; +/** + * A processor to obtain an application defined {@link OASFilter}, and generate + * an pass the OpenAPI model into it for final processing. + */ public class FilterProcessor implements OASProcessor { + private static final Logger LOGGER = Logger.getLogger(FilterProcessor.class.getName()); + + /** + * The OASFilter implementation provided by the application. + */ private OASFilter filter; @Override - public void process(OpenAPI api, OpenApiConfiguration config) { + public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { try { if (config.getFilter() != null) { filter = config.getFilter().newInstance(); } - } catch (InstantiationException | IllegalAccessException e) { - e.printStackTrace(); + } catch (InstantiationException | IllegalAccessException ex) { + LOGGER.log(Level.WARNING, "Error creating OASFilter instance.", ex); } if (filter != null) { - filterObject(api); + return (OpenAPI) filterObject(api, null); + } else { + LOGGER.fine("No OASFilter provided."); } + return api; } - private Object filterObject(Object object) { + @SuppressWarnings("unchecked") + private Object filterObject(Object object, Object parent) { if (object != null) { // If the object is a map if (object instanceof Map) { + List resultsToRemove = new ArrayList(); + + // Filter each object in the value list for (Object item : Map.class.cast(object).values()) { - filterObject(item); + Object result = filterObject(item, object); + + if (result == null) { + resultsToRemove.add(item); + } + } + + // Remove all the null values + for (Object removeTarget : resultsToRemove) { + Map.class.cast(object).values().remove(removeTarget); } } // If the object is iterable if (object instanceof Iterable) { + List resultsToRemove = new ArrayList(); + + // Filter each object in the list for (Object item : Iterable.class.cast(object)) { - filterObject(item); + Object result = filterObject(item, object); + + if (result == null) { + resultsToRemove.add(item); + } + } + + for (Object removeTarget : resultsToRemove) { + Iterator iterator = (Iterator) Iterable.class.cast(object).iterator(); + while (iterator.hasNext()) { + if (iterator.next().equals(removeTarget)) { + iterator.remove(); + } + } } } @@ -62,19 +147,24 @@ private Object filterObject(Object object) { // Visit each field for (Field field : object.getClass().getDeclaredFields()) { - boolean accessible = field.isAccessible(); try { field.setAccessible(true); Object fieldValue = field.get(object); - filterObject(fieldValue); - } catch (IllegalArgumentException | IllegalAccessException e) { - } finally { - field.setAccessible(accessible); + + // Filter the object + Object result = filterObject(fieldValue, object); + + // Remove it if it's null + if (result == null) { + field.set(object, null); + } + } catch (IllegalArgumentException | IllegalAccessException ex) { + LOGGER.log(Level.WARNING, "Unable to access field in OpenAPI model.", ex); } } // Visit the object - visitObject(object); + object = visitObject(object); } return object; @@ -124,7 +214,7 @@ private Object visitObject(Object object) { filter.filterOpenAPI((OpenAPI) object); } } - return null; + return object; } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java index 025dc3be44d..ccb3808667d 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java @@ -1,6 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.processor; -import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.merge; +import java.util.logging.Level; +import java.util.logging.Logger; import org.eclipse.microprofile.openapi.OASModelReader; import org.eclipse.microprofile.openapi.models.OpenAPI; @@ -8,23 +48,40 @@ import fish.payara.microprofile.openapi.api.processor.OASProcessor; import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; +/** + * A processor to obtain an application defined {@link OASModelReader}, and + * generate an initial OpenAPI document from it. + */ public class ModelReaderProcessor implements OASProcessor { + private static final Logger LOGGER = Logger.getLogger(ModelReaderProcessor.class.getName()); + + /** + * The OASModelReader implementation provided by the application. + */ private OASModelReader reader; @Override - public void process(OpenAPI api, OpenApiConfiguration config) { + public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { try { if (config.getModelReader() != null) { reader = config.getModelReader().newInstance(); } - } catch (InstantiationException | IllegalAccessException e) { - e.printStackTrace(); + } catch (InstantiationException | IllegalAccessException ex) { + LOGGER.log(Level.WARNING, "Error creating OASModelReader instance.", ex); } if (reader != null) { OpenAPI model = reader.buildModel(); - merge(model, api, true); + if (model != null) { + return model; + } else { + LOGGER.log(Level.WARNING, + "The OpenAPI model returned by " + reader.getClass().getName() + " was null!"); + } + } else { + LOGGER.fine("No OASModelReader provided."); } + return api; } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/utils/ProcessorUtils.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/utils/ProcessorUtils.java deleted file mode 100644 index 17d31cc6bbc..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/utils/ProcessorUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -package fish.payara.microprofile.openapi.impl.processor.utils; - -import java.lang.reflect.Field; -import java.util.HashSet; -import java.util.Set; -import java.util.Vector; -import java.util.logging.Level; -import java.util.logging.Logger; - -public final class ProcessorUtils { - - /** - * Private constructor to hide implicit public one. - */ - private ProcessorUtils() { - - } - - private static final Logger LOGGER = Logger.getLogger(ProcessorUtils.class.getName()); - - /** - * Gets the set of classes contained within a {@link ClassLoader}. The set - * returned will not be null, but could be empty. - * - * @param classLoader the classloader to get the classes from. - * @return the set of classes managed by the classloader. - */ - @SuppressWarnings("unchecked") - public static Set> getClassesFromLoader(ClassLoader classLoader) { - Set> classes = new HashSet<>(); - try { - Field classesField = ClassLoader.class.getDeclaredField("classes"); - classesField.setAccessible(true); - classes = new HashSet<>((Vector>) classesField.get(classLoader)); - classesField.setAccessible(false); - } catch (Exception ex) { - LOGGER.log(Level.WARNING, "Unable to get classes from classloader.", ex); - } - return classes; - } -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/OpenApiApplication.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/OpenApiApplication.java index 4f0c4fd7d76..0b02a549437 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/OpenApiApplication.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/OpenApiApplication.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.rest.app; import static fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication.OPEN_API_APPLICATION_PATH; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java index 9d4bb359912..7dc18968594 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java @@ -1,7 +1,45 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.rest.app.provider; -import java.util.Arrays; -import java.util.List; +import java.util.Map.Entry; import java.util.function.Function; import java.util.stream.Collectors; @@ -9,6 +47,7 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator.Feature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -16,72 +55,10 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; -import org.eclipse.microprofile.openapi.models.Components; -import org.eclipse.microprofile.openapi.models.ExternalDocumentation; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.Operation; -import org.eclipse.microprofile.openapi.models.PathItem; -import org.eclipse.microprofile.openapi.models.Paths; -import org.eclipse.microprofile.openapi.models.callbacks.Callback; -import org.eclipse.microprofile.openapi.models.examples.Example; -import org.eclipse.microprofile.openapi.models.headers.Header; -import org.eclipse.microprofile.openapi.models.info.Contact; -import org.eclipse.microprofile.openapi.models.info.Info; -import org.eclipse.microprofile.openapi.models.info.License; -import org.eclipse.microprofile.openapi.models.links.Link; -import org.eclipse.microprofile.openapi.models.media.Content; -import org.eclipse.microprofile.openapi.models.media.Discriminator; -import org.eclipse.microprofile.openapi.models.media.Encoding; -import org.eclipse.microprofile.openapi.models.media.MediaType; -import org.eclipse.microprofile.openapi.models.media.Schema; -import org.eclipse.microprofile.openapi.models.media.XML; -import org.eclipse.microprofile.openapi.models.parameters.Parameter; -import org.eclipse.microprofile.openapi.models.parameters.RequestBody; -import org.eclipse.microprofile.openapi.models.responses.APIResponse; -import org.eclipse.microprofile.openapi.models.responses.APIResponses; -import org.eclipse.microprofile.openapi.models.security.OAuthFlow; -import org.eclipse.microprofile.openapi.models.security.OAuthFlows; -import org.eclipse.microprofile.openapi.models.security.Scopes; -import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; -import org.eclipse.microprofile.openapi.models.security.SecurityScheme; -import org.eclipse.microprofile.openapi.models.servers.Server; -import org.eclipse.microprofile.openapi.models.servers.ServerVariable; -import org.eclipse.microprofile.openapi.models.servers.ServerVariables; -import org.eclipse.microprofile.openapi.models.tags.Tag; +import org.eclipse.microprofile.openapi.models.Constructible; -import fish.payara.microprofile.openapi.impl.model.ComponentsImpl; -import fish.payara.microprofile.openapi.impl.model.ExternalDocumentationImpl; -import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; -import fish.payara.microprofile.openapi.impl.model.OperationImpl; -import fish.payara.microprofile.openapi.impl.model.PathItemImpl; -import fish.payara.microprofile.openapi.impl.model.PathsImpl; -import fish.payara.microprofile.openapi.impl.model.callbacks.CallbackImpl; -import fish.payara.microprofile.openapi.impl.model.examples.ExampleImpl; -import fish.payara.microprofile.openapi.impl.model.headers.HeaderImpl; -import fish.payara.microprofile.openapi.impl.model.info.ContactImpl; -import fish.payara.microprofile.openapi.impl.model.info.InfoImpl; -import fish.payara.microprofile.openapi.impl.model.info.LicenseImpl; -import fish.payara.microprofile.openapi.impl.model.links.LinkImpl; -import fish.payara.microprofile.openapi.impl.model.media.ContentImpl; -import fish.payara.microprofile.openapi.impl.model.media.DiscriminatorImpl; -import fish.payara.microprofile.openapi.impl.model.media.EncodingImpl; -import fish.payara.microprofile.openapi.impl.model.media.MediaTypeImpl; -import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; -import fish.payara.microprofile.openapi.impl.model.media.XMLImpl; -import fish.payara.microprofile.openapi.impl.model.parameters.ParameterImpl; -import fish.payara.microprofile.openapi.impl.model.parameters.RequestBodyImpl; -import fish.payara.microprofile.openapi.impl.model.responses.APIResponseImpl; -import fish.payara.microprofile.openapi.impl.model.responses.APIResponsesImpl; -import fish.payara.microprofile.openapi.impl.model.security.OAuthFlowImpl; -import fish.payara.microprofile.openapi.impl.model.security.OAuthFlowsImpl; -import fish.payara.microprofile.openapi.impl.model.security.ScopesImpl; -import fish.payara.microprofile.openapi.impl.model.security.SecurityRequirementImpl; -import fish.payara.microprofile.openapi.impl.model.security.SecuritySchemeImpl; -import fish.payara.microprofile.openapi.impl.model.servers.ServerImpl; -import fish.payara.microprofile.openapi.impl.model.servers.ServerVariableImpl; -import fish.payara.microprofile.openapi.impl.model.servers.ServerVariablesImpl; -import fish.payara.microprofile.openapi.impl.model.tags.TagImpl; -import fish.payara.microprofile.openapi.impl.model.util.ExtensionsMixin; +import fish.payara.microprofile.openapi.impl.model.OASFactoryResolverImpl; +import fish.payara.microprofile.openapi.impl.rest.app.provider.mixin.ExtensionsMixin; public final class ObjectMapperFactory { @@ -98,58 +75,30 @@ public static ObjectMapper createYaml() { return create(factory); } - public static ObjectMapper create(JsonFactory factory) { + @SuppressWarnings("unchecked") + public static ObjectMapper create(JsonFactory factory) { ObjectMapper mapper = new ObjectMapper(factory); mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); mapper.setSerializationInclusion(Include.NON_DEFAULT); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); + mapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.enable(Feature.WRITE_BIGDECIMAL_AS_PLAIN); // Create mapping module SimpleModule module = new SimpleModule(); - module.addAbstractTypeMapping(Callback.class, CallbackImpl.class); - module.addAbstractTypeMapping(Example.class, ExampleImpl.class); - module.addAbstractTypeMapping(Header.class, HeaderImpl.class); - module.addAbstractTypeMapping(Info.class, InfoImpl.class); - module.addAbstractTypeMapping(Contact.class, ContactImpl.class); - module.addAbstractTypeMapping(License.class, LicenseImpl.class); - module.addAbstractTypeMapping(Link.class, LinkImpl.class); - module.addAbstractTypeMapping(Content.class, ContentImpl.class); - module.addAbstractTypeMapping(Discriminator.class, DiscriminatorImpl.class); - module.addAbstractTypeMapping(Encoding.class, EncodingImpl.class); - module.addAbstractTypeMapping(MediaType.class, MediaTypeImpl.class); - module.addAbstractTypeMapping(Schema.class, SchemaImpl.class); - module.addAbstractTypeMapping(XML.class, XMLImpl.class); - module.addAbstractTypeMapping(Parameter.class, ParameterImpl.class); - module.addAbstractTypeMapping(RequestBody.class, RequestBodyImpl.class); - module.addAbstractTypeMapping(APIResponse.class, APIResponseImpl.class); - module.addAbstractTypeMapping(APIResponses.class, APIResponsesImpl.class); - module.addAbstractTypeMapping(OAuthFlow.class, OAuthFlowImpl.class); - module.addAbstractTypeMapping(OAuthFlows.class, OAuthFlowsImpl.class); - module.addAbstractTypeMapping(Scopes.class, ScopesImpl.class); - module.addAbstractTypeMapping(SecurityRequirement.class, SecurityRequirementImpl.class); - module.addAbstractTypeMapping(SecurityScheme.class, SecuritySchemeImpl.class); - module.addAbstractTypeMapping(Server.class, ServerImpl.class); - module.addAbstractTypeMapping(ServerVariable.class, ServerVariableImpl.class); - module.addAbstractTypeMapping(ServerVariables.class, ServerVariablesImpl.class); - module.addAbstractTypeMapping(Tag.class, TagImpl.class); - module.addAbstractTypeMapping(Components.class, ComponentsImpl.class); - module.addAbstractTypeMapping(ExternalDocumentation.class, ExternalDocumentationImpl.class); - module.addAbstractTypeMapping(OpenAPI.class, OpenAPIImpl.class); - module.addAbstractTypeMapping(Operation.class, OperationImpl.class); - module.addAbstractTypeMapping(PathItem.class, PathItemImpl.class); - module.addAbstractTypeMapping(Paths.class, PathsImpl.class); + // Configure the implementation of each class + for (Entry, Class> entry : OASFactoryResolverImpl.MODEL_MAP + .entrySet()) { + module.addAbstractTypeMapping((Class) entry.getKey(), (Class) entry.getValue()); + } - List> mixinTargets = Arrays.asList(APIResponse.class, Callback.class, Components.class, Contact.class, - Encoding.class, Example.class, ExternalDocumentation.class, Header.class, Info.class, License.class, - Link.class, MediaType.class, OAuthFlow.class, OAuthFlows.class, OpenAPI.class, Operation.class, - Parameter.class, PathItem.class, Paths.class, RequestBody.class, Scopes.class, SecurityScheme.class, - Server.class, ServerVariable.class, ServerVariables.class, Tag.class, XML.class, Schema.class); - mapper.setMixIns( - mixinTargets.stream().collect(Collectors.toMap(Function.identity(), c -> ExtensionsMixin.class))); + // Configure the mixins for each type + mapper.setMixIns(OASFactoryResolverImpl.MODEL_MAP.keySet().stream() + .collect(Collectors.toMap(Function.identity(), c -> ExtensionsMixin.class))); mapper.registerModule(module); diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/QueryFormatFilter.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/QueryFormatFilter.java index f355b7f5273..7cedd4d8163 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/QueryFormatFilter.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/QueryFormatFilter.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.rest.app.provider; import static fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication.APPLICATION_YAML; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/mixin/ExtensionsMixin.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/mixin/ExtensionsMixin.java new file mode 100644 index 00000000000..4d8f4d18c0e --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/mixin/ExtensionsMixin.java @@ -0,0 +1,72 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.openapi.impl.rest.app.provider.mixin; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.eclipse.microprofile.openapi.models.Paths; +import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; + +public abstract class ExtensionsMixin { + + @JsonProperty("enum") + public abstract void getEnumeration(); + + @JsonProperty("default") + public abstract void getDefaultValue(); + + @JsonProperty("$ref") + public abstract void getRef(); + + @JsonIgnore + public abstract void setAdditionalProperties(Boolean additionalProperties); + + @JsonInclude(Include.NON_EMPTY) + public abstract List getSecurity(); + + @JsonInclude(Include.ALWAYS) + public abstract Paths getPaths(); + +} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java index f40ca10d488..470fed1111e 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.rest.app.provider.writer; import java.io.IOException; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/JsonWriter.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/JsonWriter.java index 59550d30c8a..39dad6635d4 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/JsonWriter.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/JsonWriter.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.rest.app.provider.writer; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/YamlWriter.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/YamlWriter.java index 4b8c39bcc4c..d4cc6b1c451 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/YamlWriter.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/YamlWriter.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.rest.app.provider.writer; import static fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication.APPLICATION_YAML; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java index 91900acdc9d..ddd3aed58fe 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java @@ -1,22 +1,88 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.rest.app.service; import static fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication.APPLICATION_YAML; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.Response.Status.FORBIDDEN; +import java.io.IOException; +import java.util.logging.Logger; + +import javax.servlet.http.HttpServletResponse; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import org.eclipse.microprofile.openapi.models.OpenAPI; import fish.payara.microprofile.openapi.impl.OpenApiService; +import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; @Path("/") public class OpenApiResource { + private Logger LOGGER = Logger.getLogger(OpenApiResource.class.getName()); + @GET @Produces({ APPLICATION_YAML, APPLICATION_JSON }) - public OpenAPI getResponse() { - return OpenApiService.getInstance().getDocument(); + public Response getResponse(@Context HttpServletResponse response) throws IOException { + + // If the server is disabled, throw an error + if (!OpenApiService.getInstance().isEnabled()) { + response.sendError(FORBIDDEN.getStatusCode(), "OpenAPI Service is disabled."); + } + + // Get the OpenAPI document + OpenAPI document = OpenApiService.getInstance().getDocument(); + + // If there are none, return an empty OpenAPI document + if (document == null) { + LOGGER.info("No document found."); + return Response.status(Status.NOT_FOUND).entity(new OpenAPIImpl()).build(); + } + + // Return the document + return Response.ok(document).build(); } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/init/OpenApiServletContainerInitializer.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/init/OpenApiServletContainerInitializer.java index 9ed37de7f31..b36c3e8238f 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/init/OpenApiServletContainerInitializer.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/init/OpenApiServletContainerInitializer.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.rest.init; import static fish.payara.microprofile.openapi.impl.rest.app.OpenApiApplication.OPEN_API_APPLICATION_PATH; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/AnnotationVisitor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/AnnotationVisitor.java deleted file mode 100644 index 109f2a9ed72..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/AnnotationVisitor.java +++ /dev/null @@ -1,498 +0,0 @@ -package fish.payara.microprofile.openapi.impl.visitor; - -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Parameter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.eclipse.microprofile.openapi.annotations.ExternalDocumentation; -import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; -import org.eclipse.microprofile.openapi.annotations.Operation; -import org.eclipse.microprofile.openapi.annotations.callbacks.Callback; -import org.eclipse.microprofile.openapi.annotations.callbacks.Callbacks; -import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn; -import org.eclipse.microprofile.openapi.annotations.extensions.Extension; -import org.eclipse.microprofile.openapi.annotations.media.Schema; -import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; -import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; -import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; -import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirements; -import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; -import org.eclipse.microprofile.openapi.annotations.servers.Server; -import org.eclipse.microprofile.openapi.annotations.servers.Servers; -import org.eclipse.microprofile.openapi.annotations.tags.Tag; -import org.eclipse.microprofile.openapi.annotations.tags.Tags; -import org.eclipse.microprofile.openapi.models.Components; -import org.eclipse.microprofile.openapi.models.Extensible; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem; -import org.eclipse.microprofile.openapi.models.media.MediaType; -import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; -import org.eclipse.microprofile.openapi.models.responses.APIResponses; - -import fish.payara.microprofile.openapi.api.visitor.ApiContext; -import fish.payara.microprofile.openapi.api.visitor.ApiVisitor; -import fish.payara.microprofile.openapi.impl.model.ExtensibleImpl; -import fish.payara.microprofile.openapi.impl.model.ExternalDocumentationImpl; -import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; -import fish.payara.microprofile.openapi.impl.model.OperationImpl; -import fish.payara.microprofile.openapi.impl.model.callbacks.CallbackImpl; -import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; -import fish.payara.microprofile.openapi.impl.model.parameters.ParameterImpl; -import fish.payara.microprofile.openapi.impl.model.parameters.RequestBodyImpl; -import fish.payara.microprofile.openapi.impl.model.responses.APIResponseImpl; -import fish.payara.microprofile.openapi.impl.model.security.SecurityRequirementImpl; -import fish.payara.microprofile.openapi.impl.model.security.SecuritySchemeImpl; -import fish.payara.microprofile.openapi.impl.model.servers.ServerImpl; -import fish.payara.microprofile.openapi.impl.model.tags.TagImpl; -import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; - -public class AnnotationVisitor implements ApiVisitor { - - @Override - public void visitClass(Class clazz, ApiContext context) { - OpenAPI api = context.getApi(); - // Handle @OpenApiDefinition - handleOpenAPIAnnotation(api, clazz); - // Handle @SecurityScheme - handleSecuritySchemeAnnotation(clazz, api.getComponents()); - // Handle @Extension - handleExtensionAnnotation(clazz, context.getApi()); - } - - @Override - public void visitMethod(Method method, ApiContext context) { - if (context.getPath() == null) { - return; - } - org.eclipse.microprofile.openapi.models.Operation operation = ModelUtils.findOperation(context.getApi(), method, - context.getPath()); - - // Handle @SecurityScheme - handleSecuritySchemeAnnotation(method, context.getApi().getComponents()); - - if (operation != null) { - // Handle @Operation - handleOperationAnnotation(method, operation, context.getApi().getPaths().get(context.getPath())); - // Handle @ExternalDocumentation - handleExternalDocumentationAnnotation(method, operation); - // Handle @Servers - handleServersAnnotation(method, operation); - // Handle @Tags - handleTagsAnnotation(method, operation, context.getApi()); - // Handle @APIResponse - handleAPIResponseAnnotation(method, operation, context.getApi().getComponents().getSchemas()); - // Handle @SecurityRequirement - handleSecurityRequirementAnnotation(method, operation); - // Handle @Parameter - handleParameterAnnotation(method, operation); - // Handle @Callback - handleCallbacksAnnotation(method, operation); - // Handle @Extension - handleExtensionAnnotation(method, operation); - } - } - - @Override - public void visitField(Field field, ApiContext context) { - // Handle @Schema - handleSchemaAnnotation(field, context.getApi()); - } - - @Override - public void visitParameter(Parameter parameter, ApiContext context) { - org.eclipse.microprofile.openapi.models.Operation operation = ModelUtils.findOperation(context.getApi(), - (Method) parameter.getDeclaringExecutable(), context.getPath()); - - if (operation != null) { - // Handle @RequestBody - handleRequestBodyAnnotation(parameter, operation, context.getApi().getComponents().getSchemas()); - // Handle @Schema - handleSchemaAnnotation(parameter, operation, context.getApi().getComponents().getSchemas()); - // Handle @Parameter - handleParameterAnnotation(parameter, operation); - } - } - - private void handleParameterAnnotation(Parameter parameter, - org.eclipse.microprofile.openapi.models.Operation operation) { - if (parameter.isAnnotationPresent(org.eclipse.microprofile.openapi.annotations.parameters.Parameter.class)) { - org.eclipse.microprofile.openapi.annotations.parameters.Parameter annotation = parameter - .getDeclaredAnnotation(org.eclipse.microprofile.openapi.annotations.parameters.Parameter.class); - for (org.eclipse.microprofile.openapi.models.parameters.Parameter param : operation.getParameters()) { - if (param.getName().equals(ModelUtils.getParameterName(parameter))) { - ParameterImpl.merge(annotation, param, false, null); - } - } - } - } - - private void handleParameterAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation) { - if (method.isAnnotationPresent(org.eclipse.microprofile.openapi.annotations.parameters.Parameter.class)) { - org.eclipse.microprofile.openapi.annotations.parameters.Parameter annotation = method - .getDeclaredAnnotation(org.eclipse.microprofile.openapi.annotations.parameters.Parameter.class); - // If the parameter reference is valid - if (annotation.name() != null && !annotation.name().isEmpty()) { - // Get all parameters with the same name - List matchingMethodParameters = Arrays.asList(method.getParameters()).stream() - .filter(x -> ModelUtils.getParameterName(x).equals(annotation.name())) - .collect(Collectors.toList()); - // If there is more than one match, filter it further - if (matchingMethodParameters.size() > 1 && annotation.in() != null - && annotation.in() != ParameterIn.DEFAULT) { - // Remove all parameters of the wrong input type - matchingMethodParameters - .removeIf(x -> ModelUtils.getParameterType(x) != In.valueOf(annotation.in().name())); - } - // If there's only one matching parameter, handle it immediately - Parameter matchingMethodParam = matchingMethodParameters.get(0); - // Find the matching operation parameter - for (org.eclipse.microprofile.openapi.models.parameters.Parameter operationParam : operation - .getParameters()) { - if (operationParam.getName().equals(ModelUtils.getParameterName(matchingMethodParam))) { - ParameterImpl.merge(annotation, operationParam, false, null); - } - } - } - } - } - - private void handleSchemaAnnotation(Field field, OpenAPI api) { - Class clazz = field.getDeclaringClass(); - if (clazz.isAnnotationPresent(Schema.class)) { - Schema annotation = clazz.getDeclaredAnnotation(Schema.class); - - // Get the actual schema name - String schemaName = annotation.name(); - if (schemaName == null || schemaName.isEmpty()) { - schemaName = clazz.getSimpleName(); - } - - // Find and correct the name of the correct schema - updateSchemaName(api.getComponents(), clazz.getSimpleName(), schemaName); - org.eclipse.microprofile.openapi.models.media.Schema model = api.getComponents().getSchemas() - .get(schemaName); - if (model == null) { - model = new SchemaImpl(); - api.getComponents().addSchema(schemaName, model); - } - SchemaImpl.merge(annotation, model, true, api.getComponents().getSchemas()); - - // Start parsing the field in the same manner - if (field.isAnnotationPresent(Schema.class) && !Modifier.isTransient(field.getModifiers())) { - annotation = field.getDeclaredAnnotation(Schema.class); - - org.eclipse.microprofile.openapi.models.media.Schema property = new SchemaImpl(); - SchemaImpl.merge(annotation, property, true, api.getComponents().getSchemas()); - if (property.getRef() == null) { - property.setType(ModelUtils.getSchemaType(field.getType())); - } - - schemaName = annotation.name(); - if (schemaName == null || schemaName.isEmpty()) { - schemaName = field.getName(); - } - - model.addProperty(schemaName, property); - } - } - } - - private void handleSchemaAnnotation(Parameter parameter, - org.eclipse.microprofile.openapi.models.Operation operation, - Map currentSchemas) { - if (parameter.isAnnotationPresent(Schema.class)) { - Schema annotation = parameter.getDeclaredAnnotation(Schema.class); - // Check if it's a request body - if (ModelUtils.isRequestBody(parameter)) { - // Insert the schema to every request body media type - for (MediaType mediaType : operation.getRequestBody().getContent().values()) { - SchemaImpl.merge(annotation, mediaType.getSchema(), true, currentSchemas); - if (annotation.ref() != null && !annotation.ref().isEmpty()) { - mediaType.setSchema(new SchemaImpl().ref(annotation.ref())); - } - } - } else if (ModelUtils.getParameterType(parameter) != null) { - for (org.eclipse.microprofile.openapi.models.parameters.Parameter param : operation.getParameters()) { - if (param.getName().equals(ModelUtils.getParameterName(parameter))) { - SchemaImpl.merge(annotation, param.getSchema(), true, currentSchemas); - if (annotation.ref() != null && !annotation.ref().isEmpty()) { - param.setSchema(new SchemaImpl().ref(annotation.ref())); - } - } - } - } - } - } - - private void updateSchemaName(Components components, String oldName, String newName) { - if (oldName.equals(newName)) { - return; - } - org.eclipse.microprofile.openapi.models.media.Schema schema = components.getSchemas().get(oldName); - if (schema == null) { - return; - } - components.getSchemas().remove(oldName); - components.addSchema(newName, schema); - } - - private void handleOpenAPIAnnotation(OpenAPI api, Class clazz) { - if (clazz.isAnnotationPresent(OpenAPIDefinition.class)) { - OpenAPIDefinition annotation = clazz.getDeclaredAnnotation(OpenAPIDefinition.class); - OpenAPIImpl.merge(annotation, api, true); - } - } - - private void handleOperationAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation, - PathItem pathItem) { - if (method.isAnnotationPresent(Operation.class)) { - Operation annotation = method.getDeclaredAnnotation(Operation.class); - OperationImpl.merge(annotation, operation, true); - if (annotation.hidden()) { - ModelUtils.removeOperation(pathItem, operation); - } - } - } - - private void handleExternalDocumentationAnnotation(Method method, - org.eclipse.microprofile.openapi.models.Operation operation) { - ExternalDocumentation annotation = null; - if (method.isAnnotationPresent(ExternalDocumentation.class)) { - annotation = method.getDeclaredAnnotation(ExternalDocumentation.class); - } else if (method.getDeclaringClass().isAnnotationPresent(ExternalDocumentation.class)) { - annotation = method.getDeclaringClass().getDeclaredAnnotation(ExternalDocumentation.class); - } else { - return; - } - operation.setExternalDocs(new ExternalDocumentationImpl()); - ExternalDocumentationImpl.merge(annotation, operation.getExternalDocs(), true); - if (operation.getExternalDocs().getUrl() == null) { - operation.setExternalDocs(null); - } - } - - private void handleServersAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation) { - List declaredServers = new ArrayList<>(); - if (method.isAnnotationPresent(Server.class) || method.isAnnotationPresent(Servers.class)) { - if (method.isAnnotationPresent(Server.class)) { - Server annotation = method.getDeclaredAnnotation(Server.class); - declaredServers.add(annotation); - } - if (method.isAnnotationPresent(Servers.class)) { - Servers annotation = method.getDeclaredAnnotation(Servers.class); - for (Server server : annotation.value()) { - declaredServers.add(server); - } - } - } else if (method.getDeclaringClass().isAnnotationPresent(Server.class) - || method.getDeclaringClass().isAnnotationPresent(Servers.class)) { - if (method.getDeclaringClass().isAnnotationPresent(Server.class)) { - Server annotation = method.getDeclaringClass().getDeclaredAnnotation(Server.class); - declaredServers.add(annotation); - } - if (method.getDeclaringClass().isAnnotationPresent(Servers.class)) { - Servers annotation = method.getDeclaringClass().getDeclaredAnnotation(Servers.class); - for (Server server : annotation.value()) { - declaredServers.add(server); - } - } - } - - for (Server annotation : declaredServers) { - org.eclipse.microprofile.openapi.models.servers.Server server = new ServerImpl(); - ServerImpl.merge(annotation, server, true); - operation.addServer(server); - } - } - - private void handleCallbacksAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation) { - List declaredCallbacks = new ArrayList<>(); - if (method.isAnnotationPresent(Callback.class) || method.isAnnotationPresent(Callbacks.class)) { - if (method.isAnnotationPresent(Callback.class)) { - Callback annotation = method.getDeclaredAnnotation(Callback.class); - declaredCallbacks.add(annotation); - } - if (method.isAnnotationPresent(Callbacks.class)) { - Callbacks annotation = method.getDeclaredAnnotation(Callbacks.class); - for (Callback callback : annotation.value()) { - declaredCallbacks.add(callback); - } - } - } else if (method.getDeclaringClass().isAnnotationPresent(Callback.class) - || method.getDeclaringClass().isAnnotationPresent(Callbacks.class)) { - if (method.getDeclaringClass().isAnnotationPresent(Callback.class)) { - Callback annotation = method.getDeclaringClass().getDeclaredAnnotation(Callback.class); - declaredCallbacks.add(annotation); - } - if (method.getDeclaringClass().isAnnotationPresent(Callbacks.class)) { - Callbacks annotation = method.getDeclaringClass().getDeclaredAnnotation(Callbacks.class); - for (Callback callback : annotation.value()) { - declaredCallbacks.add(callback); - } - } - } - - for (Callback annotation : declaredCallbacks) { - String callbackName = annotation.name(); - if (callbackName != null && !callbackName.isEmpty()) { - org.eclipse.microprofile.openapi.models.callbacks.Callback model = operation.getCallbacks() - .getOrDefault(callbackName, new CallbackImpl()); - CallbackImpl.merge(annotation, model, true); - operation.getCallbacks().put(callbackName, model); - } - } - } - - private void handleTagsAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation, - OpenAPI api) { - List annotations = new ArrayList<>(); - if (method.isAnnotationPresent(Tag.class) || method.isAnnotationPresent(Tags.class)) { - if (method.isAnnotationPresent(Tag.class)) { - Tag annotation = method.getDeclaredAnnotation(Tag.class); - annotations.add(annotation); - } - if (method.isAnnotationPresent(Tags.class)) { - Tags annotation = method.getDeclaredAnnotation(Tags.class); - for (Tag tag : annotation.value()) { - annotations.add(tag); - } - for (String ref : annotation.refs()) { - if (ref != null && !ref.isEmpty()) { - operation.addTag(ref); - } - } - } - } else if (method.getDeclaringClass().isAnnotationPresent(Tag.class) - || method.getDeclaringClass().isAnnotationPresent(Tags.class)) { - if (method.getDeclaringClass().isAnnotationPresent(Tag.class)) { - Tag annotation = method.getDeclaringClass().getDeclaredAnnotation(Tag.class); - annotations.add(annotation); - } - if (method.getDeclaringClass().isAnnotationPresent(Tags.class)) { - Tags annotation = method.getDeclaringClass().getDeclaredAnnotation(Tags.class); - for (Tag tag : annotation.value()) { - annotations.add(tag); - } - for (String ref : annotation.refs()) { - if (ref != null && !ref.isEmpty()) { - operation.addTag(ref); - } - } - } - } - - for (Tag annotation : annotations) { - TagImpl.merge(annotation, operation, true, api.getTags()); - } - } - - private void handleRequestBodyAnnotation(Parameter parameter, - org.eclipse.microprofile.openapi.models.Operation operation, - Map currentSchemas) { - if (parameter.isAnnotationPresent(RequestBody.class)) { - RequestBody annotation = parameter.getDeclaredAnnotation(RequestBody.class); - if (operation.getRequestBody() == null) { - operation.setRequestBody(new RequestBodyImpl()); - } - RequestBodyImpl.merge(annotation, operation.getRequestBody(), true, currentSchemas); - } else if (parameter.getDeclaringExecutable().isAnnotationPresent(RequestBody.class)) { - RequestBody annotation = parameter.getDeclaringExecutable().getDeclaredAnnotation(RequestBody.class); - if (operation.getRequestBody() == null) { - operation.setRequestBody(new RequestBodyImpl()); - } - RequestBodyImpl.merge(annotation, operation.getRequestBody(), true, currentSchemas); - } - } - - private void handleAPIResponseAnnotation(Method method, org.eclipse.microprofile.openapi.models.Operation operation, - Map currentSchemas) { - if (method.isAnnotationPresent(APIResponse.class)) { - APIResponse annotation = method.getDeclaredAnnotation(APIResponse.class); - - // Get the response name - String responseName = annotation.responseCode(); - if (responseName == null || responseName.isEmpty()) { - responseName = APIResponses.DEFAULT; - } - - org.eclipse.microprofile.openapi.models.responses.APIResponse response = operation.getResponses() - .getOrDefault(responseName, new APIResponseImpl()); - APIResponseImpl.merge(annotation, response, true, currentSchemas); - - operation.getResponses().addApiResponse(responseName, response); - } - } - - private void handleSecuritySchemeAnnotation(AnnotatedElement element, Components components) { - if (element.isAnnotationPresent(SecurityScheme.class)) { - SecurityScheme annotation = element.getDeclaredAnnotation(SecurityScheme.class); - - org.eclipse.microprofile.openapi.models.security.SecurityScheme model = new SecuritySchemeImpl(); - SecuritySchemeImpl.merge(annotation, model, true); - - if (annotation.securitySchemeName() != null && !annotation.securitySchemeName().isEmpty()) { - components.addSecurityScheme(annotation.securitySchemeName(), model); - } - } - } - - private void handleSecurityRequirementAnnotation(Method method, - org.eclipse.microprofile.openapi.models.Operation operation) { - List requirements = new ArrayList<>(); - - // If the method is annotated - if (method.isAnnotationPresent(SecurityRequirement.class) - || method.isAnnotationPresent(SecurityRequirements.class)) { - if (method.isAnnotationPresent(SecurityRequirement.class)) { - SecurityRequirement annotation = method.getDeclaredAnnotation(SecurityRequirement.class); - requirements.add(annotation); - } - if (method.isAnnotationPresent(SecurityRequirements.class)) { - SecurityRequirements annotation = method.getDeclaredAnnotation(SecurityRequirements.class); - for (SecurityRequirement requirement : annotation.value()) { - requirements.add(requirement); - } - } - // Else if the class is annotated, inherit it - } else if (method.getDeclaringClass().isAnnotationPresent(SecurityRequirement.class) - || method.getDeclaringClass().isAnnotationPresent(SecurityRequirements.class)) { - if (method.getDeclaringClass().isAnnotationPresent(SecurityRequirement.class)) { - SecurityRequirement annotation = method.getDeclaringClass() - .getDeclaredAnnotation(SecurityRequirement.class); - requirements.add(annotation); - } - if (method.getDeclaringClass().isAnnotationPresent(SecurityRequirements.class)) { - SecurityRequirements annotation = method.getDeclaringClass() - .getDeclaredAnnotation(SecurityRequirements.class); - for (SecurityRequirement requirement : annotation.value()) { - requirements.add(requirement); - } - } - } - - // Process all the elements to be added - for (SecurityRequirement annotation : requirements) { - org.eclipse.microprofile.openapi.models.security.SecurityRequirement model = new SecurityRequirementImpl(); - SecurityRequirementImpl.merge(annotation, model, true); - - if (annotation.name() != null && !annotation.name().isEmpty()) { - operation.addSecurityRequirement(model); - } - } - } - - private void handleExtensionAnnotation(AnnotatedElement element, Extensible extensible) { - if (element.isAnnotationPresent(Extension.class)) { - Extension annotation = element.getDeclaredAnnotation(Extension.class); - ExtensibleImpl.merge(annotation, extensible, true); - } - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/ApplicationVisitor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/ApplicationVisitor.java deleted file mode 100644 index fd7fa474ab0..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/ApplicationVisitor.java +++ /dev/null @@ -1,279 +0,0 @@ -package fish.payara.microprofile.openapi.impl.visitor; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.ws.rs.Consumes; -import javax.ws.rs.CookieParam; -import javax.ws.rs.FormParam; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; - -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.Operation; -import org.eclipse.microprofile.openapi.models.PathItem; -import org.eclipse.microprofile.openapi.models.Paths; -import org.eclipse.microprofile.openapi.models.Reference; -import org.eclipse.microprofile.openapi.models.media.MediaType; -import org.eclipse.microprofile.openapi.models.media.Schema; -import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; -import org.eclipse.microprofile.openapi.models.parameters.Parameter; -import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; -import org.eclipse.microprofile.openapi.models.parameters.RequestBody; -import org.eclipse.microprofile.openapi.models.responses.APIResponse; -import org.eclipse.microprofile.openapi.models.responses.APIResponses; - -import fish.payara.microprofile.openapi.api.visitor.ApiContext; -import fish.payara.microprofile.openapi.api.visitor.ApiVisitor; -import fish.payara.microprofile.openapi.impl.model.PathItemImpl; -import fish.payara.microprofile.openapi.impl.model.PathsImpl; -import fish.payara.microprofile.openapi.impl.model.media.MediaTypeImpl; -import fish.payara.microprofile.openapi.impl.model.media.SchemaImpl; -import fish.payara.microprofile.openapi.impl.model.parameters.ParameterImpl; -import fish.payara.microprofile.openapi.impl.model.parameters.RequestBodyImpl; -import fish.payara.microprofile.openapi.impl.model.responses.APIResponseImpl; -import fish.payara.microprofile.openapi.impl.model.responses.APIResponsesImpl; -import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; - -public class ApplicationVisitor implements ApiVisitor { - - @Override - public void visitClass(Class clazz, ApiContext context) { - } - - @Override - public void visitMethod(Method method, ApiContext context) { - // The application visitor doesn't care about non contextual objects - if (context.getPath() == null) { - return; - } - // Create the paths if they're not already created - Paths paths = context.getApi().getPaths(); - if (paths == null) { - paths = new PathsImpl(); - context.getApi().setPaths(paths); - } - - // If the path hasn't been added to the model, do so now - if (!paths.containsKey(context.getPath())) { - paths.put(context.getPath(), new PathItemImpl()); - } - PathItem pathItem = paths.get(context.getPath()); - - // Set the HTTP method type - Operation operation = ModelUtils.getOrCreateOperation(pathItem, ModelUtils.getHttpMethod(method)); - operation.setOperationId(method.getName()); - - // Add the default response - APIResponses responses = operation.getResponses(); - if (responses == null) { - responses = new APIResponsesImpl(); - operation.setResponses(responses); - } - insertDefaultResponse(context.getApi(), responses, method); - - insertDefaultRequestBody(context.getApi(), operation, method); - } - - @Override - public void visitField(Field field, ApiContext context) { - // Ignore fields - } - - @Override - public void visitParameter(java.lang.reflect.Parameter parameter, ApiContext context) { - // The application visitor doesn't care about non contextual objects - if (context.getPath() == null) { - return; - } - if (ModelUtils.getParameterType(parameter) == null) { - return; - } - // Create a jersey parameter modelling the method parameter - - Parameter newParam = new ParameterImpl(); - String sourceName = null; - if (parameter.isAnnotationPresent(PathParam.class)) { - newParam.setIn(In.PATH); - newParam.setRequired(true); - sourceName = parameter.getDeclaredAnnotation(PathParam.class).value(); - } else if (parameter.isAnnotationPresent(QueryParam.class)) { - newParam.setIn(In.QUERY); - newParam.setRequired(false); - sourceName = parameter.getDeclaredAnnotation(QueryParam.class).value(); - } else if (parameter.isAnnotationPresent(HeaderParam.class)) { - newParam.setIn(In.HEADER); - newParam.setRequired(false); - sourceName = parameter.getDeclaredAnnotation(HeaderParam.class).value(); - } else if (parameter.isAnnotationPresent(CookieParam.class)) { - newParam.setIn(In.COOKIE); - newParam.setRequired(false); - sourceName = parameter.getDeclaredAnnotation(CookieParam.class).value(); - } - newParam.setName(sourceName); - newParam.setSchema(new SchemaImpl().type(ModelUtils.getSchemaType(parameter.getType()))); - Operation operation = ModelUtils.findOperation(context.getApi(), (Method) parameter.getDeclaringExecutable(), - context.getPath()); - operation.addParameter(newParam); - } - - protected RequestBody insertDefaultRequestBody(OpenAPI api, Operation operation, Method method) { - RequestBody requestBody = new RequestBodyImpl(); - - // Get the return type of the variable - Class returnType = null; - // If form parameters are provided, this stores the general schema type for all - // of them - SchemaType formSchemaType = null; - for (java.lang.reflect.Parameter methodParam : method.getParameters()) { - if (ModelUtils.isRequestBody(methodParam)) { - returnType = methodParam.getType(); - break; - } - if (methodParam.isAnnotationPresent(FormParam.class)) { - returnType = methodParam.getType(); - formSchemaType = ModelUtils.getParentSchemaType(formSchemaType, - ModelUtils.getSchemaType(methodParam.getType())); - } - } - if (returnType == null) { - return null; - } - - // If there is an @Consumes, create a media type for each - if (method.isAnnotationPresent(Consumes.class)) { - String[] value = method.getDeclaredAnnotation(Consumes.class).value(); - for (String produceType : value) { - MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, returnType)); - requestBody.getContent().addMediaType(getContentType(produceType), mediaType); - } - } else { - // No @Consumes, create a wildcard - MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, returnType)); - requestBody.getContent().addMediaType(getContentType("*/*"), mediaType); - } - - // If there are form parameters, reconfigure the types - if (formSchemaType != null) { - for (MediaType mediaType : requestBody.getContent().values()) { - mediaType.getSchema().setType(formSchemaType); - } - } - - operation.setRequestBody(requestBody); - return requestBody; - } - - /** - * Creates a new {@link APIResponse} to model the default response of a - * {@link Method}, and inserts it into the {@link APIResponses}. - * - * @param responses the {@link APIResponses} to add the default response to. - * @param method the {@link Method} to model the default response on. - * @return the newly created {@link APIResponse}. - */ - protected APIResponse insertDefaultResponse(OpenAPI api, APIResponses responses, Method method) { - APIResponse defaultResponse = new APIResponseImpl(); - defaultResponse.setDescription("Default Response."); - - // Check if there are produce types specified - if (method.isAnnotationPresent(Produces.class)) { - // If there is an @Produces, get the value - String[] value = method.getDeclaredAnnotation(Produces.class).value(); - for (String produceType : value) { - // For each @Produces type, create a media type and add it to the response - // content - MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, method.getReturnType())); - defaultResponse.getContent().addMediaType(getContentType(produceType), mediaType); - } - } else { - // No @Produces, so create a wildcard response - MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, method.getReturnType())); - defaultResponse.getContent().addMediaType(getContentType("*/*"), mediaType); - } - - // Add the default response - responses.addApiResponse(APIResponses.DEFAULT, defaultResponse); - return defaultResponse; - } - - protected String getContentType(String name) { - try { - javax.ws.rs.core.MediaType mediaType = javax.ws.rs.core.MediaType.valueOf(name); - if (mediaType != null) { - return mediaType.toString(); - } - } catch (IllegalArgumentException ex) { - } - return javax.ws.rs.core.MediaType.WILDCARD; - } - - protected Schema createSchema(OpenAPI api, Class type) { - Schema schema = new SchemaImpl(); - SchemaType schemaType = ModelUtils.getSchemaType(type); - schema.setType(schemaType); - - // Set the subtype if it's an array (for example an array of ints) - if (schemaType == SchemaType.ARRAY) { - Class subType = type.getComponentType(); - Schema subSchema = schema; - while (subType != null) { - subSchema.setItems(new SchemaImpl().type(ModelUtils.getSchemaType(subType))); - subSchema = schema.getItems(); - subType = subType.getComponentType(); - } - } - - if (schemaType == SchemaType.OBJECT) { - if (insertObjectReference(api, schema, type)) { - schema.setType(null); - schema.setItems(null); - } - } - return schema; - } - - /** - * Replace the object in the referee with a reference, and create the reference - * in the API. - * - * @param api the OpenAPI object. - * @param referee the object containing the reference. - * @param referenceClass the class of the object being referenced. - * @return if the reference has been created. - */ - protected boolean insertObjectReference(OpenAPI api, Reference referee, Class referenceClass) { - - // If the object is java.lang.Object, exit - if (referenceClass.equals(Object.class)) { - return false; - } - - // Get the schemas - Map schemas = api.getComponents().getSchemas(); - - // Set the reference name - referee.setRef(referenceClass.getSimpleName()); - - if (!schemas.containsKey(referenceClass.getSimpleName())) { - // If the schema type doesn't already exist, create it - Schema schema = new SchemaImpl(); - schemas.put(referenceClass.getSimpleName(), schema); - schema.setType(SchemaType.OBJECT); - Map fields = new LinkedHashMap<>(); - for (Field field : referenceClass.getDeclaredFields()) { - if (!Modifier.isTransient(field.getModifiers())) { - fields.put(field.getName(), createSchema(api, field.getType())); - } - } - schema.setProperties(fields); - } - - return true; - } -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiContext.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiContext.java index 3ba976239bf..0760e46265e 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiContext.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiContext.java @@ -1,6 +1,46 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.visitor; import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; import fish.payara.microprofile.openapi.api.visitor.ApiContext; @@ -8,10 +48,16 @@ public class OpenApiContext implements ApiContext { private final OpenAPI api; private final String path; + private final Operation operation; - public OpenApiContext(OpenAPI api, String path) { + public OpenApiContext(OpenAPI api, String path, Operation operation) { this.api = api; - this.path = normaliseUrl(path); + this.path = path; + this.operation = operation; + } + + public OpenApiContext(OpenAPI api, String path) { + this(api, path, null); } @Override @@ -24,27 +70,9 @@ public String getPath() { return path; } - /** - * Normalises a path string. A normalised path has: - *
    - *
  • no multiple slashes.
  • - *
  • no trailing slash.
  • - *
- * - * @param path the path to be normalised. - */ - private String normaliseUrl(String path) { - if (path == null) { - return null; - } - // Remove multiple slashes - path = path.replaceAll("/+", "/"); - - // Remove trailing slash - if (path.endsWith("/")) { - path = path.substring(0, path.length() - 1); - } - return path; + @Override + public Operation getWorkingOperation() { + return operation; } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java index e3ca5a0aa58..aa7d89ef2b3 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java @@ -1,44 +1,114 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.visitor; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.getHttpMethod; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.normaliseUrl; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.Method; -import java.lang.reflect.Parameter; import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.ws.rs.ApplicationPath; +import javax.ws.rs.Consumes; +import javax.ws.rs.CookieParam; import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.HEAD; +import javax.ws.rs.HeaderParam; import javax.ws.rs.OPTIONS; import javax.ws.rs.PATCH; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; -import javax.ws.rs.core.Application; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import org.eclipse.microprofile.openapi.annotations.ExternalDocumentation; +import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.callbacks.Callback; +import org.eclipse.microprofile.openapi.annotations.callbacks.Callbacks; +import org.eclipse.microprofile.openapi.annotations.extensions.Extension; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirements; +import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; +import org.eclipse.microprofile.openapi.annotations.security.SecuritySchemes; +import org.eclipse.microprofile.openapi.annotations.servers.Server; +import org.eclipse.microprofile.openapi.annotations.servers.Servers; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.eclipse.microprofile.openapi.annotations.tags.Tags; import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; +import fish.payara.microprofile.openapi.api.visitor.ApiContext; import fish.payara.microprofile.openapi.api.visitor.ApiVisitor; +import fish.payara.microprofile.openapi.api.visitor.ApiVisitor.VisitorFunction; import fish.payara.microprofile.openapi.api.visitor.ApiWalker; +/** + * A walker that visits each annotation and passes it to the visitor. + */ public class OpenApiWalker implements ApiWalker { private final OpenAPI api; private final Set> classes; private final Map>> resourceMapping; - public OpenApiWalker(OpenAPI api, Set> classes) { + public OpenApiWalker(OpenAPI api, Set> allowedClasses, Map>> resourceMapping) { this.api = api; - - this.resourceMapping = new HashMap<>(); - generateResourceMapping(classes); - + this.resourceMapping = resourceMapping; this.classes = new TreeSet<>(new Comparator>() { @Override public int compare(Class class1, Class class2) { @@ -57,40 +127,138 @@ public int compare(Class class1, Class class2) { return 1; } }); - this.classes.addAll(classes); + this.classes.addAll(allowedClasses); } @Override public void accept(ApiVisitor visitor) { + // OpenAPI necessary annotations + processAnnotations(OpenAPIDefinition.class, visitor::visitOpenAPI); + processAnnotations(Schema.class, visitor::visitSchema); + + // JAX-RS methods + processAnnotations(GET.class, visitor::visitGET); + processAnnotations(POST.class, visitor::visitPOST); + processAnnotations(PUT.class, visitor::visitPUT); + processAnnotations(DELETE.class, visitor::visitDELETE); + processAnnotations(HEAD.class, visitor::visitHEAD); + processAnnotations(OPTIONS.class, visitor::visitOPTIONS); + processAnnotations(PATCH.class, visitor::visitPATCH); + + // JAX-RS parameters + processAnnotations(QueryParam.class, visitor::visitQueryParam); + processAnnotations(PathParam.class, visitor::visitPathParam); + processAnnotations(HeaderParam.class, visitor::visitHeaderParam); + processAnnotations(CookieParam.class, visitor::visitCookieParam); + processAnnotations(FormParam.class, visitor::visitFormParam); + + // All other OpenAPI annotations + processAnnotations(Schema.class, visitor::visitSchema); + processAnnotations(Server.class, visitor::visitServer, Servers.class, visitor::visitServers); + processAnnotations(Extension.class, visitor::visitExtension); + processAnnotations(Operation.class, visitor::visitOperation); + processAnnotations(Callback.class, visitor::visitCallback, Callbacks.class, visitor::visitCallbacks); + processAnnotations(APIResponse.class, visitor::visitAPIResponse, APIResponses.class, + visitor::visitAPIResponses); + processAnnotations(Parameter.class, visitor::visitParameter); + processAnnotations(ExternalDocumentation.class, visitor::visitExternalDocumentation); + processAnnotations(Tag.class, visitor::visitTag, Tags.class, visitor::visitTags); + processAnnotations(SecurityScheme.class, visitor::visitSecurityScheme, SecuritySchemes.class, + visitor::visitSecuritySchemes); + processAnnotations(SecurityRequirement.class, visitor::visitSecurityRequirement, SecurityRequirements.class, + visitor::visitSecurityRequirements); + + // JAX-RS response types + processAnnotations(Produces.class, visitor::visitProduces); + processAnnotations(Consumes.class, visitor::visitConsumes); + processAnnotations(RequestBody.class, visitor::visitRequestBody); + } + + private void processAnnotations( + Class annotationClass, VisitorFunction annotationFunction, Class altClass, + VisitorFunction altFunction) { for (Class clazz : classes) { - // Visit each class - String path = getResourcePath(clazz); - visitor.visitClass(clazz, new OpenApiContext(api, path)); + processAnnotation(clazz, annotationClass, annotationFunction, altClass, altFunction, + new OpenApiContext(api, getResourcePath(clazz))); + + for (Field field : clazz.getDeclaredFields()) { + processAnnotation(field, annotationClass, annotationFunction, altClass, altFunction, + new OpenApiContext(api, null)); + } for (Method method : clazz.getDeclaredMethods()) { - // Visit each method - path = getResourcePath(method); - method.setAccessible(true); - visitor.visitMethod(method, new OpenApiContext(api, path)); + processAnnotation(method, annotationClass, annotationFunction, altClass, altFunction, + new OpenApiContext(api, getResourcePath(method), getOperation(method))); - // Visit each parameter - for (Parameter parameter : method.getParameters()) { - visitor.visitParameter(parameter, new OpenApiContext(api, path)); + for (java.lang.reflect.Parameter parameter : method.getParameters()) { + processAnnotation(parameter, annotationClass, annotationFunction, altClass, altFunction, + new OpenApiContext(api, getResourcePath(method), getOperation(method))); } } + } + } - // Visit each field - for (Field field : clazz.getDeclaredFields()) { + private void processAnnotations(Class annotationClass, + VisitorFunction function) { + processAnnotations(annotationClass, function, null, null); + } + + @SuppressWarnings("unchecked") + private void processAnnotation( + AnnotatedElement element, Class annotationClass, VisitorFunction annotationFunction, + Class altClass, VisitorFunction altFunction, ApiContext context) { + // If it's just the one annotation class + if (altClass == null) { + // Check the element + if (element.isAnnotationPresent(annotationClass)) { + annotationFunction.apply(element.getDeclaredAnnotation(annotationClass), (E) element, context); + } else if (element instanceof Method + && Method.class.cast(element).getDeclaringClass().isAnnotationPresent(annotationClass)) { + // If the method isn't annotated, inherit the class annotation + if (context.getPath() != null) { + annotationFunction.apply( + Method.class.cast(element).getDeclaringClass().getDeclaredAnnotation(annotationClass), + (E) element, context); + } + } + } else { + // If it's annotated with both + if (element.isAnnotationPresent(annotationClass) || element.isAnnotationPresent(altClass)) { + if (element.isAnnotationPresent(annotationClass)) { + annotationFunction.apply(element.getDeclaredAnnotation(annotationClass), (E) element, context); + } + if (element.isAnnotationPresent(altClass)) { + altFunction.apply(element.getDeclaredAnnotation(altClass), (E) element, context); + } + } else if (element instanceof Method && context.getPath() != null) { + Class declaringClass = Method.class.cast(element).getDeclaringClass(); + if (declaringClass.isAnnotationPresent(annotationClass)) { + annotationFunction.apply(declaringClass.getDeclaredAnnotation(annotationClass), (E) element, + context); + } + if (declaringClass.isAnnotationPresent(altClass)) { + altFunction.apply(declaringClass.getDeclaredAnnotation(altClass), (E) element, context); + } + } + } + } - field.setAccessible(true); - visitor.visitField(field, new OpenApiContext(api, path)); + private org.eclipse.microprofile.openapi.models.Operation getOperation(Method method) { + String path = getResourcePath(method); + if (path != null) { + PathItem pathItem = api.getPaths().get(path); + if (pathItem != null) { + HttpMethod httpMethod = getHttpMethod(method); + return pathItem.readOperationsMap().get(httpMethod); } } + return null; } private String getResourcePath(GenericDeclaration declaration) { + String path = null; if (declaration instanceof Method) { Method method = (Method) declaration; @@ -100,10 +268,10 @@ private String getResourcePath(GenericDeclaration declaration) { || method.isAnnotationPresent(HEAD.class) || method.isAnnotationPresent(OPTIONS.class) || method.isAnnotationPresent(PATCH.class)) { if (method.isAnnotationPresent(Path.class)) { - return getResourcePath(method.getDeclaringClass()) + path = getResourcePath(method.getDeclaringClass()) + "/" + method.getDeclaredAnnotation(Path.class).value(); } else { - return getResourcePath(method.getDeclaringClass()); + path = getResourcePath(method.getDeclaringClass()); } } } @@ -114,39 +282,12 @@ private String getResourcePath(GenericDeclaration declaration) { if (clazz.isAnnotationPresent(Path.class)) { for (String key : resourceMapping.keySet()) { if (resourceMapping.get(key).contains(clazz)) { - return key + clazz.getDeclaredAnnotation(Path.class).value(); + path = key + "/" + clazz.getDeclaredAnnotation(Path.class).value(); } } } } - return null; - } - - private void generateResourceMapping(Set> classList) { - for (Class clazz : classList) { - if (clazz.isAnnotationPresent(ApplicationPath.class) && Application.class.isAssignableFrom(clazz)) { - // Produce the mapping - String key = clazz.getDeclaredAnnotation(ApplicationPath.class).value(); - Set> resourceClasses = new HashSet<>(); - resourceMapping.put(key, resourceClasses); - - try { - Application app = (Application) clazz.newInstance(); - // Add all classes contained in the application - resourceClasses.addAll(app.getClasses()); - } catch (InstantiationException | IllegalAccessException e) { - e.printStackTrace(); - } - } - } - - // If there is one application and it's empty, add all classes - if (resourceMapping.keySet().size() == 1) { - Set> classes = resourceMapping.values().iterator().next(); - if (classes.isEmpty()) { - classes.addAll(classList); - } - } + return normaliseUrl(path); } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/resources/META-INF/services/org.eclipse.microprofile.openapi.spi.OASFactoryResolver b/appserver/payara-appserver-modules/microprofile/openapi/src/main/resources/META-INF/services/org.eclipse.microprofile.openapi.spi.OASFactoryResolver new file mode 100644 index 00000000000..7ddbcad5c53 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/resources/META-INF/services/org.eclipse.microprofile.openapi.spi.OASFactoryResolver @@ -0,0 +1 @@ +fish.payara.microprofile.openapi.impl.model.OASFactoryResolverImpl \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtilsTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtilsTest.java index eaf2d798aa6..95b41c38e1a 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtilsTest.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtilsTest.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.impl.model.util; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java index 3d09ca4f2e3..583125a3181 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java @@ -1,3 +1,42 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.resource.classloader; import java.io.DataInputStream; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/AnnotationProcessedDocument.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/AnnotationProcessedDocument.java deleted file mode 100644 index 7800e3d03b3..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/AnnotationProcessedDocument.java +++ /dev/null @@ -1,30 +0,0 @@ -package fish.payara.microprofile.openapi.resource.rule; - -import static java.util.Arrays.asList; -import static java.util.stream.Collectors.toSet; - -import fish.payara.microprofile.openapi.impl.processor.AnnotationProcessor; -import fish.payara.microprofile.openapi.impl.processor.ApplicationProcessor; -import fish.payara.microprofile.openapi.impl.processor.BaseProcessor; -import fish.payara.microprofile.openapi.resource.classloader.ApplicationClassLoader; -import fish.payara.microprofile.openapi.test.app.TestApplication; -import fish.payara.microprofile.openapi.test.app.annotation.OpenAPIDefinitionTest; -import fish.payara.microprofile.openapi.test.app.data.SchemaComponentTest; - -public class AnnotationProcessedDocument extends ProcessedDocument { - - public AnnotationProcessedDocument() { - // Apply base processor - new BaseProcessor("/testlocation_123").process(this, null); - - ClassLoader appClassLoader = new ApplicationClassLoader(new TestApplication(), - asList(SchemaComponentTest.class, OpenAPIDefinitionTest.class).stream().collect(toSet())); - - // Apply application processor - new ApplicationProcessor(appClassLoader).process(this, null); - - // Apply annotation processor - new AnnotationProcessor(appClassLoader).process(this, null); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java index d2440e20994..1836620c0da 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java @@ -1,21 +1,60 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.resource.rule; import static java.util.Collections.singleton; +import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; import fish.payara.microprofile.openapi.impl.processor.ApplicationProcessor; import fish.payara.microprofile.openapi.impl.processor.BaseProcessor; import fish.payara.microprofile.openapi.resource.classloader.ApplicationClassLoader; import fish.payara.microprofile.openapi.test.app.TestApplication; -import fish.payara.microprofile.openapi.test.app.data.SchemaComponentTest; +import fish.payara.microprofile.openapi.test.app.data.TestComponent; -public class ApplicationProcessedDocument extends ProcessedDocument { +public class ApplicationProcessedDocument extends OpenAPIImpl { public ApplicationProcessedDocument() { // Apply base processor new BaseProcessor("/testlocation_123").process(this, null); - ClassLoader appClassLoader = new ApplicationClassLoader(new TestApplication(), - singleton(SchemaComponentTest.class)); + ClassLoader appClassLoader = new ApplicationClassLoader(new TestApplication(), singleton(TestComponent.class)); // Apply application processor new ApplicationProcessor(appClassLoader).process(this, null); diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/BaseProcessedDocument.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/BaseProcessedDocument.java deleted file mode 100644 index 8eb12df5a05..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/BaseProcessedDocument.java +++ /dev/null @@ -1,12 +0,0 @@ -package fish.payara.microprofile.openapi.resource.rule; - -import fish.payara.microprofile.openapi.impl.processor.BaseProcessor; - -public class BaseProcessedDocument extends ProcessedDocument { - - public BaseProcessedDocument() { - // Apply base processor - new BaseProcessor("/testlocation_123").process(this, null); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ProcessedDocument.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ProcessedDocument.java deleted file mode 100644 index c4f844d7e75..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ProcessedDocument.java +++ /dev/null @@ -1,7 +0,0 @@ -package fish.payara.microprofile.openapi.resource.rule; - -import fish.payara.microprofile.openapi.impl.model.OpenAPIImpl; - -public abstract class ProcessedDocument extends OpenAPIImpl { - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/util/TestUtils.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/util/TestUtils.java deleted file mode 100644 index 0a678160cf6..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/util/TestUtils.java +++ /dev/null @@ -1,480 +0,0 @@ -package fish.payara.microprofile.openapi.resource.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; - -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.Operation; -import org.eclipse.microprofile.openapi.models.PathItem; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.eclipse.microprofile.openapi.models.callbacks.Callback; -import org.eclipse.microprofile.openapi.models.media.Content; -import org.eclipse.microprofile.openapi.models.media.Schema; -import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; -import org.eclipse.microprofile.openapi.models.parameters.Parameter; -import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; -import org.eclipse.microprofile.openapi.models.parameters.RequestBody; -import org.eclipse.microprofile.openapi.models.responses.APIResponses; -import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; -import org.eclipse.microprofile.openapi.models.servers.Server; -import org.eclipse.microprofile.openapi.models.servers.ServerVariable; -import org.eclipse.microprofile.openapi.models.tags.Tag; - -public final class TestUtils { - - private TestUtils() { - } - - /** - * Tests that a given operation exists in the document. - * - * @param document the OpenAPI document to scan for the operation. - * @param endpointPath the path of the operation. - * @param httpMethod the name of the method mapped to the operation. If this - * value is null, it will not be checked. - * @param method the HTTP Method of the operation. - * - * @throws AssertionError if the operation isn't found. - */ - public static void testOperation(OpenAPI document, String endpointPath, String httpMethod, HttpMethod method) { - assertNotNull(endpointPath + " doesn't exist.", document.getPaths().get(endpointPath)); - assertNotNull(endpointPath + " has no operations.", document.getPaths().get(endpointPath).readOperationsMap()); - assertNotNull(endpointPath + " has no " + method.toString() + ".", - document.getPaths().get(endpointPath).readOperationsMap().get(method)); - if (httpMethod != null) { - assertEquals(endpointPath + " has the wrong method name.", - document.getPaths().get(endpointPath).readOperationsMap().get(method).getOperationId(), httpMethod); - } - } - - /** - * Tests that a named parameter with the given type exists for an endpoint in - * the document. - * - * @param document the OpenAPI document to scan for the operation. - * @param endpointPath the path of the operation. - * @param httpMethod the name of the method mapped to the operation. If this - * value is null, it will not be checked. - * @param parameterMap A map mapping the name of a parameter to search for to - * the parameter type. - * - * @throws AssertionError if the parameter or operation aren't found. - */ - public static void testParameter(OpenAPI document, String endpointPath, HttpMethod httpMethod, - Map parameterMap) { - testOperation(document, endpointPath, null, httpMethod); - Operation operation = document.getPaths().get(endpointPath).readOperationsMap().get(httpMethod); - - // If the parameter map is null, check there are no parameters - if (parameterMap == null) { - assertTrue(endpointPath + " has parameters.", - operation.getParameters() == null || operation.getParameters().isEmpty()); - return; - } - - List parameters = operation.getParameters(); - for (Entry entry : parameterMap.entrySet()) { - String parameterName = entry.getKey(); - In parameterType = entry.getValue(); - - assertTrue(endpointPath + " has no parameter with name " + parameterName, - parameters.stream().anyMatch(param -> param.getName().equals(parameterName))); - assertEquals( - endpointPath + " parameter " + parameterName + " is the wrong type.", parameters.stream() - .filter(param -> param.getName().equals(parameterName)).findFirst().get().getIn(), - parameterType); - } - } - - /** - * Tests that a named content type with the given schema type exists in the - * request body of an endpoint in the document. - * - * @param document the OpenAPI document to scan for the operation. - * @param endpointPath the path of the operation. - * @param httpMethod the name of the method mapped to the operation. If this - * value is null, it will not be checked. - * @param requestMap A map mapping the content type to search for to the - * schema type or reference. A value of type - * {@link SchemaType} will attempt to match a content type. - * A value of type {@link String} will attempt to match a - * reference instead. If this map is null, the operation - * will be tested for no request body. - * - * @return the request body found. - * - * @throws AssertionError if the content type or operation aren't found. - */ - public static RequestBody testRequestBody(OpenAPI document, String endpointPath, HttpMethod httpMethod, - Map requestMap) { - testOperation(document, endpointPath, null, httpMethod); - Operation operation = document.getPaths().get(endpointPath).readOperationsMap().get(httpMethod); - if (requestMap == null) { - assertNull(endpointPath + " has a requestBody.", operation.getRequestBody()); - return null; - } - assertNotNull(endpointPath + " has no requestBody.", operation.getRequestBody()); - assertNotNull(endpointPath + " has no content.", operation.getRequestBody().getContent()); - - Content content = operation.getRequestBody().getContent(); - try { - testContent(content, requestMap); - } catch (AssertionError ex) { - fail(endpointPath + " -> " + ex.getMessage()); - } - return operation.getRequestBody(); - } - - /** - * Tests that a named content type with the given schema type exists in a named - * response of an endpoint in the document. - * - * @param document the OpenAPI document to scan for the operation. - * @param endpointPath the path of the operation. - * @param httpMethod the name of the method mapped to the operation. If this - * value is null, it will not be checked. - * @param responseCode the name of the response. - * @param responseMap A map mapping the content type to search for to the - * schema type or reference. A value of type - * {@link SchemaType} will attempt to match a content type. - * A value of type {@link String} will attempt to match a - * reference instead. If this map is null, the operation - * will be tested for no response with the given name. - * - * @return the found response. - * - * @throws AssertionError if the content type or operation aren't found. - */ - public static org.eclipse.microprofile.openapi.models.responses.APIResponse testResponse(OpenAPI document, - String endpointPath, HttpMethod httpMethod, String responseCode, Map responseMap) { - assertNotNull(endpointPath + " doesn't exist.", document.getPaths().get(endpointPath)); - Operation operation = document.getPaths().get(endpointPath).readOperationsMap().get(httpMethod); - assertNotNull("Operation not found.", operation); - assertNotNull("No responses found.", operation.getResponses()); - assertNotNull("Response not found.", operation.getResponses().get(responseCode)); - - Content content = operation.getResponses().get(responseCode).getContent(); - try { - testContent(content, responseMap); - } catch (AssertionError ex) { - fail(endpointPath + " -> " + ex.getMessage()); - } - return operation.getResponses().get(responseCode); - } - - /** - * Tests that a named content type with the given schema type exists in the - * default response of an endpoint in the document. - * - * @param document the OpenAPI document to scan for the operation. - * @param endpointPath the path of the operation. - * @param httpMethod the name of the method mapped to the operation. If this - * value is null, it will not be checked. - * @param responseMap A map mapping the content type to search for to the - * schema type or reference. A value of type - * {@link SchemaType} will attempt to match a content type. - * A value of type {@link String} will attempt to match a - * reference instead. - * - * @return the found response. - * - * @throws AssertionError if the content type or operation aren't found. - */ - public static org.eclipse.microprofile.openapi.models.responses.APIResponse testResponse(OpenAPI document, - String endpointPath, HttpMethod httpMethod, Map responseMap) { - return testResponse(document, endpointPath, httpMethod, APIResponses.DEFAULT, responseMap); - } - - private static void testContent(Content content, Map typeMap) { - assertNotNull("The found content was null.", content); - for (Entry entry : typeMap.entrySet()) { - String contentName = entry.getKey(); - assertTrue("No content type found with name " + contentName, content.keySet().contains(contentName)); - if (entry.getValue() instanceof SchemaType) { - SchemaType schemaType = (SchemaType) entry.getValue(); - assertNull("The found schema should not contain a reference.", - content.get(contentName).getSchema().getRef()); - assertEquals("The found schema had the wrong type.", content.get(contentName).getSchema().getType(), - schemaType); - } else if (entry.getValue() instanceof String) { - String refName = (String) entry.getValue(); - assertEquals("The found schema had the wrong reference.", refName, - content.get(contentName).getSchema().getRef()); - assertNull("The found schema should not contain a type when it has a reference.", - content.get(contentName).getSchema().getType()); - } - } - } - - /** - * Tests that a component with the specified name exists in the document. Will - * also test that the found component contains a property with the given name - * and type. - * - * @param document the OpenAPI document to search in. - * @param componentName the name of the component to search for. - * @param propertyName the name of the property to search for. - * @param propertyType the type of the propetry to search for. If more than one - * type is specified, the sub item type will be checked - * recursively. For example, the types "array, array, int", - * will check for a multidimensional array of ints. - * - * @throws AssertionError if the component or property aren't found. - */ - public static void testComponentProperty(OpenAPI document, String componentName, String propertyName, - SchemaType... propertyTypes) { - // Check the property exists - assertNotNull("The component property " + propertyName + " wasn't found.", - document.getComponents().getSchemas().get(componentName).getProperties().get(propertyName)); - // Check the property and each sub property has the correct type - Schema schema = document.getComponents().getSchemas().get(componentName).getProperties().get(propertyName); - for (SchemaType propertyType : propertyTypes) { - assertNotNull("The schema had no sub items.", schema); - // Check the property has the correct type - assertEquals("The component property " + propertyName + " wasn't the correct type.", schema.getType(), - propertyType); - try { - schema = schema.getItems(); - } catch (NullPointerException ex) { - // Ignore - } - } - } - - /** - * Tests that a server with the given values exists at the given operation. If - * url is null, then the server is searched for at the document - * root. - * - * @param document the OpenAPI document to search in. - * @param url the url of the path item to test. - * @param method the {@link HttpMethod} of the operation. - * @param serverUrl the url of the server to expect. - * @param serverDescription the expected description of the found server. - * - * @return the found server. - */ - public static Server testServer(OpenAPI document, String url, HttpMethod method, String serverUrl, - String serverDescription) { - List servers = null; - if (url == null) { - servers = document.getServers(); - } else { - PathItem pathItem = document.getPaths().get(url); - assertNotNull("No Path item found for url: " + url, pathItem); - Operation operation = pathItem.readOperationsMap().get(method); - assertNotNull("No operation found for method: " + method + " at url: " + url, pathItem); - servers = operation.getServers(); - } - assertFalse("There were no servers found for element", servers == null || servers.isEmpty()); - Optional optional = servers.stream().filter(s -> s.getUrl().equals(serverUrl)).findAny(); - assertTrue("No server found with url: " + serverUrl, optional.isPresent()); - Server server = optional.get(); - assertEquals("Server with url: " + server.getUrl() + " has the wrong description.", server.getDescription(), - serverDescription); - return server; - } - - /** - * Tests that a server with the given values doesn't exist at the given - * operation. If url is null, then the server is searched for at - * the document root. - * - * @param document the OpenAPI document to search in. - * @param url the url of the path item to test. - * @param method the {@link HttpMethod} of the operation. - * @param serverUrl the url of the server to expect. - * - * @throws AssertionError if the server isn't found. - */ - public static void testNotServer(OpenAPI document, String url, HttpMethod method, String serverUrl) { - List servers = null; - if (url == null) { - servers = document.getServers(); - } else { - PathItem pathItem = document.getPaths().get(url); - assertNotNull("No Path item found for url: " + url, pathItem); - Operation operation = pathItem.readOperationsMap().get(method); - assertNotNull("No operation found for method: " + method + " at url: " + url, pathItem); - servers = operation.getServers(); - } - if (servers == null || servers.isEmpty()) { - return; - } - Optional optional = document.getServers().stream().filter(s -> s.getUrl().equals(serverUrl)).findAny(); - assertFalse("No server found with url: " + url, optional.isPresent()); - } - - /** - * Tests that a server with the given url contains a variable with the given - * values. - * - * @param server the server to test. - * @param variableName the name of the variable. - * @param variableDescription the description of the variable. - * @param defaultValue the default value of the variable. - * @param enumValue the enum value of the variable. - * - * @throws AssertionError if the server variable isn't found. - */ - public static void testServerContainsVariable(Server server, String variableName, String variableDescription, - String defaultValue, String enumValue) { - assertNotNull(server.getUrl() + " has no variables.", server.getVariables()); - ServerVariable variable = server.getVariables().get(variableName); - assertNotNull(variableName + " has no variable called: " + variableName, variable); - assertEquals(variableName + " has the wrong description.", variable.getDescription(), variableDescription); - assertEquals(variableName + " has the wrong default value.", variable.getDefaultValue(), defaultValue); - if (enumValue == null) { - assertTrue(variableName + " contains enum values.", - variable.getEnumeration() == null || variable.getEnumeration().isEmpty()); - } else { - assertFalse(variableName + " contains no enum values.", - variable.getEnumeration() == null || variable.getEnumeration().isEmpty()); - } - } - - /** - * Tests that a tag with the given values exists at the given operation. If - * url is null, then the tag is searched for at the document root. - * - * @param document the OpenAPI document to search in. - * @param url the url of the path item to test. - * @param method the {@link HttpMethod} of the operation. - * @param tagName the name of the tag to expect. - * @param tagDescription the expected description of the found tag. - * - * @throws AssertionError if the tag isn't found. - */ - public static void testTag(OpenAPI document, String url, HttpMethod method, String tagName, String tagDescription) { - List tags = document.getTags(); - if (url != null) { - PathItem pathItem = document.getPaths().get(url); - assertNotNull("No Path item found for url: " + url, pathItem); - Operation operation = pathItem.readOperationsMap().get(method); - assertNotNull("No operation found for method: " + method + " at url: " + url, pathItem); - List operationTags = operation.getTags(); - assertNotNull("The operation: " + method + " at url: " + url + " contains no tags.", operationTags); - assertTrue("There were no tags with the name: " + tagName, operationTags.contains(tagName)); - } - assertFalse("There were no tags found.", tags == null || tags.isEmpty()); - Optional optional = tags.stream().filter(t -> t.getName().equals(tagName)).findAny(); - assertTrue("There were no tags found with name: " + tagName, optional.isPresent()); - Tag tag = optional.get(); - assertEquals("The tag " + tagName + " had the wrong description.", tag.getDescription(), tagDescription); - } - - /** - * Tests that a tag with the given values does not exist at the given operation. - * If url is null, then the tag is searched for at the document - * root. - * - * @param document the OpenAPI document to search in. - * @param url the url of the path item to test. - * @param method the {@link HttpMethod} of the operation. - * @param tagName the name of the tag to expect. - * - * @throws AssertionError if the tag is found. - */ - public static void testNotTag(OpenAPI document, String url, HttpMethod method, String tagName) { - List tags = document.getTags(); - if (url != null) { - PathItem pathItem = document.getPaths().get(url); - assertNotNull("No Path item found for url: " + url, pathItem); - Operation operation = pathItem.readOperationsMap().get(method); - assertNotNull("No operation found for method: " + method + " at url: " + url, pathItem); - List operationTags = operation.getTags(); - assertTrue("The operation: " + method + " at url: " + url + " contains no tags.", - operationTags == null || !operationTags.contains(tagName)); - } else { - boolean found = tags.stream().anyMatch(t -> t.getName().equals(tagName)); - assertFalse("There was a tag found with name: " + tagName, found); - } - } - - /** - * Tests that a security configuration with the specified name exists at the - * given operation. - * - * @param document the OpenAPI document to search in. - * @param url the url of the path item to test. - * @param httpMethod the {@link HttpMethod} of the operation. - * @param schemeName the name of the expected scheme. - * @param scope the name of the scope to expect in the found scheme. - */ - public static void testSecurityRequirement(OpenAPI document, String url, HttpMethod httpMethod, String schemeName, String scope) { - testOperation(document, url, null, httpMethod); - Operation operation = document.getPaths().get(url).readOperationsMap().get(httpMethod); - List operationRequirements = operation.getSecurity(); - assertNotNull("No security requirements found at operation.", operationRequirements); - for (SecurityRequirement requirement : operationRequirements) { - if (requirement.containsKey(schemeName)) { - assertTrue("Scope not found.", requirement.get(schemeName).contains(scope)); - return; - } - } - fail("Security requirement not found."); - } - - /** - * Tests that a security configuration with the specified name doesn't exist at - * the given operation. - * - * @param document the OpenAPI document to search in. - * @param url the url of the path item to test. - * @param httpMethod the {@link HttpMethod} of the operation. - * @param schemeName the name of the scheme to test for. - */ - public static void testNotSecurityRequirement(OpenAPI document, String url, HttpMethod httpMethod, String schemeName) { - testOperation(document, url, null, httpMethod); - Operation operation = document.getPaths().get(url).readOperationsMap().get(httpMethod); - List operationRequirements = operation.getSecurity(); - for (SecurityRequirement requirement : operationRequirements) { - if (requirement.containsKey(schemeName)) { - fail("Security requirement found."); - return; - } - } - } - - /** - * Tests that a named callback operation with the given values exists for an - * endpoint in the document. - * - * @param document the OpenAPI document to scan for the operation. - * @param endpointPath the path of the operation. - * @param httpMethod the name of the method mapped to the operation. If - * this value is null, it will not be checked. - * @param callbackName the name of the callback to test for. - * @param callbackUrl the url of the callback to test for. - * @param callbackOperation the method of the callback operation to test for. - * @param callbackDescription the description of the callback operation, or null - * to not test. - * - * @throws AssertionError if the callback or callback operation aren't found. - */ - public static void testCallback(OpenAPI document, String endpointPath, HttpMethod httpMethod, String callbackName, - String callbackUrl, HttpMethod callbackOperation, String callbackDescription) { - testOperation(document, endpointPath, null, httpMethod); - Operation operation = document.getPaths().get(endpointPath).readOperationsMap().get(httpMethod); - assertNotNull("No callbacks found.", operation.getCallbacks()); - Callback callback = operation.getCallbacks().get(callbackName); - assertNotNull("Callback " + callbackName + " not found.", callback); - PathItem callbackPath = callback.get(callbackUrl); - assertNotNull("Callback url expression " + callbackUrl + " not found.", callbackPath); - operation = callbackPath.readOperationsMap().get(callbackOperation); - assertNotNull("Callback operation " + callbackOperation + " not found.", operation); - if (callbackDescription != null) { - assertEquals("Wrong callback operation description.", callbackDescription, operation.getDescription()); - } - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/TestApplication.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/TestApplication.java index 9e343728fae..ab28ceecf20 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/TestApplication.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/TestApplication.java @@ -1,98 +1,62 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.test.app; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - import java.util.HashSet; import java.util.Set; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.BaseProcessedDocument; -import fish.payara.microprofile.openapi.test.app.application.FormParamTest; -import fish.payara.microprofile.openapi.test.app.application.MethodMergeTest; -import fish.payara.microprofile.openapi.test.app.application.MethodTest; -import fish.payara.microprofile.openapi.test.app.application.ParameterTest; -import fish.payara.microprofile.openapi.test.app.application.PathTest; -import fish.payara.microprofile.openapi.test.app.application.RequestTest; import fish.payara.microprofile.openapi.test.app.application.ResponseTest; import fish.payara.microprofile.openapi.test.app.application.RootPathTest; -import fish.payara.microprofile.openapi.test.app.annotation.APIResponseTest; -import fish.payara.microprofile.openapi.test.app.annotation.CallbackTest; -import fish.payara.microprofile.openapi.test.app.annotation.ExtensionTest; -import fish.payara.microprofile.openapi.test.app.annotation.ExternalDocumentationTest; -import fish.payara.microprofile.openapi.test.app.annotation.OpenAPIDefinitionTest; -import fish.payara.microprofile.openapi.test.app.annotation.OperationTest; -import fish.payara.microprofile.openapi.test.app.annotation.ParameterAnnotationTest; -import fish.payara.microprofile.openapi.test.app.annotation.RequestBodyTest; -import fish.payara.microprofile.openapi.test.app.annotation.SchemaTest; -import fish.payara.microprofile.openapi.test.app.annotation.SecurityRequirementTest; -import fish.payara.microprofile.openapi.test.app.annotation.SecuritySchemeTest; -import fish.payara.microprofile.openapi.test.app.annotation.ServerTest; -import fish.payara.microprofile.openapi.test.app.annotation.TagTest; - @ApplicationPath("/test") public class TestApplication extends Application { - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new BaseProcessedDocument(); - } - @Override public Set> getClasses() { Set> classes = new HashSet<>(); - // All JAX-RS classes classes.add(RootPathTest.class); - classes.add(PathTest.class); - classes.add(MethodTest.class); - classes.add(ParameterTest.class); - classes.add(RequestTest.class); classes.add(ResponseTest.class); - classes.add(MethodMergeTest.class); - classes.add(FormParamTest.class); - - // All OpenAPI classes - classes.add(OpenAPIDefinitionTest.class); - classes.add(ServerTest.class); - classes.add(TagTest.class); - classes.add(RequestBodyTest.class); - classes.add(APIResponseTest.class); - classes.add(SchemaTest.class); - classes.add(SecuritySchemeTest.class); - classes.add(SecurityRequirementTest.class); - classes.add(ParameterAnnotationTest.class); - classes.add(OperationTest.class); - classes.add(CallbackTest.class); - classes.add(ExternalDocumentationTest.class); - classes.add(ExtensionTest.class); return classes; } - - @Test - public void testBase() { - assertEquals("The document has the wrong version.", "3.0.0", document.getOpenapi()); - assertEquals("The document has the wrong title.", "Deployed Resources", document.getInfo().getTitle()); - assertEquals("The document has the wrong info version.", "1.0.0", document.getInfo().getVersion()); - assertEquals("The document has the wrong server list.", 1, document.getServers().size()); - assertEquals("The document has the wrong server URL.", "http://localhost:8080/testlocation_123", - document.getServers().get(0).getUrl()); - assertNotNull("The document paths should be an empty array if empty.", document.getPaths()); - assertTrue("The document shouldn't have any paths.", document.getPaths().isEmpty()); - assertTrue("The document shouldn't have any extensions.", document.getExtensions().isEmpty()); - assertNull("The document shouldn't have any external docs.", document.getExternalDocs()); - assertTrue("The document shouldn't have any security properties.", document.getSecurity().isEmpty()); - assertTrue("The document shouldn't have any tags.", document.getTags().isEmpty()); - } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/APIResponseTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/APIResponseTest.java deleted file mode 100644 index 3bca380b0b4..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/APIResponseTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import static java.util.Collections.singletonMap; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static javax.ws.rs.core.MediaType.APPLICATION_XML; -import static org.junit.Assert.assertEquals; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; - -import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A resource to test that the @APIResponse annotation is handled correctly. - */ -@Path("/apiresponse") -public class APIResponseTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @GET - @Path("/default") - @Produces(APPLICATION_XML) - @APIResponse(description = "specified by an annotation.") - public String defaultResponse() { - return null; - } - - @Test - public void testDefaultResponse() { - TestUtils.testOperation(document, "/test/apiresponse/default", "defaultResponse", HttpMethod.GET); - org.eclipse.microprofile.openapi.models.responses.APIResponse response = TestUtils.testResponse(document, - "/test/apiresponse/default", HttpMethod.GET, singletonMap(APPLICATION_XML, SchemaType.STRING)); - assertEquals("/test/apiresponse/default response had the wrong description.", "specified by an annotation.", - response.getDescription()); - } - - @GET - @Path("/specified") - @Produces(APPLICATION_JSON) - @APIResponse(responseCode = "200", description = "specified by an annotation.") - public String specifiedResponse() { - return null; - } - - @Test - public void testSpecifiedResponse() { - TestUtils.testOperation(document, "/test/apiresponse/specified", "specifiedResponse", HttpMethod.GET); - - // Check the default response is still in tact - org.eclipse.microprofile.openapi.models.responses.APIResponse defaultResponse = TestUtils.testResponse(document, - "/test/apiresponse/specified", HttpMethod.GET, singletonMap(APPLICATION_JSON, SchemaType.STRING)); - assertEquals("Description was incorrect.", "Default Response.", defaultResponse.getDescription()); - - // Check the new response is created - org.eclipse.microprofile.openapi.models.responses.APIResponse successResponse = document.getPaths() - .get("/test/apiresponse/specified").getGET().getResponses().get("200"); - assertEquals("Description was incorrect.", "specified by an annotation.", successResponse.getDescription()); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/CallbackTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/CallbackTest.java deleted file mode 100644 index 8e93a9ba4c2..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/CallbackTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -import org.eclipse.microprofile.openapi.annotations.callbacks.Callback; -import org.eclipse.microprofile.openapi.annotations.callbacks.CallbackOperation; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -@Path("/callback") -public class CallbackTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @GET - @Path("/specified") - @Callback( - name = "testCallback", - callbackUrlExpression = "api/callbackUrl", - operations = { - @CallbackOperation(method = "POST", description = "The callback operation.") - } - ) - @Callback( - name = "testCallback", - callbackUrlExpression = "api/callbackUrl2", - operations = { - @CallbackOperation(method = "POST", description = "The callback2 operation.") - } - ) - @Callback( - name = "testCallback2", - callbackUrlExpression = "whatever", - operations = { - @CallbackOperation(method = "OPTIONS", description = "The second callback operation.") - } - ) - public String specifiedCallback() { - return null; - } - - @Test - public void specifiedCallbackTest() { - TestUtils.testCallback(document, "/test/callback/specified", HttpMethod.GET, "testCallback", "api/callbackUrl", - HttpMethod.POST, "The callback operation."); - TestUtils.testCallback(document, "/test/callback/specified", HttpMethod.GET, "testCallback", "api/callbackUrl2", - HttpMethod.POST, "The callback2 operation."); - TestUtils.testCallback(document, "/test/callback/specified", HttpMethod.GET, "testCallback2", "whatever", - HttpMethod.OPTIONS, "The second callback operation."); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExtensionTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExtensionTest.java deleted file mode 100644 index c8256668fea..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExtensionTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.Map; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -import org.eclipse.microprofile.openapi.annotations.extensions.Extension; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; - -@Path("/extension") -@Extension(name = "class-extension", value = "http://extension") -public class ExtensionTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @Test - public void classExtensionTest() { - Map extensions = document.getExtensions(); - assertNotNull("No extensions were found.", extensions); - assertTrue("Extension not found.", extensions.containsKey("class-extension")); - assertEquals("Extension had wrong value.", extensions.get("class-extension"), "http://extension"); - } - - @GET - @Path("/specified") - @Extension(name = "method-extension", value = "http://extension2") - public String methodExtension() { - return null; - } - - @Test - public void methodExtensionTest() { - Map extensions = document.getPaths().get("/test/extension/specified").getGET().getExtensions(); - assertNotNull("No extensions were found.", extensions); - assertTrue("Extension not found.", extensions.containsKey("method-extension")); - assertEquals("Extension had wrong value.", extensions.get("method-extension"), "http://extension2"); - } -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExternalDocumentationTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExternalDocumentationTest.java deleted file mode 100644 index 643b06ca225..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ExternalDocumentationTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -import org.eclipse.microprofile.openapi.annotations.ExternalDocumentation; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; - -@Path("/externaldocs") -@ExternalDocumentation(description = "Inherited external documentation.", url = "http://inherited") -public class ExternalDocumentationTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @GET - @Path("/specified") - @ExternalDocumentation(description = "Specified external documentation.", url = "http://external-docs") - public String specifiedExternalDocs() { - return null; - } - - @Test - public void specifiedExternalDocsTest() { - org.eclipse.microprofile.openapi.models.ExternalDocumentation externalDocs = document.getPaths() - .get("/test/externaldocs/specified").getGET().getExternalDocs(); - assertNotNull("No external docs found.", externalDocs); - assertEquals("Incorrect description.", "Specified external documentation.", externalDocs.getDescription()); - assertEquals("Incorrect url.", "http://external-docs", externalDocs.getUrl()); - } - - @GET - @Path("/inherited") - public String inheritedExternalDocs() { - return null; - } - - @Test - public void inheritedExternalDocsTest() { - org.eclipse.microprofile.openapi.models.ExternalDocumentation externalDocs = document.getPaths() - .get("/test/externaldocs/inherited").getGET().getExternalDocs(); - assertNotNull("No external docs found.", externalDocs); - assertEquals("Incorrect description.", "Inherited external documentation.", externalDocs.getDescription()); - assertEquals("Incorrect url.", "http://inherited", externalDocs.getUrl()); - } - - @GET - @Path("/ignored") - @ExternalDocumentation - public String ignoredExternalDocs() { - return null; - } - - @Test - public void ignoredExternalDocsTest() { - org.eclipse.microprofile.openapi.models.ExternalDocumentation externalDocs = document.getPaths() - .get("/test/externaldocs/ignored").getGET().getExternalDocs(); - assertNull("External docs found.", externalDocs); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OpenAPIDefinitionTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OpenAPIDefinitionTest.java deleted file mode 100644 index 7a7a30c949c..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OpenAPIDefinitionTest.java +++ /dev/null @@ -1,420 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.eclipse.microprofile.openapi.annotations.Components; -import org.eclipse.microprofile.openapi.annotations.ExternalDocumentation; -import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; -import org.eclipse.microprofile.openapi.annotations.callbacks.Callback; -import org.eclipse.microprofile.openapi.annotations.callbacks.CallbackOperation; -import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn; -import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; -import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType; -import org.eclipse.microprofile.openapi.annotations.extensions.Extension; -import org.eclipse.microprofile.openapi.annotations.headers.Header; -import org.eclipse.microprofile.openapi.annotations.info.Contact; -import org.eclipse.microprofile.openapi.annotations.info.Info; -import org.eclipse.microprofile.openapi.annotations.info.License; -import org.eclipse.microprofile.openapi.annotations.links.Link; -import org.eclipse.microprofile.openapi.annotations.media.Content; -import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; -import org.eclipse.microprofile.openapi.annotations.media.Schema; -import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; -import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; -import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; -import org.eclipse.microprofile.openapi.annotations.security.OAuthFlow; -import org.eclipse.microprofile.openapi.annotations.security.OAuthFlows; -import org.eclipse.microprofile.openapi.annotations.security.OAuthScope; -import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; -import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; -import org.eclipse.microprofile.openapi.annotations.servers.Server; -import org.eclipse.microprofile.openapi.annotations.servers.ServerVariable; -import org.eclipse.microprofile.openapi.annotations.tags.Tag; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.Operation; -import org.eclipse.microprofile.openapi.models.examples.Example; -import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; -import org.eclipse.microprofile.openapi.models.security.SecurityScheme.Type; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -@OpenAPIDefinition( - info = @Info( - title = "Test Application", - version = "1.2.3", - description = "Application to test OpenAPI implementation.", - termsOfService = "OpenAPI terms of service.", - contact = @Contact( - name = "test-person", - email = "openapi-test@payara.fish", - url = "http://payara.fish" - ), - license = @License( - name = "test-license", - url = "http://payara.fish/openapi-test-license" - ) - ), - servers = { - @Server( - url = "http://server1", - description = "the first server.", - variables = { - @ServerVariable( - name = "serverVariable1", - description = "the first server variable for server1.", - defaultValue = "null" - ) - } - ) - }, - externalDocs = @ExternalDocumentation( - description = "the first external docs.", - url = "http://external-docs" - ), - security = { - @SecurityRequirement( - name = "securityRequirement1", - scopes = {"scope1", "scope2"} - ), - @SecurityRequirement( - name = "securityRequirement2", - scopes = {"lion", "tiger"} - ) - }, - tags = { - @Tag( - name = "tag1", - description = "the first tag.", - externalDocs = @ExternalDocumentation( - description = "the first external docs for tag1.", - url = "http://external-docs/tag1" - ) - ), - @Tag( - name = "tag2", - description = "the second tag.", - externalDocs = @ExternalDocumentation( - description = "the first external docs for tag2.", - url = "http://external-docs/tag2" - ) - ) - }, - components = @Components( - schemas = { - @Schema( - name = "schema1", - title = "the first schema.", - description = "An integer that is divisible by 2.3.", - deprecated = false, - type = SchemaType.INTEGER, - multipleOf = 2.3, - defaultValue = "23" - ) - }, - callbacks = { - @Callback( - name = "callback1", - callbackUrlExpression = "http://callback1.org", - operations = { - @CallbackOperation( - description = "callback1 operation1", - method = "OPTIONS", - summary = "The first operation of callback1.", - extensions = { - @Extension(name = "extension1", value = "extension2") - } - ) - } - ) - }, - examples = { - @ExampleObject( - name = "exampleObject1", - summary = "The first example object.", - description = "longer description of the same thing", - value = "test content", - externalValue = "http://test-content" - ) - }, - headers = { - @Header( - name = "header1", - description = "the first header.", - required = false - ) - }, - links = { - @Link( - name = "link1", - description = "the first link.", - requestBody = "request body content." - ) - }, - parameters = { - @Parameter( - name = "parameter1", - description = "the first parameter.", - in = ParameterIn.PATH - ) - }, - requestBodies = { - @RequestBody( - name = "requestBody1", - description = "the first request body.", - content = { - @Content( - mediaType = "app/test", - schema = @Schema( - ref = "schema1" - ) - ) - } - ) - }, - responses = { - @APIResponse( - name = "response1", - description = "the first response.", - responseCode = "200", - content = { - @Content( - mediaType = "app/test2", - schema = @Schema( - ref = "schema1" - ) - ) - } - ) - }, - securitySchemes = { - @SecurityScheme( - securitySchemeName = "securityScheme1", - description = "the first security scheme.", - scheme = "BASIC", - type = SecuritySchemeType.HTTP, - flows = @OAuthFlows( - password = @OAuthFlow( - authorizationUrl = "http://auth", - scopes = { - @OAuthScope( - name = "oauthScope1", - description = "the first OAuth scope." - ) - } - ) - ) - ) - } - ) -) -public class OpenAPIDefinitionTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - - @Test - public void infoTest() { - org.eclipse.microprofile.openapi.models.info.Info info = document.getInfo(); - assertNotNull("The document has no info.", info); - assertEquals("The Info object has the wrong title.", "Test Application", info.getTitle()); - assertEquals("The Info object has the wrong version.", "1.2.3", info.getVersion()); - assertEquals("The Info object has the wrong description.", "Application to test OpenAPI implementation.", - info.getDescription()); - assertEquals("The Info object has the wrong terms of service.", "OpenAPI terms of service.", - info.getTermsOfService()); - } - - @Test - public void contactTest() { - org.eclipse.microprofile.openapi.models.info.Contact contact = document.getInfo().getContact(); - assertNotNull("The document has no contacts.", contact); - assertEquals("The Contacts object has the wrong name.", "test-person", contact.getName()); - assertEquals("The Contacts object has the wrong email.", "openapi-test@payara.fish", contact.getEmail()); - assertEquals("The Contacts object has the wrong url.", "http://payara.fish", contact.getUrl()); - } - - @Test - public void licenseTest() { - org.eclipse.microprofile.openapi.models.info.License license = document.getInfo().getLicense(); - assertNotNull("The document has no license.", license); - assertEquals("The document has the wrong license name.", "test-license", license.getName()); - assertEquals("The document has the wrong license url.", "http://payara.fish/openapi-test-license", - license.getUrl()); - } - - @Test - public void serversTest() { - org.eclipse.microprofile.openapi.models.servers.Server server = TestUtils.testServer(document, null, - null, "http://server1", "the first server."); - TestUtils.testServerContainsVariable(server, "serverVariable1", "the first server variable for server1.", - "null", null); - } - - @Test - public void externalDocsTest() { - org.eclipse.microprofile.openapi.models.ExternalDocumentation externalDocs = document - .getExternalDocs(); - assertNotNull("The document has no external docs.", externalDocs); - assertEquals("The external docs has the wrong description.", "the first external docs.", - externalDocs.getDescription()); - assertEquals("The external docs has the wrong url.", "http://external-docs", externalDocs.getUrl()); - } - - @Test - public void securityTest() { - List requirements = document - .getSecurity(); - assertNotNull("The document has no security requirements.", requirements); - - Optional optional = requirements.stream() - .filter(r -> r.containsKey("securityRequirement1")).findAny(); - assertTrue("securityRequirement1 wasn't found.", optional.isPresent()); - org.eclipse.microprofile.openapi.models.security.SecurityRequirement requirement = optional.get(); - assertTrue("securityRequirement1 didn't contain scope1.", - requirement.get("securityRequirement1").contains("scope1")); - assertTrue("securityRequirement1 didn't contain scope2.", - requirement.get("securityRequirement1").contains("scope2")); - - optional = requirements.stream().filter(r -> r.containsKey("securityRequirement2")).findAny(); - assertTrue("securityRequirement2 wasn't found.", optional.isPresent()); - requirement = optional.get(); - assertTrue("securityRequirement2 didn't contain scope1.", - requirement.get("securityRequirement2").contains("lion")); - assertTrue("securityRequirement2 didn't contain scope2.", - requirement.get("securityRequirement2").contains("tiger")); - } - @Test - public void tagsTest() { - // Test that the base tags were created - TestUtils.testTag(document, null, null, "tag1", "the first tag."); - TestUtils.testTag(document, null, null, "tag2", "the second tag."); - } - - @Test - public void schemaTest() { - assertNotNull("The document components were null.", document.getComponents()); - Map schemas = document.getComponents() - .getSchemas(); - assertFalse("The document contained no schemas.", schemas == null || schemas.isEmpty()); - } - - @Test - public void componentsTest() { - org.eclipse.microprofile.openapi.models.Components components = document.getComponents(); - assertNotNull("The document has no components.", components); - - // Test the schema components - Map schemas = components.getSchemas(); - assertTrue("schema1 wasn't found.", schemas.containsKey("schema1")); - assertEquals("schema1 has the wrong title.", "the first schema.", schemas.get("schema1").getTitle()); - assertEquals("schema1 has the wrong deprecated value.", false, schemas.get("schema1").getDeprecated()); - - // Test the callback components - Map callbacks = components.getCallbacks(); - assertTrue("callback1 wasn't found.", callbacks.containsKey("callback1")); - assertTrue("callback1 has the wrong url.", callbacks.get("callback1").containsKey("http://callback1.org")); - Operation callbackOperation = callbacks.get("callback1").get("http://callback1.org").getOPTIONS(); - assertNotNull("callback1 operation has the wrong HTTP method.", callbackOperation); - assertEquals("callback1 operation has the wrong description.", "callback1 operation1", - callbackOperation.getDescription()); - assertEquals("callback1 operation has the wrong summary.", "The first operation of callback1.", - callbackOperation.getSummary()); - - // Test the example components - Map examples = components.getExamples(); - assertTrue("exampleObject1 wasn't found.", examples.containsKey("exampleObject1")); - assertEquals("exampleObject1 has the wrong summary.", "The first example object.", - examples.get("exampleObject1").getSummary()); - assertEquals("exampleObject1 has the wrong description.", "longer description of the same thing", - examples.get("exampleObject1").getDescription()); - assertEquals("exampleObject1 has the wrong value.", "test content", - examples.get("exampleObject1").getValue().toString()); - assertEquals("exampleObject1 has the wrong externalValue.", "http://test-content", - examples.get("exampleObject1").getExternalValue()); - - // Test the header components - Map headers = components.getHeaders(); - assertTrue("header1 wasn't found.", headers.containsKey("header1")); - assertEquals("header1 has the wrong description.", "the first header.", - headers.get("header1").getDescription()); - assertEquals("header1 has the wrong required value.", false, headers.get("header1").getRequired()); - - // Test the link components - Map links = components.getLinks(); - assertTrue("link1 wasn't found.", links.containsKey("link1")); - assertEquals("link1 has the wrong description.", "the first link.", links.get("link1").getDescription()); - assertEquals("link1 has the wrong request body.", "request body content.", links.get("link1").getRequestBody()); - - // Test the parameter components - Map parameters = components - .getParameters(); - assertTrue("parameter1 wasn't found.", parameters.containsKey("parameter1")); - assertEquals("parameter1 has the wrong description.", "the first parameter.", - parameters.get("parameter1").getDescription()); - assertEquals("parameter1 has the wrong request body.", In.PATH, parameters.get("parameter1").getIn()); - - // Test the request body components - Map requestBodies = components - .getRequestBodies(); - assertTrue("requestBody1 wasn't found.", requestBodies.containsKey("requestBody1")); - assertEquals("requestBody1 has the wrong description.", "the first request body.", - requestBodies.get("requestBody1").getDescription()); - assertNotNull("requestBody1 has the wrong content type.", - requestBodies.get("requestBody1").getContent().get("app/test")); - assertNotNull("requestBody1 has the wrong schema.", - requestBodies.get("requestBody1").getContent().get("app/test").getSchema()); - assertNotNull("requestBody1 has no schema ref.", - requestBodies.get("requestBody1").getContent().get("app/test").getSchema().getRef()); - assertEquals("requestBody1 has the wrong schema ref.", "#/components/schemas/schema1", - requestBodies.get("requestBody1").getContent().get("app/test").getSchema().getRef()); - - // Test the response components - Map responses = components - .getResponses(); - assertTrue("response1 wasn't found.", responses.containsKey("response1")); - assertEquals("response1 has the wrong description.", "the first response.", - responses.get("response1").getDescription()); - assertNotNull("response1 has the wrong content type.", - responses.get("response1").getContent().get("app/test2")); - assertNotNull("response1 has the wrong schema.", - responses.get("response1").getContent().get("app/test2").getSchema()); - assertNotNull("response1 has no schema ref.", - responses.get("response1").getContent().get("app/test2").getSchema().getRef()); - assertEquals("response1 has the wrong schema ref.", "#/components/schemas/schema1", - responses.get("response1").getContent().get("app/test2").getSchema().getRef()); - - // Test the security scheme components - Map security = components - .getSecuritySchemes(); - assertTrue("securityScheme1 wasn't found.", security.containsKey("securityScheme1")); - assertEquals("securityScheme1 has the wrong description.", "the first security scheme.", - security.get("securityScheme1").getDescription()); - assertEquals("securityScheme1 has the wrong scheme.", "BASIC", security.get("securityScheme1").getScheme()); - assertEquals("securityScheme1 has the wrong type.", Type.HTTP, security.get("securityScheme1").getType()); - assertNotNull("securityScheme1 has no flows.", security.get("securityScheme1").getFlows()); - assertNotNull("securityScheme1 has no flow password.", - security.get("securityScheme1").getFlows().getPassword()); - assertEquals("securityScheme1 has the wrong auth url.", "http://auth", - security.get("securityScheme1").getFlows().getPassword().getAuthorizationUrl()); - assertNotNull("securityScheme1 has no scope.", - security.get("securityScheme1").getFlows().getPassword().getScopes().get("oauthScope1")); - assertEquals("securityScheme1 has the wrong auth url.", "the first OAuth scope.", - security.get("securityScheme1").getFlows().getPassword().getScopes().get("oauthScope1")); - } -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OperationTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OperationTest.java deleted file mode 100644 index f07a12fc4be..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/OperationTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -import org.eclipse.microprofile.openapi.annotations.Operation; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; - -@Path("/operation") -public class OperationTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @GET - @Path("/specified") - @Operation(description = "A GET operation.", operationId = "getOperation", summary = "GET operation summary.") - public String specifiedOperation() { - return null; - } - - @Test - public void specifiedOperationTest() { - org.eclipse.microprofile.openapi.models.Operation operation = document.getPaths() - .get("/test/operation/specified").getGET(); - assertEquals("Operation had the wrong description.", "A GET operation.", operation.getDescription()); - assertEquals("Operation had the wrong operation id.", "getOperation", operation.getOperationId()); - assertEquals("Operation had the wrong summary.", "GET operation summary.", operation.getSummary()); - } - - @GET - @Path("/hidden") - @Operation(hidden = true) - public String hiddenOperation() { - return null; - } - - @Test - public void hiddenOperationTest() { - org.eclipse.microprofile.openapi.models.Operation operation = document.getPaths() - .get("/test/operation/hidden").getGET(); - assertNull("Operation was not hidden.", operation); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ParameterAnnotationTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ParameterAnnotationTest.java deleted file mode 100644 index 0a6f46f1760..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ParameterAnnotationTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import java.util.List; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; - -import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn; -import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; -import org.eclipse.microprofile.openapi.annotations.media.Schema; -import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; - -@Path("/parameter/openapi/") -public class ParameterAnnotationTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @GET - @Path("/inline") - public String inlineQueryAnnotation( - @QueryParam("format") - @Parameter( - description = "The format of the output.", - in = ParameterIn.COOKIE, - schema = @Schema( - type = SchemaType.ARRAY, - description = "format parameter schema." - ) - ) - String formatVar) { - return null; - } - - @Test - public void inlineQueryAnnotationTest() { - List parameters = document.getPaths() - .get("/test/parameter/openapi/inline").getGET().getParameters(); - for (org.eclipse.microprofile.openapi.models.parameters.Parameter parameter : parameters) { - if (parameter.getName().equals("format")) { - // Test that the description has been set - assertEquals("The parameter has the wrong description.", "The format of the output.", - parameter.getDescription()); - // Test that the input type hasn't been changed - assertEquals("The parameter has the wrong location.", In.QUERY, parameter.getIn()); - // Test that the schema type hasn't been changed - assertEquals("The parameter has the wrong schema type.", - org.eclipse.microprofile.openapi.models.media.Schema.SchemaType.STRING, - parameter.getSchema().getType()); - // Test that the schema description has filtered through - assertEquals("The parameter has the wrong schema description.", "format parameter schema.", - parameter.getSchema().getDescription()); - return; - } - } - fail("Parameter not found."); - } - - @GET - @Path("/method") - @Parameter(name = "format", in = ParameterIn.QUERY, description = "The format of the output.") - public String methodQueryAnnotation(@QueryParam("format") String formatVar) { - return null; - } - - @Test - public void methodQueryAnnotationTest() { - List parameters = document.getPaths() - .get("/test/parameter/openapi/method").getGET().getParameters(); - for (org.eclipse.microprofile.openapi.models.parameters.Parameter parameter : parameters) { - if (parameter.getName().equals("format")) { - // Test that the description has been set - assertEquals("The parameter has the wrong description.", "The format of the output.", - parameter.getDescription()); - return; - } - } - fail("Parameter not found."); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/RequestBodyTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/RequestBodyTest.java deleted file mode 100644 index 6118d02e22d..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/RequestBodyTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import static java.util.Collections.singletonMap; -import static javax.ws.rs.core.MediaType.APPLICATION_XML; -import static javax.ws.rs.core.MediaType.WILDCARD; -import static org.junit.Assert.assertEquals; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.UriInfo; - -import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A resource to test that the @RequestBody annotation is handled correctly. - */ -@Path("/requestbody") -public class RequestBodyTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @GET - @Path("/empty") - @RequestBody - public String emptyRequestBody() { - return null; - } - - @Test - public void testEmptyRequestBody() { - TestUtils.testOperation(document, "/test/requestbody/empty", "emptyRequestBody", HttpMethod.GET); - TestUtils.testRequestBody(document, "/test/requestbody/empty", HttpMethod.GET, null); - } - - @GET - @Path("/specified") - @Consumes(APPLICATION_XML) - @RequestBody(description = "specified by an annotation.", required = true) - public String specifiedRequestBody(String test, @Context UriInfo info) { - return null; - } - - @Test - public void testSpecifiedRequestBody() { - TestUtils.testOperation(document, "/test/requestbody/specified", "specifiedRequestBody", HttpMethod.GET); - org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = TestUtils.testRequestBody(document, - "/test/requestbody/specified", HttpMethod.GET, singletonMap(APPLICATION_XML, SchemaType.STRING)); - assertEquals("/test/requestbody/specified requestBody had the wrong description.", - "specified by an annotation.", requestBody.getDescription()); - assertEquals("/test/requestbody/specified requestBody had the wrong required value.", true, - requestBody.getRequired()); - } - - @POST - @Path("/override") - @RequestBody(name = "ignored", description = "ignore me", required = true) - public String overrideRequestBody( - @RequestBody(name = "override", description = "overrides method.", required = false) String input) { - return null; - } - - @Test - public void testOverridenRequestBody() { - TestUtils.testOperation(document, "/test/requestbody/override", "overrideRequestBody", HttpMethod.POST); - org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = TestUtils.testRequestBody(document, - "/test/requestbody/override", HttpMethod.POST, singletonMap(WILDCARD, SchemaType.STRING)); - assertEquals("/test/requestbody/override requestBody had the wrong description.", "overrides method.", - requestBody.getDescription()); - assertEquals("/test/requestbody/override requestBody had the wrong required value.", false, - requestBody.getRequired()); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SchemaTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SchemaTest.java deleted file mode 100644 index 7c60be69204..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SchemaTest.java +++ /dev/null @@ -1,121 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.core.MediaType; - -import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; -import org.eclipse.microprofile.openapi.annotations.media.Content; -import org.eclipse.microprofile.openapi.annotations.media.Schema; -import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; -import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.responses.APIResponses; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; -import fish.payara.microprofile.openapi.test.app.data.SchemaComponentTest; - -@Path("/schema") -public class SchemaTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @GET - @Path("/schema1") - @APIResponse(content = @Content(schema = @Schema(ref = "Schema1"))) - public Object getSchema1Response() { - return null; - } - - @Test - public void responseSchemaTest() { - org.eclipse.microprofile.openapi.models.responses.APIResponse response = document.getPaths() - .get("/test/schema/schema1").getGET().getResponses().get(APIResponses.DEFAULT); - assertNotNull("No default response found.", response); - assertNotNull("No content found.", response.getContent()); - assertNotNull("No mediatype found.", response.getContent().get("*/*")); - org.eclipse.microprofile.openapi.models.media.Schema schema = response.getContent().get("*/*").getSchema(); - assertNotNull("No content schema found.", schema); - assertEquals("Incorrect reference.", "#/components/schemas/Schema1", schema.getRef()); - assertNull("Incorrect type.", schema.getType()); - } - - @POST - @Path("/array") - @RequestBody(content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(description = "An array of strings.", type = SchemaType.ARRAY, format = "[item1,item2,item3]"))) - @Consumes(MediaType.APPLICATION_JSON) - public String postStringArray(String[] value) { - return null; - } - - @Test - public void newRequestSchemaTest() { - org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = document.getPaths() - .get("/test/schema/array").getPOST().getRequestBody(); - assertNotNull("No request body found.", requestBody); - assertNotNull("No content found.", requestBody.getContent()); - assertNotNull("No mediatype found.", requestBody.getContent().get(MediaType.APPLICATION_JSON)); - org.eclipse.microprofile.openapi.models.media.Schema schema = requestBody.getContent() - .get(MediaType.APPLICATION_JSON).getSchema(); - assertNotNull("No content schema found.", schema); - assertEquals("Incorrect schema description.", "An array of strings.", schema.getDescription()); - assertEquals("Incorrect schema schema type.", - org.eclipse.microprofile.openapi.models.media.Schema.SchemaType.ARRAY, schema.getType()); - assertEquals("Incorrect schema format.", "[item1,item2,item3]", schema.getFormat()); - } - - @POST - @Path("/component") - public String addSchemaComponent(@Schema(ref = "TestComponent") Object component) { - return null; - } - - @Test - public void parameterSchemaTest() { - org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = document.getPaths() - .get("/test/schema/component").getPOST().getRequestBody(); - assertNotNull("No request body found.", requestBody); - assertNotNull("No content found.", requestBody.getContent()); - assertNotNull("No mediatype found.", requestBody.getContent().get("*/*")); - org.eclipse.microprofile.openapi.models.media.Schema schema = requestBody.getContent().get("*/*").getSchema(); - assertNotNull("No content schema found.", schema); - assertEquals("Incorrect reference.", "#/components/schemas/TestComponent", schema.getRef()); - assertNull("Incorrect type.", schema.getType()); - } - - @POST - @Path("/component/override") - @RequestBody(content = @Content(schema = @Schema(title = "Schema Component RequestBody", implementation = SchemaComponentTest.class))) - public String addOverridenSchemaComponent(SchemaComponentTest component) { - return null; - } - - @Test - public void overridenSchemaTest() { - org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = document.getPaths() - .get("/test/schema/component/override").getPOST().getRequestBody(); - assertNotNull("No request body found.", requestBody); - assertNotNull("No content found.", requestBody.getContent()); - assertNotNull("No mediatype found.", requestBody.getContent().get("*/*")); - org.eclipse.microprofile.openapi.models.media.Schema schema = requestBody.getContent().get("*/*").getSchema(); - assertNotNull("No content schema found.", schema); - assertNull("Incorrect reference.", schema.getRef()); - assertEquals("Incorrect schema title.", "Schema Component RequestBody", schema.getTitle()); - assertEquals("Incorrect schema description.", "A component for testing the @Schema annotation.", - schema.getDescription()); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecurityRequirementTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecurityRequirementTest.java deleted file mode 100644 index e9a8abce39b..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecurityRequirementTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; -import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirements; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -@SecurityRequirement(name = "securityScheme1", scopes = { "oauthScope1" }) -@SecurityRequirements({ - @SecurityRequirement(name = "methodDeclaredApiKey", scopes = { "read" }) -}) -@Path("/security/requirement") -public class SecurityRequirementTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @GET - @Path("/specified") - @SecurityRequirement(name = "classDeclaredOAuth", scopes = { "write" }) - @SecurityRequirements({ - @SecurityRequirement(name = "notDeclared", scopes = { "whatever" }) - }) - public String specifiedRequirement() { - return null; - } - - @Test - public void specifiedRequirementTest() { - TestUtils.testSecurityRequirement(document, "/test/security/requirement/specified", HttpMethod.GET, - "classDeclaredOAuth", "write"); - TestUtils.testSecurityRequirement(document, "/test/security/requirement/specified", HttpMethod.GET, - "notDeclared", "whatever"); - TestUtils.testNotSecurityRequirement(document, "/test/security/requirement/specified", HttpMethod.GET, - "securityScheme1"); - TestUtils.testNotSecurityRequirement(document, "/test/security/requirement/specified", HttpMethod.GET, - "methodDeclaredApiKey"); - } - - @GET - @Path("/inherited") - public String inheritedRequirement() { - return null; - } - - @Test - public void inheritedRequirementTest() { - TestUtils.testSecurityRequirement(document, "/test/security/requirement/inherited", HttpMethod.GET, - "securityScheme1", "oauthScope1"); - TestUtils.testSecurityRequirement(document, "/test/security/requirement/inherited", HttpMethod.GET, - "methodDeclaredApiKey", "read"); - TestUtils.testNotSecurityRequirement(document, "/test/security/requirement/inherited", HttpMethod.GET, - "classDeclaredOAuth"); - TestUtils.testNotSecurityRequirement(document, "/test/security/requirement/inherited", HttpMethod.GET, - "notDeclared"); - } -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecuritySchemeTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecuritySchemeTest.java deleted file mode 100644 index f24c887dfdc..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/SecuritySchemeTest.java +++ /dev/null @@ -1,103 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; - -import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeIn; -import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType; -import org.eclipse.microprofile.openapi.annotations.security.OAuthFlow; -import org.eclipse.microprofile.openapi.annotations.security.OAuthFlows; -import org.eclipse.microprofile.openapi.annotations.security.OAuthScope; -import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.security.SecurityScheme.In; -import org.eclipse.microprofile.openapi.models.security.SecurityScheme.Type; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; - -/** - * A resource to test that the @SecurityScheme annotation is handled correctly. - */ -@SecurityScheme( - securitySchemeName = "classDeclaredOAuth", - type = SecuritySchemeType.OAUTH2, - description = "OAuth key.", - flows = @OAuthFlows( - authorizationCode = @OAuthFlow( - tokenUrl = "/api/auth", - scopes = { - @OAuthScope(name = "read", description = "Permission to read."), - @OAuthScope(name = "write", description = "Permission to write.") - } - ) - ) -) -@Path("/security/scheme") -public class SecuritySchemeTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @Test - public void testClassDeclaredOauth() { - org.eclipse.microprofile.openapi.models.security.SecurityScheme scheme = document.getComponents() - .getSecuritySchemes().get("classDeclaredOAuth"); - assertNotNull("The scheme wasn't found.", scheme); - // Test the type - assertEquals("The scheme had the wrong type.", Type.OAUTH2, scheme.getType()); - // Test the description - assertEquals("The scheme had the wrong description.", "OAuth key.", scheme.getDescription()); - // Test the OAuth Flows - assertNotNull("The scheme had no flows.", scheme.getFlows()); - assertNotNull("The scheme had no authorization flow.", scheme.getFlows().getAuthorizationCode()); - // Test the OAuth Flow - assertEquals("The authorization flow had the wrong token url.", "/api/auth", - scheme.getFlows().getAuthorizationCode().getTokenUrl()); - // Test the OAuth Scopes - assertNotNull("The authorization flow had no scopes.", scheme.getFlows().getAuthorizationCode().getScopes()); - // Test the OAuth READ scope - assertTrue("The authorization flow had no read scope.", - scheme.getFlows().getAuthorizationCode().getScopes().containsKey("read")); - assertEquals("The write scope has the wrong description.", "Permission to read.", - scheme.getFlows().getAuthorizationCode().getScopes().get("read")); - // Test the OAuth WRITE scope - assertTrue("The authorization flow had no write scope.", - scheme.getFlows().getAuthorizationCode().getScopes().containsKey("write")); - assertEquals("The write scope has the wrong description.", "Permission to write.", - scheme.getFlows().getAuthorizationCode().getScopes().get("write")); - } - - @GET - @SecurityScheme( - securitySchemeName = "methodDeclaredApiKey", - type = SecuritySchemeType.APIKEY, - in = SecuritySchemeIn.QUERY, - apiKeyName = "key", - description = "Insecure key." - ) - public String specifiedScheme(@QueryParam(value = "key") String authKey) { - return null; - } - - @Test - public void testMethodDeclaredApiKey() { - org.eclipse.microprofile.openapi.models.security.SecurityScheme scheme = document.getComponents() - .getSecuritySchemes().get("methodDeclaredApiKey"); - assertNotNull("The scheme wasn't found.", scheme); - assertEquals("The scheme had the wrong type.", Type.APIKEY, scheme.getType()); - assertEquals("The scheme had the wrong location.", In.QUERY, scheme.getIn()); - assertEquals("The scheme had the wrong apiKeyName.", "key", scheme.getName()); - assertEquals("The scheme had the wrong description.", "Insecure key.", scheme.getDescription()); - } -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ServerTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ServerTest.java deleted file mode 100644 index 2d87294cedc..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/ServerTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -import org.eclipse.microprofile.openapi.annotations.servers.Server; -import org.eclipse.microprofile.openapi.annotations.servers.ServerVariable; -import org.eclipse.microprofile.openapi.annotations.servers.Servers; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A resource to test that the @Server and @Servers annotations are handled - * correctly. - */ -@Path("/servers") -@Server(description = "override me", url = "http://override-me") -@Servers(value = { @Server(description = "also override me", url = "http://also-override-me") }) -public class ServerTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @GET - @Path("/override") - @Server(description = "overridden", url = "http://overriden", variables = { - @ServerVariable(name = "name", description = "description", defaultValue = "value", enumeration = { "TEST", - "ENUM" }) }) - public String getOverriden() { - return null; - } - - @Test - public void overridenServerTest() { - org.eclipse.microprofile.openapi.models.servers.Server server = TestUtils.testServer(document, - "/test/servers/override", HttpMethod.GET, "http://overriden", "overridden"); - TestUtils.testNotServer(document, "/test/servers/override", HttpMethod.GET, "http://override-me"); - TestUtils.testNotServer(document, "/test/servers/override", HttpMethod.GET, "http://also-override-me"); - TestUtils.testServerContainsVariable(server, "name", "description", "value", "TEST"); - TestUtils.testServerContainsVariable(server, "name", "description", "value", "ENUM"); - } - - @GET - @Path("/inherit") - public String getInherited() { - return null; - } - - @Test - public void inheritedServerTest() { - TestUtils.testNotServer(document, "/test/servers/inherit", HttpMethod.GET, "http://overriden"); - TestUtils.testServer(document, "/test/servers/inherit", HttpMethod.GET, "http://override-me", "override me"); - TestUtils.testServer(document, "/test/servers/inherit", HttpMethod.GET, "http://also-override-me", - "also override me"); - } - - @GET - @Path("/ignore") - @Server - public String getIgnored() { - return null; - } - - @GET - @Path("/ignore2") - @Servers - public String getIgnored2() { - return null; - } - - @Test - public void ignoredServerTest() { - TestUtils.testNotServer(document, "/test/servers/ignore", HttpMethod.GET, "http://overriden"); - TestUtils.testNotServer(document, "/test/servers/ignore", HttpMethod.GET, "http://override-me"); - TestUtils.testNotServer(document, "/test/servers/ignore", HttpMethod.GET, "http://also-override-me"); - TestUtils.testNotServer(document, "/test/servers/ignore2", HttpMethod.GET, "http://overriden"); - TestUtils.testNotServer(document, "/test/servers/ignore2", HttpMethod.GET, "http://override-me"); - TestUtils.testNotServer(document, "/test/servers/ignore2", HttpMethod.GET, "http://also-override-me"); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/TagTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/TagTest.java deleted file mode 100644 index 0f95e4b9e58..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/annotation/TagTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.annotation; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -import org.eclipse.microprofile.openapi.annotations.tags.Tag; -import org.eclipse.microprofile.openapi.annotations.tags.Tags; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A resource to test that the @Tag and @Tags annotations are handled correctly. - */ -@Path("/tags") -@Tag(name = "new", description = "Created from the class.") -@Tags(refs = { "tag1" }) -public class TagTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @GET - @Path("/override") - @Tag(name = "override", description = "Overriden.") - @Tag(ref = "tag2") - public String getOverriden() { - return null; - } - - @Test - public void overridenTagTest() { - TestUtils.testTag(document, "/test/tags/override", HttpMethod.GET, "override", "Overriden."); - TestUtils.testTag(document, "/test/tags/override", HttpMethod.GET, "tag2", "the second tag."); - TestUtils.testNotTag(document, "/test/tags/override", HttpMethod.GET, "tag1"); - TestUtils.testNotTag(document, "/test/tags/override", HttpMethod.GET, "new"); - } - - @GET - @Path("/inherit") - public String getInherited() { - return null; - } - - @Test - public void inheritedTagTest() { - TestUtils.testTag(document, "/test/tags/inherit", HttpMethod.GET, "new", "Created from the class."); - TestUtils.testTag(document, "/test/tags/inherit", HttpMethod.GET, "tag1", "the first tag."); - } - - @GET - @Path("/ignore") - @Tag - public String getIgnored() { - return null; - } - - @GET - @Path("/ignore2") - @Tags - public String getIgnored2() { - return null; - } - - @Test - public void ignoredTagsTest() { - TestUtils.testNotTag(document, "/test/tags/ignore", HttpMethod.GET, "tag1"); - TestUtils.testNotTag(document, "/test/tags/ignore", HttpMethod.GET, "tag2"); - TestUtils.testNotTag(document, "/test/tags/ignore", HttpMethod.GET, "new"); - TestUtils.testNotTag(document, "/test/tags/ignore2", HttpMethod.GET, "tag1"); - TestUtils.testNotTag(document, "/test/tags/ignore2", HttpMethod.GET, "tag2"); - TestUtils.testNotTag(document, "/test/tags/ignore2", HttpMethod.GET, "new"); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/FormParamTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/FormParamTest.java deleted file mode 100644 index 926dce31be7..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/FormParamTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.application; - -import static java.util.Collections.singletonMap; - -import javax.ws.rs.FormParam; -import javax.ws.rs.POST; -import javax.ws.rs.Path; - -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A resource to test that various types of form parameters are successfully - * generalised into one data type. - */ -@Path("/form") -public class FormParamTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new ApplicationProcessedDocument(); - } - - @POST - @Path("/stringint") - public String getStringAndInt(@FormParam("stringParam") String stringParam, @FormParam("intParam") int intParam) { - return null; - } - - @Test - public void stringIntTest() { - TestUtils.testRequestBody(document, "/test/form/stringint", HttpMethod.POST, - singletonMap("*/*", SchemaType.STRING)); - } - - @POST - @Path("/intstring") - public String getIntAndString(@FormParam("intParam") int intParam, @FormParam("stringParam") String stringParam) { - return null; - } - - @Test - public void intStringTest() { - TestUtils.testRequestBody(document, "/test/form/intstring", HttpMethod.POST, - singletonMap("*/*", SchemaType.STRING)); - } - - @POST - @Path("/intint") - public String getIntAndInt(@FormParam("intParam") int intParam, @FormParam("stringParam") int intParam2) { - return null; - } - - @Test - public void intIntTest() { - TestUtils.testRequestBody(document, "/test/form/intint", HttpMethod.POST, - singletonMap("*/*", SchemaType.INTEGER)); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodMergeTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodMergeTest.java deleted file mode 100644 index bac1d79e298..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodMergeTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.application; - -import static java.util.Collections.singletonMap; -import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static javax.ws.rs.core.MediaType.APPLICATION_XML; - -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; - -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.Operation; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A resource to test the results of multiple methods sharing the same endpoint. - */ -@Path("/merge") -public class MethodMergeTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new ApplicationProcessedDocument(); - } - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_XML) - public Object jsonToXml(Object data) { - return data; - } - - @POST - @Consumes(APPLICATION_FORM_URLENCODED) - @Produces(APPLICATION_JSON) - public Object formToJson(@FormParam("paramName") String name, @FormParam("paramValue") int value) { - return null; - } - - @Test - public void testMergedEndpoint() { - TestUtils.testOperation(document, "/test/merge", null, HttpMethod.POST); - Operation operation = document.getPaths().get("/test/merge").getPOST(); - if ("jsonToXml".equals(operation.getOperationId())) { - TestUtils.testRequestBody(document, "/test/merge", HttpMethod.POST, - singletonMap(APPLICATION_JSON, SchemaType.OBJECT)); - TestUtils.testResponse(document, "/test/merge", HttpMethod.POST, - singletonMap(APPLICATION_XML, SchemaType.OBJECT)); - } - if ("formToJson".equals(operation.getOperationId())) { - TestUtils.testRequestBody(document, "/test/merge", HttpMethod.POST, - singletonMap(APPLICATION_FORM_URLENCODED, SchemaType.STRING)); - TestUtils.testResponse(document, "/test/merge", HttpMethod.POST, - singletonMap(APPLICATION_JSON, SchemaType.OBJECT)); - } - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodTest.java deleted file mode 100644 index 124a23102e7..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/MethodTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.application; - -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.HEAD; -import javax.ws.rs.OPTIONS; -import javax.ws.rs.PATCH; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; - -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A resource to test each of the JAX-RS methods. - */ -@Path("/method") -public class MethodTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new ApplicationProcessedDocument(); - } - - @GET - public String getResource() { - return null; - } - - @Test - public void testGet() { - TestUtils.testOperation(document, "/test/method", "getResource", HttpMethod.GET); - } - - @POST - public String postResource() { - return null; - } - - @Test - public void testPost() { - TestUtils.testOperation(document, "/test/method", "postResource", HttpMethod.POST); - } - - @PUT - public String putResource() { - return null; - } - - @Test - public void testPut() { - TestUtils.testOperation(document, "/test/method", "putResource", HttpMethod.PUT); - } - - @DELETE - public String deleteResource() { - return null; - } - - @Test - public void testDelete() { - TestUtils.testOperation(document, "/test/method", "deleteResource", HttpMethod.DELETE); - } - - @HEAD - public String headResource() { - return null; - } - - @Test - public void testHead() { - TestUtils.testOperation(document, "/test/method", "headResource", HttpMethod.HEAD); - } - - @OPTIONS - public String optionsResource() { - return null; - } - - @Test - public void testOptions() { - TestUtils.testOperation(document, "/test/method", "optionsResource", HttpMethod.OPTIONS); - } - - @PATCH - public String patchResource() { - return null; - } - - @Test - public void testPatch() { - TestUtils.testOperation(document, "/test/method", "patchResource", HttpMethod.PATCH); - } -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ParameterTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ParameterTest.java deleted file mode 100644 index 34bc8c6fdac..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ParameterTest.java +++ /dev/null @@ -1,149 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.application; - -import java.util.Collections; - -import javax.ws.rs.CookieParam; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.MatrixParam; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.UriInfo; - -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A resource to test various types of parameters. - */ -@Path("/parameter") -public class ParameterTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new ApplicationProcessedDocument(); - } - - // Recognise parameter name - - @GET - @Path("/name") - public String name(@QueryParam("name1") String blarg, @QueryParam("name2") int glarg) { - return null; - } - - @Test - public void testParameterName() { - TestUtils.testParameter(document, "/test/parameter/name", HttpMethod.GET, - Collections.singletonMap("name1", In.QUERY)); - TestUtils.testParameter(document, "/test/parameter/name", HttpMethod.GET, - Collections.singletonMap("name2", In.QUERY)); - } - - // Recognise different types of parameters - - @GET - @Path("/query") - public String query(@QueryParam("param") String param) { - return null; - } - - @Test - public void testQueryParameter() { - TestUtils.testParameter(document, "/test/parameter/query", HttpMethod.GET, - Collections.singletonMap("param", In.QUERY)); - } - - @GET - @Path("/path/{param}") - public String path(@PathParam("param") String param) { - return null; - } - - @Test - public void testPathParameter() { - TestUtils.testParameter(document, "/test/parameter/path/{param}", HttpMethod.GET, - Collections.singletonMap("param", In.PATH)); - } - - @GET - @Path("/cookie") - public String cookie(@CookieParam("param") String param) { - return null; - } - - @Test - public void testCookieParameter() { - TestUtils.testParameter(document, "/test/parameter/cookie", HttpMethod.GET, - Collections.singletonMap("param", In.COOKIE)); - } - - @GET - @Path("/header") - public String header(@HeaderParam("param") String param) { - return null; - } - - @Test - public void testHeaderParameter() { - TestUtils.testParameter(document, "/test/parameter/header", HttpMethod.GET, - Collections.singletonMap("param", In.HEADER)); - } - - // PARAMETERS EXPECTED TO BE IGNORED - - @GET - @Path("/fake") - public String fake(/* Not a parameter */ String param) { - return null; - } - - @Test - public void testFakeParameter() { - TestUtils.testParameter(document, "/test/parameter/fake", HttpMethod.GET, null); - } - - @GET - @Path("/form") - public String form(@FormParam("param") String param) { - return null; - } - - @Test - public void testFormParameter() { - TestUtils.testParameter(document, "/test/parameter/form", HttpMethod.GET, null); - } - - @GET - @Path("/matrix") - public String matrix(@MatrixParam("param") String param) { - return null; - } - - @Test - public void testMatrixParameter() { - TestUtils.testParameter(document, "/test/parameter/matrix", HttpMethod.GET, null); - } - - @GET - @Path("/context") - public String context(@Context UriInfo uriInfo) { - return null; - } - - @Test - public void testContextParameter() { - TestUtils.testParameter(document, "/test/parameter/context", HttpMethod.GET, null); - } -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/PathTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/PathTest.java deleted file mode 100644 index 12861dcb9a6..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/PathTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.application; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; - -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A resource to test that various paths are created correctly. - */ -@Path("/path") -public class PathTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new ApplicationProcessedDocument(); - } - - @GET - public String getRootResource() { - return null; - } - - @Test - public void testRootResource() { - TestUtils.testOperation(document, "/test/path", "getRootResource", HttpMethod.GET); - } - - @GET - @Path("/1") - public String getResource() { - return null; - } - - @Test - public void testResource() { - TestUtils.testOperation(document, "/test/path/1", "getResource", HttpMethod.GET); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RequestTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RequestTest.java deleted file mode 100644 index 8638a6719a1..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RequestTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.application; - -import static java.util.Collections.singletonMap; -import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; -import static javax.ws.rs.core.MediaType.APPLICATION_XML; -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; - -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; - -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A resource to test that various types of request body are created properly. - */ -@Path("/request") -public class RequestTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new ApplicationProcessedDocument(); - } - - @GET - @Path("/none") - public String untyped(Object data, @QueryParam("ignoreme") String param) { - return null; - } - - @Test - public void testUntyped() { - TestUtils.testRequestBody(document, "/test/request/none", HttpMethod.GET, - singletonMap("*/*", SchemaType.OBJECT)); - } - - @POST - @Consumes(APPLICATION_FORM_URLENCODED) - @Path("/form") - public String form(@FormParam("test") String test, @QueryParam("ignoreme") String param) { - return null; - } - - @Test - public void testForm() { - TestUtils.testRequestBody(document, "/test/request/form", HttpMethod.POST, - singletonMap(APPLICATION_FORM_URLENCODED, SchemaType.STRING)); - } - - @GET - @Consumes(APPLICATION_JSON) - @Path("/json") - public String json(String[] data) { - return null; - } - - @Test - public void testArray() { - TestUtils.testRequestBody(document, "/test/request/json", HttpMethod.GET, - singletonMap(APPLICATION_JSON, SchemaType.ARRAY)); - } - - @GET - @Consumes({ APPLICATION_XML, TEXT_PLAIN }) - @Path("/multiple") - public String multiple(Boolean data) { - return null; - } - - @Test - public void testMultiple() { - TestUtils.testRequestBody(document, "/test/request/multiple", HttpMethod.GET, - singletonMap(APPLICATION_XML, SchemaType.BOOLEAN)); - TestUtils.testRequestBody(document, "/test/request/multiple", HttpMethod.GET, - singletonMap(TEXT_PLAIN, SchemaType.BOOLEAN)); - } -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ResponseTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ResponseTest.java index 8792bd640ea..f957b941d7c 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ResponseTest.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ResponseTest.java @@ -1,101 +1,107 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.test.app.application; -import static java.util.Collections.singletonMap; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static javax.ws.rs.core.MediaType.APPLICATION_XML; -import static javax.ws.rs.core.MediaType.TEXT_PLAIN; -import static javax.ws.rs.core.MediaType.TEXT_XML; -import static javax.ws.rs.core.MediaType.WILDCARD; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.eclipse.microprofile.openapi.models.responses.APIResponses; import org.junit.BeforeClass; import org.junit.Test; import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; -import fish.payara.microprofile.openapi.test.app.data.ComponentTest; /** * A resource to test that various response types are mapped properly. */ @Path("/response") +@Produces({ APPLICATION_JSON, APPLICATION_XML }) public class ResponseTest { public transient static OpenAPI document; @BeforeClass public static void createDocument() { - document = new ApplicationProcessedDocument(); - } - - @GET - @Path("/none") - public String none() { - return null; - } - - @Test - public void noResponseTest() { - TestUtils.testResponse(document, "/test/response/none", HttpMethod.GET, - singletonMap(WILDCARD, SchemaType.STRING)); - } - - @GET - @Path("/component") - public ComponentTest component() { - return null; - } - - @Test - public void componentResponseTest() { - TestUtils.testResponse(document, "/test/response/component", HttpMethod.GET, - singletonMap(WILDCARD, "#/components/schemas/ComponentTest")); - } - - @GET - @Produces(APPLICATION_JSON) - @Path("/json") - public Object json() { - return null; - } - - @Test - public void jsonResponseTest() { - TestUtils.testResponse(document, "/test/response/json", HttpMethod.GET, - singletonMap(APPLICATION_JSON, SchemaType.OBJECT)); + try { + document = new ApplicationProcessedDocument(); + } catch (Exception ex) { + ex.printStackTrace(); + fail("Failed to build document."); + } } @GET - @Produces(TEXT_PLAIN) - @Path("/text") - public Float text() { + @APIResponse(responseCode = "200", content = @Content(schema = @Schema(description = "hello!"))) + @APIResponse(responseCode = "400", description = "error") + public String getInheritedMediaType() { return null; } @Test - public void floatResponseTest() { - TestUtils.testResponse(document, "/test/response/text", HttpMethod.GET, - singletonMap(TEXT_PLAIN, SchemaType.NUMBER)); + public void inheritedMediaTypeTest() { + APIResponses responses = document.getPaths().get("/test/response").getGET().getResponses(); + // Test the default response doesn't exist + assertNull("The default response should be removed when not used.", responses.getDefault()); + + // Test the 200 response + assertNotNull("The 200 response should have been created.", responses.get("200")); + assertNotNull("The 200 response should return application/json.", + responses.get("200").getContent().get(APPLICATION_JSON)); + assertEquals("The 200 response application/json should match the specified schema.", "hello!", + responses.get("200").getContent().get(APPLICATION_JSON).getSchema().getDescription()); + assertNotNull("The 200 response should return application/xml.", + responses.get("200").getContent().get(APPLICATION_XML)); + assertEquals("The 200 response application/xml should match the specified schema.", "hello!", + responses.get("200").getContent().get(APPLICATION_XML).getSchema().getDescription()); } - @GET - @Produces({ APPLICATION_XML, TEXT_XML }) - @Path("/multiple") - public Integer multiple() { - return null; - } - - @Test - public void multipleResponsesTest() { - TestUtils.testResponse(document, "/test/response/multiple", HttpMethod.GET, - singletonMap(APPLICATION_XML, SchemaType.INTEGER)); - TestUtils.testResponse(document, "/test/response/multiple", HttpMethod.GET, - singletonMap(TEXT_XML, SchemaType.INTEGER)); - } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RootPathTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RootPathTest.java index 05054dbd94a..8e5676a1c29 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RootPathTest.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RootPathTest.java @@ -1,30 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ package fish.payara.microprofile.openapi.test.app.application; -import java.util.Collections; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; import javax.ws.rs.GET; import javax.ws.rs.Path; import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.PathItem.HttpMethod; -import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; import org.junit.BeforeClass; import org.junit.Test; import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; /** * A resource to test that a resource at the context root is mapped correctly. */ -@Path("/") +@Path("") public class RootPathTest { public transient static OpenAPI document; @BeforeClass public static void createDocument() { - document = new ApplicationProcessedDocument(); + try { + document = new ApplicationProcessedDocument(); + } catch (Exception ex) { + ex.printStackTrace(); + fail("Failed to build document."); + } } @GET @@ -34,8 +77,8 @@ public String getRoot() { @Test public void testRoot() { - TestUtils.testOperation(document, "/test", "getRoot", HttpMethod.GET); - TestUtils.testResponse(document, "/test", HttpMethod.GET, Collections.singletonMap("*/*", SchemaType.STRING)); - TestUtils.testParameter(document, "/test", HttpMethod.GET, null); + assertNotNull("The root resource wasn't found.", document.getPaths().get("/test")); + assertEquals("The root resource had the wrong origin.", "getRoot", + document.getPaths().get("/test").getGET().getOperationId()); } } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/ComponentTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/ComponentTest.java deleted file mode 100644 index f91ba8c9ed2..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/ComponentTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.data; - -import static org.junit.Assert.assertNull; - -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; -import fish.payara.microprofile.openapi.resource.util.TestUtils; - -/** - * A test component to be added to the model. - */ -@SuppressWarnings("unused") -public class ComponentTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new ApplicationProcessedDocument(); - } - - @Test - public void ignoreTransientFieldTest() { - assertNull(document.getComponents().getSchemas().get("ComponentTest").getProperties().get("document")); - } - - // Should be INTEGER type - private int id; - private Integer id2; - - @Test - public void testIntegers() { - TestUtils.testComponentProperty(document, "ComponentTest", "id", SchemaType.INTEGER); - TestUtils.testComponentProperty(document, "ComponentTest", "id2", SchemaType.INTEGER); - } - - // Should be NUMBER type - private float $ref; - private Float $ref2; - private double $ref3; - private Double $ref4; - - @Test - public void testNumbers() { - TestUtils.testComponentProperty(document, "ComponentTest", "$ref", SchemaType.NUMBER); - TestUtils.testComponentProperty(document, "ComponentTest", "$ref2", SchemaType.NUMBER); - TestUtils.testComponentProperty(document, "ComponentTest", "$ref3", SchemaType.NUMBER); - TestUtils.testComponentProperty(document, "ComponentTest", "$ref4", SchemaType.NUMBER); - } - - // Should be String type - private String this$0; - - @Test - public void testStrings() { - TestUtils.testComponentProperty(document, "ComponentTest", "this$0", SchemaType.STRING); - } - - // Should be BOOLEAN type - private boolean bool; - private Boolean bool2; - - @Test - public void testBooleans() { - TestUtils.testComponentProperty(document, "ComponentTest", "bool", SchemaType.BOOLEAN); - TestUtils.testComponentProperty(document, "ComponentTest", "bool2", SchemaType.BOOLEAN); - } - - // Should be OBJECT type - private Object _var_; - - @Test - public void testObjects() { - TestUtils.testComponentProperty(document, "ComponentTest", "_var_", SchemaType.OBJECT); - } - - // Should be ARRAY type - private String[] array; - private Object[] array2; - private int[][] array3; - - @Test - public void testArrays() { - TestUtils.testComponentProperty(document, "ComponentTest", "array", SchemaType.ARRAY, SchemaType.STRING); - TestUtils.testComponentProperty(document, "ComponentTest", "array2", SchemaType.ARRAY, SchemaType.OBJECT); - TestUtils.testComponentProperty(document, "ComponentTest", "array3", SchemaType.ARRAY, SchemaType.ARRAY, - SchemaType.INTEGER); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/SchemaComponentTest.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/SchemaComponentTest.java deleted file mode 100644 index 8bdd3eb134b..00000000000 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/SchemaComponentTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package fish.payara.microprofile.openapi.test.app.data; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import org.eclipse.microprofile.openapi.annotations.media.Schema; -import org.eclipse.microprofile.openapi.models.OpenAPI; -import org.junit.BeforeClass; -import org.junit.Test; - -import fish.payara.microprofile.openapi.resource.rule.AnnotationProcessedDocument; - -@Schema(name = "TestComponent", title = "Test Component", description = "A component for testing the @Schema annotation.") -public class SchemaComponentTest { - - public transient static OpenAPI document; - - @BeforeClass - public static void createDocument() { - document = new AnnotationProcessedDocument(); - } - - @Test - public void schemaCreationTest() { - org.eclipse.microprofile.openapi.models.media.Schema component = document.getComponents().getSchemas() - .get("TestComponent"); - assertNotNull("TestComponent was not found.", component); - assertEquals("TestComponent had the wrong title.", "Test Component", component.getTitle()); - assertEquals("TestComponent had the wrong description.", "A component for testing the @Schema annotation.", - component.getDescription()); - } - - @Schema(name = "componentHeight", title = "The height of the component.", example = "50m") - public int height; - - @Test - public void heightCreationTest() { - org.eclipse.microprofile.openapi.models.media.Schema component = document.getComponents().getSchemas() - .get("TestComponent").getProperties().get("componentHeight"); - assertNotNull("TestComponent had no height property.", component); - assertEquals("TestComponent height had the wrong type.", - org.eclipse.microprofile.openapi.models.media.Schema.SchemaType.INTEGER, component.getType()); - assertEquals("TestComponent height had the wrong example.", "50m", component.getExample()); - assertEquals("TestComponent height had the wrong title.", "The height of the component.", component.getTitle()); - } - - @Schema(title = "An ignored reference to Schema1", ref = "Schema1") - public Object reference; - - @Test - public void referenceCreationTest() { - org.eclipse.microprofile.openapi.models.media.Schema component = document.getComponents().getSchemas() - .get("TestComponent").getProperties().get("reference"); - assertNotNull("TestComponent had no reference property.", component); - assertNull("TestComponent reference had a type.", component.getType()); - assertNull("TestComponent title should be empty.", component.getTitle()); - assertEquals("TestComponent reference had the wrong reference.", "#/components/schemas/Schema1", - component.getRef()); - } - -} \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/TestComponent.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/TestComponent.java new file mode 100644 index 00000000000..0e65f086af8 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/data/TestComponent.java @@ -0,0 +1,78 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.microprofile.openapi.test.app.data; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; + +/** + * A test to check that schema objects without a @Schema annotation at the top are created. + */ +public class TestComponent { + + public transient static OpenAPI document; + + @BeforeClass + public static void createDocument() { + try { + document = new ApplicationProcessedDocument(); + } catch (Exception ex) { + ex.printStackTrace(); + fail("Failed to build document."); + } + } + + @Schema(description = "Test property") + private int property; + + @Test + public void pojoCreationTest() { + assertNotNull(document.getComponents().getSchemas().get("TestComponent")); + } + + +} \ No newline at end of file diff --git a/nucleus/packager/nucleus-jersey/pom.xml b/nucleus/packager/nucleus-jersey/pom.xml index 5fb97a83a3a..52fb8743ae9 100644 --- a/nucleus/packager/nucleus-jersey/pom.xml +++ b/nucleus/packager/nucleus-jersey/pom.xml @@ -40,7 +40,7 @@ holder. --> - + 4.0.0 @@ -138,14 +138,14 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml - - org.yaml - snakeyaml - com.fasterxml.jackson.dataformat jackson-dataformat-yaml + + org.yaml + snakeyaml + org.glassfish javax.json diff --git a/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/converters/StringArrayConverter.java b/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/converters/StringArrayConverter.java new file mode 100644 index 00000000000..02e1d0feff7 --- /dev/null +++ b/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/converters/StringArrayConverter.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright (c) [2018] Payara Foundation and/or its affiliates. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common Development + * and Distribution License("CDDL") (collectively, the "License"). You + * may not use this file except in compliance with the License. You can + * obtain a copy of the License at + * https://github.com/payara/Payara/blob/master/LICENSE.txt + * See the License for the specific + * language governing permissions and limitations under the License. + * + * When distributing the software, include this License Header Notice in each + * file and include the License file at glassfish/legal/LICENSE.txt. + * + * GPL Classpath Exception: + * The Payara Foundation designates this particular file as subject to the "Classpath" + * exception as provided by the Payara Foundation in the GPL Version 2 section of the License + * file that accompanied this code. + * + * Modifications: + * If applicable, add the following below the License Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyright [year] [name of copyright owner]" + * + * Contributor(s): + * If you wish your version of this file to be governed by only the CDDL or + * only the GPL Version 2, indicate your decision by adding "[Contributor] + * elects to include this software in this distribution under the [CDDL or GPL + * Version 2] license." If you don't indicate a single choice of license, a + * recipient has the option to distribute your version of this file under + * either the CDDL, the GPL Version 2 or to extend the choice of license to + * its licensees as provided above. However, if you add GPL Version 2 code + * and therefore, elected the GPL Version 2 license, then the option applies + * only if the new code is made subject to such option by the copyright + * holder. + */ +package fish.payara.nucleus.microprofile.config.converters; + +import javax.annotation.Priority; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.Converter; + +@Priority(1) +public class StringArrayConverter implements Converter { + + @Override + public String[] convert(String value) { + if (value == null || value.equals(ConfigProperty.UNCONFIGURED_VALUE)) + return null; + String[] result = null; + if (!value.contains(",")) { + result = new String[] { value }; + } else { + result = value.split(","); + } + return result; + } + +} diff --git a/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java b/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java index 9e0f30df4c3..8c92c84e4a5 100644 --- a/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java +++ b/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright (c) 2017-2018 Payara Foundation and/or its affiliates. All rights reserved. + * Copyright (c) [2017-2018] Payara Foundation and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development @@ -39,34 +39,6 @@ */ package fish.payara.nucleus.microprofile.config.spi; -import fish.payara.nucleus.microprofile.config.converters.BooleanConverter; -import fish.payara.nucleus.microprofile.config.converters.ChronoUnitConverter; -import fish.payara.nucleus.microprofile.config.converters.DoubleConverter; -import fish.payara.nucleus.microprofile.config.converters.DurationConverter; -import fish.payara.nucleus.microprofile.config.converters.FloatConverter; -import fish.payara.nucleus.microprofile.config.converters.InetAddressConverter; -import fish.payara.nucleus.microprofile.config.converters.InstantConverter; -import fish.payara.nucleus.microprofile.config.converters.IntegerConverter; -import fish.payara.nucleus.microprofile.config.converters.LocalDateConverter; -import fish.payara.nucleus.microprofile.config.converters.LocalDateTimeConverter; -import fish.payara.nucleus.microprofile.config.converters.LocalTimeConverter; -import fish.payara.nucleus.microprofile.config.converters.LongConverter; -import fish.payara.nucleus.microprofile.config.converters.OffsetDateTimeConverter; -import fish.payara.nucleus.microprofile.config.converters.OffsetTimeConverter; -import fish.payara.nucleus.microprofile.config.converters.URLConverter; -import fish.payara.nucleus.microprofile.config.source.ApplicationConfigSource; -import fish.payara.nucleus.microprofile.config.source.ClusterConfigSource; -import fish.payara.nucleus.microprofile.config.source.ConfigConfigSource; -import fish.payara.nucleus.microprofile.config.source.DomainConfigSource; -import fish.payara.nucleus.microprofile.config.source.EnvironmentConfigSource; -import fish.payara.nucleus.microprofile.config.source.JNDIConfigSource; -import fish.payara.nucleus.microprofile.config.source.ModuleConfigSource; -import fish.payara.nucleus.microprofile.config.source.PasswordAliasConfigSource; -import fish.payara.nucleus.microprofile.config.source.PayaraServerProperties; -import fish.payara.nucleus.microprofile.config.source.PropertiesConfigSource; -import fish.payara.nucleus.microprofile.config.source.SecretsDirConfigSource; -import fish.payara.nucleus.microprofile.config.source.ServerConfigSource; -import fish.payara.nucleus.microprofile.config.source.SystemPropertyConfigSource; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -79,9 +51,11 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; + import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Named; + import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.spi.ConfigBuilder; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; @@ -100,6 +74,36 @@ import org.jvnet.hk2.annotations.Optional; import org.jvnet.hk2.annotations.Service; +import fish.payara.nucleus.microprofile.config.converters.BooleanConverter; +import fish.payara.nucleus.microprofile.config.converters.ChronoUnitConverter; +import fish.payara.nucleus.microprofile.config.converters.DoubleConverter; +import fish.payara.nucleus.microprofile.config.converters.DurationConverter; +import fish.payara.nucleus.microprofile.config.converters.FloatConverter; +import fish.payara.nucleus.microprofile.config.converters.InetAddressConverter; +import fish.payara.nucleus.microprofile.config.converters.InstantConverter; +import fish.payara.nucleus.microprofile.config.converters.IntegerConverter; +import fish.payara.nucleus.microprofile.config.converters.LocalDateConverter; +import fish.payara.nucleus.microprofile.config.converters.LocalDateTimeConverter; +import fish.payara.nucleus.microprofile.config.converters.LocalTimeConverter; +import fish.payara.nucleus.microprofile.config.converters.LongConverter; +import fish.payara.nucleus.microprofile.config.converters.OffsetDateTimeConverter; +import fish.payara.nucleus.microprofile.config.converters.OffsetTimeConverter; +import fish.payara.nucleus.microprofile.config.converters.StringArrayConverter; +import fish.payara.nucleus.microprofile.config.converters.URLConverter; +import fish.payara.nucleus.microprofile.config.source.ApplicationConfigSource; +import fish.payara.nucleus.microprofile.config.source.ClusterConfigSource; +import fish.payara.nucleus.microprofile.config.source.ConfigConfigSource; +import fish.payara.nucleus.microprofile.config.source.DomainConfigSource; +import fish.payara.nucleus.microprofile.config.source.EnvironmentConfigSource; +import fish.payara.nucleus.microprofile.config.source.JNDIConfigSource; +import fish.payara.nucleus.microprofile.config.source.ModuleConfigSource; +import fish.payara.nucleus.microprofile.config.source.PasswordAliasConfigSource; +import fish.payara.nucleus.microprofile.config.source.PayaraServerProperties; +import fish.payara.nucleus.microprofile.config.source.PropertiesConfigSource; +import fish.payara.nucleus.microprofile.config.source.SecretsDirConfigSource; +import fish.payara.nucleus.microprofile.config.source.ServerConfigSource; +import fish.payara.nucleus.microprofile.config.source.SystemPropertyConfigSource; + /** * This Service implements the Microprofile Config API and provides integration * into the guts of Payara Server. @@ -374,6 +378,7 @@ List getDiscoveredSources(ApplicationInfo appInfo) { List getDefaultConverters() { LinkedList result = new LinkedList<>(); + result.add(new StringArrayConverter()); result.add(new BooleanConverter()); result.add(new IntegerConverter()); result.add(new LongConverter()); From 99370d7a433f9e86c0a54c9a00a21042e9d98f77 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Mon, 21 May 2018 13:08:42 +0100 Subject: [PATCH 11/20] Patched config API and OpenAPI to search root META-INF as well. --- .../impl/processor/ApplicationProcessor.java | 1 - .../openapi/impl/processor/FileProcessor.java | 13 +++++----- .../spi/ConfigProviderResolverImpl.java | 25 ++++++++++++------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java index 596cb47a1c8..7e4be51c237 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -748,7 +748,6 @@ private Set> getClassesFromLoader(ClassLoader classLoader) { Field classesField = ClassLoader.class.getDeclaredField("classes"); classesField.setAccessible(true); classes = new HashSet<>((Vector>) classesField.get(classLoader)); - classesField.setAccessible(false); } catch (Exception ex) { LOGGER.log(Level.WARNING, "Unable to get classes from classloader.", ex); } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java index 32b3a002f38..f2ca5932a55 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java @@ -79,12 +79,13 @@ public FileProcessor(ClassLoader appClassLoader) { try { // Search for a the correct file URL fileUrl = appClassLoader.getResource("META-INF/openapi.json"); - if (fileUrl == null) { - fileUrl = appClassLoader.getResource("META-INF/openapi.yaml"); - if (fileUrl == null) { - fileUrl = appClassLoader.getResource("META-INF/openapi.yml"); - } - } + fileUrl = (fileUrl != null) ? fileUrl : appClassLoader.getResource("../../META-INF/openapi.json"); + fileUrl = (fileUrl != null) ? fileUrl : appClassLoader.getResource("META-INF/openapi.yaml"); + fileUrl = (fileUrl != null) ? fileUrl : appClassLoader.getResource("../../META-INF/openapi.yaml"); + fileUrl = (fileUrl != null) ? fileUrl : appClassLoader.getResource("META-INF/openapi.yml"); + fileUrl = (fileUrl != null) ? fileUrl : appClassLoader.getResource("../../META-INF/openapi.yml"); + + // If the file is found, configure the public variables if (fileUrl != null) { file = new File(fileUrl.toURI()); if (file.getPath().endsWith(".json")) { diff --git a/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java b/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java index 8c92c84e4a5..f46906a0982 100644 --- a/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java +++ b/nucleus/payara-modules/nucleus-microprofile/config-service/src/main/java/fish/payara/nucleus/microprofile/config/spi/ConfigProviderResolverImpl.java @@ -417,18 +417,25 @@ private void initialiseApplicationConfig(ApplicationInfo info) { info.addTransientAppMetaData(APP_METADATA_KEY, appConfigProperties); try { // Read application defined properties and add as transient metadata - Enumeration resources = info.getAppClassLoader().getResources("META-INF/microprofile-config.properties"); - while (resources.hasMoreElements()) { - URL url = resources.nextElement(); - Properties p = new Properties(); - try (InputStream is = url.openStream()) { - p.load(url.openStream()); - } - appConfigProperties.add(p); - } + appConfigProperties.add(getPropertiesFromFile(info.getAppClassLoader(), "META-INF/microprofile-config.properties")); + appConfigProperties.add(getPropertiesFromFile(info.getAppClassLoader(), "../../META-INF/microprofile-config.properties")); } catch (IOException ex) { Logger.getLogger(ConfigProviderResolverImpl.class.getName()).log(Level.SEVERE, null, ex); } } + private Properties getPropertiesFromFile(ClassLoader appClassLoader, String fileName) throws IOException { + // Read application defined properties and add as transient metadata + Enumeration resources = appClassLoader.getResources(fileName); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + Properties p = new Properties(); + try (InputStream is = url.openStream()) { + p.load(url.openStream()); + } + return p; + } + return new Properties(); + } + } From 52f26b12d48047399a05209c973ecafb4da59d25 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Tue, 22 May 2018 09:33:43 +0100 Subject: [PATCH 12/20] Moved OpenAPI logging into service, and disabled OpenAPI for non WARs. --- .../openapi/impl/OpenApiService.java | 51 ++++++++++++++++++- .../admin/SetOpenApiConfigurationCommand.java | 6 --- .../rest/app/service/OpenApiResource.java | 1 + 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java index 7a254d480d2..10d5d6e71fb 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java @@ -39,6 +39,7 @@ */ package fish.payara.microprofile.openapi.impl; +import java.beans.PropertyChangeEvent; import java.util.Collections; import java.util.Deque; import java.util.Map; @@ -59,6 +60,12 @@ import org.glassfish.internal.deployment.Deployment; import org.glassfish.web.deployment.descriptor.WebBundleDescriptorImpl; import org.jvnet.hk2.annotations.Service; +import org.jvnet.hk2.config.Changed; +import org.jvnet.hk2.config.ConfigBeanProxy; +import org.jvnet.hk2.config.ConfigListener; +import org.jvnet.hk2.config.ConfigSupport; +import org.jvnet.hk2.config.NotProcessed; +import org.jvnet.hk2.config.UnprocessedChangeEvents; import fish.payara.microprofile.openapi.impl.admin.OpenApiServiceConfiguration; import fish.payara.microprofile.openapi.impl.config.OpenApiConfiguration; @@ -71,7 +78,7 @@ @Service(name = "microprofile-openapi-service") @RunLevel(StartupRunLevel.VAL) -public class OpenApiService implements PostConstruct, PreDestroy, EventListener { +public class OpenApiService implements PostConstruct, PreDestroy, EventListener, ConfigListener { private static final Logger LOGGER = Logger.getLogger(OpenApiService.class.getName()); @@ -98,6 +105,31 @@ public boolean isEnabled() { return Boolean.parseBoolean(config.getEnabled()); } + /** + * Listen for OpenAPI config changes. + */ + @Override + public UnprocessedChangeEvents changed(PropertyChangeEvent[] event) { + return ConfigSupport.sortAndDispatch(event, new Changed(){ + @Override + public NotProcessed changed(TYPE type, Class tClass, T t) { + if (tClass == OpenApiServiceConfiguration.class) { + if (type == TYPE.CHANGE) { + if (isEnabled()) { + LOGGER.info("OpenAPIService enabled."); + } else { + LOGGER.info("OpenAPIService disabled."); + } + } + } + return null; + } + }, LOGGER); + } + + /** + * Listen for application deployment events. + */ @Override public void event(Event event) { if (event.is(Deployment.APPLICATION_STARTED)) { @@ -105,11 +137,16 @@ public void event(Event event) { ApplicationInfo appInfo = (ApplicationInfo) event.hook(); // Create all the relevant resources - if (isEnabled()) { + if (isEnabled() && isValidApp(appInfo)) { + // Create the OpenAPI config OpenApiConfiguration appConfig = new OpenApiConfiguration(appInfo.getAppClassLoader()); + + // Map the application info to a new openapi document, and store it in the list Map map = Collections.singletonMap(appInfo, createOpenApiDocument(appInfo.getAppClassLoader(), getContextRoot(appInfo), appConfig)); models.add(map); + + LOGGER.info("OpenAPI document created."); } } else if (event.is(Deployment.APPLICATION_UNLOADED)) { ApplicationInfo appInfo = (ApplicationInfo) event.hook(); @@ -122,6 +159,16 @@ public void event(Event event) { } } + /** + * @return boolean if the app is a valid target for an OpenAPI document. + */ + private boolean isValidApp(ApplicationInfo appInfo) { + return appInfo.getMetaData(WebBundleDescriptorImpl.class) != null; + } + + /** + * @return boolean the context root of the application. + */ private String getContextRoot(ApplicationInfo appInfo) { return appInfo.getMetaData(WebBundleDescriptorImpl.class).getContextRoot(); } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java index fa48c8e4634..b3e58c9402e 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java @@ -106,12 +106,6 @@ public void execute(AdminCommandContext adminCommandContext) { adminCommandContext.getActionReport().failure(LOGGER, "Failed to update OpenAPI configuration", ex); return; } - - if (enabled) { - LOGGER.info("OpenAPIService enabled."); - } else { - LOGGER.info("OpenAPIService disabled."); - } } } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java index ddd3aed58fe..b481b231cd4 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java @@ -71,6 +71,7 @@ public Response getResponse(@Context HttpServletResponse response) throws IOExce // If the server is disabled, throw an error if (!OpenApiService.getInstance().isEnabled()) { response.sendError(FORBIDDEN.getStatusCode(), "OpenAPI Service is disabled."); + return Response.status(FORBIDDEN).build(); } // Get the OpenAPI document From a227301a5f85eb30d5a335001a720b32fe159f66 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Wed, 23 May 2018 09:26:36 +0100 Subject: [PATCH 13/20] Fixed embedded classloader issue. --- .../openapi/impl/model/OpenAPIImpl.java | 5 +++ .../impl/model/examples/ExampleImpl.java | 3 +- .../impl/model/headers/HeaderImpl.java | 3 +- .../openapi/impl/model/links/LinkImpl.java | 5 ++- .../impl/processor/ApplicationProcessor.java | 45 +++++++++++++++++++ 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java index 096eeac49ee..2cc5d900744 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java @@ -63,6 +63,11 @@ public class OpenAPIImpl extends ExtensibleImpl implements OpenAPI { + /** + * The name of a variable in the model tree that is unrecognised. + */ + public static transient String UNKNOWN_NAME = "?"; + protected String openapi; protected Info info; protected ExternalDocumentation externalDocs; diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java index 0d5f1e83775..2eb501b8885 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java @@ -39,6 +39,7 @@ */ package fish.payara.microprofile.openapi.impl.model.examples; +import static fish.payara.microprofile.openapi.impl.model.OpenAPIImpl.UNKNOWN_NAME; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; @@ -158,7 +159,7 @@ public static void merge(ExampleObject example, Map examples, b // Get the example name String exampleName = example.name(); if (example.name() == null || example.name().isEmpty()) { - exampleName = "?"; + exampleName = UNKNOWN_NAME; } // Get or create the example diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java index 5de0243bd9c..69484692d88 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java @@ -39,6 +39,7 @@ */ package fish.payara.microprofile.openapi.impl.model.headers; +import static fish.payara.microprofile.openapi.impl.model.OpenAPIImpl.UNKNOWN_NAME; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; @@ -284,7 +285,7 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.headers.He // Get the header name String headerName = header.name(); if (header.name() == null || header.name().isEmpty()) { - headerName = "?"; + headerName = UNKNOWN_NAME; } // Get or create the header diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java index 336f86b7e5d..ed2a1ea6ee8 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java @@ -39,6 +39,7 @@ */ package fish.payara.microprofile.openapi.impl.model.links; +import static fish.payara.microprofile.openapi.impl.model.OpenAPIImpl.UNKNOWN_NAME; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; @@ -209,7 +210,7 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.links.Link // Get the link name String linkName = link.name(); if (link.name() == null || link.name().isEmpty()) { - linkName = "?"; + linkName = UNKNOWN_NAME; } // Get or create the link @@ -231,7 +232,7 @@ private static void applyLinkParameter(LinkParameter parameter, Map> getClassesFromLoader(ClassLoader classLoader) { } catch (Exception ex) { LOGGER.log(Level.WARNING, "Unable to get classes from classloader.", ex); } + + // If no classes were found, the classloader could be deploying from a directory. + // If so, scan the directory structure for expected classes. + if (classes.isEmpty()) { + LOGGER.fine("Unable to find loaded classes in classloader, searching in classpath for files."); + try { + // Get the classpath url. + // If the classpath is currently WEB-INF/lib, resolve WEB-INF/classes instead + URI classpath = classLoader.getResource("../classes").toURI(); + + if (classpath != null) { + + List expand = new LinkedList<>(); + expand.add(new File(classpath)); + + while (!expand.isEmpty()) { + List subFiles = new LinkedList<>(); + for (File file : expand) { + if (file.isDirectory()) { + subFiles.addAll(Arrays.asList(file.listFiles())); + } else if (file.getPath().endsWith(".class")) { + String className = file.getPath().replaceAll(".+WEB-INF/classes/", "").replace("/", ".").replace(".class", ""); + LOGGER.finer("Adding class: " + className); + try { + classes.add(Class.forName(className)); + } catch (ClassNotFoundException ex) { + LOGGER.log(Level.WARNING, "Unable to find class.", ex); + } + } + } + expand.clear(); + expand.addAll(subFiles); + } + } else { + LOGGER.log(Level.WARNING, "Unrecognised classpath."); + } + } catch (URISyntaxException ex) { + LOGGER.log(Level.WARNING, "Unable to get classes from classpath.", ex); + } + } + return classes; } From 770db5ccddf2248bfd45aac7498f0da4dab89375 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Wed, 23 May 2018 10:13:11 +0100 Subject: [PATCH 14/20] Removed harmless ClassNotFound errors to allow for arquillian tests. --- .../openapi/impl/processor/ApplicationProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java index dcea420afa4..ff5c742b828 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -777,11 +777,11 @@ private Set> getClassesFromLoader(ClassLoader classLoader) { subFiles.addAll(Arrays.asList(file.listFiles())); } else if (file.getPath().endsWith(".class")) { String className = file.getPath().replaceAll(".+WEB-INF/classes/", "").replace("/", ".").replace(".class", ""); - LOGGER.finer("Adding class: " + className); + LOGGER.finer("Attempting to add class: " + className); try { classes.add(Class.forName(className)); } catch (ClassNotFoundException ex) { - LOGGER.log(Level.WARNING, "Unable to find class.", ex); + LOGGER.finer("Unable to add class: " + className); } } } From ea1e84fcf1124cfba76806f4a58ba69826a212a5 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Wed, 23 May 2018 10:46:52 +0100 Subject: [PATCH 15/20] Fixed NPE if no classes are present. --- .../openapi/impl/processor/ApplicationProcessor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java index ff5c742b828..fff44e6330e 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -46,6 +46,7 @@ import java.lang.reflect.Modifier; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -763,12 +764,12 @@ private Set> getClassesFromLoader(ClassLoader classLoader) { try { // Get the classpath url. // If the classpath is currently WEB-INF/lib, resolve WEB-INF/classes instead - URI classpath = classLoader.getResource("../classes").toURI(); + URL classpath = classLoader.getResource("../classes"); if (classpath != null) { List expand = new LinkedList<>(); - expand.add(new File(classpath)); + expand.add(new File(classpath.toURI())); while (!expand.isEmpty()) { List subFiles = new LinkedList<>(); From 3ca3a1829b45969fc27673bd66c88cce398cc3c7 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Thu, 24 May 2018 10:50:17 +0100 Subject: [PATCH 16/20] Fixed a bunch of sonarqube issues. --- .../openapi/impl/OpenApiService.java | 2 +- .../admin/GetOpenApiConfigurationCommand.java | 18 ++-- .../admin/SetOpenApiConfigurationCommand.java | 27 +++--- .../impl/config/OpenApiConfiguration.java | 6 +- .../openapi/impl/model/ComponentsImpl.java | 10 +-- .../impl/model/OASFactoryResolverImpl.java | 85 +++++++++++-------- .../openapi/impl/model/OpenAPIImpl.java | 7 +- .../impl/model/examples/ExampleImpl.java | 4 +- .../impl/model/headers/HeaderImpl.java | 4 +- .../openapi/impl/model/links/LinkImpl.java | 6 +- .../openapi/impl/model/media/SchemaImpl.java | 20 +++-- .../security/SecurityRequirementImpl.java | 2 +- .../openapi/impl/model/util/ModelUtils.java | 21 +++-- .../impl/processor/ApplicationProcessor.java | 41 ++++----- .../openapi/impl/processor/BaseProcessor.java | 9 +- .../openapi/impl/processor/FileProcessor.java | 6 +- .../impl/processor/FilterProcessor.java | 21 ++--- .../impl/processor/ModelReaderProcessor.java | 8 +- .../app/provider/ObjectMapperFactory.java | 6 ++ .../app/provider/mixin/ExtensionsMixin.java | 2 +- .../app/provider/writer/AbstractWriter.java | 4 +- .../rest/app/service/OpenApiResource.java | 2 +- .../openapi/impl/visitor/OpenApiWalker.java | 37 ++++---- 23 files changed, 187 insertions(+), 161 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java index 10d5d6e71fb..7cbb69ee6a3 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java @@ -110,7 +110,7 @@ public boolean isEnabled() { */ @Override public UnprocessedChangeEvents changed(PropertyChangeEvent[] event) { - return ConfigSupport.sortAndDispatch(event, new Changed(){ + return ConfigSupport.sortAndDispatch(event, new Changed() { @Override public NotProcessed changed(TYPE type, Class tClass, T t) { if (tClass == OpenApiServiceConfiguration.class) { diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/GetOpenApiConfigurationCommand.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/GetOpenApiConfigurationCommand.java index 238441141dd..ba7ac57afea 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/GetOpenApiConfigurationCommand.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/GetOpenApiConfigurationCommand.java @@ -53,9 +53,6 @@ import javax.inject.Inject; -import com.sun.enterprise.config.serverbeans.Config; -import com.sun.enterprise.util.ColumnFormatter; - import org.glassfish.api.ActionReport; import org.glassfish.api.Param; import org.glassfish.api.admin.AdminCommand; @@ -89,21 +86,17 @@ public class GetOpenApiConfigurationCommand implements AdminCommand { @Override public void execute(AdminCommandContext adminCommandContext) { - Config targetConfig = targetUtil.getConfig(target); - - if (targetConfig == null) { + // Check for the existing config + if (targetUtil.getConfig(target) == null) { adminCommandContext.getActionReport().setMessage("No such config name: " + targetUtil); adminCommandContext.getActionReport().setActionExitCode(ActionReport.ExitCode.FAILURE); return; } - OpenApiServiceConfiguration openApiConfig = targetConfig.getExtensionByType(OpenApiServiceConfiguration.class); + OpenApiServiceConfiguration openApiConfig = targetUtil.getConfig(target) + .getExtensionByType(OpenApiServiceConfiguration.class); - ColumnFormatter columnFormatter = new ColumnFormatter(new String[] { "Enabled" }); - Object[] outputValues = { openApiConfig.getEnabled() }; - columnFormatter.addRow(outputValues); - - adminCommandContext.getActionReport().appendMessage(columnFormatter.toString()); + adminCommandContext.getActionReport().appendMessage("Enabled: " + openApiConfig.getEnabled()); Map extraPropertiesMap = new HashMap<>(); extraPropertiesMap.put("enabled", openApiConfig.getEnabled()); @@ -112,4 +105,5 @@ public void execute(AdminCommandContext adminCommandContext) { extraProperties.put("openApiConfiguration", extraPropertiesMap); adminCommandContext.getActionReport().setExtraProperties(extraProperties); } + } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java index b3e58c9402e..407b1372437 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java @@ -51,8 +51,7 @@ import javax.inject.Inject; -import com.sun.enterprise.config.serverbeans.Config; - +import org.glassfish.api.ActionReport; import org.glassfish.api.Param; import org.glassfish.api.admin.AdminCommand; import org.glassfish.api.admin.AdminCommandContext; @@ -80,21 +79,28 @@ public class SetOpenApiConfigurationCommand implements AdminCommand { private static final Logger LOGGER = Logger.getLogger(SetOpenApiConfigurationCommand.class.getName()); - + @Inject private Target targetUtil; - + @Param(name = "enabled", optional = true) private Boolean enabled; - + @Param(optional = true, defaultValue = "server-config") private String target; - + @Override public void execute(AdminCommandContext adminCommandContext) { - Config targetConfig = targetUtil.getConfig(target); - OpenApiServiceConfiguration openApiConfig = targetConfig.getExtensionByType(OpenApiServiceConfiguration.class); - + // Check for the existing config + if (targetUtil.getConfig(target) == null) { + adminCommandContext.getActionReport().setMessage("No such config name: " + targetUtil); + adminCommandContext.getActionReport().setActionExitCode(ActionReport.ExitCode.FAILURE); + return; + } + + OpenApiServiceConfiguration openApiConfig = targetUtil.getConfig(target) + .getExtensionByType(OpenApiServiceConfiguration.class); + try { ConfigSupport.apply(configProxy -> { if (enabled != null) { @@ -104,8 +110,7 @@ public void execute(AdminCommandContext adminCommandContext) { }, openApiConfig); } catch (TransactionFailure ex) { adminCommandContext.getActionReport().failure(LOGGER, "Failed to update OpenAPI configuration", ex); - return; } } - + } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java index 3b25f5e4336..fb104683f8b 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java @@ -39,6 +39,7 @@ */ package fish.payara.microprofile.openapi.impl.config; +import static java.util.logging.Level.WARNING; import static java.util.stream.Collectors.toSet; import java.util.ArrayList; @@ -49,7 +50,6 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; -import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.microprofile.config.Config; @@ -192,7 +192,7 @@ public Set> getValidClasses(Set> classes) { // If exclude packages are specified, check that the class package doesn't start // with any in the list .filter(clazz -> excludePackages.isEmpty() - || !excludePackages.stream().anyMatch(pkg -> clazz.getPackage().getName().startsWith(pkg))) + || excludePackages.stream().noneMatch(pkg -> clazz.getPackage().getName().startsWith(pkg))) .collect(toSet()); } @@ -336,7 +336,7 @@ private Class getClassFromName(String className, ClassLoader classLoader) { try { return classLoader.loadClass(className); } catch (ClassNotFoundException ex) { - LOGGER.log(Level.WARNING, "Unable to find class.", ex); + LOGGER.log(WARNING, "Unable to find class.", ex); } return null; } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java index 0b668c9b0fa..95f05cf6068 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java @@ -292,12 +292,10 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.Components // Handle @Callback if (from.callbacks() != null) { for (org.eclipse.microprofile.openapi.annotations.callbacks.Callback callback : from.callbacks()) { - if (callback != null) { - if (callback.name() != null) { - Callback newCallback = new CallbackImpl(); - CallbackImpl.merge(callback, newCallback, override, currentSchemas); - to.addCallback(callback.name(), newCallback); - } + if (callback != null && callback.name() != null) { + Callback newCallback = new CallbackImpl(); + CallbackImpl.merge(callback, newCallback, override, currentSchemas); + to.addCallback(callback.name(), newCallback); } } } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OASFactoryResolverImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OASFactoryResolverImpl.java index aab1b130548..b359ef293cd 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OASFactoryResolverImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OASFactoryResolverImpl.java @@ -39,6 +39,8 @@ */ package fish.payara.microprofile.openapi.impl.model; +import static java.util.Collections.unmodifiableMap; + import java.util.HashMap; import java.util.Map; @@ -106,40 +108,45 @@ public class OASFactoryResolverImpl extends OASFactoryResolver { - public static final Map, Class> MODEL_MAP = new HashMap<>(); + /** + * A map of each OpenAPI model element to the class that models it. + */ + public static final Map, Class> MODEL_MAP; static { - MODEL_MAP.put(Components.class, ComponentsImpl.class); - MODEL_MAP.put(ExternalDocumentation.class, ExternalDocumentationImpl.class); - MODEL_MAP.put(OpenAPI.class, OpenAPIImpl.class); - MODEL_MAP.put(Operation.class, OperationImpl.class); - MODEL_MAP.put(PathItem.class, PathItemImpl.class); - MODEL_MAP.put(Paths.class, PathsImpl.class); - MODEL_MAP.put(Callback.class, CallbackImpl.class); - MODEL_MAP.put(Example.class, ExampleImpl.class); - MODEL_MAP.put(Header.class, HeaderImpl.class); - MODEL_MAP.put(Contact.class, ContactImpl.class); - MODEL_MAP.put(Info.class, InfoImpl.class); - MODEL_MAP.put(License.class, LicenseImpl.class); - MODEL_MAP.put(Link.class, LinkImpl.class); - MODEL_MAP.put(Content.class, ContentImpl.class); - MODEL_MAP.put(Discriminator.class, DiscriminatorImpl.class); - MODEL_MAP.put(Encoding.class, EncodingImpl.class); - MODEL_MAP.put(MediaType.class, MediaTypeImpl.class); - MODEL_MAP.put(Schema.class, SchemaImpl.class); - MODEL_MAP.put(XML.class, XMLImpl.class); - MODEL_MAP.put(Parameter.class, ParameterImpl.class); - MODEL_MAP.put(RequestBody.class, RequestBodyImpl.class); - MODEL_MAP.put(APIResponse.class, APIResponseImpl.class); - MODEL_MAP.put(APIResponses.class, APIResponsesImpl.class); - MODEL_MAP.put(OAuthFlow.class, OAuthFlowImpl.class); - MODEL_MAP.put(OAuthFlows.class, OAuthFlowsImpl.class); - MODEL_MAP.put(Scopes.class, ScopesImpl.class); - MODEL_MAP.put(SecurityRequirement.class, SecurityRequirementImpl.class); - MODEL_MAP.put(SecurityScheme.class, SecuritySchemeImpl.class); - MODEL_MAP.put(Server.class, ServerImpl.class); - MODEL_MAP.put(ServerVariable.class, ServerVariableImpl.class); - MODEL_MAP.put(ServerVariables.class, ServerVariablesImpl.class); - MODEL_MAP.put(Tag.class, TagImpl.class); + Map, Class> map = new HashMap<>(); + map.put(Components.class, ComponentsImpl.class); + map.put(ExternalDocumentation.class, ExternalDocumentationImpl.class); + map.put(OpenAPI.class, OpenAPIImpl.class); + map.put(Operation.class, OperationImpl.class); + map.put(PathItem.class, PathItemImpl.class); + map.put(Paths.class, PathsImpl.class); + map.put(Callback.class, CallbackImpl.class); + map.put(Example.class, ExampleImpl.class); + map.put(Header.class, HeaderImpl.class); + map.put(Contact.class, ContactImpl.class); + map.put(Info.class, InfoImpl.class); + map.put(License.class, LicenseImpl.class); + map.put(Link.class, LinkImpl.class); + map.put(Content.class, ContentImpl.class); + map.put(Discriminator.class, DiscriminatorImpl.class); + map.put(Encoding.class, EncodingImpl.class); + map.put(MediaType.class, MediaTypeImpl.class); + map.put(Schema.class, SchemaImpl.class); + map.put(XML.class, XMLImpl.class); + map.put(Parameter.class, ParameterImpl.class); + map.put(RequestBody.class, RequestBodyImpl.class); + map.put(APIResponse.class, APIResponseImpl.class); + map.put(APIResponses.class, APIResponsesImpl.class); + map.put(OAuthFlow.class, OAuthFlowImpl.class); + map.put(OAuthFlows.class, OAuthFlowsImpl.class); + map.put(Scopes.class, ScopesImpl.class); + map.put(SecurityRequirement.class, SecurityRequirementImpl.class); + map.put(SecurityScheme.class, SecuritySchemeImpl.class); + map.put(Server.class, ServerImpl.class); + map.put(ServerVariable.class, ServerVariableImpl.class); + map.put(ServerVariables.class, ServerVariablesImpl.class); + map.put(Tag.class, TagImpl.class); + MODEL_MAP = unmodifiableMap(map); } @SuppressWarnings("unchecked") @@ -162,7 +169,17 @@ public T createObject(Class clazz) { try { return (T) implClass.newInstance(); } catch (Exception e) { - throw new RuntimeException(e); + throw new OpenAPIClassCreationException(e); } } + + private class OpenAPIClassCreationException extends RuntimeException { + + private static final long serialVersionUID = 7668110028310822354L; + + OpenAPIClassCreationException(Exception ex) { + super(ex); + } + } + } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java index 2cc5d900744..371eba7b123 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java @@ -63,11 +63,6 @@ public class OpenAPIImpl extends ExtensibleImpl implements OpenAPI { - /** - * The name of a variable in the model tree that is unrecognised. - */ - public static transient String UNKNOWN_NAME = "?"; - protected String openapi; protected Info info; protected ExternalDocumentation externalDocs; @@ -276,7 +271,7 @@ public static void merge(OpenAPIDefinition from, OpenAPI to, boolean override) { .security()) { if (!isAnnotationNull(requirement)) { SecurityRequirement newRequirement = new SecurityRequirementImpl(); - SecurityRequirementImpl.merge(requirement, newRequirement, override); + SecurityRequirementImpl.merge(requirement, newRequirement); if (!to.getSecurity().contains(newRequirement)) { to.addSecurityRequirement(newRequirement); } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java index 2eb501b8885..b042d9577de 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java @@ -39,7 +39,7 @@ */ package fish.payara.microprofile.openapi.impl.model.examples; -import static fish.payara.microprofile.openapi.impl.model.OpenAPIImpl.UNKNOWN_NAME; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.UNKNOWN_ELEMENT_NAME; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; @@ -159,7 +159,7 @@ public static void merge(ExampleObject example, Map examples, b // Get the example name String exampleName = example.name(); if (example.name() == null || example.name().isEmpty()) { - exampleName = UNKNOWN_NAME; + exampleName = UNKNOWN_ELEMENT_NAME; } // Get or create the example diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java index 69484692d88..469f19aade1 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java @@ -39,7 +39,7 @@ */ package fish.payara.microprofile.openapi.impl.model.headers; -import static fish.payara.microprofile.openapi.impl.model.OpenAPIImpl.UNKNOWN_NAME; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.UNKNOWN_ELEMENT_NAME; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; @@ -285,7 +285,7 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.headers.He // Get the header name String headerName = header.name(); if (header.name() == null || header.name().isEmpty()) { - headerName = UNKNOWN_NAME; + headerName = UNKNOWN_ELEMENT_NAME; } // Get or create the header diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java index ed2a1ea6ee8..d4929ab43b0 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java @@ -39,7 +39,7 @@ */ package fish.payara.microprofile.openapi.impl.model.links; -import static fish.payara.microprofile.openapi.impl.model.OpenAPIImpl.UNKNOWN_NAME; +import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.UNKNOWN_ELEMENT_NAME; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.applyReference; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.isAnnotationNull; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.mergeProperty; @@ -210,7 +210,7 @@ public static void merge(org.eclipse.microprofile.openapi.annotations.links.Link // Get the link name String linkName = link.name(); if (link.name() == null || link.name().isEmpty()) { - linkName = UNKNOWN_NAME; + linkName = UNKNOWN_ELEMENT_NAME; } // Get or create the link @@ -232,7 +232,7 @@ private static void applyLinkParameter(LinkParameter parameter, Map @@ -378,7 +386,7 @@ public static String getParameterName(Parameter parameter) { return null; } - public static boolean isAnnotationNull(Annotation annotation) { + public static boolean isAnnotationNull(Annotation annotation) { if (annotation == null) { return true; } @@ -390,7 +398,7 @@ public static boolean isAnnotationNull(Annotation anno if (value != null) { if (value.getClass().isArray() && Array.getLength(value) > 0) { return false; - } else if (value instanceof Collection && Collection.class.cast(value).size() > 0) { + } else if (value instanceof Collection && !Collection.class.cast(value).isEmpty()) { return false; } else if (value instanceof Boolean && Boolean.class.cast(value)) { return false; @@ -406,8 +414,8 @@ public static boolean isAnnotationNull(Annotation anno allNull = isAnnotationNull((Annotation) value); } } - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - e.printStackTrace(); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + LOGGER.log(WARNING, "Unable to access annotation element.", ex); } } } @@ -486,6 +494,7 @@ public static void overwrite(T from, T to) { } } + @SuppressWarnings("unchecked") public static void merge(T from, T to, boolean override) { if (from != null && to != null) { for (Field f : to.getClass().getDeclaredFields()) { diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java index fff44e6330e..60902be41ae 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -39,12 +39,14 @@ */ package fish.payara.microprofile.openapi.impl.processor; +import static java.util.logging.Level.FINE; +import static java.util.logging.Level.WARNING; + import java.io.File; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Arrays; @@ -56,7 +58,6 @@ import java.util.Map; import java.util.Set; import java.util.Vector; -import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -451,9 +452,8 @@ public void visitSchema(Schema schema, AnnotatedElement element, ApiContext cont // Get the parent schema object name String parentName = null; - try { + if (Field.class.cast(element).getDeclaringClass().isAnnotationPresent(Schema.class)) { parentName = Field.class.cast(element).getDeclaringClass().getDeclaredAnnotation(Schema.class).name(); - } catch (NullPointerException ex) { } if (parentName == null || parentName.isEmpty()) { parentName = Field.class.cast(element).getDeclaringClass().getSimpleName(); @@ -567,8 +567,8 @@ public void visitAPIResponse(APIResponse apiResponse, AnnotatedElement element, if (apiResponse.responseCode() != null && !apiResponse.responseCode().isEmpty() && !apiResponse.responseCode() .equals(org.eclipse.microprofile.openapi.models.responses.APIResponses.DEFAULT)) { // If the element doesn't also contain a response mapping to the default - if (!Arrays.asList(element.getDeclaredAnnotationsByType(APIResponse.class)).stream() - .anyMatch(a -> a.responseCode() == null || a.responseCode().isEmpty() || a.responseCode() + if (Arrays.asList(element.getDeclaredAnnotationsByType(APIResponse.class)).stream() + .noneMatch(a -> a.responseCode() == null || a.responseCode().isEmpty() || a.responseCode() .equals(org.eclipse.microprofile.openapi.models.responses.APIResponses.DEFAULT))) { // Then remove the default response context.getWorkingOperation().getResponses() @@ -723,7 +723,7 @@ public void visitSecurityRequirement(SecurityRequirement securityRequirement, An if (element instanceof Method) { if (securityRequirement.name() != null && !securityRequirement.name().isEmpty()) { org.eclipse.microprofile.openapi.models.security.SecurityRequirement model = new SecurityRequirementImpl(); - SecurityRequirementImpl.merge(securityRequirement, model, true); + SecurityRequirementImpl.merge(securityRequirement, model); context.getWorkingOperation().addSecurityRequirement(model); } } @@ -748,18 +748,18 @@ public void visitSecurityRequirements(SecurityRequirements securityRequirements, */ @SuppressWarnings("unchecked") private Set> getClassesFromLoader(ClassLoader classLoader) { - Set> classes = new HashSet<>(); + Set> loaderClasses = new HashSet<>(); try { Field classesField = ClassLoader.class.getDeclaredField("classes"); classesField.setAccessible(true); - classes = new HashSet<>((Vector>) classesField.get(classLoader)); + loaderClasses = new HashSet<>((Vector>) classesField.get(classLoader)); } catch (Exception ex) { - LOGGER.log(Level.WARNING, "Unable to get classes from classloader.", ex); + LOGGER.log(WARNING, "Unable to get classes from classloader.", ex); } // If no classes were found, the classloader could be deploying from a directory. // If so, scan the directory structure for expected classes. - if (classes.isEmpty()) { + if (loaderClasses.isEmpty()) { LOGGER.fine("Unable to find loaded classes in classloader, searching in classpath for files."); try { // Get the classpath url. @@ -780,7 +780,7 @@ private Set> getClassesFromLoader(ClassLoader classLoader) { String className = file.getPath().replaceAll(".+WEB-INF/classes/", "").replace("/", ".").replace(".class", ""); LOGGER.finer("Attempting to add class: " + className); try { - classes.add(Class.forName(className)); + loaderClasses.add(Class.forName(className)); } catch (ClassNotFoundException ex) { LOGGER.finer("Unable to add class: " + className); } @@ -790,14 +790,14 @@ private Set> getClassesFromLoader(ClassLoader classLoader) { expand.addAll(subFiles); } } else { - LOGGER.log(Level.WARNING, "Unrecognised classpath."); + LOGGER.log(WARNING, "Unrecognised classpath."); } } catch (URISyntaxException ex) { - LOGGER.log(Level.WARNING, "Unable to get classes from classpath.", ex); + LOGGER.log(WARNING, "Unable to get classes from classpath.", ex); } } - return classes; + return loaderClasses; } /** @@ -816,8 +816,8 @@ private Map>> generateResourceMapping(Set> classLi Application app = (Application) clazz.newInstance(); // Add all classes contained in the application resourceClasses.addAll(app.getClasses()); - } catch (InstantiationException | IllegalAccessException e) { - e.printStackTrace(); + } catch (InstantiationException | IllegalAccessException ex) { + LOGGER.log(WARNING, "Unable to initialise application class.", ex); } } } @@ -831,7 +831,6 @@ private Map>> generateResourceMapping(Set> classLi } // If there is no application, add all classes to the context root. - // TODO: parse the web xml to find the correct mapping in this case if (resourceMapping.isEmpty()) { resourceMapping.put("/", classList); } @@ -890,14 +889,16 @@ private org.eclipse.microprofile.openapi.models.responses.APIResponse insertDefa * @return the {@link javax.ws.rs.core.MediaType} with the given name. Defaults to WILDCARD. */ private String getContentType(String name) { + String contentType = javax.ws.rs.core.MediaType.WILDCARD; try { javax.ws.rs.core.MediaType mediaType = javax.ws.rs.core.MediaType.valueOf(name); if (mediaType != null) { - return mediaType.toString(); + contentType = mediaType.toString(); } } catch (IllegalArgumentException ex) { + LOGGER.log(FINE, "Unrecognised content type.", ex); } - return javax.ws.rs.core.MediaType.WILDCARD; + return contentType; } private org.eclipse.microprofile.openapi.models.media.Schema createSchema(OpenAPI api, Class type) { diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java index 04f840b784f..d5857b7a721 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java @@ -80,7 +80,7 @@ public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { } // Add the config specified servers - if (config != null && config.getServers().size() > 0) { + if (config != null && !config.getServers().isEmpty()) { // Clear all the other servers api.getServers().clear(); // Add all the specified ones @@ -88,9 +88,10 @@ public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { } // Add the default server if there are none - if (api.getServers().size() == 0) { - api.addServer( - new ServerImpl().url("http://localhost:8080" + applicationPath).description("Default Server.")); + if (api.getServers().isEmpty()) { + api.addServer(new ServerImpl() + .url("http://localhost:8080" + applicationPath) + .description("Default Server.")); } // Add the path servers diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java index f2ca5932a55..e7dadca1909 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java @@ -40,12 +40,12 @@ package fish.payara.microprofile.openapi.impl.processor; import static fish.payara.microprofile.openapi.impl.model.util.ModelUtils.merge; +import static java.util.logging.Level.WARNING; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; -import java.util.logging.Level; import java.util.logging.Logger; import com.fasterxml.jackson.databind.ObjectMapper; @@ -97,7 +97,7 @@ public FileProcessor(ClassLoader appClassLoader) { LOGGER.fine("No static OpenAPI document provided."); } } catch (URISyntaxException ex) { - LOGGER.log(Level.WARNING, "Invalid URI syntax", ex); + LOGGER.log(WARNING, "Invalid URI syntax", ex); } } @@ -108,7 +108,7 @@ public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { try { readResult = mapper.readValue(file, OpenAPIImpl.class); } catch (IOException ex) { - LOGGER.log(Level.WARNING, "Error when reading static OpenAPI document.", ex); + LOGGER.log(WARNING, "Error when reading static OpenAPI document.", ex); } if (readResult != null) { merge(readResult, api, true); diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java index f86651e6a6c..ec1b3360ba1 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java @@ -39,12 +39,13 @@ */ package fish.payara.microprofile.openapi.impl.processor; +import static java.util.logging.Level.WARNING; + import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.microprofile.openapi.OASFilter; @@ -86,10 +87,10 @@ public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { filter = config.getFilter().newInstance(); } } catch (InstantiationException | IllegalAccessException ex) { - LOGGER.log(Level.WARNING, "Error creating OASFilter instance.", ex); + LOGGER.log(WARNING, "Error creating OASFilter instance.", ex); } if (filter != null) { - return (OpenAPI) filterObject(api, null); + return (OpenAPI) filterObject(api); } else { LOGGER.fine("No OASFilter provided."); } @@ -97,16 +98,16 @@ public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { } @SuppressWarnings("unchecked") - private Object filterObject(Object object, Object parent) { + private Object filterObject(Object object) { if (object != null) { // If the object is a map if (object instanceof Map) { - List resultsToRemove = new ArrayList(); + List resultsToRemove = new ArrayList<>(); // Filter each object in the value list for (Object item : Map.class.cast(object).values()) { - Object result = filterObject(item, object); + Object result = filterObject(item); if (result == null) { resultsToRemove.add(item); @@ -121,11 +122,11 @@ private Object filterObject(Object object, Object parent) { // If the object is iterable if (object instanceof Iterable) { - List resultsToRemove = new ArrayList(); + List resultsToRemove = new ArrayList<>(); // Filter each object in the list for (Object item : Iterable.class.cast(object)) { - Object result = filterObject(item, object); + Object result = filterObject(item); if (result == null) { resultsToRemove.add(item); @@ -152,14 +153,14 @@ private Object filterObject(Object object, Object parent) { Object fieldValue = field.get(object); // Filter the object - Object result = filterObject(fieldValue, object); + Object result = filterObject(fieldValue); // Remove it if it's null if (result == null) { field.set(object, null); } } catch (IllegalArgumentException | IllegalAccessException ex) { - LOGGER.log(Level.WARNING, "Unable to access field in OpenAPI model.", ex); + LOGGER.log(WARNING, "Unable to access field in OpenAPI model.", ex); } } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java index ccb3808667d..722d13e7687 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java @@ -39,7 +39,8 @@ */ package fish.payara.microprofile.openapi.impl.processor; -import java.util.logging.Level; +import static java.util.logging.Level.WARNING; + import java.util.logging.Logger; import org.eclipse.microprofile.openapi.OASModelReader; @@ -68,15 +69,14 @@ public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { reader = config.getModelReader().newInstance(); } } catch (InstantiationException | IllegalAccessException ex) { - LOGGER.log(Level.WARNING, "Error creating OASModelReader instance.", ex); + LOGGER.log(WARNING, "Error creating OASModelReader instance.", ex); } if (reader != null) { OpenAPI model = reader.buildModel(); if (model != null) { return model; } else { - LOGGER.log(Level.WARNING, - "The OpenAPI model returned by " + reader.getClass().getName() + " was null!"); + LOGGER.log(WARNING, "The OpenAPI model returned by " + reader.getClass().getName() + " was null!"); } } else { LOGGER.fine("No OASModelReader provided."); diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java index 7dc18968594..47118609de8 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/ObjectMapperFactory.java @@ -62,6 +62,12 @@ public final class ObjectMapperFactory { + /** + * Private constructor to hide default public one. + */ + private ObjectMapperFactory() { + } + public static ObjectMapper createJson() { return create(new JsonFactory()); } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/mixin/ExtensionsMixin.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/mixin/ExtensionsMixin.java index 4d8f4d18c0e..1189a77de0e 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/mixin/ExtensionsMixin.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/mixin/ExtensionsMixin.java @@ -49,7 +49,7 @@ import org.eclipse.microprofile.openapi.models.Paths; import org.eclipse.microprofile.openapi.models.security.SecurityRequirement; -public abstract class ExtensionsMixin { +public interface ExtensionsMixin { @JsonProperty("enum") public abstract void getEnumeration(); diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java index 470fed1111e..78e9121dc1f 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/provider/writer/AbstractWriter.java @@ -44,7 +44,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyWriter; @@ -68,8 +67,7 @@ public boolean isWriteable(Class type, Type genericType, Annotation[] annotat @Override public void writeTo(OpenAPI t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, - MultivaluedMap httpHeaders, OutputStream entityStream) - throws IOException, WebApplicationException { + MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException { entityStream.write(mapper.writeValueAsBytes(t)); } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java index b481b231cd4..b9dff295261 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/service/OpenApiResource.java @@ -62,7 +62,7 @@ @Path("/") public class OpenApiResource { - private Logger LOGGER = Logger.getLogger(OpenApiResource.class.getName()); + private static final Logger LOGGER = Logger.getLogger(OpenApiResource.class.getName()); @GET @Produces({ APPLICATION_YAML, APPLICATION_JSON }) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java index aa7d89ef2b3..80245887485 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java @@ -47,8 +47,8 @@ import java.lang.reflect.Field; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.Method; -import java.util.Comparator; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; @@ -109,23 +109,20 @@ public class OpenApiWalker implements ApiWalker { public OpenApiWalker(OpenAPI api, Set> allowedClasses, Map>> resourceMapping) { this.api = api; this.resourceMapping = resourceMapping; - this.classes = new TreeSet<>(new Comparator>() { - @Override - public int compare(Class class1, Class class2) { - if (class1.equals(class2)) { - return 0; - } - // Non contextual objects at the start - if (!class1.isAnnotationPresent(ApplicationPath.class) && !class1.isAnnotationPresent(Path.class)) { - return -1; - } - // Followed by applications - if (class1.isAnnotationPresent(ApplicationPath.class)) { - return -1; - } - // Followed by everything else - return 1; + this.classes = new TreeSet<>((class1, class2) -> { + if (class1.equals(class2)) { + return 0; + } + // Non contextual objects at the start + if (!class1.isAnnotationPresent(ApplicationPath.class) && !class1.isAnnotationPresent(Path.class)) { + return -1; + } + // Followed by applications + if (class1.isAnnotationPresent(ApplicationPath.class)) { + return -1; } + // Followed by everything else + return 1; }); this.classes.addAll(allowedClasses); } @@ -280,9 +277,9 @@ private String getResourcePath(GenericDeclaration declaration) { // If the class is a resource and contains a mapping if (clazz.isAnnotationPresent(Path.class)) { - for (String key : resourceMapping.keySet()) { - if (resourceMapping.get(key).contains(clazz)) { - path = key + "/" + clazz.getDeclaredAnnotation(Path.class).value(); + for (Entry>> entry : resourceMapping.entrySet()) { + if (entry.getValue() != null && entry.getValue().contains(clazz)) { + path = entry.getKey() + "/" + clazz.getDeclaredAnnotation(Path.class).value(); } } } From 012572ec38d20598d9639a92f220d1c3c4e3c89e Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Tue, 29 May 2018 10:45:21 +0100 Subject: [PATCH 17/20] Removed weld proxy classes as well as java base classes from model. --- .../openapi/impl/processor/ApplicationProcessor.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java index 60902be41ae..2a7d055f45a 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -757,6 +757,8 @@ private Set> getClassesFromLoader(ClassLoader classLoader) { LOGGER.log(WARNING, "Unable to get classes from classloader.", ex); } + loaderClasses.removeIf(clazz -> clazz.getName().contains("$Proxy$")); + // If no classes were found, the classloader could be deploying from a directory. // If so, scan the directory structure for expected classes. if (loaderClasses.isEmpty()) { @@ -937,8 +939,8 @@ private org.eclipse.microprofile.openapi.models.media.Schema createSchema(OpenAP */ private boolean insertObjectReference(OpenAPI api, Reference referee, Class referenceClass) { - // If the object is java.lang.Object, exit - if (referenceClass.equals(Object.class)) { + // If the object is a java core class + if (referenceClass == null || referenceClass.getName().startsWith("java.") || referenceClass.isPrimitive()) { return false; } From 4372ed9b9876342240ac301df8b08e16b3d66535 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Tue, 29 May 2018 10:49:08 +0100 Subject: [PATCH 18/20] Also ignored Java EE response types. --- .../openapi/impl/processor/ApplicationProcessor.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java index 2a7d055f45a..36ff68a6328 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -944,6 +944,11 @@ private boolean insertObjectReference(OpenAPI api, Reference referee, Class schemas = api.getComponents().getSchemas(); From 33e3624cc0ba0efa72536d1c000a53c3ed1dc345 Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Tue, 29 May 2018 11:53:46 +0100 Subject: [PATCH 19/20] Load the classes directly from the archive. --- .../openapi/impl/OpenApiService.java | 78 ++++++++++++++----- .../impl/processor/ApplicationProcessor.java | 72 +---------------- .../classloader/ApplicationClassLoader.java | 4 + .../rule/ApplicationProcessedDocument.java | 4 +- 4 files changed, 68 insertions(+), 90 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java index 7cbb69ee6a3..1221a5dc354 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java @@ -39,10 +39,14 @@ */ package fish.payara.microprofile.openapi.impl; +import static java.util.stream.Collectors.toSet; + import java.beans.PropertyChangeEvent; import java.util.Collections; import java.util.Deque; +import java.util.Enumeration; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.logging.Logger; @@ -50,6 +54,7 @@ import org.eclipse.microprofile.openapi.models.OpenAPI; import org.glassfish.api.StartupRunLevel; +import org.glassfish.api.deployment.archive.ReadableArchive; import org.glassfish.api.event.EventListener; import org.glassfish.api.event.Events; import org.glassfish.hk2.api.PostConstruct; @@ -143,7 +148,7 @@ public void event(Event event) { // Map the application info to a new openapi document, and store it in the list Map map = Collections.singletonMap(appInfo, - createOpenApiDocument(appInfo.getAppClassLoader(), getContextRoot(appInfo), appConfig)); + createOpenApiDocument(appInfo, appConfig)); models.add(map); LOGGER.info("OpenAPI document created."); @@ -159,20 +164,6 @@ public void event(Event event) { } } - /** - * @return boolean if the app is a valid target for an OpenAPI document. - */ - private boolean isValidApp(ApplicationInfo appInfo) { - return appInfo.getMetaData(WebBundleDescriptorImpl.class) != null; - } - - /** - * @return boolean the context root of the application. - */ - private String getContextRoot(ApplicationInfo appInfo) { - return appInfo.getMetaData(WebBundleDescriptorImpl.class).getContextRoot(); - } - /** * Gets the document for the most recently deployed application. */ @@ -186,11 +177,16 @@ public OpenAPI getDocument() { return lastDocument; } - private OpenAPI createOpenApiDocument(ClassLoader appClassLoader, String contextRoot, OpenApiConfiguration config) { + private OpenAPI createOpenApiDocument(ApplicationInfo appInfo, OpenApiConfiguration config) { OpenAPI document = new OpenAPIImpl(); + + String contextRoot = getContextRoot(appInfo); + ReadableArchive archive = appInfo.getSource(); + Set> classes = getClassesFromArchive(archive, appInfo.getAppClassLoader()); + document = new ModelReaderProcessor().process(document, config); - document = new FileProcessor(appClassLoader).process(document, config); - document = new ApplicationProcessor(appClassLoader).process(document, config); + document = new FileProcessor(appInfo.getAppClassLoader()).process(document, config); + document = new ApplicationProcessor(classes).process(document, config); document = new BaseProcessor(contextRoot).process(document, config); document = new FilterProcessor().process(document, config); return document; @@ -203,4 +199,50 @@ public static OpenApiService getInstance() { return Globals.getStaticBaseServiceLocator().getService(OpenApiService.class); } + /** + * @param appInfo the application descriptor. + * @return boolean if the app is a valid target for an OpenAPI document. + */ + private static boolean isValidApp(ApplicationInfo appInfo) { + return appInfo.getMetaData(WebBundleDescriptorImpl.class) != null; + } + + /** + * @param appInfo the application descriptor. + * @return boolean the context root of the application. + */ + private static String getContextRoot(ApplicationInfo appInfo) { + return appInfo.getMetaData(WebBundleDescriptorImpl.class).getContextRoot(); + } + + /** + * @param archive the archive to read from. + * @param appClassLoader the classloader to use to load the classes. + * @return a list of all loadable classes in the archive. + */ + private static Set> getClassesFromArchive(ReadableArchive archive, ClassLoader appClassLoader) { + return Collections.list((Enumeration) archive.entries()).stream() + // Only use the classes + .filter(x -> x.endsWith(".class")) + // Remove the WEB-INF/classes and return the proper class name format + .map(x -> x.replaceAll("WEB-INF/classes/", "").replace("/", ".").replace(".class", "")) + // Attempt to load the classes + .map(x -> { + Class loadedClass = null; + // Attempt to load the class, ignoring any errors + try { + loadedClass = appClassLoader.loadClass(x); + } catch (Throwable t) { + } + try { + loadedClass = Class.forName(x); + } catch (Throwable t) { + } + return loadedClass; + }) + // Don't return null classes + .filter(x -> x != null) + .collect(toSet()); + } + } \ No newline at end of file diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java index 36ff68a6328..e2832fab7b8 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -42,22 +42,17 @@ import static java.util.logging.Level.FINE; import static java.util.logging.Level.WARNING; -import java.io.File; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.net.URISyntaxException; -import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.Vector; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -146,8 +141,8 @@ public class ApplicationProcessor implements OASProcessor, ApiVisitor { /** * @param appClassLoader the class loader for the application. */ - public ApplicationProcessor(ClassLoader appClassLoader) { - this.classes = getClassesFromLoader(appClassLoader); + public ApplicationProcessor(Set> appClasses) { + this.classes = appClasses; } @Override @@ -739,69 +734,6 @@ public void visitSecurityRequirements(SecurityRequirements securityRequirements, // PRIVATE METHODS - /** - * Gets the set of classes contained within a {@link ClassLoader}. The set - * returned will not be null, but could be empty. - * - * @param classLoader the classloader to get the classes from. - * @return the set of classes managed by the classloader. - */ - @SuppressWarnings("unchecked") - private Set> getClassesFromLoader(ClassLoader classLoader) { - Set> loaderClasses = new HashSet<>(); - try { - Field classesField = ClassLoader.class.getDeclaredField("classes"); - classesField.setAccessible(true); - loaderClasses = new HashSet<>((Vector>) classesField.get(classLoader)); - } catch (Exception ex) { - LOGGER.log(WARNING, "Unable to get classes from classloader.", ex); - } - - loaderClasses.removeIf(clazz -> clazz.getName().contains("$Proxy$")); - - // If no classes were found, the classloader could be deploying from a directory. - // If so, scan the directory structure for expected classes. - if (loaderClasses.isEmpty()) { - LOGGER.fine("Unable to find loaded classes in classloader, searching in classpath for files."); - try { - // Get the classpath url. - // If the classpath is currently WEB-INF/lib, resolve WEB-INF/classes instead - URL classpath = classLoader.getResource("../classes"); - - if (classpath != null) { - - List expand = new LinkedList<>(); - expand.add(new File(classpath.toURI())); - - while (!expand.isEmpty()) { - List subFiles = new LinkedList<>(); - for (File file : expand) { - if (file.isDirectory()) { - subFiles.addAll(Arrays.asList(file.listFiles())); - } else if (file.getPath().endsWith(".class")) { - String className = file.getPath().replaceAll(".+WEB-INF/classes/", "").replace("/", ".").replace(".class", ""); - LOGGER.finer("Attempting to add class: " + className); - try { - loaderClasses.add(Class.forName(className)); - } catch (ClassNotFoundException ex) { - LOGGER.finer("Unable to add class: " + className); - } - } - } - expand.clear(); - expand.addAll(subFiles); - } - } else { - LOGGER.log(WARNING, "Unrecognised classpath."); - } - } catch (URISyntaxException ex) { - LOGGER.log(WARNING, "Unable to get classes from classpath.", ex); - } - } - - return loaderClasses; - } - /** * Generates a map listing the location each resource class is mapped to. */ diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java index 583125a3181..e21a5827064 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java @@ -69,6 +69,10 @@ public ApplicationClassLoader(Application app, Set> extraClasses) { } } + public Set> getApplicationClasses() { + return appClasses; + } + public ApplicationClassLoader(Application app) { this(app, null); } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java index 1836620c0da..7d21b0f15c6 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.java @@ -54,10 +54,10 @@ public ApplicationProcessedDocument() { // Apply base processor new BaseProcessor("/testlocation_123").process(this, null); - ClassLoader appClassLoader = new ApplicationClassLoader(new TestApplication(), singleton(TestComponent.class)); + ApplicationClassLoader appClassLoader = new ApplicationClassLoader(new TestApplication(), singleton(TestComponent.class)); // Apply application processor - new ApplicationProcessor(appClassLoader).process(this, null); + new ApplicationProcessor(appClassLoader.getApplicationClasses()).process(this, null); } } \ No newline at end of file From b9493446da164c331641cf9dc9f4aefc3df7262a Mon Sep 17 00:00:00 2001 From: Matt Gill Date: Tue, 29 May 2018 16:40:58 +0100 Subject: [PATCH 20/20] Added support for schema inheritance. --- .../openapi/impl/OpenApiService.java | 5 +- .../impl/processor/ApplicationProcessor.java | 121 +++++++++++------- .../openapi/impl/visitor/OpenApiWalker.java | 4 + 3 files changed, 80 insertions(+), 50 deletions(-) diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java index 1221a5dc354..5d4909708cc 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java @@ -171,10 +171,7 @@ public OpenAPI getDocument() { if (models.isEmpty()) { return null; } - OpenAPI lastDocument = null; - for (Map model : models) - lastDocument = (OpenAPI) model.values().toArray()[0]; - return lastDocument; + return (OpenAPI) models.getLast().values().toArray()[0]; } private OpenAPI createOpenApiDocument(ApplicationInfo appInfo, OpenApiConfiguration config) { diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java index e2832fab7b8..fe74aeb40fe 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -176,10 +176,10 @@ public void visitGET(GET get, Method element, ApiContext context) { operation.setOperationId(element.getName()); // Add the default request - insertDefaultRequestBody(context.getApi(), operation, element); + insertDefaultRequestBody(context, operation, element); // Add the default response - insertDefaultResponse(context.getApi(), operation, element); + insertDefaultResponse(context, operation, element); } @Override @@ -197,10 +197,10 @@ public void visitPOST(POST post, Method element, ApiContext context) { operation.setOperationId(element.getName()); // Add the default request - insertDefaultRequestBody(context.getApi(), operation, element); + insertDefaultRequestBody(context, operation, element); // Add the default response - insertDefaultResponse(context.getApi(), operation, element); + insertDefaultResponse(context, operation, element); } @Override @@ -218,10 +218,10 @@ public void visitPUT(PUT put, Method element, ApiContext context) { operation.setOperationId(element.getName()); // Add the default request - insertDefaultRequestBody(context.getApi(), operation, element); + insertDefaultRequestBody(context, operation, element); // Add the default response - insertDefaultResponse(context.getApi(), operation, element); + insertDefaultResponse(context, operation, element); } @Override @@ -239,10 +239,10 @@ public void visitDELETE(DELETE delete, Method element, ApiContext context) { operation.setOperationId(element.getName()); // Add the default request - insertDefaultRequestBody(context.getApi(), operation, element); + insertDefaultRequestBody(context, operation, element); // Add the default response - insertDefaultResponse(context.getApi(), operation, element); + insertDefaultResponse(context, operation, element); } @Override @@ -260,10 +260,10 @@ public void visitHEAD(HEAD head, Method element, ApiContext context) { operation.setOperationId(element.getName()); // Add the default request - insertDefaultRequestBody(context.getApi(), operation, element); + insertDefaultRequestBody(context, operation, element); // Add the default response - insertDefaultResponse(context.getApi(), operation, element); + insertDefaultResponse(context, operation, element); } @Override @@ -281,10 +281,10 @@ public void visitOPTIONS(OPTIONS options, Method element, ApiContext context) { operation.setOperationId(element.getName()); // Add the default request - insertDefaultRequestBody(context.getApi(), operation, element); + insertDefaultRequestBody(context, operation, element); // Add the default response - insertDefaultResponse(context.getApi(), operation, element); + insertDefaultResponse(context, operation, element); } @Override @@ -302,10 +302,10 @@ public void visitPATCH(PATCH patch, Method element, ApiContext context) { operation.setOperationId(element.getName()); // Add the default request - insertDefaultRequestBody(context.getApi(), operation, element); + insertDefaultRequestBody(context, operation, element); // Add the default response - insertDefaultResponse(context.getApi(), operation, element); + insertDefaultResponse(context, operation, element); } @Override @@ -394,7 +394,8 @@ public void visitFormParam(FormParam param, java.lang.reflect.Parameter element, } // Set the request body type accordingly. - context.getWorkingOperation().getRequestBody().getContent().get(javax.ws.rs.core.MediaType.WILDCARD).getSchema().setType(formSchemaType); + context.getWorkingOperation().getRequestBody().getContent().get(javax.ws.rs.core.MediaType.WILDCARD).getSchema() + .setType(formSchemaType); } @Override @@ -427,15 +428,52 @@ public void visitSchema(Schema schema, AnnotatedElement element, ApiContext cont if (element instanceof Class) { // Get the schema object name - String schemaName = schema.name(); + String schemaName = (schema == null) ? null : schema.name(); if (schemaName == null || schemaName.isEmpty()) { schemaName = Class.class.cast(element).getSimpleName(); } - // Add the new schema + // Add a new schema org.eclipse.microprofile.openapi.models.media.Schema newSchema = new SchemaImpl(); context.getApi().getComponents().addSchema(schemaName, newSchema); - SchemaImpl.merge(schema, newSchema, true, context.getApi().getComponents().getSchemas()); + + // If there is an annotation + if (schema != null) { + SchemaImpl.merge(schema, newSchema, true, context.getApi().getComponents().getSchemas()); + } else { + newSchema.setType(SchemaType.OBJECT); + Map fields = new LinkedHashMap<>(); + for (Field field : Class.class.cast(element).getDeclaredFields()) { + if (!Modifier.isTransient(field.getModifiers())) { + fields.put(field.getName(), createSchema(context, field.getType())); + } + } + newSchema.setProperties(fields); + } + + // If there is an extending class, add the data + if (Class.class.cast(element).getSuperclass() != null) { + Class superClass = Class.class.cast(element).getSuperclass(); + + // If the super class is legitimate + if (!superClass.equals(Object.class)) { + + // Get the parent schema annotation + Schema parentSchema = superClass.getDeclaredAnnotation(Schema.class); + + // Create a schema for the parent + visitSchema(parentSchema, superClass, context); + + // Get the superclass schema name + String parentSchemaName = (parentSchema == null) ? null : parentSchema.name(); + if (parentSchemaName == null || parentSchemaName.isEmpty()) { + parentSchemaName = Class.class.cast(superClass).getSimpleName(); + } + + // Link the schemas + newSchema.addAllOf(new SchemaImpl().ref(parentSchemaName)); + } + } } if (element instanceof Field) { @@ -478,7 +516,8 @@ public void visitSchema(Schema schema, AnnotatedElement element, ApiContext cont context.getWorkingOperation().setRequestBody(new RequestBodyImpl()); } // Insert the schema to the request body media type - MediaType mediaType = context.getWorkingOperation().getRequestBody().getContent().get(javax.ws.rs.core.MediaType.WILDCARD); + MediaType mediaType = context.getWorkingOperation().getRequestBody().getContent() + .get(javax.ws.rs.core.MediaType.WILDCARD); SchemaImpl.merge(schema, mediaType.getSchema(), true, context.getApi().getComponents().getSchemas()); if (schema.ref() != null && !schema.ref().isEmpty()) { mediaType.setSchema(new SchemaImpl().ref(schema.ref())); @@ -772,7 +811,7 @@ private Map>> generateResourceMapping(Set> classLi return resourceMapping; } - private org.eclipse.microprofile.openapi.models.parameters.RequestBody insertDefaultRequestBody(OpenAPI api, + private org.eclipse.microprofile.openapi.models.parameters.RequestBody insertDefaultRequestBody(ApiContext context, org.eclipse.microprofile.openapi.models.Operation operation, Method method) { org.eclipse.microprofile.openapi.models.parameters.RequestBody requestBody = new RequestBodyImpl(); @@ -789,7 +828,7 @@ private org.eclipse.microprofile.openapi.models.parameters.RequestBody insertDef } // Create the default request body with a wildcard mediatype - MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, bodyType)); + MediaType mediaType = new MediaTypeImpl().schema(createSchema(context, bodyType)); requestBody.getContent().addMediaType(javax.ws.rs.core.MediaType.WILDCARD, mediaType); operation.setRequestBody(requestBody); @@ -798,19 +837,20 @@ private org.eclipse.microprofile.openapi.models.parameters.RequestBody insertDef /** * Creates a new {@link APIResponse} to model the default response of a - * {@link Method}, and inserts it into the {@link APIResponses}. + * {@link Method}, and inserts it into the {@link Operation} responses. * - * @param responses the {@link APIResponses} to add the default response to. + * @param context the API context. + * @param operation the {@link Operation} to add the default response to. * @param method the {@link Method} to model the default response on. * @return the newly created {@link APIResponse}. */ - private org.eclipse.microprofile.openapi.models.responses.APIResponse insertDefaultResponse(OpenAPI api, + private org.eclipse.microprofile.openapi.models.responses.APIResponse insertDefaultResponse(ApiContext context, org.eclipse.microprofile.openapi.models.Operation operation, Method method) { org.eclipse.microprofile.openapi.models.responses.APIResponse defaultResponse = new APIResponseImpl(); defaultResponse.setDescription("Default Response."); // Create the default response with a wildcard mediatype - MediaType mediaType = new MediaTypeImpl().schema(createSchema(api, method.getReturnType())); + MediaType mediaType = new MediaTypeImpl().schema(createSchema(context, method.getReturnType())); defaultResponse.getContent().addMediaType(javax.ws.rs.core.MediaType.WILDCARD, mediaType); // Add the default response @@ -820,7 +860,8 @@ private org.eclipse.microprofile.openapi.models.responses.APIResponse insertDefa } /** - * @return the {@link javax.ws.rs.core.MediaType} with the given name. Defaults to WILDCARD. + * @return the {@link javax.ws.rs.core.MediaType} with the given name. Defaults + * to WILDCARD. */ private String getContentType(String name) { String contentType = javax.ws.rs.core.MediaType.WILDCARD; @@ -835,7 +876,7 @@ private String getContentType(String name) { return contentType; } - private org.eclipse.microprofile.openapi.models.media.Schema createSchema(OpenAPI api, Class type) { + private org.eclipse.microprofile.openapi.models.media.Schema createSchema(ApiContext context, Class type) { org.eclipse.microprofile.openapi.models.media.Schema schema = new SchemaImpl(); SchemaType schemaType = ModelUtils.getSchemaType(type); schema.setType(schemaType); @@ -851,12 +892,14 @@ private org.eclipse.microprofile.openapi.models.media.Schema createSchema(OpenAP } } + // If the schema is an object, insert the reference if (schemaType == SchemaType.OBJECT) { - if (insertObjectReference(api, schema, type)) { + if (insertObjectReference(context, schema, type)) { schema.setType(null); schema.setItems(null); } } + return schema; } @@ -864,12 +907,12 @@ private org.eclipse.microprofile.openapi.models.media.Schema createSchema(OpenAP * Replace the object in the referee with a reference, and create the reference * in the API. * - * @param api the OpenAPI object. + * @param context the API context. * @param referee the object containing the reference. * @param referenceClass the class of the object being referenced. * @return if the reference has been created. */ - private boolean insertObjectReference(OpenAPI api, Reference referee, Class referenceClass) { + private boolean insertObjectReference(ApiContext context, Reference referee, Class referenceClass) { // If the object is a java core class if (referenceClass == null || referenceClass.getName().startsWith("java.") || referenceClass.isPrimitive()) { @@ -881,25 +924,11 @@ private boolean insertObjectReference(OpenAPI api, Reference referee, Class schemas = api.getComponents().getSchemas(); - // Set the reference name referee.setRef(referenceClass.getSimpleName()); - if (!schemas.containsKey(referenceClass.getSimpleName())) { - // If the schema type doesn't already exist, create it - org.eclipse.microprofile.openapi.models.media.Schema schema = new SchemaImpl(); - schemas.put(referenceClass.getSimpleName(), schema); - schema.setType(SchemaType.OBJECT); - Map fields = new LinkedHashMap<>(); - for (Field field : referenceClass.getDeclaredFields()) { - if (!Modifier.isTransient(field.getModifiers())) { - fields.put(field.getName(), createSchema(api, field.getType())); - } - } - schema.setProperties(fields); - } + // Create the schema + visitSchema(referenceClass.getDeclaredAnnotation(Schema.class), referenceClass, context); return true; } diff --git a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java index 80245887485..34dc5110fe1 100644 --- a/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java @@ -113,6 +113,10 @@ public OpenApiWalker(OpenAPI api, Set> allowedClasses, Map