From 464d7e04bac1a376e3f8e8621edb6165764f6c59 Mon Sep 17 00:00:00 2001 From: Jean-Louis Monteiro Date: Mon, 30 Oct 2023 15:29:04 +0100 Subject: [PATCH] feat(#TOMEE-4268): MicroProfile OpenAPI Reader example. --- examples/mp-openapi-reader/pom.xml | 127 ++++++++++++++++ .../microprofile/openapi/CustomReader.java | 77 ++++++++++ .../microprofile/openapi/WeatherService.java | 37 +++++ .../microprofile/openapi/moviefun/Movie.java | 102 +++++++++++++ .../openapi/moviefun/MoviesBean.java | 92 ++++++++++++ .../moviefun/rest/ApplicationConfig.java | 34 +++++ .../openapi/moviefun/rest/LoadRest.java | 41 ++++++ .../openapi/moviefun/rest/MoviesRest.java | 82 +++++++++++ .../META-INF/microprofile-config.properties | 2 + .../main/resources/META-INF/persistence.xml | 31 ++++ ...akarta.servlet.ServletContainerInitializer | 1 + .../src/main/webapp/WEB-INF/web.xml | 25 ++++ .../src/main/webapp/my-openapi.json | 25 ++++ .../src/main/webapp/openapi/openapi.yaml | 135 ++++++++++++++++++ .../src/test/java/WeatherServiceTest.java | 95 ++++++++++++ .../src/test/resources/arquillian.xml | 31 ++++ examples/pom.xml | 1 + 17 files changed, 938 insertions(+) create mode 100644 examples/mp-openapi-reader/pom.xml create mode 100644 examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/CustomReader.java create mode 100644 examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/WeatherService.java create mode 100644 examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/Movie.java create mode 100644 examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/MoviesBean.java create mode 100644 examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/ApplicationConfig.java create mode 100644 examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/LoadRest.java create mode 100644 examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/MoviesRest.java create mode 100644 examples/mp-openapi-reader/src/main/resources/META-INF/microprofile-config.properties create mode 100644 examples/mp-openapi-reader/src/main/resources/META-INF/persistence.xml create mode 100644 examples/mp-openapi-reader/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer create mode 100644 examples/mp-openapi-reader/src/main/webapp/WEB-INF/web.xml create mode 100644 examples/mp-openapi-reader/src/main/webapp/my-openapi.json create mode 100644 examples/mp-openapi-reader/src/main/webapp/openapi/openapi.yaml create mode 100644 examples/mp-openapi-reader/src/test/java/WeatherServiceTest.java create mode 100644 examples/mp-openapi-reader/src/test/resources/arquillian.xml diff --git a/examples/mp-openapi-reader/pom.xml b/examples/mp-openapi-reader/pom.xml new file mode 100644 index 00000000000..95ae07fc7e8 --- /dev/null +++ b/examples/mp-openapi-reader/pom.xml @@ -0,0 +1,127 @@ + + + + 4.0.0 + org.superbiz + mp-openapi-reader + 10.0.0-SNAPSHOT + war + TomEE :: Examples :: Microprofile OpenAPI Reader + + 9.1.1 + 1.7.0.Final + 4.13.2 + 10.0.0-SNAPSHOT + 3.0.1 + 3.1.1 + + + + org.apache.tomee + jakartaee-api + ${version.jakartaee-api} + provided + + + org.eclipse.microprofile.openapi + microprofile-openapi-api + ${version.openapi-api} + + + io.smallrye + smallrye-open-api + ${version.microprofile.impl.openapi} + provided + pom + + + io.smallrye + smallrye-open-api-core + ${version.microprofile.impl.openapi} + provided + + + junit + junit + ${junit.version} + test + + + org.apache.tomee + openejb-cxf-rs + ${tomee.version} + test + + + org.jboss.arquillian.junit + arquillian-junit-container + ${version.arquillian.bom} + test + + + org.apache.tomee + arquillian-tomee-remote + ${tomee.version} + test + + + org.apache.tomee + apache-tomee + ${tomee.version} + zip + microprofile + test + + + + + + org.apache.tomee.maven + tomee-maven-plugin + ${tomee.version} + + microprofile + ${project.artifactId} + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 1.8 + 1.8 + + + + + + + + + localhost + file://${basedir}/target/repo/ + + + localhost + file://${basedir}/target/snapshot-repo/ + + + diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/CustomReader.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/CustomReader.java new file mode 100644 index 00000000000..2dc481d8380 --- /dev/null +++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/CustomReader.java @@ -0,0 +1,77 @@ +package org.superbiz.microprofile.openapi; + +import io.smallrye.openapi.api.OpenApiConfig; +import io.smallrye.openapi.api.OpenApiConfigImpl; +import io.smallrye.openapi.api.OpenApiDocument; +import io.smallrye.openapi.runtime.OpenApiProcessor; +import io.smallrye.openapi.runtime.OpenApiStaticFile; +import io.smallrye.openapi.runtime.io.Format; +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.openapi.OASModelReader; +import org.eclipse.microprofile.openapi.models.OpenAPI; + +import java.net.URL; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Logger; + +import static io.smallrye.openapi.runtime.io.Format.JSON; +import static io.smallrye.openapi.runtime.io.Format.YAML; + +public class CustomReader implements OASModelReader, ServletContainerInitializer { + + private static final Logger LOGGER = Logger.getLogger(CustomReader.class.getName()); + + private static Optional openAPI; + + @Override + public OpenAPI buildModel() { + return openAPI.orElseThrow(() -> new IllegalStateException("OpenAPI document not available.")); + } + + @Override + public void onStartup(final Set> c, final ServletContext ctx) throws ServletException { + try { + final OpenApiConfig openApiConfig = new OpenApiConfigImpl(ConfigProvider.getConfig());; + final Optional yaml = readOpenApiFile(ctx, "/WEB-INF/classes/openapi/openapi.yaml", YAML); + final Optional json = readOpenApiFile(ctx, "/WEB-INF/classes/my-openapi.json", JSON); + + final OpenApiDocument document = OpenApiDocument.INSTANCE; + try { + document.reset(); + document.config(openApiConfig); + yaml.ifPresent(document::modelFromStaticFile); + json.ifPresent(document::modelFromStaticFile); + document.initialize(); + + openAPI = Optional.ofNullable(document.get()); + + } finally { + document.reset(); + } + + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + + private static Optional readOpenApiFile( + final ServletContext servletContext, final String location, + final Format format) throws Exception { + + final URL resource = servletContext.getResource(location); + if (resource == null) { + LOGGER.fine("Could not find static OpenAPI file " + location); + return Optional.empty(); + } + + LOGGER.fine("Found static OpenAPI file " + location); + + try (OpenApiStaticFile staticFile = new OpenApiStaticFile(resource.openStream(), format)) { + return Optional.of(OpenApiProcessor.modelFromStaticFile(staticFile)); + } + } +} diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/WeatherService.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/WeatherService.java new file mode 100644 index 00000000000..bc6df1889d4 --- /dev/null +++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/WeatherService.java @@ -0,0 +1,37 @@ +package org.superbiz.microprofile.openapi; /** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Path("/weather") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@ApplicationScoped +public class WeatherService { + + @Path("/day/status") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String dayStatus() { + return "Hi, today is a sunny day!"; + } +} diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/Movie.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/Movie.java new file mode 100644 index 00000000000..5bf82643174 --- /dev/null +++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/Movie.java @@ -0,0 +1,102 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.superbiz.microprofile.openapi.moviefun; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.xml.bind.annotation.XmlRootElement; + +@Entity +@XmlRootElement(name = "movie") +public class Movie { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private long id; + + private String director; + private String title; + private int year; + private String genre; + private int rating; + + public Movie() { + } + + public Movie(String title, String director, String genre, int rating, int year) { + this.director = director; + this.title = title; + this.year = year; + this.genre = genre; + this.rating = rating; + } + + public Movie(String director, String title, int year) { + this.director = director; + this.title = title; + this.year = year; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getDirector() { + return director; + } + + public void setDirector(String director) { + this.director = director; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } + + public String getGenre() { + return genre; + } + + public void setGenre(String genre) { + this.genre = genre; + } + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } +} \ No newline at end of file diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/MoviesBean.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/MoviesBean.java new file mode 100644 index 00000000000..803e53f40c2 --- /dev/null +++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/MoviesBean.java @@ -0,0 +1,92 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.superbiz.microprofile.openapi.moviefun; + +import jakarta.ejb.Stateless; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.metamodel.EntityType; + +import java.util.List; + +@Stateless +public class MoviesBean { + + @PersistenceContext(unitName = "movie-unit") + private EntityManager entityManager; + + public Movie find(Long id) { + return entityManager.find(Movie.class, id); + } + + public void addMovie(Movie movie) { + entityManager.persist(movie); + } + + public void editMovie(Movie movie) { + entityManager.merge(movie); + } + + public void deleteMovie(long id) { + Movie movie = entityManager.find(Movie.class, id); + entityManager.remove(movie); + } + + public List getMovies(Integer firstResult, Integer maxResults, String field, String searchTerm) { + CriteriaBuilder qb = entityManager.getCriteriaBuilder(); + CriteriaQuery cq = qb.createQuery(Movie.class); + Root root = cq.from(Movie.class); + EntityType type = entityManager.getMetamodel().entity(Movie.class); + if (field != null && searchTerm != null && !"".equals(field.trim()) && !"".equals(searchTerm.trim())) { + Path path = root.get(type.getDeclaredSingularAttribute(field.trim(), String.class)); + Predicate condition = qb.like(path, "%" + searchTerm.trim() + "%"); + cq.where(condition); + } + TypedQuery q = entityManager.createQuery(cq); + if (maxResults != null) { + q.setMaxResults(maxResults); + } + if (firstResult != null) { + q.setFirstResult(firstResult); + } + return q.getResultList(); + } + + public int count(String field, String searchTerm) { + CriteriaBuilder qb = entityManager.getCriteriaBuilder(); + CriteriaQuery cq = qb.createQuery(Long.class); + Root root = cq.from(Movie.class); + EntityType type = entityManager.getMetamodel().entity(Movie.class); + cq.select(qb.count(root)); + if (field != null && searchTerm != null && !"".equals(field.trim()) && !"".equals(searchTerm.trim())) { + Path path = root.get(type.getDeclaredSingularAttribute(field.trim(), String.class)); + Predicate condition = qb.like(path, "%" + searchTerm.trim() + "%"); + cq.where(condition); + } + return entityManager.createQuery(cq).getSingleResult().intValue(); + } + + public void clean() { + entityManager.createQuery("delete from Movie").executeUpdate(); + } +} \ No newline at end of file diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/ApplicationConfig.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/ApplicationConfig.java new file mode 100644 index 00000000000..625515c3fe9 --- /dev/null +++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/ApplicationConfig.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.superbiz.microprofile.openapi.moviefun.rest; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +@ApplicationPath("/rest") +public class ApplicationConfig extends Application { + + @Override + @SuppressWarnings("unchecked") + public Set> getClasses() { + return new HashSet>(Arrays.asList(LoadRest.class, MoviesRest.class)); + } +} \ No newline at end of file diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/LoadRest.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/LoadRest.java new file mode 100644 index 00000000000..2d5d1e6e679 --- /dev/null +++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/LoadRest.java @@ -0,0 +1,41 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.superbiz.microprofile.openapi.moviefun.rest; + +import jakarta.ejb.EJB; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import org.superbiz.microprofile.openapi.moviefun.Movie; +import org.superbiz.microprofile.openapi.moviefun.MoviesBean; + +@Path("load") +public class LoadRest { + @EJB + private MoviesBean moviesBean; + + @POST + public void load() { + moviesBean.addMovie(new Movie("Wedding Crashers", "David Dobkin", "Comedy", 7, 2005)); + moviesBean.addMovie(new Movie("Starsky & Hutch", "Todd Phillips", "Action", 6, 2004)); + moviesBean.addMovie(new Movie("Shanghai Knights", "David Dobkin", "Action", 6, 2003)); + moviesBean.addMovie(new Movie("I-Spy", "Betty Thomas", "Adventure", 5, 2002)); + moviesBean.addMovie(new Movie("The Royal Tenenbaums", "Wes Anderson", "Comedy", 8, 2001)); + moviesBean.addMovie(new Movie("Zoolander", "Ben Stiller", "Comedy", 6, 2001)); + moviesBean.addMovie(new Movie("Shanghai Noon", "Tom Dey", "Comedy", 7, 2000)); + } + +} \ No newline at end of file diff --git a/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/MoviesRest.java b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/MoviesRest.java new file mode 100644 index 00000000000..80d2e889bf9 --- /dev/null +++ b/examples/mp-openapi-reader/src/main/java/org/superbiz/microprofile/openapi/moviefun/rest/MoviesRest.java @@ -0,0 +1,82 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.superbiz.microprofile.openapi.moviefun.rest; + +import jakarta.ejb.EJB; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import org.superbiz.microprofile.openapi.moviefun.Movie; +import org.superbiz.microprofile.openapi.moviefun.MoviesBean; + +import java.util.List; + +@Path("movies") +@Produces({"application/json"}) +public class MoviesRest { + + @EJB + private MoviesBean service; + + @GET + @Path("{id}") + public Movie find(@PathParam("id") Long id) { + return service.find(id); + } + + @GET + public List getMovies(@QueryParam("first") Integer first, @QueryParam("max") Integer max, + @QueryParam("field") String field, @QueryParam("searchTerm") String searchTerm) { + return service.getMovies(first, max, field, searchTerm); + } + + @POST + @Consumes("application/json") + public Movie addMovie(Movie movie) { + service.addMovie(movie); + return movie; + } + + @PUT + @Path("{id}") + @Consumes("application/json") + public Movie editMovie(Movie movie) { + service.editMovie(movie); + return movie; + } + + @DELETE + @Path("{id}") + public void deleteMovie(@PathParam("id") long id) { + service.deleteMovie(id); + } + + @GET + @Path("count") + @Produces(MediaType.TEXT_PLAIN) + public int count(@QueryParam("field") String field, @QueryParam("searchTerm") String searchTerm) { + return service.count(field, searchTerm); + } + +} \ No newline at end of file diff --git a/examples/mp-openapi-reader/src/main/resources/META-INF/microprofile-config.properties b/examples/mp-openapi-reader/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 00000000000..268e2ad12cb --- /dev/null +++ b/examples/mp-openapi-reader/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,2 @@ +mp.openapi.model.reader=org.superbiz.microprofile.openapi.CustomReader +mp.openapi.scan.disable=false diff --git a/examples/mp-openapi-reader/src/main/resources/META-INF/persistence.xml b/examples/mp-openapi-reader/src/main/resources/META-INF/persistence.xml new file mode 100644 index 00000000000..ec38aaa1170 --- /dev/null +++ b/examples/mp-openapi-reader/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,31 @@ + + + + + movieDatabase + movieDatabaseUnmanaged + org.superbiz.moviefun.Movie + + + + + + diff --git a/examples/mp-openapi-reader/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/examples/mp-openapi-reader/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100644 index 00000000000..d0eefa40f3b --- /dev/null +++ b/examples/mp-openapi-reader/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +org.superbiz.microprofile.openapi.CustomReader \ No newline at end of file diff --git a/examples/mp-openapi-reader/src/main/webapp/WEB-INF/web.xml b/examples/mp-openapi-reader/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..6e1611111fe --- /dev/null +++ b/examples/mp-openapi-reader/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,25 @@ + + + + + Microprofile OpenAPI Reader + diff --git a/examples/mp-openapi-reader/src/main/webapp/my-openapi.json b/examples/mp-openapi-reader/src/main/webapp/my-openapi.json new file mode 100644 index 00000000000..21cefed3762 --- /dev/null +++ b/examples/mp-openapi-reader/src/main/webapp/my-openapi.json @@ -0,0 +1,25 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Generated API", + "version" : "1.0" + }, + "paths" : { + "/rest/weather/day/status" : { + "get" : { + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "text/plain" : { + "schema" : { + "type" : "string" + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/examples/mp-openapi-reader/src/main/webapp/openapi/openapi.yaml b/examples/mp-openapi-reader/src/main/webapp/openapi/openapi.yaml new file mode 100644 index 00000000000..53fb4a7fe9c --- /dev/null +++ b/examples/mp-openapi-reader/src/main/webapp/openapi/openapi.yaml @@ -0,0 +1,135 @@ +--- +openapi: 3.0.3 +info: + title: Generated API + version: "1.0" +paths: + /rest/load: + post: + responses: + "201": + description: Created + /rest/movies: + get: + parameters: + - name: field + in: query + schema: + type: string + - name: first + in: query + schema: + format: int32 + type: integer + - name: max + in: query + schema: + format: int32 + type: integer + - name: searchTerm + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Movie' + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Movie' + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Movie' + /rest/movies/count: + get: + parameters: + - name: field + in: query + schema: + type: string + - name: searchTerm + in: query + schema: + type: string + responses: + "200": + description: OK + content: + text/plain: + schema: + format: int32 + type: integer + /rest/movies/{id}: + get: + parameters: + - name: id + in: path + required: true + schema: + format: int64 + type: integer + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Movie' + put: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Movie' + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Movie' + delete: + parameters: + - name: id + in: path + required: true + schema: + format: int64 + type: integer + responses: + "204": + description: No Content +components: + schemas: + Movie: + type: object + properties: + id: + format: int64 + type: integer + director: + type: string + title: + type: string + year: + format: int32 + type: integer + genre: + type: string + rating: + format: int32 + type: integer + xml: + name: movie \ No newline at end of file diff --git a/examples/mp-openapi-reader/src/test/java/WeatherServiceTest.java b/examples/mp-openapi-reader/src/test/java/WeatherServiceTest.java new file mode 100644 index 00000000000..01e717c7992 --- /dev/null +++ b/examples/mp-openapi-reader/src/test/java/WeatherServiceTest.java @@ -0,0 +1,95 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Response; +import org.apache.openejb.loader.IO; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.superbiz.microprofile.openapi.CustomReader; +import org.superbiz.microprofile.openapi.WeatherService; +import org.superbiz.microprofile.openapi.moviefun.Movie; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +@RunWith(Arquillian.class) +public class WeatherServiceTest { + + @Deployment(testable = false) + public static WebArchive createDeployment() { + final WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "test.war") + .addClass(WeatherService.class) + .addClass(CustomReader.class) + .addPackages(true, Movie.class.getPackage()) + .addAsResource("META-INF/services/jakarta.servlet.ServletContainerInitializer") + .addAsResource("META-INF/persistence.xml") + .addAsResource("META-INF/microprofile-config.properties") + .addAsResource(new File("src/main/webapp/my-openapi.json"), "my-openapi.json") + .addAsResource(new File("src/main/webapp/openapi/openapi.yaml"), "openapi/openapi.yaml") + .addAsWebInfResource(new StringAsset(""), "beans.xml"); + return webArchive; + } + + @ArquillianResource + private URL base; + + private Client client; + + @Before + public void before() { + this.client = ClientBuilder.newClient(); + } + + @After + public void after() { + this.client.close(); + } + + @Test + public void openapi() throws IOException { + final WebTarget webTarget = client.target(base.toExternalForm()); + + final Response response = webTarget.path("/openapi") + .request() + .accept("application/json") + .buildGet().invoke(); + final InputStream entity = (InputStream) response.getEntity(); + final String payload = IO.slurp(entity); + + // assert that it returns a payload + Assert.assertNotNull(payload); + + // now check if the 2 openapi documents (yaml and json) got successfully loaded and merged + Assert.assertTrue(payload.contains("/rest/weather/day/status")); // from one openapi file + Assert.assertTrue(payload.contains("/rest/movies")); // from the other one + } + +} diff --git a/examples/mp-openapi-reader/src/test/resources/arquillian.xml b/examples/mp-openapi-reader/src/test/resources/arquillian.xml new file mode 100644 index 00000000000..0b54540a348 --- /dev/null +++ b/examples/mp-openapi-reader/src/test/resources/arquillian.xml @@ -0,0 +1,31 @@ + + + + + + + -1 + -1 + false + microprofile + target/apache-tomee-remote + target/arquillian-test-working-dir + + + \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index f126a11a8e8..b5af5082d37 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -129,6 +129,7 @@ mp-rest-jwt-public-key mp-rest-client mp-custom-healthcheck + mp-openapi-reader mtom multi-jpa-provider-testing multiple-arquillian-adapters