diff --git a/pom.xml b/pom.xml
index 15bc0bbd..dfd27ced 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,12 +32,12 @@
UTF-8
- 6.2.1
- 3.4.0
- 4.5.13
- 2.24.3
+ 7.0.1
+ 4.1.1
+ 5.0.8
+ 2.25.3
+ org.folio.eusage.reports.MainVerticle
-
@@ -101,19 +101,19 @@
io.vertx
- vertx-web
+ vertx-launcher-application
io.vertx
- vertx-web-openapi
+ vertx-web
io.vertx
- vertx-rx-java2
+ vertx-web-openapi-router
io.vertx
- vertx-web-api-contract
+ vertx-rx-java2
io.vertx
@@ -190,7 +190,7 @@
org.mockito
mockito-core
- 5.16.0
+ 5.23.0
test
@@ -216,8 +216,8 @@
- io.vertx.core.Launcher
- org.folio.eusage.reports.MainVerticle
+ io.vertx.launcher.application.VertxApplication
+ ${exec.mainClass}
true
diff --git a/src/main/java/org/folio/eusage/reports/api/CostPerUse.java b/src/main/java/org/folio/eusage/reports/api/CostPerUse.java
index a32bfbea..afeefc45 100644
--- a/src/main/java/org/folio/eusage/reports/api/CostPerUse.java
+++ b/src/main/java/org/folio/eusage/reports/api/CostPerUse.java
@@ -134,7 +134,7 @@ static JsonObject titlesToJsonObject(RowSet rowSet, Periods usePeriods) {
item = new JsonObject();
totalItems.put(itemKey, item);
items.add(item);
- item.put("kbId", kbId)
+ item.put("kbId", kbId.toString())
.put("title", row.getString("title"))
.put("derivedTitle", kbPackageId != null);
String printIssn = row.getString("printissn");
diff --git a/src/main/java/org/folio/eusage/reports/api/EusageReportsApi.java b/src/main/java/org/folio/eusage/reports/api/EusageReportsApi.java
index 70ac1f9a..7fe844be 100644
--- a/src/main/java/org/folio/eusage/reports/api/EusageReportsApi.java
+++ b/src/main/java/org/folio/eusage/reports/api/EusageReportsApi.java
@@ -16,10 +16,12 @@
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.codec.BodyCodec;
-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.contract.Operation;
+import io.vertx.openapi.contract.Parameter;
+import io.vertx.openapi.validation.RequestParameter;
+import io.vertx.openapi.validation.ValidatedRequest;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowIterator;
import io.vertx.sqlclient.RowSet;
@@ -33,13 +35,13 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.folio.okapi.common.GenericCompositeFuture;
import org.folio.okapi.common.XOkapiHeaders;
import org.folio.tlib.RouterCreator;
import org.folio.tlib.TenantInitHooks;
@@ -92,8 +94,9 @@ static String statusTable(TenantPgPool pool) {
static void failHandler(RoutingContext ctx) {
Throwable t = ctx.failure();
// both semantic errors and syntax errors are from same pile ... Choosing 400 over 422.
- int statusCode = t.getClass().getName().startsWith("io.vertx.ext.web.validation") ? 400 : 500;
- failHandler(statusCode, ctx, t.getMessage());
+ int statusCode = t.getClass().getName().startsWith("io.vertx.ext.web.handler") ? 400 : 500;
+ failHandler(statusCode, ctx,
+ t.getCause() == null ? t.getMessage() : t.getCause().getMessage());
}
static void failHandler(int statusCode, RoutingContext ctx, Throwable e) {
@@ -115,7 +118,11 @@ private static JsonObject copyWithoutNulls(JsonObject obj) {
JsonObject n = new JsonObject();
obj.getMap().forEach((key, value) -> {
if (value != null) {
- n.put(key, value);
+ if (value instanceof UUID) {
+ n.put(key, value.toString());
+ } else {
+ n.put(key, value);
+ }
}
});
return n;
@@ -191,7 +198,7 @@ static Future streamResult(RoutingContext ctx, SqlConnection sqlConnection
log.error(f.getMessage(), f);
resultFooter(ctx, null, facets, f.getMessage());
})
- .eventually(x -> tx.commit().compose(y -> sqlConnection.close())));
+ .eventually(() -> tx.commit().compose(y -> sqlConnection.close()).mapEmpty()));
stream.exceptionHandler(e -> {
log.error("stream error {}", e.getMessage(), e);
resultFooter(ctx, null, facets, e.getMessage());
@@ -214,10 +221,8 @@ static Future streamResult(RoutingContext ctx, TenantPgPool pool,
List fromList, List facets, String orderByClause,
String property,
Function handler) {
-
- RequestParameters params = ctx.get(ValidationHandler.REQUEST_CONTEXT_KEY);
- Integer offset = params.queryParameter("offset").getInteger();
- Integer limit = params.queryParameter("limit").getInteger();
+ String offset = ctx.request().params().get("offset");
+ String limit = ctx.request().params().get("limit");
String query = "SELECT " + (distinctMain != null ? "DISTINCT ON (" + distinctMain + ")" : "")
+ " * FROM " + fromList.get(0)
+ (orderByClause == null ? "" : " ORDER BY " + orderByClause)
@@ -241,6 +246,25 @@ static Future streamResult(RoutingContext ctx, TenantPgPool pool,
.onFailure(x -> sqlConnection.close()));
}
+ /**
+ * The Open API 3.0 parser in Vert.x 5.0 appears to only forward query parameters that are
+ * actually provided by client, ignoring if the spec declares defaults. Thus looking up
+ * the defaults programmatically.
+ */
+ static void getParameterDefaults(RoutingContext ctx) {
+ Operation operation = (Operation) ctx.currentRoute().metadata().get("openApiOperation");
+ if (operation != null) {
+ for (Parameter specParameter : operation.getParameters()) {
+ String specName = specParameter.getName();
+ Object specDefault = specParameter.getSchema().get("default");
+ if (specDefault != null && !ctx.request().params().contains(specName)) {
+ ctx.request().params().add(specName, specDefault.toString());
+ }
+ }
+ }
+
+ }
+
Future getReportTitles(Vertx vertx, RoutingContext ctx) {
PgCqlDefinition definition = PgCqlDefinition.create();
definition.addField("cql.allRecords", new PgCqlFieldAlwaysMatches());
@@ -259,16 +283,32 @@ Future getReportTitles(Vertx vertx, RoutingContext ctx) {
definition.addField("kbManualMatch", new PgCqlFieldBoolean()
.withColumn("title_entries.kbmanualmatch"));
- RequestParameters params = ctx.get(ValidationHandler.REQUEST_CONTEXT_KEY);
- final String counterReportId = stringOrNull(params.queryParameter("counterReportId"));
- final String providerId = stringOrNull(params.queryParameter("providerId"));
+ // The Open API 3.0 parser in Vert.x 5.0 appears to not support validation of all
+ // the possible CQL query strings that are needed for operating the report titles API.
+ // Thus validating some parameters programmatically.
+ try {
+ String offsetParam = ctx.request().params().get("offset");
+ String limitParam = ctx.request().params().get("limit");
+ if (limitParam != null && Integer.parseInt(limitParam) < 0) {
+ throw new IllegalArgumentException("limit in location QUERY: value should be >= 0");
+ }
+ if (offsetParam != null && Integer.parseInt(offsetParam) < 0) {
+ throw new IllegalArgumentException("offset in location QUERY: value should be >= 0");
+ }
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("Parameters limit and offset must be integers: "
+ + nfe.getMessage());
+ }
+
+ final String counterReportId = ctx.request().getParam("counterReportId");
+ final String providerId = ctx.request().getParam("providerId");
final TenantPgPool pool = TenantPgPool.pool(vertx, TenantUtil.tenant(ctx));
final String distinctCount = titleEntriesTable(pool) + ".id";
- final String query = stringOrNull(params.queryParameter("query"));
- RequestParameter facetsParameter = params.queryParameter("facets");
- String [] facetsList = facetsParameter == null
+ final String query = ctx.request().getParam("query");
+ final String facets = ctx.request().getParam("facets");
+ String [] facetsList = facets == null
? new String[0]
- : facetsParameter.getString().split(",");
+ : facets.split(",");
boolean includeStatusFacet = false;
for (String name : facetsList) {
@@ -351,10 +391,11 @@ private String getFromTitleDataForeignKey(PgCqlQuery pgCqlQuery, String counterR
Future postReportTitles(Vertx vertx, RoutingContext ctx) {
TenantPgPool pool = TenantPgPool.pool(vertx, TenantUtil.tenant(ctx));
+ ValidatedRequest request = ctx.get(RouterBuilder.KEY_META_DATA_VALIDATED_REQUEST);
return pool.getConnection()
.compose(sqlConnection -> {
- Future future = Future.succeededFuture();
- final JsonArray titles = ctx.getBodyAsJson().getJsonArray("titles");
+ Future