feat: GraphQL subscription latestRecords#126
Conversation
* Move the records notifier from its crate into a module of the `records-lib` crate. * Add documentations to the various items of this new module, with a unit test. * Fix bug of the creation of many records notifier when constructing the web server, instead of a single one. Refs: #124.
There was a problem hiding this comment.
Pull request overview
This PR introduces real-time “latest records” notifications by adding a records broadcast notifier in records_lib, emitting events when records are saved in game_api, and exposing them via a new GraphQL subscription endpoint.
Changes:
- Added
RecordsNotifier/LatestRecordsSubscriptionandNewRecordEventtypes backed by a Tokio broadcast stream. - Wired record-saving endpoints to emit
NewRecordEventnotifications after successful persistence. - Added GraphQL subscription support (schema + websocket route) to stream latest records to clients.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/records_lib/src/records_notifier.rs | New broadcast-based notifier + tests for record events. |
| crates/records_lib/src/lib.rs | Exposes the new records_notifier module. |
| crates/records_lib/Cargo.toml | Adds tokio-stream dependency and dev-deps for tests. |
| crates/graphql-api/src/subscriptions/root.rs | Adds latest_records GraphQL subscription backed by the notifier stream. |
| crates/graphql-api/src/subscriptions/mod.rs | Declares the new subscriptions module. |
| crates/graphql-api/src/schema.rs | Switches schema to use SubscriptionRoot and threads in LatestRecordsSubscription. |
| crates/graphql-api/src/lib.rs | Exposes the new subscriptions module. |
| crates/game_api/tests/base.rs | Updates app wiring to provide a RecordsNotifier to configuration in tests. |
| crates/game_api/src/utils.rs | Removes now-unused Either responder helper. |
| crates/game_api/src/main.rs | Instantiates and injects RecordsNotifier into the Actix app. |
| crates/game_api/src/http/staggered.rs | Passes notifier through staggered “finished” endpoints; uses DateTime<Utc>. |
| crates/game_api/src/http/player.rs | Threads notifier through record-finish flow; switches at to DateTime<Utc>. |
| crates/game_api/src/http/player_finished.rs | Emits NewRecordEvent when a record improves; updates time handling to DateTime<Utc>. |
| crates/game_api/src/http/event.rs | Threads notifier through event-finish flow; aligns time handling and response types. |
| crates/game_api/src/graphql.rs | Converts /graphql to a scope and adds websocket subscriptions route + playground config. |
| crates/game_api/src/env.rs | Removes the unused GQL_ENDPOINT config entry. |
| crates/game_api/src/configure.rs | Wires notifier + subscription into app data and GraphQL schema creation. |
| Cargo.toml | Reorders workspace members (no functional change). |
Comments suppressed due to low confidence (1)
crates/game_api/src/http/player.rs:185
get_ban_duringnow takesDateTime<Utc>but thebanishments.date_bancolumn is modeled as SeaORMDateTime(naive). Usinglt(at)/gt(at)here will fail to compile (and/or compare using the wrong type). Convertattoat.naive_utc()(and use that consistently in the filter expression).
pub async fn get_ban_during<C: ConnectionTrait>(
conn: &C,
player_id: u32,
at: DateTime<Utc>,
) -> RecordsResult<Option<banishments::Model>> {
banishments::Entity::find()
.filter(
banishments::Column::PlayerId
.eq(player_id)
.and(banishments::Column::DateBan.lt(at))
.and(
banishments::Column::Duration.is_null().or(Expr::col(
banishments::Column::DateBan,
)
.add(Expr::col(banishments::Column::Duration))
.gt(at)),
),
)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Refs: #126. Co-authored-by: Copilot Autofix powered by AI <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR introduces real-time “latest records” notifications across the system by adding a broadcast-based notifier in records_lib, emitting events when a record is saved in game_api, and exposing those events via a new GraphQL subscription endpoint (WebSocket) in graphql-api/game_api.
Changes:
- Added
records_lib::records_notifierwith atokio::broadcast-backed stream and supporting types/tests. - Wired
RecordsNotifierthroughgame_apiand emitNewRecordEventon improved records inplayer_finished. - Added GraphQL subscriptions (
latest_records) and WebSocket route under/graphql/subscriptions, plus schema updates to includeSubscriptionRoot.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/records_lib/src/records_notifier.rs | New notifier + stream wrapper + tests for broadcasting record events. |
| crates/records_lib/src/lib.rs | Exposes the new records_notifier module. |
| crates/records_lib/Cargo.toml | Adds tokio-stream (sync wrapper) and dev-dep tokio features for tests. |
| crates/graphql-api/src/subscriptions/root.rs | New latest_records GraphQL subscription that resolves emitted record IDs to DB records. |
| crates/graphql-api/src/subscriptions/mod.rs | Exports the new subscriptions module. |
| crates/graphql-api/src/schema.rs | Switches schema to include SubscriptionRoot and threads in LatestRecordsSubscription. |
| crates/graphql-api/src/lib.rs | Exposes subscriptions. |
| crates/game_api/src/configure.rs | Accepts and stores RecordsNotifier, passes its subscription into the GraphQL schema. |
| crates/game_api/src/graphql.rs | Converts GraphQL routing into a scope and adds WebSocket subscriptions endpoint; masks internal errors for streams too. |
| crates/game_api/src/http/player_finished.rs | Emits NewRecordEvent upon rank computation for improved records. |
| crates/game_api/src/http/player.rs | Threads RecordsNotifier through finished handlers and switches timestamps to DateTime<Utc>. |
| crates/game_api/src/http/event.rs | Threads RecordsNotifier through event-finished paths; switches timestamps to DateTime<Utc>. |
| crates/game_api/src/http/staggered.rs | Threads RecordsNotifier into staggered endpoints; switches timestamp parsing to DateTime<Utc>. |
| crates/game_api/src/main.rs | Creates a single RecordsNotifier instance and injects it into app configuration. |
| crates/game_api/tests/base.rs | Updates test app configuration to pass a notifier. |
| crates/game_api/src/utils.rs | Removes unused Either responder wrapper and tightens actix imports. |
| crates/game_api/src/http/player/auth/with_auth.rs | Moves WEB_TOKEN_SESS_KEY constant local to the module. |
| crates/game_api/src/auth.rs | Removes WEB_TOKEN_SESS_KEY constant from this module. |
| crates/game_api/src/env.rs | Removes now-unused gql_endpoint config entry. |
| Cargo.toml | Reorders workspace members list (no functional change). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
This pull request introduces support for GraphQL subscriptions (WebSocket-based) in the
game_api,graphql-apiandrecords-libcrates, along with the handling of record notifications, and standardizes time handling across the API to usechrono::DateTime<Utc>instead ofNaiveDateTime. These changes improve real-time features, ensure consistent time representation, and offers an extensible "new record" notification system.Key changes include:
GraphQL API Improvements:
/graphql/subscriptionsendpoint using WebSockets, enabling real-time GraphQL subscriptions and refactored thegraphql_routeto use aScopeand accept aLatestRecordsSubscriptionfor live updates. [1] [2]/graphqland/graphql/subscriptionsendpoints, removing theGQL_ENDPOINTenvironment variable. [1] [2]Record Notification System:
RecordsNotifierand related types in therecords-libcrate. It enables the system to broadcast new record events (including player, map, rank, and time) to subscribers, and integrated it into the main configuration and record-handling flows. [1] [2] [3] [4] [5]Time Handling Standardization:
chrono::NaiveDateTimewithchrono::DateTime<Utc>for more precise and unambiguous time handling in record submission, event, and player-related endpoints and logic. This affects function signatures and struct fields, mostly in the endpoint handlers related to the finish of a run. [1] [2] [3] [4] [5]API Refactoring and Cleanups:
RecordsNotifierand new time types, ensuring all new records trigger notifications and time is handled consistently. [1] [2] [3] [4]