Compare commits

...
5 changed files with 110 additions and 15 deletions

No files matched your search

+3
View File
@@ -336,6 +336,9 @@ jobs:
restore-keys: |
rebar3-${{ runner.os }}-otp28-rebar3.27.0-
- name: Drop restored gateway build output
run: rm -rf fluxer_gateway/_build/default/lib/fluxer_gateway fluxer_gateway/_build/test/lib/fluxer_gateway
- name: Check formatting
run: |
"$FLUXER_CI_BIN" ci --step gateway_fmt
@@ -588,7 +588,12 @@ const ComposerInner = ({
if (disabledRef.current) {
return false;
}
selectLastSelectionOrEnd();
const selection = $getSelection();
if ($isRangeSelection(selection)) {
$setSelection(selection.clone());
} else {
selectLastSelectionOrEnd();
}
return false;
},
COMMAND_PRIORITY_LOW,
@@ -1077,9 +1077,15 @@ deliver_after_grace(Params, Sessions, RecheckAt, Presences) ->
ok = apply_push_worker_priority(),
LiveSessions = await_grace_recheck(monitor_grace_sessions(Sessions), RecheckAt),
send_push_now(
users_left_offline(lists:usort(maps:values(LiveSessions)), Presences), Params
grace_recipients(maps:keys(Sessions), maps:values(LiveSessions), Presences), Params
).
-spec grace_recipients([user_id()], [user_id()], term()) -> [user_id()].
grace_recipients(HeldUserIds, LiveUserIds, Presences) ->
Live = lists:usort(LiveUserIds),
Gone = lists:usort(HeldUserIds) -- Live,
lists:usort(Gone ++ users_left_offline(Live, Presences)).
-spec monitor_grace_sessions(grace_sessions()) -> #{reference() => user_id()}.
monitor_grace_sessions(Sessions) ->
maps:fold(fun monitor_user_sessions/3, #{}, Sessions).
@@ -1315,6 +1321,57 @@ presence_eligibility_allows_when_presence_table_missing_test() ->
build_push_presence_eligibility(#{<<"s1">> => #{user_id => 1}}, #{})
).
grace_recipients_push_a_user_whose_held_sessions_all_ended_test() ->
Presences = #{1 => #{<<"status">> => <<"online">>}},
?assertEqual([1], grace_recipients([1], [], Presences)).
grace_recipients_skip_a_user_still_online_on_a_held_session_test() ->
Presences = #{1 => #{<<"status">> => <<"online">>}},
?assertEqual([], grace_recipients([1], [1], Presences)).
grace_recipients_push_a_user_whose_live_session_went_offline_test() ->
Presences = #{1 => #{<<"status">> => <<"offline">>}},
?assertEqual([1], grace_recipients([1], [1], Presences)).
grace_recipients_skip_a_user_with_one_ended_and_one_live_online_session_test() ->
Presences = #{1 => #{<<"status">> => <<"online">>}},
?assertEqual([], grace_recipients([1], [1], Presences)).
grace_recipients_mix_users_test() ->
Presences = #{
1 => #{<<"status">> => <<"online">>},
2 => #{<<"status">> => <<"online">>},
3 => #{<<"status">> => <<"offline">>}
},
?assertEqual([1, 3], grace_recipients([1, 2, 3], [2, 3, 3], Presences)).
deliver_after_grace_pushes_a_user_whose_session_exits_inside_the_window_test() ->
Self = self(),
ok = meck:new(push, [passthrough, non_strict]),
try
ok = meck:expect(push, handle_message_create, fun(Params) ->
Self ! {pushed, maps:get(user_ids, Params)},
ok
end),
Session = spawn(fun() ->
receive
stop -> ok
end
end),
Held = #{42 => [Session]},
Presences = #{42 => #{<<"status">> => <<"online">>}},
RecheckAt = erlang:monotonic_time(millisecond) + 300,
_ = spawn(fun() -> deliver_after_grace(#{}, Held, RecheckAt, Presences) end),
timer:sleep(50),
Session ! stop,
receive
{pushed, UserIds} -> ?assertEqual([42], UserIds)
after 2000 -> erlang:error(no_push_after_session_exit)
end
after
meck:unload(push)
end.
legacy_session_eligibility_suppresses_every_real_session_test() ->
RealSession = #{
session_id => <<"s1">>,
+30 -12
View File
@@ -2382,8 +2382,6 @@ fn decode_postgres_message(row: serde_json::Value) -> anyhow::Result<Message> {
.get("pinned_timestamp")
.is_some_and(|value| !value.is_null());
row.insert("pinned".to_owned(), serde_json::Value::Bool(pinned));
default_i32_field(&mut row, "type", 0);
default_i32_field(&mut row, "version", 0);
Ok(serde_json::from_value(serde_json::Value::Object(row))?)
}
@@ -2415,16 +2413,6 @@ fn decode_postgres_attachment_decay(
Ok((row.attachment_id, expires_at))
}
fn default_i32_field(
row: &mut serde_json::Map<String, serde_json::Value>,
field: &str,
value: i32,
) {
if row.get(field).is_none_or(serde_json::Value::is_null) {
row.insert(field.to_owned(), serde_json::Value::Number(value.into()));
}
}
#[cfg(feature = "scylla")]
fn rows_to_messages(rows: QueryRowsResult) -> anyhow::Result<Vec<Message>> {
let rows: Vec<MessageDbRow> = rows.rows::<MessageDbRow>()?.collect::<Result<_, _>>()?;
@@ -3404,6 +3392,36 @@ mod tests {
);
}
#[test]
fn build_responses_request_accepts_legacy_null_version_rows() {
let request: MessageRequest = serde_json::from_value(json!({
"op": "BuildResponses",
"messages": [{
"message_id": "1449544529132171273",
"channel_id": "1431572375251247158",
"bucket": 399,
"author_id": "1130650140672000000",
"type": null,
"version": null,
"content": ""
}],
"viewer_user_id": "1130650140672000000",
"source_guild_id": null,
"message_history_cutoff_ms": null,
"can_read_message_history": true,
"media_endpoint": "https://media.example",
"media_proxy_secret_key": "secret",
"include_reactions": true
}))
.unwrap();
let MessageRequest::BuildResponses { messages, .. } = request else {
panic!("expected BuildResponses");
};
assert_eq!(messages[0].message_type, 0);
assert_eq!(messages[0].version, 0);
}
#[test]
fn mention_context_carries_embed_user_ids_for_message_and_snapshots() {
let message: Message = serde_json::from_value(json!({
+13 -1
View File
@@ -58,6 +58,13 @@ mod serde_id {
.map(StringOrI64::into_i64)
.collect()
}
pub fn i32_or_null_as_default<'de, D>(deserializer: D) -> Result<i32, D::Error>
where
D: Deserializer<'de>,
{
Ok(Option::<i32>::deserialize(deserializer)?.unwrap_or_default())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -164,7 +171,11 @@ pub struct Message {
pub bucket: i32,
#[serde(default, deserialize_with = "serde_id::opt_i64_from_string_or_number")]
pub author_id: Option<i64>,
#[serde(rename = "type")]
#[serde(
rename = "type",
default,
deserialize_with = "serde_id::i32_or_null_as_default"
)]
pub message_type: i32,
#[serde(default, deserialize_with = "serde_id::opt_i64_from_string_or_number")]
pub webhook_id: Option<i64>,
@@ -191,6 +202,7 @@ pub struct Message {
)]
pub mention_channels: Vec<i64>,
pub has_reaction: Option<bool>,
#[serde(default, deserialize_with = "serde_id::i32_or_null_as_default")]
pub version: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub attachments: Option<Vec<MessageAttachment>>,