Skip to content

Commit

Permalink
migrate to updated openapi
Browse files Browse the repository at this point in the history
90% migration of open api
  • Loading branch information
yannicklamprecht committed Dec 14, 2024
1 parent 9f92bfe commit 479d7d9
Show file tree
Hide file tree
Showing 12 changed files with 564 additions and 374 deletions.
6 changes: 6 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ dependencies {
exclude(group = "club.minnced", module = "opus-java")
}

val openapi = "6.3.0"

annotationProcessor("io.javalin.community.openapi:openapi-annotation-processor:$openapi")
implementation("io.javalin.community.openapi:javalin-openapi-plugin:$openapi") // for /openapi route with JSON scheme
implementation("io.javalin.community.openapi:javalin-swagger-plugin:$openapi") // for Swagger UI

// database
implementation("org.postgresql", "postgresql", "42.7.4")
implementation(libs.bundles.sadu)
Expand Down
36 changes: 24 additions & 12 deletions src/main/java/de/chojo/repbot/core/Web.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@
import de.chojo.repbot.config.Configuration;
import de.chojo.repbot.web.Api;
import io.javalin.Javalin;
import io.javalin.plugin.openapi.OpenApiOptions;
import io.javalin.plugin.openapi.OpenApiPlugin;
import io.javalin.plugin.openapi.ui.ReDocOptions;
import io.javalin.plugin.openapi.ui.SwaggerOptions;
import io.javalin.plugin.openapi.utils.OpenApiVersionUtil;
import io.swagger.v3.oas.models.info.License;
import io.javalin.openapi.OpenApiLicense;
import io.javalin.openapi.plugin.OpenApiPlugin;
import io.javalin.openapi.plugin.OpenApiPluginConfiguration;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
import net.dv8tion.jda.api.requests.ErrorResponse;
Expand Down Expand Up @@ -51,21 +48,36 @@ public void init() {
private void initApi() {
var api = configuration.api();

var info = new io.swagger.v3.oas.models.info.Info().version("1.0").title("Reputation Bot API")
.description("Documentation for the Reputation Bot API")
.license(new License().name("GNU Affero General Public License v3.0")
.url("https://github.com/RainbowDashLabs/reputation-bot/blob/master/LICENSE.md"));
/*
var options = new OpenApiOptions(info)
.path("/json-docs")
.reDoc(new ReDocOptions("/redoc")) // endpoint for redoc
.swagger(new SwaggerOptions("/docs").title("Reputation Bot API"));
OpenApiVersionUtil.INSTANCE.setLogWarnings(false);
OpenApiVersionUtil.INSTANCE.setLogWarnings(false);*/

javalin = Javalin.create(config -> config.registerPlugin(new OpenApiPlugin(options)))
javalin = Javalin.create(config -> config.registerPlugin(new OpenApiPlugin(this::configureOpenApi)))
.start(api.host(), api.port());
new Api(javalin, data.metrics()).init();
}

private void configureOpenApi(OpenApiPluginConfiguration config) {
config.withDefinitionConfiguration((version, definition) -> {
definition.withInfo(info -> {
info.setTitle("Reputation Bot API");
info.setVersion("1.0");
info.setDescription("Documentation for the Reputation Bot API");
info.setLicense(new OpenApiLicense()
.name("GNU Affero General Public License v3.0")
.url("https://github.com/RainbowDashLabs/reputation-bot/blob/master/LICENSE.md")
);
});
definition.withServer(openApiServer -> {
openApiServer.setUrl("https://repbot.rainbowdashlabs.de");
openApiServer.setDescription("Main server");
});
});
}

private void initBotList() {
var botlist = configuration.botlist();
if (!botlist.isSubmit()) return;
Expand Down
10 changes: 4 additions & 6 deletions src/main/java/de/chojo/repbot/web/Api.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
package de.chojo.repbot.web;

import de.chojo.repbot.dao.provider.Metrics;
import de.chojo.repbot.web.erros.ApiException;
import de.chojo.repbot.web.error.ApiException;
import de.chojo.repbot.web.routes.v1.MetricsRoute;
import io.javalin.Javalin;
import io.javalin.apibuilder.EndpointGroup;
import org.slf4j.Logger;

import static io.javalin.apibuilder.ApiBuilder.before;
Expand All @@ -27,10 +28,7 @@ public Api(Javalin javalin, Metrics metrics) {

public void init() {
javalin.exception(ApiException.class, (err, ctx) -> ctx.result(err.getMessage()).status(err.status()));
javalin.routes(() -> {
before(ctx -> log.debug("Received request on {}.", ctx.path()));

path("v1", () -> path("metrics", metricsRoute::buildRoutes));
});
javalin.before(ctx -> log.debug("Received request on {}.", ctx.path()));
path("v1", () -> path("metrics", metricsRoute::buildRoutes));
}
}
11 changes: 6 additions & 5 deletions src/main/java/de/chojo/repbot/web/error/ApiException.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
*
* Copyright (C) RainbowDashLabs and Contributor
*/
package de.chojo.repbot.web.erros;
package de.chojo.repbot.web.error;

import io.javalin.http.HttpCode;

import io.javalin.http.HttpStatus;

public class ApiException extends RuntimeException {
private final HttpCode status;
private final HttpStatus status;

public ApiException(HttpCode status, String message) {
public ApiException(HttpStatus status, String message) {
super(message);
this.status = status;
}

public HttpCode status() {
public HttpStatus status() {
return status;
}
}
15 changes: 7 additions & 8 deletions src/main/java/de/chojo/repbot/web/routes/v1/MetricsHolder.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
package de.chojo.repbot.web.routes.v1;

import de.chojo.repbot.dao.provider.Metrics;
import de.chojo.repbot.web.erros.ApiException;
import de.chojo.repbot.web.error.ApiException;
import de.chojo.repbot.web.routes.RoutesBuilder;
import de.chojo.repbot.web.routes.v1.metrics.MetricCache;
import io.javalin.http.Context;
import io.javalin.http.Handler;
import io.javalin.http.HttpCode;
import org.eclipse.jetty.http.HttpStatus;
import io.javalin.http.HttpStatus;
import org.slf4j.Logger;

import static org.slf4j.LoggerFactory.getLogger;
Expand All @@ -36,7 +35,7 @@ protected void writeImage(Context ctx, byte[] png) {
ctx.header("X-Content-Type-Options", "nosniff");
ctx.contentType("image/png");

ctx.result(png).status(HttpStatus.OK_200);
ctx.result(png).status(HttpStatus.OK);
}

protected int offset(Context context, int max) {
Expand All @@ -46,7 +45,7 @@ protected int offset(Context context, int max) {
assertSize(offset, 0, max);
return offset;
} catch (NumberFormatException e) {
throw new ApiException(HttpCode.BAD_REQUEST, "Offset is not a number, Got: " + param);
throw new ApiException(HttpStatus.BAD_REQUEST, "Offset is not a number, Got: " + param);
}
}

Expand All @@ -57,16 +56,16 @@ protected int count(Context context, int max) {
assertSize(offset, 2, max);
return offset;
} catch (NumberFormatException e) {
throw new ApiException(HttpCode.BAD_REQUEST, "Count is not a number, Got: " + param);
throw new ApiException(HttpStatus.BAD_REQUEST, "Count is not a number, Got: " + param);
}
}

private void assertSize(int value, int min, int max) {
if (value < min) {
throw new ApiException(HttpCode.BAD_REQUEST, String.format("Value %s is too small. Min: %s", value, min));
throw new ApiException(HttpStatus.BAD_REQUEST, String.format("Value %s is too small. Min: %s", value, min));
}
if (value > max) {
throw new ApiException(HttpCode.BAD_REQUEST, String.format("Value %s is too large. Max: %s", value, max));
throw new ApiException(HttpStatus.BAD_REQUEST, String.format("Value %s is too large. Max: %s", value, max));
}
}

Expand Down
49 changes: 0 additions & 49 deletions src/main/java/de/chojo/repbot/web/routes/v1/MetricsRoute.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import de.chojo.repbot.web.routes.v1.metrics.Reputation;
import de.chojo.repbot.web.routes.v1.metrics.Service;
import de.chojo.repbot.web.routes.v1.metrics.Users;
import io.swagger.v3.oas.models.parameters.Parameter;

public class MetricsRoute implements RoutesBuilder {

Expand Down Expand Up @@ -52,52 +51,4 @@ public void buildRoutes() {
users.buildRoutes();
service.buildRoutes();
}

private static void offsetDoc(Parameter parameter, String resolution, int maxValue) {
setParameter(parameter, "%s offset. 0 is current %s. Max value is %s".formatted(resolution, resolution.toLowerCase(), maxValue));
}

public static void offsetDayDoc(Parameter p) {
offsetDoc(p, "Day", MAX_DAY_OFFSET);
}

public static void offsetHourDoc(Parameter p) {
offsetDoc(p, "Hour", MAX_HOUR_OFFSET);
}

public static void offsetWeekDoc(Parameter p) {
offsetDoc(p, "Week", MAX_WEEK_OFFSET);
}

public static void offsetMonthDoc(Parameter p) {
offsetDoc(p, "Month", MAX_MONTH_OFFSET);
}

public static void offsetYearDoc(Parameter p) {
offsetDoc(p, "Year", MAX_YEAR_OFFSET);
}

private static void countDoc(Parameter parameter, String resolution, int maxValue) {
setParameter(parameter, "%s count. Amount of previously %s in the chart. Max value is %s".formatted(resolution, resolution.toLowerCase(), maxValue));
}

public static void countHourDoc(Parameter p) {
countDoc(p, "Hours", MAX_HOURS);
}

public static void countDayDoc(Parameter p) {
countDoc(p, "Days", MAX_DAYS);
}

public static void countWeekDoc(Parameter p) {
countDoc(p, "Weeks", MAX_WEEKS);
}

public static void countMonthDoc(Parameter p) {
countDoc(p, "Months", MAX_MONTH);
}

private static void setParameter(Parameter p, String description) {
p.setDescription(description);
}
}
103 changes: 67 additions & 36 deletions src/main/java/de/chojo/repbot/web/routes/v1/metrics/Commands.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
import de.chojo.repbot.dao.snapshots.statistics.CommandsStatistic;
import de.chojo.repbot.util.Text;
import de.chojo.repbot.web.routes.v1.MetricsHolder;
import de.chojo.repbot.web.routes.v1.MetricsRoute;
import io.javalin.http.Context;
import io.javalin.plugin.openapi.dsl.OpenApiBuilder;
import io.javalin.openapi.HttpMethod;
import io.javalin.openapi.OpenApi;
import io.javalin.openapi.OpenApiContent;
import io.javalin.openapi.OpenApiParam;
import io.javalin.openapi.OpenApiResponse;

import static de.chojo.repbot.web.routes.v1.MetricsRoute.MAX_MONTH;
import static de.chojo.repbot.web.routes.v1.MetricsRoute.MAX_MONTH_OFFSET;
Expand All @@ -25,6 +28,20 @@ public Commands(Metrics metrics, MetricCache cache) {
super(cache, metrics);
}

@OpenApi(
summary = "Get command usages for a week.",
operationId = "usageWeek",
path = "usage/week/{offset}",
methods = HttpMethod.GET,
tags = {"Commands"},
responses = {
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = byte[].class, type = "image/png")}),
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = CommandsStatistic.class, type = "application/json")})
},
pathParams = {
@OpenApiParam(name = "offset", type = Integer.class, required = true)
}
)
public void usageWeek(Context ctx) {
var stats = metrics().commands().week(offset(ctx, MAX_WEEK_OFFSET));
if ("application/json".equals(ctx.header("Accept"))) {
Expand All @@ -34,6 +51,20 @@ public void usageWeek(Context ctx) {
}
}

@OpenApi(
summary = "Get command usages for a month.",
operationId = "usageMonth",
path = "usage/month/{offset}",
methods = HttpMethod.GET,
tags = {"Commands"},
responses = {
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = byte[].class, type = "image/png")}),
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = CommandsStatistic.class, type = "application/json")})
},
pathParams = {
@OpenApiParam(name = "offset", type = Integer.class, required = true)
}
)
public void usageMonth(Context ctx) {
var stats = metrics().commands().month(offset(ctx, MAX_MONTH_OFFSET));
if ("application/json".equals(ctx.header("Accept"))) {
Expand All @@ -43,6 +74,21 @@ public void usageMonth(Context ctx) {
}
}

@OpenApi(
summary = "Get the amount of executed commands per week.",
operationId = "countWeek",
path = "count/week/{offset}/{count}",
methods = HttpMethod.GET,
tags = {"Commands"},
responses = {
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = byte[].class, type = "image/png")}),
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = CommandsStatistic.class, type = "application/json")})
},
pathParams = {
@OpenApiParam(name = "offset", type = Integer.class, required = true),
@OpenApiParam(name = "count", type = Integer.class, required = true)
}
)
public void countWeek(Context ctx) {
var stats = metrics().commands().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS));
if ("application/json".equals(ctx.header("Accept"))) {
Expand All @@ -52,6 +98,21 @@ public void countWeek(Context ctx) {
}
}

