Add admin status check endpoint to guild routes

Introduced `/guilds/members/{guildId}/{userId}/admin` endpoint to determine if a guild member is an admin. Added `AdminStatusResponse` DTO and documented the new route with OpenAPI annotations. This feature allows for efficient admin status verification with fallback to REST fetch on cache miss.
This commit is contained in:
anthorob committed 2026-10-09 12:43:44 -04:00
1 parent 861b3f90e7
commit e0ec43813d
2 files changed
+52

No files matched your search

@@ -19,6 +19,7 @@ import io.javalin.openapi.OpenApiRequestBody;
import io.javalin.openapi.OpenApiResponse; import io.javalin.openapi.OpenApiResponse;
import me.bobert.netneutrality.db.service.GuildService; import me.bobert.netneutrality.db.service.GuildService;
import me.bobert.netneutrality.db.service.GuildServiceProvider; import me.bobert.netneutrality.db.service.GuildServiceProvider;
import me.bobert.netneutrality.http.server.dto.AdminStatusResponse;
import me.bobert.netneutrality.http.server.dto.ChannelSummaryResponse; import me.bobert.netneutrality.http.server.dto.ChannelSummaryResponse;
import me.bobert.netneutrality.http.server.dto.ErrorResponse; import me.bobert.netneutrality.http.server.dto.ErrorResponse;
import me.bobert.netneutrality.http.server.dto.GuildBooleanUpdateRequest; import me.bobert.netneutrality.http.server.dto.GuildBooleanUpdateRequest;
@@ -31,6 +32,7 @@ import me.bobert.netneutrality.http.server.dto.MemberSummaryResponse;
import me.bobert.netneutrality.http.server.dto.OwnerResponse; import me.bobert.netneutrality.http.server.dto.OwnerResponse;
import me.bobert.netneutrality.http.server.dto.RoleSummaryResponse; import me.bobert.netneutrality.http.server.dto.RoleSummaryResponse;
import me.bobert.netneutrality.http.server.dto.VoiceChannelSummaryResponse; import me.bobert.netneutrality.http.server.dto.VoiceChannelSummaryResponse;
import me.bobert.netneutrality.utils.UserFunction;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
@@ -59,6 +61,7 @@ public final class GuildRoutes {
routes.get("/guilds/roles/{guildId}", ctx -> roles(jdaSupplier, ctx)); routes.get("/guilds/roles/{guildId}", ctx -> roles(jdaSupplier, ctx));
routes.get("/guilds/members/{guildId}/{userId}", ctx -> member(jdaSupplier, ctx)); routes.get("/guilds/members/{guildId}/{userId}", ctx -> member(jdaSupplier, ctx));
routes.get("/guilds/members/{guildId}/{userId}/roles", ctx -> memberRoles(jdaSupplier, ctx)); routes.get("/guilds/members/{guildId}/{userId}/roles", ctx -> memberRoles(jdaSupplier, ctx));
routes.get("/guilds/members/{guildId}/{userId}/admin", ctx -> memberAdmin(jdaSupplier, ctx));
routes.get("/guilds/voice-channels/{guildId}", ctx -> voiceChannels(jdaSupplier, ctx)); routes.get("/guilds/voice-channels/{guildId}", ctx -> voiceChannels(jdaSupplier, ctx));
routes.get("/guilds/voice-channels/{guildId}/{channelId}", ctx -> voiceChannel(jdaSupplier, ctx)); routes.get("/guilds/voice-channels/{guildId}/{channelId}", ctx -> voiceChannel(jdaSupplier, ctx));
routes.get("/guilds/config/{guildId}", ctx -> config(jdaSupplier, guildServices, ctx)); routes.get("/guilds/config/{guildId}", ctx -> config(jdaSupplier, guildServices, ctx));
@@ -235,6 +238,38 @@ public final class GuildRoutes {
); );
} }
@OpenApi(
path = "/guilds/members/{guildId}/{userId}/admin",
methods = HttpMethod.GET,
summary = "Check whether a guild member is an admin of the guild, falling back to a REST fetch on a member cache miss",
tags = {"Guilds"},
pathParams = {
@io.javalin.openapi.OpenApiParam(name = "guildId", description = "The Discord guild id"),
@io.javalin.openapi.OpenApiParam(name = "userId", description = "The Discord user id")
},
responses = {
@OpenApiResponse(status = "200", content = @OpenApiContent(from = AdminStatusResponse.class)),
@OpenApiResponse(status = "404", content = @OpenApiContent(from = ErrorResponse.class))
}
)
private static void memberAdmin(Supplier<JDA> jdaSupplier, Context ctx) {
Guild guild = guild(jdaSupplier, ctx);
String userId = ctx.pathParam("userId");
CacheOrFetch.<Member>resolve(
ctx,
() -> guild.getMemberById(userId),
() -> guild.retrieveMemberById(userId),
member -> {
Map<String, Object> summary = new LinkedHashMap<>();
summary.put("guild_id", guild.getId());
summary.put("user_id", member.getId());
summary.put("is_admin", UserFunction.isAdmin(member));
return summary;
}
);
}
/** /**
* Builds the {@code /guilds/members/{guildId}/{userId}} response for a resolved member. * Builds the {@code /guilds/members/{guildId}/{userId}} response for a resolved member.
*/ */
@@ -0,0 +1,17 @@
package me.bobert.netneutrality.http.server.dto;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
/**
* OpenAPI schema for the {@code /guilds/members/{guildId}/{userId}/admin} response.
*/
public record AdminStatusResponse(
String guildId,
String userId,
boolean isAdmin
) {
}