perf(guild): add a lean channel auth context rpc (#2226)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-31 01:04:55 +02:00
1 parent 8dc2bad843
commit ba54b61dcf
11 files changed
+383 -25

No files matched your search

@@ -37,6 +37,7 @@ route(M, P) ->
-define(HANDLER_MAP, #{
<<"guild.dispatch">> => fun gateway_rpc_guild_lifecycle:handle/2,
<<"guild.get_data">> => fun gateway_rpc_guild_lifecycle:handle/2,
<<"guild.get_auth_context">> => fun gateway_rpc_guild_lifecycle:handle/2,
<<"guild.start">> => fun gateway_rpc_guild_lifecycle:handle/2,
<<"guild.stop">> => fun gateway_rpc_guild_lifecycle:handle/2,
<<"guild.reload">> => fun gateway_rpc_guild_lifecycle:handle/2,
@@ -15,6 +15,7 @@
-spec handle(binary(), map()) -> term().
handle(<<"guild.dispatch">>, P) -> handle_dispatch(P);
handle(<<"guild.get_data">>, P) -> handle_get_data(P);
handle(<<"guild.get_auth_context">>, P) -> handle_get_auth_context(P);
handle(<<"guild.start">>, P) -> handle_start(P);
handle(<<"guild.stop">>, P) -> handle_stop(P);
handle(<<"guild.reload">>, P) -> handle_reload(P);
@@ -47,6 +48,39 @@ handle_get_data(#{<<"guild_id">> := GuildIdBin, <<"user_id">> := UserIdBin}) ->
<<"guild_not_found">>
).
-spec handle_get_auth_context(map()) -> term().
handle_get_auth_context(#{<<"guild_id">> := GuildIdBin, <<"user_id">> := UserIdBin} = Params) ->
GuildId = validation:snowflake_or_throw(<<"guild_id">>, GuildIdBin),
UserId = optional_user_id(UserIdBin),
ChannelId = optional_channel_id(maps:get(<<"channel_id">>, Params, null)),
gateway_rpc_guild_infra:with_guild(
GuildId,
fun(Pid) ->
get_auth_context_from_guild(Pid, UserId, ChannelId)
end,
<<"guild_not_found">>
).
-spec optional_channel_id(term()) -> integer() | null.
optional_channel_id(Value) ->
case validation:validate_optional_snowflake(Value) of
{ok, null} -> null;
{ok, ChannelId} when is_integer(ChannelId) -> ChannelId;
_ -> gateway_rpc_error:raise(validation_invalid_params)
end.
-spec get_auth_context_from_guild(pid(), integer() | null, integer() | null) -> term().
get_auth_context_from_guild(Pid, UserId, ChannelId) ->
Request = {get_guild_auth_context, #{user_id => UserId, channel_id => ChannelId}},
case gen_server:call(Pid, Request, ?GUILD_CALL_TIMEOUT) of
#{auth_context := null} ->
gateway_rpc_error:raise(<<"forbidden">>);
#{auth_context := AuthContext} ->
AuthContext;
_ ->
gateway_rpc_error:raise(<<"guild_data_error">>)
end.
-spec optional_user_id(term()) -> integer() | null.
optional_user_id(Value) ->
case validation:validate_optional_snowflake(Value) of
@@ -194,4 +228,13 @@ optional_user_id_preserves_null_for_skip_membership_check_test() ->
optional_user_id_accepts_snowflake_test() ->
?assertEqual(123, optional_user_id(<<"123">>)).
optional_channel_id_defaults_to_null_test() ->
?assertEqual(null, optional_channel_id(null)).
optional_channel_id_accepts_snowflake_test() ->
?assertEqual(456, optional_channel_id(<<"456">>)).
optional_channel_id_rejects_garbage_test() ->
?assertError({gateway_rpc_error, _}, optional_channel_id(<<"nope">>)).
-endif.
+1
View File
@@ -89,6 +89,7 @@ query_call_handler(get_user_permissions) -> query;
query_call_handler(can_manage_roles) -> query;
query_call_handler(can_manage_role) -> query;
query_call_handler(get_guild_data) -> query;
query_call_handler(get_guild_auth_context) -> query;
query_call_handler(get_assignable_roles) -> query;
query_call_handler(get_user_max_role_position) -> query;
query_call_handler(check_target_member) -> query;
+45
View File
@@ -4,6 +4,7 @@
-typing([eqwalizer]).
-export([get_guild_data/2]).
-export([get_auth_context/2]).
-export([get_guild_member/2]).
-export([get_guild_members_batch/2]).
-export([has_member/2]).
@@ -33,6 +34,50 @@ get_guild_data(#{user_id := UserId}, State) ->
get_guild_data_for_user(UserId, Data, State)
end.
-spec get_auth_context(map(), guild_state()) -> guild_reply(map()).
get_auth_context(#{user_id := UserId, channel_id := ChannelId}, State) ->
Data = guild_data_index:ensure_data_map(State),
case UserId of
null ->
{reply, #{auth_context => build_auth_context(ChannelId, Data, State)}, State};
_ ->
get_auth_context_for_member(UserId, ChannelId, Data, State)
end.
-spec get_auth_context_for_member(user_id(), integer() | null, map(), guild_state()) ->
guild_reply(map()).
get_auth_context_for_member(UserId, ChannelId, Data, State) ->
case guild_data_index:get_member(UserId, Data) of
undefined ->
{reply, #{auth_context => null, error_reason => <<"forbidden">>}, State};
_Member ->
{reply, #{auth_context => build_auth_context(ChannelId, Data, State)}, State}
end.
-spec build_auth_context(integer() | null, map(), guild_state()) -> map().
build_auth_context(ChannelId, Data, State) ->
#{
<<"guild">> => build_auth_guild(Data, State),
<<"parent_channel">> => find_channel(ChannelId, Data)
}.
-spec build_auth_guild(map(), guild_state()) -> map().
build_auth_guild(Data, State) ->
GuildProperties = map_utils:ensure_map(maps:get(<<"guild">>, Data, #{})),
GuildProperties#{
<<"roles">> => map_utils:ensure_list(maps:get(<<"roles">>, Data, [])),
<<"member_count">> => maps:get(
member_count, State, guild_data_index:member_count(Data)
),
<<"online_count">> => guild_member_list:get_online_count(State)
}.
-spec find_channel(integer() | null, map()) -> map() | null.
find_channel(null, _Data) ->
null;
find_channel(ChannelId, Data) ->
maps:get(ChannelId, guild_data_index:channel_index(Data), null).
-spec get_guild_member(map(), guild_state()) -> guild_reply(map()).
get_guild_member(Request, State) ->
guild_data_members:get_guild_member(Request, State).
@@ -241,6 +241,8 @@ handle_max_role_position(#{user_id := UserId}, State) ->
-spec handle_call_data(term(), guild_state()) -> {reply, term(), guild_state()}.
handle_call_data({get_guild_data, Req}, State) ->
guild_data:get_guild_data(request_map(Req), State);
handle_call_data({get_guild_auth_context, Req}, State) ->
guild_data:get_auth_context(request_map(Req), State);
handle_call_data({get_guild_member, Req}, State) ->
guild_data:get_guild_member(request_map(Req), State);
handle_call_data({get_guild_members_batch, Req}, State) ->