diff --git a/appserver/packager/microprofile-package/pom.xml b/appserver/packager/microprofile-package/pom.xml index 1c7a0bd95f5..ba300f1449f 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/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..e27bbc2c69a --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/processor/OASProcessor.java @@ -0,0 +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 { + + /** + * 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 new file mode 100644 index 00000000000..082f985c498 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiContext.java @@ -0,0 +1,67 @@ +/* + * 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. + */ +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(); + + /** + * 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 new file mode 100644 index 00000000000..d65b96b40b9 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiVisitor.java @@ -0,0 +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.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Method; + +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 { + + /** + * Generic representation of each of these functions. + */ + @FunctionalInterface + interface VisitorFunction { + void apply(A annotation, E element, ApiContext context); + } + + // JAX-RS annotations + + 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); + +} \ 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..d0b89cfeea3 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/api/visitor/ApiWalker.java @@ -0,0 +1,53 @@ +/* + * 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; + +/** + * 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/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..5d4909708cc --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/OpenApiService.java @@ -0,0 +1,245 @@ +/* + * 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 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; + +import javax.inject.Inject; + +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; +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 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; +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.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 PostConstruct, PreDestroy, EventListener, ConfigListener { + + 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 ConcurrentLinkedDeque<>(); + events.register(this); + } + + @Override + public void preDestroy() { + events.unregister(this); + } + + 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)) { + // Get the application information + ApplicationInfo appInfo = (ApplicationInfo) event.hook(); + + // Create all the relevant resources + 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, appConfig)); + models.add(map); + + LOGGER.info("OpenAPI document created."); + } + } else if (event.is(Deployment.APPLICATION_UNLOADED)) { + ApplicationInfo appInfo = (ApplicationInfo) event.hook(); + for (Map map : models) { + if (map.keySet().toArray()[0].equals(appInfo)) { + models.remove(map); + break; + } + } + } + } + + /** + * Gets the document for the most recently deployed application. + */ + public OpenAPI getDocument() { + if (models.isEmpty()) { + return null; + } + return (OpenAPI) models.getLast().values().toArray()[0]; + } + + 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(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; + } + + /** + * Retrieves an instance of this service from HK2. + */ + 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/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..ba7ac57afea --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/GetOpenApiConfigurationCommand.java @@ -0,0 +1,109 @@ +/* + * 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 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) { + // 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); + + adminCommandContext.getActionReport().appendMessage("Enabled: " + openApiConfig.getEnabled()); + + 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..407b1372437 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/admin/SetOpenApiConfigurationCommand.java @@ -0,0 +1,116 @@ +/* + * 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 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; +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) { + // 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) { + configProxy.setEnabled(Boolean.toString(enabled)); + } + return configProxy; + }, openApiConfig); + } catch (TransactionFailure ex) { + adminCommandContext.getActionReport().failure(LOGGER, "Failed to update OpenAPI configuration", ex); + } + } + +} 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..fb104683f8b --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/config/OpenApiConfiguration.java @@ -0,0 +1,344 @@ +/* + * 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.logging.Level.WARNING; +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; +import java.util.Set; +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.servers.path."; + private static final String OPERATION_PREFIX_KEY = "mp.openapi.servers.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); + } + + /** + * @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; + } + + /** + * @return a map of paths to the servers it contains. + */ + public Map> getPathServerMap() { + return pathServerMap; + } + + /** + * @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().noneMatch(pkg -> clazz.getPackage().getName().startsWith(pkg))) + .collect(toSet()); + } + + @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; + } + + private List findScanPackagesFromConfig(Config config) { + List packages = new ArrayList<>(); + try { + packages.addAll(Arrays.asList(config.getValue(SCAN_PACKAGES_KEY, String[].class))); + } catch (NoSuchElementException ex) { + // Ignore + } + return packages; + } + + private List> findScanClassesFromConfig(Config config, ClassLoader classLoader) { + List> classes = new ArrayList<>(); + try { + List classNames = Arrays.asList(config.getValue(SCAN_CLASSES_KEY, String[].class)); + for (String className : classNames) { + Class clazz = getClassFromName(className, classLoader); + if (clazz != null) { + classes.add(clazz); + } + } + } catch (NoSuchElementException ex) { + // Ignore + } + return classes; + } + + private List findExcludePackages(Config config) { + List packages = new ArrayList<>(); + try { + packages.addAll(Arrays.asList(config.getValue(SCAN_EXCLUDE_PACKAGES_KEY, String[].class))); + } catch (NoSuchElementException ex) { + // Ignore + } + return packages; + } + + private List> findExcludeClasses(Config config, ClassLoader classLoader) { + List> classes = new ArrayList<>(); + try { + List classNames = Arrays.asList(config.getValue(SCAN_EXCLUDE_CLASSES_KEY, String[].class)); + for (String className : classNames) { + Class clazz = getClassFromName(className, classLoader); + if (clazz != null) { + classes.add(clazz); + } + } + } catch (NoSuchElementException ex) { + // Ignore + } + return classes; + } + + private List findServers(Config config) { + List serverList = new ArrayList<>(); + try { + 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<>(); + try { + for (String propertyName : config.getPropertyNames()) { + if (propertyName.startsWith(PATH_PREFIX_KEY)) { + 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) { + // 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(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..95f05cf6068 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ComponentsImpl.java @@ -0,0 +1,376 @@ +/* + * 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; +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 && callback.name() != null) { + Callback newCallback = new CallbackImpl(); + CallbackImpl.merge(callback, newCallback, override, currentSchemas); + 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..96555b12abd --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExtensibleImpl.java @@ -0,0 +1,101 @@ +/* + * 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; +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..ae7b6aa08bf --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/ExternalDocumentationImpl.java @@ -0,0 +1,93 @@ +/* + * 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; +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/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..b359ef293cd --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OASFactoryResolverImpl.java @@ -0,0 +1,185 @@ +/* + * 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 java.util.Collections.unmodifiableMap; + +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 { + + /** + * A map of each OpenAPI model element to the class that models it. + */ + public static final Map, Class> MODEL_MAP; + static { + 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") + @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 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 new file mode 100644 index 00000000000..371eba7b123 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OpenAPIImpl.java @@ -0,0 +1,298 @@ +/* + * 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; + +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; +import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; + +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) { + 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; + } + + @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); + 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..5e4cb8817dc --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/OperationImpl.java @@ -0,0 +1,345 @@ +/* + * 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; +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.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 { + + 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)); + } + + 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 new file mode 100644 index 00000000000..92244103bcb --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathItemImpl.java @@ -0,0 +1,351 @@ +/* + * 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; +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) { + 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 new file mode 100644 index 00000000000..fb6b666a2aa --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/PathsImpl.java @@ -0,0 +1,91 @@ +/* + * 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 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..a38e6313b7f --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/callbacks/CallbackImpl.java @@ -0,0 +1,137 @@ +/* + * 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 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 org.eclipse.microprofile.openapi.models.media.Schema; + +import fish.payara.microprofile.openapi.impl.model.OperationImpl; +import fish.payara.microprofile.openapi.impl.model.PathItemImpl; + +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, Map currentSchemas) { + if (isAnnotationNull(from)) { + return; + } + if (from.ref() != null && !from.ref().isEmpty()) { + applyReference(to, from.ref()); + return; + } + if (!from.callbackUrlExpression().isEmpty()) { + 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, currentSchemas); + } + } + } + } + + private static void applyCallbackOperationAnnotation(PathItem pathItem, CallbackOperation annotation, + boolean override, Map currentSchemas) { + HttpMethod method = getHttpMethod(annotation.method()); + if (method != null) { + 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 new file mode 100644 index 00000000000..b042d9577de --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/examples/ExampleImpl.java @@ -0,0 +1,179 @@ +/* + * 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.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; + +import java.util.Map; + +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)); + } + + 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 = UNKNOWN_ELEMENT_NAME; + } + + // 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 new file mode 100644 index 00000000000..469f19aade1 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/headers/HeaderImpl.java @@ -0,0 +1,305 @@ +/* + * 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.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; + +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); + } + 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 = UNKNOWN_ELEMENT_NAME; + } + + // 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 new file mode 100644 index 00000000000..169d07090c6 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/ContactImpl.java @@ -0,0 +1,113 @@ +/* + * 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; +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..5405b4c12b5 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/InfoImpl.java @@ -0,0 +1,178 @@ +/* + * 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; +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..773cf073d9c --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/info/LicenseImpl.java @@ -0,0 +1,95 @@ +/* + * 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; +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..d4929ab43b0 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/links/LinkImpl.java @@ -0,0 +1,244 @@ +/* + * 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.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; + +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; + +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)); + 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 = UNKNOWN_ELEMENT_NAME; + } + + // 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 = UNKNOWN_ELEMENT_NAME; + } + + // 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 new file mode 100644 index 00000000000..cbe163eb295 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/ContentImpl.java @@ -0,0 +1,101 @@ +/* + * 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 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; + + @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; + } + + // 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 new file mode 100644 index 00000000000..c71cba9bf15 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/DiscriminatorImpl.java @@ -0,0 +1,90 @@ +/* + * 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; +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..4436050fdaa --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/EncodingImpl.java @@ -0,0 +1,173 @@ +/* + * 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 { + + 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; + } + + 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 new file mode 100644 index 00000000000..f24e84c62d9 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/MediaTypeImpl.java @@ -0,0 +1,135 @@ +/* + * 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; +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..3fd065d0480 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/SchemaImpl.java @@ -0,0 +1,895 @@ +/* + * 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 java.util.logging.Level.WARNING; + +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 java.util.logging.Logger; + +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; +import fish.payara.microprofile.openapi.impl.model.util.ModelUtils; + +public class SchemaImpl extends ExtensibleImpl implements Schema { + + private static final Logger LOGGER = Logger.getLogger(SchemaImpl.class.getName()); + + 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.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 (!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 = 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); + 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()) { + 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()).stripTrailingZeros(), 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.not() != null) { + if (Schema.class.isAssignableFrom(from.not())) { + try { + to.setNot((Schema) from.not().newInstance()); + } catch (InstantiationException | IllegalAccessException ex) { + LOGGER.log(WARNING, "Unable to create Schema class instance.", ex); + } + } + } + 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 ex) { + LOGGER.log(WARNING, "Unable to create Schema class instance.", ex); + } + } + } + } + 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 ex) { + LOGGER.log(WARNING, "Unable to create Schema class instance.", ex); + } + } + } + } + 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 ex) { + LOGGER.log(WARNING, "Unable to create Schema class instance.", ex); + } + } + } + } + } + +} \ 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..479d480bd26 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/media/XMLImpl.java @@ -0,0 +1,134 @@ +/* + * 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; + +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..786381a65bd --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/ParameterImpl.java @@ -0,0 +1,362 @@ +/* + * 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; +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..6843fea8aaa --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/parameters/RequestBodyImpl.java @@ -0,0 +1,151 @@ +/* + * 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; +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..feb2f7ea4e2 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponseImpl.java @@ -0,0 +1,193 @@ +/* + * 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; +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.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 { + + 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); + } + } + 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 new file mode 100644 index 00000000000..19ae86f59a4 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/responses/APIResponsesImpl.java @@ -0,0 +1,95 @@ +/* + * 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; + +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; + } + + 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 new file mode 100644 index 00000000000..74f3d2ebaf9 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowImpl.java @@ -0,0 +1,139 @@ +/* + * 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; +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..bd46c9afa3e --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/OAuthFlowsImpl.java @@ -0,0 +1,148 @@ +/* + * 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; +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..20f58d4b0bb --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/ScopesImpl.java @@ -0,0 +1,75 @@ +/* + * 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; +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..0706cb66f5b --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecurityRequirementImpl.java @@ -0,0 +1,83 @@ +/* + * 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; + +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) { + if (isAnnotationNull(from)) { + 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..1f710ee2dc6 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/security/SecuritySchemeImpl.java @@ -0,0 +1,242 @@ +/* + * 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 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 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; + } + + 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)); + } + 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..7147050d097 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerImpl.java @@ -0,0 +1,122 @@ +/* + * 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; +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..188a649505e --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariableImpl.java @@ -0,0 +1,112 @@ +/* + * 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; +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 String description; + protected String defaultValue; + + protected List enumeration = new ArrayList<>(); + + @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; + } + + @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 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) { + 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 new file mode 100644 index 00000000000..c0fec9e06bc --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/servers/ServerVariablesImpl.java @@ -0,0 +1,101 @@ +/* + * 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; +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()) { + 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..8b9c56e7c11 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/tags/TagImpl.java @@ -0,0 +1,145 @@ +/* + * 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; +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; + } + + // If there is a reference, add the reference and return + if (from.ref() != null && !from.ref().isEmpty()) { + to.addTag(from.ref()); + return; + } + + 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()); + } + } + +} \ 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..e63651b65c0 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtils.java @@ -0,0 +1,540 @@ +/* + * 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 java.util.logging.Level.WARNING; + +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 java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.logging.Logger; + +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.Constructible; +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 static final Logger LOGGER = Logger.getLogger(ModelUtils.class.getName()); + + /** + * The name of a variable in the model tree that is unrecognised. + */ + public static final String UNKNOWN_ELEMENT_NAME = "?"; + + /** + * Private constructor to hide public one. + */ + 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 + * 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; + } + + 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}. + * + * @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) { + return false; + } else if (value instanceof Collection && !Collection.class.cast(value).isEmpty()) { + 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()) { + return false; + } else if (value instanceof Annotation) { + allNull = isAnnotationNull((Annotation) value); + } + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + LOGGER.log(WARNING, "Unable to access annotation element.", ex); + } + } + } + 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; + } + // 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) { + 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 && from != null) { + for (Field f : to.getClass().getDeclaredFields()) { + f.setAccessible(true); + try { + f.set(to, f.get(from)); + } catch (IllegalArgumentException | IllegalAccessException e) { + // Ignore errors + } + } + } + } + + @SuppressWarnings("unchecked") + public static void merge(T from, T to, boolean override) { + if (from != null && to != null) { + for (Field f : to.getClass().getDeclaredFields()) { + f.setAccessible(true); + try { + // 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 + } + } + } + } +} \ 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..fe74aeb40fe --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ApplicationProcessor.java @@ -0,0 +1,936 @@ +/* + * 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 java.util.logging.Level.FINE; +import static java.util.logging.Level.WARNING; + +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.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.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; + +/** + * 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(Set> appClasses) { + this.classes = appClasses; + } + + @Override + 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, operation, element); + + // Add the default response + insertDefaultResponse(context, 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, operation, element); + + // Add the default response + insertDefaultResponse(context, 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, operation, element); + + // Add the default response + insertDefaultResponse(context, 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, operation, element); + + // Add the default response + insertDefaultResponse(context, 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, operation, element); + + // Add the default response + insertDefaultResponse(context, 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, operation, element); + + // Add the default response + insertDefaultResponse(context, 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, operation, element); + + // Add the default response + insertDefaultResponse(context, 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 == null) ? null : schema.name(); + if (schemaName == null || schemaName.isEmpty()) { + schemaName = Class.class.cast(element).getSimpleName(); + } + + // Add a new schema + org.eclipse.microprofile.openapi.models.media.Schema newSchema = new SchemaImpl(); + context.getApi().getComponents().addSchema(schemaName, newSchema); + + // 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) { + + // 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; + if (Field.class.cast(element).getDeclaringClass().isAnnotationPresent(Schema.class)) { + parentName = Field.class.cast(element).getDeclaringClass().getDeclaredAnnotation(Schema.class).name(); + } + 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() + .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() + .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); + 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 + + /** + * 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 ex) { + LOGGER.log(WARNING, "Unable to initialise application class.", ex); + } + } + } + + // 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. + if (resourceMapping.isEmpty()) { + resourceMapping.put("/", classList); + } + + return resourceMapping; + } + + 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(); + + // 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(context, 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 Operation} responses. + * + * @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(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(context, 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) { + 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) { + contentType = mediaType.toString(); + } + } catch (IllegalArgumentException ex) { + LOGGER.log(FINE, "Unrecognised content type.", ex); + } + return contentType; + } + + 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); + + // 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 the schema is an object, insert the reference + if (schemaType == SchemaType.OBJECT) { + if (insertObjectReference(context, 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 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(ApiContext context, Reference referee, Class referenceClass) { + + // If the object is a java core class + if (referenceClass == null || referenceClass.getName().startsWith("java.") || referenceClass.isPrimitive()) { + return false; + } + + // If the object is a Java EE object type + if (referenceClass.getName().startsWith("javax.")) { + return false; + } + + // Set the reference name + referee.setRef(referenceClass.getSimpleName()); + + // Create the schema + visitSchema(referenceClass.getDeclaredAnnotation(Schema.class), referenceClass, context); + + 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 new file mode 100644 index 00000000000..d5857b7a721 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/BaseProcessor.java @@ -0,0 +1,143 @@ +/* + * 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; +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; + +/** + * 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; + + public BaseProcessor(String applicationPath) { + this.applicationPath = applicationPath; + } + + @Override + public OpenAPI 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 config specified servers + if (config != null && !config.getServers().isEmpty()) { + // 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().isEmpty()) { + 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()) { + // 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)); + } + } + } + + // Add the operation servers + if (config != null && !config.getOperationServerMap().isEmpty()) { + for (Entry> entry : config.getOperationServerMap().entrySet()) { + + // Find the matching operation + for (PathItem pathItem : api.getPaths().values()) { + for (Operation operation : pathItem.readOperations()) { + if (operation.getOperationId().equals(entry.getKey())) { + + // 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 new file mode 100644 index 00000000000..e7dadca1909 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FileProcessor.java @@ -0,0 +1,120 @@ +/* + * 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 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.Logger; + +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; + +/** + * 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 { + // Search for a the correct file + URL fileUrl = appClassLoader.getResource("META-INF/openapi.json"); + 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")) { + mapper = ObjectMapperFactory.createJson(); + } else { + mapper = ObjectMapperFactory.createYaml(); + } + } else { + LOGGER.fine("No static OpenAPI document provided."); + } + } catch (URISyntaxException ex) { + LOGGER.log(WARNING, "Invalid URI syntax", ex); + } + } + + @Override + public OpenAPI process(OpenAPI api, OpenApiConfiguration config) { + if (file != null) { + OpenAPI readResult = null; + try { + readResult = mapper.readValue(file, OpenAPIImpl.class); + } catch (IOException ex) { + LOGGER.log(WARNING, "Error when reading static OpenAPI document.", ex); + } + if (readResult != null) { + merge(readResult, api, true); + } + } + 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 new file mode 100644 index 00000000000..ec1b3360ba1 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/FilterProcessor.java @@ -0,0 +1,221 @@ +/* + * 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 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.Logger; + +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; + +/** + * 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 OpenAPI process(OpenAPI api, OpenApiConfiguration config) { + try { + if (config.getFilter() != null) { + filter = config.getFilter().newInstance(); + } + } catch (InstantiationException | IllegalAccessException ex) { + LOGGER.log(WARNING, "Error creating OASFilter instance.", ex); + } + if (filter != null) { + return (OpenAPI) filterObject(api); + } else { + LOGGER.fine("No OASFilter provided."); + } + return api; + } + + @SuppressWarnings("unchecked") + private Object filterObject(Object object) { + 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()) { + Object result = filterObject(item); + + 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)) { + Object result = filterObject(item); + + 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(); + } + } + } + } + + // 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()) { + try { + field.setAccessible(true); + Object fieldValue = field.get(object); + + // Filter the object + Object result = filterObject(fieldValue); + + // Remove it if it's null + if (result == null) { + field.set(object, null); + } + } catch (IllegalArgumentException | IllegalAccessException ex) { + LOGGER.log(WARNING, "Unable to access field in OpenAPI model.", ex); + } + } + + // Visit the object + 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 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 new file mode 100644 index 00000000000..722d13e7687 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/processor/ModelReaderProcessor.java @@ -0,0 +1,87 @@ +/* + * 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 java.util.logging.Level.WARNING; + +import java.util.logging.Logger; + +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; + +/** + * 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 OpenAPI process(OpenAPI api, OpenApiConfiguration config) { + try { + if (config.getModelReader() != null) { + reader = config.getModelReader().newInstance(); + } + } catch (InstantiationException | IllegalAccessException ex) { + LOGGER.log(WARNING, "Error creating OASModelReader instance.", ex); + } + if (reader != null) { + OpenAPI model = reader.buildModel(); + if (model != null) { + return model; + } else { + LOGGER.log(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/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..0b02a549437 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/app/OpenApiApplication.java @@ -0,0 +1,68 @@ +/* + * 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; +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..47118609de8 --- /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,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.rest.app.provider; + +import java.util.Map.Entry; +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.core.JsonGenerator.Feature; +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.Constructible; + +import fish.payara.microprofile.openapi.impl.model.OASFactoryResolverImpl; +import fish.payara.microprofile.openapi.impl.rest.app.provider.mixin.ExtensionsMixin; + +public final class ObjectMapperFactory { + + /** + * Private constructor to hide default public one. + */ + private 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); + } + + @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(); + + // Configure the implementation of each class + for (Entry, Class> entry : OASFactoryResolverImpl.MODEL_MAP + .entrySet()) { + module.addAbstractTypeMapping((Class) entry.getKey(), (Class) entry.getValue()); + } + + // Configure the mixins for each type + mapper.setMixIns(OASFactoryResolverImpl.MODEL_MAP.keySet().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..7cedd4d8163 --- /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,89 @@ +/* + * 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; +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/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..1189a77de0e --- /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 interface 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 new file mode 100644 index 00000000000..78e9121dc1f --- /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,74 @@ +/* + * 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; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +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 { + 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..39dad6635d4 --- /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,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.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..d4cc6b1c451 --- /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,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.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..b9dff295261 --- /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,89 @@ +/* + * 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 static final Logger LOGGER = Logger.getLogger(OpenApiResource.class.getName()); + + @GET + @Produces({ APPLICATION_YAML, APPLICATION_JSON }) + 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."); + return Response.status(FORBIDDEN).build(); + } + + // 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 new file mode 100644 index 00000000000..b36c3e8238f --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/rest/init/OpenApiServletContainerInitializer.java @@ -0,0 +1,83 @@ +/* + * 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; +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 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..0760e46265e --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiContext.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.impl.visitor; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; + +import fish.payara.microprofile.openapi.api.visitor.ApiContext; + +public class OpenApiContext implements ApiContext { + + private final OpenAPI api; + private final String path; + private final Operation operation; + + public OpenApiContext(OpenAPI api, String path, Operation operation) { + this.api = api; + this.path = path; + this.operation = operation; + } + + public OpenApiContext(OpenAPI api, String path) { + this(api, path, null); + } + + @Override + public OpenAPI getApi() { + return api; + } + + @Override + public String getPath() { + 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 new file mode 100644 index 00000000000..34dc5110fe1 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/main/java/fish/payara/microprofile/openapi/impl/visitor/OpenApiWalker.java @@ -0,0 +1,294 @@ +/* + * 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.util.Map; +import java.util.Map.Entry; +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.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> allowedClasses, Map>> resourceMapping) { + this.api = api; + this.resourceMapping = resourceMapping; + this.classes = new TreeSet<>((class1, class2) -> { + if (class1.equals(class2)) { + return 0; + } + // Subclasses first + if (class1.isAssignableFrom(class2)) { + return -1; + } + // 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); + } + + @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) { + + 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()) { + + processAnnotation(method, annotationClass, annotationFunction, altClass, altFunction, + new OpenApiContext(api, getResourcePath(method), getOperation(method))); + + for (java.lang.reflect.Parameter parameter : method.getParameters()) { + processAnnotation(parameter, annotationClass, annotationFunction, altClass, altFunction, + new OpenApiContext(api, getResourcePath(method), getOperation(method))); + } + } + } + } + + 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); + } + } + } + } + + 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; + + // 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)) { + path = getResourcePath(method.getDeclaringClass()) + "/" + + method.getDeclaredAnnotation(Path.class).value(); + } else { + path = 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 (Entry>> entry : resourceMapping.entrySet()) { + if (entry.getValue() != null && entry.getValue().contains(clazz)) { + path = entry.getKey() + "/" + clazz.getDeclaredAnnotation(Path.class).value(); + } + } + } + } + return normaliseUrl(path); + } + +} \ 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/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 new file mode 100644 index 00000000000..95b41c38e1a --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/impl/model/util/ModelUtilsTest.java @@ -0,0 +1,68 @@ +/* + * 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; +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..e21a5827064 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/classloader/ApplicationClassLoader.java @@ -0,0 +1,116 @@ +/* + * 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; +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 Set> getApplicationClasses() { + return appClasses; + } + + 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/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..7d21b0f15c6 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/resource/rule/ApplicationProcessedDocument.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.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.TestComponent; + +public class ApplicationProcessedDocument extends OpenAPIImpl { + + public ApplicationProcessedDocument() { + // Apply base processor + new BaseProcessor("/testlocation_123").process(this, null); + + ApplicationClassLoader appClassLoader = new ApplicationClassLoader(new TestApplication(), singleton(TestComponent.class)); + + // Apply application processor + new ApplicationProcessor(appClassLoader.getApplicationClasses()).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/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..ab28ceecf20 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/TestApplication.java @@ -0,0 +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 java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +import fish.payara.microprofile.openapi.test.app.application.ResponseTest; +import fish.payara.microprofile.openapi.test.app.application.RootPathTest; + +@ApplicationPath("/test") +public class TestApplication extends Application { + + @Override + public Set> getClasses() { + Set> classes = new HashSet<>(); + classes.add(RootPathTest.class); + classes.add(ResponseTest.class); + return classes; + } + +} \ 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..f957b941d7c --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/ResponseTest.java @@ -0,0 +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 javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.MediaType.APPLICATION_XML; +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.responses.APIResponses; +import org.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; + +/** + * 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() { + try { + document = new ApplicationProcessedDocument(); + } catch (Exception ex) { + ex.printStackTrace(); + fail("Failed to build document."); + } + } + + @GET + @APIResponse(responseCode = "200", content = @Content(schema = @Schema(description = "hello!"))) + @APIResponse(responseCode = "400", description = "error") + public String getInheritedMediaType() { + return null; + } + + @Test + 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()); + } + +} \ 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..8e5676a1c29 --- /dev/null +++ b/appserver/payara-appserver-modules/microprofile/openapi/src/test/java/fish/payara/microprofile/openapi/test/app/application/RootPathTest.java @@ -0,0 +1,84 @@ +/* + * 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 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.junit.BeforeClass; +import org.junit.Test; + +import fish.payara.microprofile.openapi.resource.rule.ApplicationProcessedDocument; + +/** + * 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() { + try { + document = new ApplicationProcessedDocument(); + } catch (Exception ex) { + ex.printStackTrace(); + fail("Failed to build document."); + } + } + + @GET + public String getRoot() { + return null; + } + + @Test + public void testRoot() { + 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/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/appserver/payara-appserver-modules/microprofile/pom.xml b/appserver/payara-appserver-modules/microprofile/pom.xml index 46a0053eadd..e99012493b9 100644 --- a/appserver/payara-appserver-modules/microprofile/pom.xml +++ b/appserver/payara-appserver-modules/microprofile/pom.xml @@ -57,6 +57,7 @@ holder. healthcheck jwt-auth metrics + openapi opentracing diff --git a/appserver/pom.xml b/appserver/pom.xml index 2554c35484f..ec41fbc6f3f 100644 --- a/appserver/pom.xml +++ b/appserver/pom.xml @@ -217,6 +217,7 @@ 1.0.payara-p1 1.1.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..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,6 +138,14 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml + + 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 39c4b2baf99..5c9e2cb3f3d 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,36 +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.ClassConverter; -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.StringConverter; -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.lang.reflect.Type; @@ -93,9 +63,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; @@ -114,6 +86,38 @@ 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.ClassConverter; +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.StringConverter; +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. @@ -240,7 +244,7 @@ Config getConfig(ApplicationInfo appInfo) { initialiseApplicationConfig(appInfo); LinkedList sources = new LinkedList<>(); Map converters = new HashMap<>(); - sources.addAll(getDefaultSources()); + sources.addAll(getDefaultSources(appInfo)); sources.addAll(getDiscoveredSources(appInfo)); converters.putAll(getDefaultConverters()); converters.putAll(getDiscoveredConverters(appInfo)); @@ -274,22 +278,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()); @@ -307,9 +297,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) { @@ -396,6 +409,7 @@ Map getDefaultConverters() { result.put(ChronoUnit.class, new ChronoUnitConverter()); result.put(Class.class, new ClassConverter()); result.put(String.class, new StringConverter()); + result.put(String[].class, new StringArrayConverter()); return result; } @@ -422,18 +436,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(); + } + } diff --git a/nucleus/pom.xml b/nucleus/pom.xml index da5bf37e17e..f79c7a2850a 100644 --- a/nucleus/pom.xml +++ b/nucleus/pom.xml @@ -305,7 +305,8 @@ 2.9.5 - + + 1.18 1.1 @@ -1178,6 +1179,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