Skip to content

Commit

Permalink
Update sample project
Browse files Browse the repository at this point in the history
  • Loading branch information
amanteaux committed Oct 20, 2023
1 parent 47d81c5 commit f2250bd
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

@Singleton
public class ConfigurationService {

private final Config config;

@Inject
Expand All @@ -19,13 +18,11 @@ public String hello() {
return config.getString("hello");
}

public String swaggerAccessUsername() {
return config.getString("swagger.access.username");
}

public String swaggerAccessPassword() {
return config.getString("swagger.access.password");
}
public String internalApiAuthUsername() {
return config.getString("internal-api.auth-username");
}

public String internalApiAuthPassword() {
return config.getString("internal-api.auth-password");
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.coreoz.webservices.internal;

import javax.inject.Inject;
import javax.inject.Singleton;

import com.coreoz.plume.jersey.security.basic.BasicAuthenticator;
import com.coreoz.services.configuration.ConfigurationService;

@Singleton
public class InternalApiAuthenticator {
private final BasicAuthenticator<String> basicAuthenticator;

@Inject
public InternalApiAuthenticator(ConfigurationService configurationService) {
this.basicAuthenticator = BasicAuthenticator.fromSingleCredentials(
configurationService.internalApiAuthUsername(),
configurationService.internalApiAuthPassword(),
"API plume-showcase"
);
}

public BasicAuthenticator<String> get() {
return this.basicAuthenticator;
}
}
14 changes: 7 additions & 7 deletions src/main/java/com/coreoz/webservices/internal/MonitoringWs.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
import javax.ws.rs.core.MediaType;

import com.codahale.metrics.Metric;
import com.coreoz.plume.db.transaction.TransactionManager;
import com.coreoz.plume.jersey.monitoring.utils.health.HealthCheckBuilder;
import com.coreoz.plume.jersey.monitoring.utils.health.beans.HealthStatus;
import com.coreoz.plume.jersey.monitoring.utils.info.ApplicationInfoProvider;

import com.coreoz.plume.db.transaction.TransactionManager;
import com.coreoz.plume.jersey.monitoring.utils.info.beans.ApplicationInfo;
import com.coreoz.plume.jersey.monitoring.utils.metrics.MetricsCheckBuilder;
import com.coreoz.plume.jersey.security.basic.BasicAuthenticator;
Expand All @@ -36,7 +36,11 @@ public class MonitoringWs {
private final BasicAuthenticator<String> basicAuthenticator;

@Inject
public MonitoringWs(ApplicationInfoProvider applicationInfoProvider, TransactionManager transactionManager) {
public MonitoringWs(
ApplicationInfoProvider applicationInfoProvider,
TransactionManager transactionManager,
InternalApiAuthenticator apiAuthenticator
) {
this.applicationInfo = applicationInfoProvider.get();
// Registering health checks
this.healthStatusProvider = new HealthCheckBuilder()
Expand All @@ -49,11 +53,7 @@ public MonitoringWs(ApplicationInfoProvider applicationInfoProvider, Transaction
.build();

// Require authentication to access monitoring endpoints
this.basicAuthenticator = BasicAuthenticator.fromSingleCredentials(
"plume",
"rocks",
"Plume showcase"
);
this.basicAuthenticator = apiAuthenticator.get();
}

@GET
Expand Down
24 changes: 7 additions & 17 deletions src/main/java/com/coreoz/webservices/internal/SwaggerWs.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,25 @@

import com.coreoz.plume.jersey.security.basic.BasicAuthenticator;
import com.coreoz.plume.jersey.security.permission.PublicApi;
import com.coreoz.services.configuration.ConfigurationService;
import com.fasterxml.jackson.core.JsonProcessingException;

import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.jaxrs2.integration.JaxrsOpenApiContextBuilder;
import io.swagger.v3.oas.integration.OpenApiConfigurationException;
import io.swagger.v3.oas.integration.SwaggerConfiguration;
import io.swagger.v3.oas.integration.api.OpenApiContext;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.servers.Server;
import lombok.SneakyThrows;

@Path("/swagger")
@Singleton
@PublicApi
public class SwaggerWs {

private final String swaggerDefinition;
private final BasicAuthenticator<String> basicAuthenticator;

@Inject
public SwaggerWs(ConfigurationService configurationService) throws OpenApiConfigurationException {
public SwaggerWs(InternalApiAuthenticator apiAuthenticator) throws Exception {
// Basic configuration
SwaggerConfiguration openApiConfig = new SwaggerConfiguration()
.resourcePackages(Set.of("com.coreoz.webservices.api"))
.sortOutput(true)
Expand All @@ -45,24 +42,19 @@ public SwaggerWs(ConfigurationService configurationService) throws OpenApiConfig
.description("API plume-showcase")
)));

// Generation of the OpenApi object
OpenApiContext context = new JaxrsOpenApiContextBuilder<>()
.openApiConfiguration(openApiConfig)
.buildContext(true);
// the OpenAPI object can be changed to add security definition
// or to alter the generated mapping
OpenAPI openApi = context.read();

// serialization of the Swagger definition
try {
this.swaggerDefinition = Yaml.mapper().writeValueAsString(openApi);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
this.swaggerDefinition = Yaml.mapper().writeValueAsString(openApi);

// require authentication to access the API documentation
this.basicAuthenticator = BasicAuthenticator.fromSingleCredentials(
configurationService.swaggerAccessUsername(),
configurationService.swaggerAccessPassword(),
"API plume-showcase"
);
this.basicAuthenticator = apiAuthenticator.get();
}

@Produces(MediaType.APPLICATION_JSON)
Expand All @@ -72,6 +64,4 @@ public String get(@Context ContainerRequestContext requestContext) throws JsonPr

return swaggerDefinition;
}

}

12 changes: 12 additions & 0 deletions src/test/java/com/coreoz/SampleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
import org.assertj.core.api.Assertions;
import org.junit.Test;

/**
* A unit test sample.
*
* Unit tests are a great tool for:
* - Testing exhaustively a function by changing all the parameters to verify that is fully respects its specification
* - Testing a function that does not have a lot of dependencies
*
* To test something that has interactions with the database, or not only one function but a chain of services,
* integration tests are preferred. See {@link SampleIntegrationTest} for an example.
*
* Once there are other unit tests in the project, this sample should be deleted.
*/
public class SampleTest {
@Test
public void methodToTest__test_scenario_description() {
Expand Down
24 changes: 15 additions & 9 deletions src/test/java/com/coreoz/guice/TestModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@
import com.google.inject.AbstractModule;
import com.google.inject.util.Modules;

/**
* The Guice module that will be used for integration tests.
*
* In this module, it is possible to override the behaviors of some services as it is shown with the {@link TimeProvider}
* module.
*/
public class TestModule extends AbstractModule {
@Override
protected void configure() {
install(Modules.override(new ApplicationModule()).with(new AbstractModule() {
@Override
protected void configure() {
bind(TimeProvider.class).to(TimeProviderForTest.class);
}
}));
@Override
protected void configure() {
install(Modules.override(new ApplicationModule()).with(new AbstractModule() {
@Override
protected void configure() {
bind(TimeProvider.class).to(TimeProviderForTest.class);
}
}));
install(new GuiceDbTestModule());
}
}
}
66 changes: 53 additions & 13 deletions src/test/java/com/coreoz/guice/TimeProviderForTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,68 @@

import javax.inject.Singleton;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;

/**
* Override the default {@link TimeProvider} for testing purposes.
* This adds the possibility to change how time flows during a test.
* This works only for code that relies on the {@link TimeProvider}
*/
@Singleton
public class TimeProviderForTest implements TimeProvider {
private Clock clock;
private Clock clock;

public TimeProviderForTest() {
this.clock = Clock.systemDefaultZone();
}
public TimeProviderForTest() {
this.clock = Clock.systemDefaultZone();
}

@Override
public Clock clock() {
return clock;
}
/**
* Returns the current clock used
*/
@Override
public Clock clock() {
return clock;
}

public void changeClock(Clock clock) {
this.clock = clock;
}
/**
* Changes the current clock used. This is generally a temporary measure that should be reverted.
* See {@link #executeWithClock(Clock, Runnable)} for usage
* @param newClock The new clock to use
*/
public void changeClock(Clock newClock) {
this.clock = newClock;
}

public void withClock(Clock clock, Runnable toExecuteWithClock) {
/**
* Execute a function with a custom clock. If unsure, use {@link #executeWithInstant(Instant, Runnable)} or {@link #executeWithConstantTime(Runnable)} instead
* @param newClock The custom clock
* @param toExecuteWithClock The function to execute
*/
public void executeWithClock(Clock newClock, Runnable toExecuteWithClock) {
Clock oldClock = this.clock;
changeClock(clock);
changeClock(newClock);
toExecuteWithClock.run();
changeClock(oldClock);
}

/**
* Execute a function for which for time does not change
* @param fixedInstantForExecution The instant that will be used to execute the function
* @param toExecuteWithInstant The function to execute
*/
public void executeWithInstant(Instant fixedInstantForExecution, Runnable toExecuteWithInstant) {
executeWithClock(
Clock.fixed(fixedInstantForExecution, ZoneId.systemDefault()),
toExecuteWithInstant
);
}

/**
* Execute a function for which for time does not change
* @param toExecuteWithConstantTime The function to execute
*/
public void executeWithConstantTime(Runnable toExecuteWithConstantTime) {
executeWithInstant(Instant.now(), toExecuteWithConstantTime);
}
}
15 changes: 15 additions & 0 deletions src/test/java/com/coreoz/integration/SampleIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@

import javax.inject.Inject;

/**
* An integration test sample.
*
* This tests differs from an unit tests, cf {@link SampleTest}, because:
* - It will initialize and rely on the dependency injection, see {@link TestModule} for tests specific overrides
* - Other services can be referenced for this tests
* - These other services can be altered for tests, see {@link TimeProviderForTest} for an example
* - If a database is used in the project, an H2 in memory database will be available to run queries and verify that data is correctly being inserted/updated in the database
* - The H2 in memory database will be created by playing Flyway initialization scripts: these scripts must be correctly setup
*
* Integration tests are a great tool to test the whole chain of services with one automated test.
* Although, to test intensively a function, a unit test is preferred, see {@link TimeProviderForTest} for an example.
*
* Once there are other integration tests in the project, this sample should be deleted.
*/
@RunWith(GuiceTestRunner.class)
@GuiceModules(TestModule.class)
public class SampleIntegrationTest {
Expand Down

0 comments on commit f2250bd

Please sign in to comment.