Skip to content

feat: GraphQL subscription latestRecords#126

Merged
ahmadbky merged 6 commits intomasterfrom
124-server-sent-event
Mar 16, 2026
Merged

feat: GraphQL subscription latestRecords#126
ahmadbky merged 6 commits intomasterfrom
124-server-sent-event

Conversation

@ahmadbky
Copy link
Copy Markdown
Member

This pull request introduces support for GraphQL subscriptions (WebSocket-based) in the game_api, graphql-api and records-lib crates, along with the handling of record notifications, and standardizes time handling across the API to use chrono::DateTime<Utc> instead of NaiveDateTime. These changes improve real-time features, ensure consistent time representation, and offers an extensible "new record" notification system.

Key changes include:

GraphQL API Improvements:

  • Added a /graphql/subscriptions endpoint using WebSockets, enabling real-time GraphQL subscriptions and refactored the graphql_route to use a Scope and accept a LatestRecordsSubscription for live updates. [1] [2]
  • The GraphQL playground is now hardcoded to /graphql and /graphql/subscriptions endpoints, removing the GQL_ENDPOINT environment variable. [1] [2]

Record Notification System:

  • Introduced RecordsNotifier and related types in the records-lib crate. 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:

  • Replaced all uses of chrono::NaiveDateTime with chrono::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:

  • Updated relevant endpoints and internal logic to pass RecordsNotifier and new time types, ensuring all new records trigger notifications and time is handled consistently. [1] [2] [3] [4]
  • Improved a comment regarding Redis and SQL transaction ordering to avoid race conditions.

ahmadbky added 2 commits March 9, 2026 00:12
* 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.
@ahmadbky ahmadbky linked an issue Mar 16, 2026 that may be closed by this pull request
@ahmadbky ahmadbky marked this pull request as ready for review March 16, 2026 17:30
@ahmadbky ahmadbky requested a review from Copilot March 16, 2026 17:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 / LatestRecordsSubscription and NewRecordEvent types backed by a Tokio broadcast stream.
  • Wired record-saving endpoints to emit NewRecordEvent notifications 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_during now takes DateTime<Utc> but the banishments.date_ban column is modeled as SeaORM DateTime (naive). Using lt(at)/gt(at) here will fail to compile (and/or compare using the wrong type). Convert at to at.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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_notifier with a tokio::broadcast-backed stream and supporting types/tests.
  • Wired RecordsNotifier through game_api and emit NewRecordEvent on improved records in player_finished.
  • Added GraphQL subscriptions (latest_records) and WebSocket route under /graphql/subscriptions, plus schema updates to include SubscriptionRoot.

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.

@ahmadbky ahmadbky merged commit 65e0fed into master Mar 16, 2026
7 checks passed
@ahmadbky ahmadbky deleted the 124-server-sent-event branch March 16, 2026 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Server sent event for latest records

2 participants