Skip to content

Commit

Permalink
Merge pull request #41 from spals/timkral/swagger
Browse files Browse the repository at this point in the history
Add Swagger
  • Loading branch information
timkral authored Mar 22, 2018
2 parents 4835705 + 3fb5822 commit c739562
Show file tree
Hide file tree
Showing 24 changed files with 439 additions and 19 deletions.
4 changes: 4 additions & 0 deletions app-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-jaxrs2</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package net.spals.appbuilder.app.core.jaxrs;

import com.google.common.base.Joiner;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import io.swagger.jaxrs.config.BeanConfig;
import io.swagger.jaxrs.listing.AcceptHeaderApiListingResource;
import io.swagger.jaxrs.listing.SwaggerSerializers;
import net.spals.appbuilder.config.service.ServiceScan;
import org.inferred.freebuilder.FreeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.core.Configurable;

/**
* A Guice {@link Module} which automatically creates
* a documentation API.
*
* @author tkral
*/
@FreeBuilder
public abstract class JaxRsDocModule extends AbstractModule {
private static final Logger LOGGER = LoggerFactory.getLogger(JaxRsDocModule.class);

public abstract boolean isWebServerAutoBindingEnabled();
public abstract String getApplicationName();
public abstract Configurable<?> getConfigurable();
public abstract ServiceScan getServiceScan();

public static class Builder extends JaxRsDocModule_Builder {
public Builder() {
setWebServerAutoBindingEnabled(true);
}
}

@Override
protected void configure() {
if (!isWebServerAutoBindingEnabled()) {
return;
}

LOGGER.info("Registering documentation API via Swagger");
// Automatically register Swagger API documentation endpoints
getConfigurable().register(AcceptHeaderApiListingResource.class);
getConfigurable().register(SwaggerSerializers.class);

// Automatically create and register a Swagger scanner
final BeanConfig swaggerConfig = new BeanConfig();
swaggerConfig.setResourcePackage(Joiner.on(',').join(getServiceScan().getServicePackages()));
swaggerConfig.setTitle(getApplicationName() + " API");
// Turn on automatic scanning. This should be the last value set in the config.
swaggerConfig.setScan();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ public abstract class JaxRsWebApp implements App {
public static class Builder extends JaxRsWebApp_Builder implements WebAppBuilder<JaxRsWebApp> {

private final GenericWorkerApp.Builder appDelegateBuilder;
private final JaxRsDocModule.Builder docModuleBuilder =
new JaxRsDocModule.Builder();
private final JaxRsMonitorModule.Builder monitorModuleBuilder =
new JaxRsMonitorModule.Builder();
new JaxRsMonitorModule.Builder();
private final JaxRsWebServerModule.Builder webServerModuleBuilder =
new JaxRsWebServerModule.Builder();
new JaxRsWebServerModule.Builder();

public Builder(final String name, final Logger logger) {
this.appDelegateBuilder = new GenericWorkerApp.Builder(name, logger);
docModuleBuilder.setApplicationName(name);
}

public Builder addBootstrapModule(final BootstrapModule bootstrapModule) {
Expand All @@ -60,7 +63,8 @@ public Builder disableErrorOnServiceLeaks() {

@Override
public Builder disableWebServerAutoBinding() {
webServerModuleBuilder.setActive(false);
docModuleBuilder.setWebServerAutoBindingEnabled(false);
webServerModuleBuilder.setWebServerAutoBindingEnabled(false);
return this;
}

Expand All @@ -86,6 +90,7 @@ public Builder enableServiceGraph(final ServiceGraphFormat graphFormat) {

@Override
public Builder setConfigurable(final Configurable<?> configurable) {
docModuleBuilder.setConfigurable(configurable);
monitorModuleBuilder.setConfigurable(configurable);
webServerModuleBuilder.setConfigurable(configurable);
return super.setConfigurable(configurable);
Expand All @@ -106,11 +111,13 @@ public Builder setServiceConfigFromClasspath(final String serviceConfigFileName)
@Override
public Builder setServiceScan(final ServiceScan serviceScan) {
appDelegateBuilder.setServiceScan(serviceScan);
docModuleBuilder.setServiceScan(serviceScan);
return this;
}

@Override
public JaxRsWebApp build() {
appDelegateBuilder.addModule(docModuleBuilder.build());
appDelegateBuilder.addModule(monitorModuleBuilder.build());

webServerModuleBuilder.setServiceGraph(appDelegateBuilder.getServiceGraph());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@
public abstract class JaxRsWebServerModule extends AbstractModule implements InjectionListener<Object>, TypeListener {
private static final Logger LOGGER = LoggerFactory.getLogger(JaxRsWebServerModule.class);

public abstract boolean isActive();
public abstract boolean isWebServerAutoBindingEnabled();
public abstract Configurable<?> getConfigurable();
public abstract ServiceGraph getServiceGraph();

public static class Builder extends JaxRsWebServerModule_Builder {
public Builder() {
setActive(true);
setWebServerAutoBindingEnabled(true);
}
}

Expand Down Expand Up @@ -69,7 +69,7 @@ public void afterInjection(final Object wsComponent) {
@Override
public <I> void hear(final TypeLiteral<I> typeLiteral,
final TypeEncounter<I> typeEncounter) {
if (isActive()) {
if (isWebServerAutoBindingEnabled()) {
// Add a dummy JAXRS WEBSERVER vertex to the service graph to show how WebServer components
// relate to one another
if (!getServiceGraph().containsVertex(theWebServerVertex)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package net.spals.appbuilder.app.dropwizard;

import io.dropwizard.Configuration;
import io.dropwizard.testing.DropwizardTestSupport;
import net.spals.appbuilder.app.dropwizard.doc.DocDropwizardWebApp;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Map;

import static javax.ws.rs.core.Response.Status.OK;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

/**
* Functional tests for API documentation in a {@link DropwizardWebApp}
* (see {@link DocDropwizardWebApp}).
*
* @author tkral
*/
public class DocDropwizardWebAppFTest {

private final DropwizardTestSupport<Configuration> testServerWrapper =
new DropwizardTestSupport<>(DocDropwizardWebApp.class, new Configuration());

private final Client webClient = ClientBuilder.newClient();

@BeforeClass
void classSetup() {
testServerWrapper.before();
}

@AfterClass
void classTearDown() {
testServerWrapper.after();
}

@Test
public void testApiDocumentation() {
final String target = "http://localhost:" + testServerWrapper.getLocalPort() + "/swagger";
final WebTarget webTarget = webClient.target(target);
final Response docResponse = webTarget.request(MediaType.APPLICATION_JSON_TYPE).get();

assertThat(docResponse.getStatus(), is(OK.getStatusCode()));
final Map<String, Object> json = docResponse.readEntity(new GenericType<Map<String, Object>>() {});

assertThat(json, hasKey("info"));
final Map<String, Object> info = (Map<String, Object>)json.get("info");
assertThat(info, hasEntry("title", "DocDropwizardWebApp API"));

assertThat(json, hasKey("paths"));
final Map<String, Object> paths = (Map<String, Object>)json.get("paths");
assertThat(paths, hasKey("/doc/get"));
assertThat(paths, hasKey("/doc/get/{id}"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
import io.dropwizard.testing.DropwizardTestSupport;
import io.opentracing.NoopTracer;
import io.opentracing.Tracer;
import net.spals.appbuilder.app.dropwizard.sample.SampleDropwizardCustomService;
import net.spals.appbuilder.app.dropwizard.sample.SampleDropwizardCustomSet;
import net.spals.appbuilder.app.dropwizard.sample.SampleDropwizardCustomSingleton;
import net.spals.appbuilder.app.dropwizard.sample.SampleDropwizardWebApp;
import net.spals.appbuilder.executor.core.ExecutorServiceFactory;
import net.spals.appbuilder.filestore.core.FileStore;
Expand Down Expand Up @@ -97,9 +98,18 @@ public void testCustomModuleInjection(
}

@Test
public void testCustomServiceInjection() {
public void testCustomSetInjection() {
final Injector serviceInjector = webAppDelegate.getServiceInjector();
assertThat(serviceInjector.getInstance(SampleDropwizardCustomService.class), notNullValue());
final Set<SampleDropwizardCustomSet> serviceSet =
serviceInjector.getInstance(Key.get(new TypeLiteral<Set<SampleDropwizardCustomSet>>(){}));
assertThat(serviceSet, notNullValue());
assertThat(serviceSet, hasSize(1));
}

@Test
public void testCustomSingletonInjection() {
final Injector serviceInjector = webAppDelegate.getServiceInjector();
assertThat(serviceInjector.getInstance(SampleDropwizardCustomSingleton.class), notNullValue());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static javax.ws.rs.core.Response.Status.OK;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package net.spals.appbuilder.app.dropwizard.doc;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import net.spals.appbuilder.annotations.service.AutoBindSingleton;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;

/**
* A sample web resource.
*
* @author tkral
*/
@AutoBindSingleton
@Api(value = "doc")
@Path("doc")
public class DocDropwizardResource {

DocDropwizardResource() { }

@GET
@Path("get")
@ApiOperation(value = "Execute get request with no parameters")
public Response getCallback() {
return Response.ok().build();
}

@GET
@Path("get/{id}")
@ApiOperation(value = "Execute get request with single id parameter")
public Response getCallback(@PathParam("id") final String id) {
return Response.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package net.spals.appbuilder.app.dropwizard.doc;

import io.dropwizard.Application;
import io.dropwizard.Configuration;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import net.spals.appbuilder.app.dropwizard.DropwizardWebApp;
import net.spals.appbuilder.config.service.ServiceScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A minimally viable {@link DropwizardWebApp}
*
* @author tkral
*/
public class DocDropwizardWebApp extends Application<Configuration> {
private static final Logger LOGGER = LoggerFactory.getLogger(DocDropwizardWebApp.class);

public static void main(final String[] args) throws Throwable {
new DocDropwizardWebApp().run("server");
}

private DropwizardWebApp.Builder webAppDelegateBuilder;

@Override
public void initialize(final Bootstrap<Configuration> bootstrap) {
webAppDelegateBuilder = new DropwizardWebApp.Builder(bootstrap, LOGGER)
.setServiceScan(new ServiceScan.Builder()
.addServicePackages("net.spals.appbuilder.app.dropwizard.doc")
.build());
}

@Override
public void run(final Configuration configuration, final Environment env) {
webAppDelegateBuilder.setEnvironment(env).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package net.spals.appbuilder.app.dropwizard.sample;

/**
* A multi service definition for {@link SampleDropwizardWebApp}
*
* @author tkral
*/
public interface SampleDropwizardCustomSet { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package net.spals.appbuilder.app.dropwizard.sample;

import net.spals.appbuilder.annotations.service.AutoBindInSet;

/**
* An auto-bound multi service for {@link SampleDropwizardWebApp}
*
* @author tkral
*/
@AutoBindInSet(baseClass = SampleDropwizardCustomSet.class)
class SampleDropwizardCustomSetInstance implements SampleDropwizardCustomSet {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import net.spals.appbuilder.annotations.service.AutoBindSingleton;

/**
* An auto-bound service for {@link SampleDropwizardWebApp}
* An auto-bound singleton service for {@link SampleDropwizardWebApp}
*
* @author tkral
*/
@AutoBindSingleton
public class SampleDropwizardCustomService {
public class SampleDropwizardCustomSingleton {

@Inject
SampleDropwizardCustomService(@Named("AutoBoundModule") final String autoBoundModuleName) { }
SampleDropwizardCustomSingleton(@Named("AutoBoundModule") final String autoBoundModuleName) { }
}
Loading

0 comments on commit c739562

Please sign in to comment.