@OpenApi(
summary = "Get the amount of executed commands per month.",
operationId = "countMonth",
path = "count/month/{offset}/{count}",
methods = HttpMethod.GET,
tags = {"Commands"},
responses = {
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = byte[].class, type = "image/png")}),
@OpenApiResponse(status = "200", content = {@OpenApiContent(from = CommandsStatistic.class, type = "application/json")})
},
pathParams = {
@OpenApiParam(name = "offset", type = Integer.class, required = true),
@OpenApiParam(name = "count", type = Integer.class, required = true)
}
)
public void countMonth(Context ctx) {
var stats = metrics().commands().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH));
if ("application/json".equals(ctx.header("Accept"))) {
Expand All @@ -65,43 +126,13 @@ public void countMonth(Context ctx) {
public void buildRoutes() {
path("commands", () -> {
path("count", () -> {
get("week/{offset}/{count}", OpenApiBuilder.documented(OpenApiBuilder.document()
.operation(op -> {
op.summary("Get the amount of exectued commands per week.");
})
.result("200", byte[].class, "image/png")
.result("200", CommandsStatistic.class, "application/json")
.pathParam("offset", Integer.class, MetricsRoute::offsetWeekDoc)
.pathParam("count", Integer.class, MetricsRoute::countWeekDoc),
cache(this::countWeek)));
get("month/{offset}/{count}", OpenApiBuilder.documented(OpenApiBuilder.document()
.operation(op -> {
op.summary("Get the amount of exectued commands per month.");
})
.result("200", byte[].class, "image/png")
.result("200", CommandsStatistic.class, "application/json")
.pathParam("offset", Integer.class, MetricsRoute::offsetMonthDoc)
.pathParam("count", Integer.class, MetricsRoute::countMonthDoc),
cache(this::countMonth)));
get("week/{offset}/{count}", this::countWeek);
get("month/{offset}/{count}", this::countMonth);
});

path("usage", () -> {
get("week/{offset}", OpenApiBuilder.documented(OpenApiBuilder.document()
.operation(op -> {
op.summary("Get command usages for a week.");
})
.result("200", byte[].class, "image/png")
.result("200", CommandsStatistic.class, "application/json")
.pathParam("offset", Integer.class, MetricsRoute::offsetWeekDoc),
cache(this::usageWeek)));
get("month/{offset}", OpenApiBuilder.documented(OpenApiBuilder.document()
.operation(op -> {
op.summary("Get command usages for a month.");
})
.result("200", byte[].class, "image/png")
.result("200", CommandsStatistic.class, "application/json")
.pathParam("offset", Integer.class, MetricsRoute::offsetMonthDoc),
cache(this::usageMonth)));
get("week/{offset}", this::usageWeek);
get("month/{offset}", this::usageMonth);
});
});
}
Expand Down
Loading

0 comments on commit 479d7d9

Please sign in to comment.