Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 65 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ library, not a framework, with utilities such as:

## Main Verticle

The [Vert.x OpenAPI](https://vertx.io/docs/vertx-web-openapi/java/) unlike
The [Vert.x OpenAPI](https://vertx.io/docs/vertx-openapi/java/) unlike
many OpenAPI implementations does not generate any code for you. Everything
happens at run-time. Only requests are validated, not responses.

The OpenAPI implementaion of Vert.x 5 does not allow external references - even
if they are local files [ref](https://vertx.io/docs/vertx-openapi/java/#_openapicontract).
If the OpenAPI spec in use has local file references the YAML must be preprocessed with the
openapi-deref-plugin. See the [openapi-deref-plugin](#plugin-openapi-deref-plugin) section
for details about handling external references in OpenAPI specifications.

Place your OpenAPI specification and auxiliary files somewhere in `resources`,
such as `resources/openapi`.

Expand Down Expand Up @@ -89,24 +95,25 @@ For an OpenAPI based implementation it could look as follows:
public MyApi implements RouterCreator, TenantInitHooks {
@Override
public Future<Router> createRouter(Vertx vertx) {
return RouterBuilder.create(vertx, "openapi/myapi-1.0.yaml")
.map(routerBuilder -> {
handlers(vertx, routerBuilder);
return routerBuilder.createRouter();
});
return OpenAPIContract.from(vertx, "openapi/myapi-1.0.yaml")
.map(contract -> {
RouterBuilder routerBuilder = RouterBuilder.create(vertx, contract);
handlers(vertx, routerBuilder);
return routerBuilder.createRouter();
});
}

private void handlers(Vertx vertx, RouterBuilder routerBuilder) {
routerBuilder
.operation("postTitles") // operationId in spec
.handler(ctx -> {
.getRoute("postTitles") // operationId in spec
.addHandler(ctx -> {
// doesn't do anything at the moment!
ctx.response().setStatusCode(204);
ctx.response().end();
});
routerBuilder
.operation("getTitles")
.handler(ctx -> getTitles(vertx, ctx)
.getRoute("getTitles")
.addHandler(ctx -> getTitles(vertx, ctx)
.onFailure(cause -> {
ctx.response().setStatusCode(500);
ctx.response().end(cause.getMessage());
Expand All @@ -128,6 +135,54 @@ The Tenant2Api implementation deals with purge (removes schema with cascade).
Your implementation should only consider upgrade/downgrade. On purge,
`preInit` is called, but `postInit` is not.

## Plugin openapi-deref-plugin

The purpose of the openapi-deref-plugin is to de-reference `$ref` references in the OpenAPI
specification. The result is one YAML file with all resources embedded. If there are
only references to components inside the OpenAPI YAML file from the beginning, it is not
necessary to use this plugin.

If the OpenAPI specification is located in `resources/openapi` (recommened), then
the minimal way to use the plugin is to use:

```
<plugin>
<groupId>org.folio</groupId>
<artifactId>openapi-deref-plugin</artifactId>
<version>4.0.0</version>
<executions>
<execution>
<id>dereference-books</id>
<goals>
<goal>dereference</goal>
</goals>
<phase>generate-resources</phase>
</execution>
</executions>
</plugin>
```

The configuration has the following properties:

* `input` : glob-path for input files to search. Default value is `${basedir}/src/main/resources/openapi/*.yaml`
* `output` : output directory. Default value is `${project.build.directory}/classes/openapi`.

As an example if there are OpenAPI specs in test resources, the `extensions` list could be extended with:

```
<execution>
<id>dereference-echo</id>
<goals>
<goal>dereference</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<input>${project.basedir}/src/test/resources/openapi/*.yaml</input>
<output>${project.build.directory}/test-classes/openapi</output>
</configuration>
</execution>
```

## PostgreSQL

The PostgreSQL support is minimal. There's just enough to perform tenant
Expand Down
38 changes: 33 additions & 5 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.folio</groupId>
<artifactId>folio-vertx-lib</artifactId>
<version>3.5.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
</parent>
<artifactId>vertx-lib</artifactId>

Expand All @@ -22,15 +22,19 @@
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-openapi</artifactId>
<artifactId>vertx-openapi</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-rx-java2</artifactId>
<artifactId>vertx-web-openapi-router</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-validation</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-api-contract</artifactId>
<artifactId>vertx-rx-java2</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
Expand Down Expand Up @@ -148,7 +152,31 @@

<build>
<plugins>

<plugin>
<groupId>org.folio</groupId>
<artifactId>openapi-deref-plugin</artifactId>
<version>4.0.0-SNAPSHOT</version>
<executions>
<execution>
<id>dereference-tenant</id>
<goals>
<goal>dereference</goal>
</goals>
<phase>generate-resources</phase>
</execution>
<execution>
<id>dereference-echo</id>
<goals>
<goal>dereference</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<input>${project.basedir}/src/test/resources/openapi/*.yaml</input>
<output>${project.build.directory}/test-classes/openapi</output>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
Expand Down
9 changes: 5 additions & 4 deletions core/src/main/java/org/folio/tlib/RouterCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import io.vertx.core.Vertx;
import io.vertx.ext.web.Router;
import org.folio.okapi.common.XOkapiHeaders;
import org.folio.okapi.common.logging.FolioLocal;
import org.folio.okapi.common.logging.FolioLoggingContext;

/**
Expand Down Expand Up @@ -40,13 +41,13 @@ static Future<Router> mountAll(Vertx vertx, RouterCreator [] routerCreators, Str
Future<Void> future = Future.succeededFuture();
Router router = Router.router(vertx);
router.route().handler(ctx -> {
FolioLoggingContext.put(FolioLoggingContext.MODULE_ID_LOGGING_VAR_NAME, module);
FolioLoggingContext.put(FolioLocal.MODULE_ID, module);
MultiMap headers = ctx.request().headers();
FolioLoggingContext.put(FolioLoggingContext.TENANT_ID_LOGGING_VAR_NAME,
FolioLoggingContext.put(FolioLocal.TENANT_ID,
headers.get(XOkapiHeaders.TENANT));
FolioLoggingContext.put(FolioLoggingContext.REQUEST_ID_LOGGING_VAR_NAME,
FolioLoggingContext.put(FolioLocal.REQUEST_ID,
headers.get(XOkapiHeaders.REQUEST_ID));
FolioLoggingContext.put(FolioLoggingContext.USER_ID_LOGGING_VAR_NAME,
FolioLoggingContext.put(FolioLocal.USER_ID,
headers.get(XOkapiHeaders.USER_ID));
ctx.next();
});
Expand Down
77 changes: 39 additions & 38 deletions core/src/main/java/org/folio/tlib/api/Tenant2Api.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.openapi.RouterBuilder;
import io.vertx.ext.web.validation.RequestParameter;
import io.vertx.ext.web.validation.RequestParameters;
import io.vertx.ext.web.validation.ValidationHandler;
import io.vertx.ext.web.openapi.router.RouterBuilder;
import io.vertx.openapi.contract.OpenAPIContract;
import io.vertx.openapi.validation.ValidatedRequest;
import io.vertx.sqlclient.Tuple;
import java.util.HashMap;
import java.util.LinkedList;
Expand All @@ -20,7 +19,6 @@
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.okapi.common.XOkapiHeaders;
import org.folio.tlib.RouterCreator;
import org.folio.tlib.TenantInitHooks;
import org.folio.tlib.postgres.impl.TenantPgPoolImpl;
Expand Down Expand Up @@ -62,8 +60,12 @@ static void failHandler(RoutingContext ctx, int code, Throwable e) {
failHandler(ctx, ctx.statusCode(),
HttpResponseStatus.valueOf(ctx.statusCode()).reasonPhrase());
} else {
log.error("{}", e.getMessage());
failHandler(ctx, code, e.getMessage());
Throwable t = e.getCause();
if (t == null) {
t = e;
}
log.error("{}", e.getMessage(), e);
failHandler(ctx, code, t.getMessage());
}
}

Expand Down Expand Up @@ -187,12 +189,13 @@ private static Future<Void> saveJob(Vertx vertx, JsonObject tenantJob) {

private void handlers(Vertx vertx, RouterBuilder routerBuilder) {
log.info("setting up tenant handlers ... begin");
routerBuilder
.operation("postTenant")
.handler(ctx -> {
RequestParameters params = ctx.get(ValidationHandler.REQUEST_CONTEXT_KEY);
log.info("postTenant handler {}", params.toJson().encode());
JsonObject tenantAttributes = ctx.body().asJsonObject();

routerBuilder.getRoute("postTenant")
.addHandler(ctx -> {
ValidatedRequest validatedRequest =
ctx.get(RouterBuilder.KEY_META_DATA_VALIDATED_REQUEST);
JsonObject tenantAttributes = validatedRequest.getBody().getJsonObject();
log.info("postTenant handler {}", tenantAttributes.encode());
String tenant = TenantUtil.tenant(ctx);

createJob(vertx, tenant, tenantAttributes)
Expand All @@ -218,17 +221,15 @@ private void handlers(Vertx vertx, RouterBuilder routerBuilder) {
failHandler(ctx, 500, e);
});
})
.failureHandler(ctx -> Tenant2Api.failHandler(ctx, 400, ctx.failure()));
routerBuilder
.operation("getTenantJob")
.handler(ctx -> {
RequestParameters params = ctx.get(ValidationHandler.REQUEST_CONTEXT_KEY);
String id = params.pathParameter("id").getString();
String tenant = params.headerParameter(XOkapiHeaders.TENANT).getString();
RequestParameter waitParameter = params.queryParameter("wait");
int wait = waitParameter != null ? waitParameter.getInteger() : 0;
log.info("getTenantJob handler id={} wait={}", id,
waitParameter != null ? waitParameter.getInteger() : "null");
.addFailureHandler(ctx -> Tenant2Api.failHandler(ctx, 400, ctx.failure()));

routerBuilder.getRoute("getTenantJob")
.addHandler(ctx -> {
String id = ctx.pathParam("id");
String tenant = TenantUtil.tenant(ctx);
List<String> waitParameter = ctx.queryParam("wait");
int wait = waitParameter.isEmpty() ? 0 : Integer.parseInt(waitParameter.get(0));
log.info("getTenantJob handler id={} wait={}", id, wait);
getJob(vertx, tenant, UUID.fromString(id), wait)
.onSuccess(res -> {
if (res == null) {
Expand All @@ -241,13 +242,12 @@ private void handlers(Vertx vertx, RouterBuilder routerBuilder) {
})
.onFailure(e -> failHandler(ctx, 500, e));
})
.failureHandler(ctx -> Tenant2Api.failHandler(ctx, 400, ctx.failure()));
routerBuilder
.operation("deleteTenantJob")
.handler(ctx -> {
RequestParameters params = ctx.get(ValidationHandler.REQUEST_CONTEXT_KEY);
String id = params.pathParameter("id").getString();
String tenant = params.headerParameter(XOkapiHeaders.TENANT).getString();
.addFailureHandler(ctx -> Tenant2Api.failHandler(ctx, 400, ctx.failure()));

routerBuilder.getRoute("deleteTenantJob")
.addHandler(ctx -> {
String id = ctx.pathParam("id");
String tenant = TenantUtil.tenant(ctx);
log.info("deleteTenantJob handler id={}", id);
deleteJob(vertx, tenant, UUID.fromString(id))
.onSuccess(res -> {
Expand All @@ -260,7 +260,8 @@ private void handlers(Vertx vertx, RouterBuilder routerBuilder) {
})
.onFailure(e -> failHandler(ctx, 500, e));
})
.failureHandler(ctx -> Tenant2Api.failHandler(ctx, 400, ctx.failure()));
.addFailureHandler(ctx -> Tenant2Api.failHandler(ctx, 400, ctx.failure()));

log.info("setting up tenant handlers ... done");
}

Expand All @@ -272,11 +273,11 @@ private void handlers(Vertx vertx, RouterBuilder routerBuilder) {
*/
@Override
public Future<Router> createRouter(Vertx vertx) {
return RouterBuilder.create(vertx, "openapi/tenant-2.0.yaml")
.map(routerBuilder -> {
handlers(vertx, routerBuilder);
return routerBuilder.createRouter();
});
return OpenAPIContract.from(vertx, "openapi/tenant-2.0.yaml")
.map(contract -> {
RouterBuilder routerBuilder = RouterBuilder.create(vertx, contract);
handlers(vertx, routerBuilder);
return routerBuilder.createRouter();
});
}

}
5 changes: 2 additions & 3 deletions core/src/main/java/org/folio/tlib/postgres/TenantPgPool.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.pgclient.PgConnectOptions;
import io.vertx.pgclient.PgPool;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.PoolOptions;
import io.vertx.sqlclient.Row;
Expand All @@ -14,9 +13,9 @@
import org.folio.tlib.postgres.impl.TenantPgPoolImpl;

/**
* The {@link PgPool} for a tenant.
* The {@link Pool} for a tenant.
*/
public interface TenantPgPool extends PgPool {
public interface TenantPgPool extends Pool {

/**
* create tenant pool for tenant.
Expand Down
Loading