Skip to content

Feature/capr 55 test event db calls#175

Open
simtiaz5 wants to merge 10 commits intodevelopfrom
feature/capr-55-test-event-db-calls
Open

Feature/capr 55 test event db calls#175
simtiaz5 wants to merge 10 commits intodevelopfrom
feature/capr-55-test-event-db-calls

Conversation

@simtiaz5
Copy link
Copy Markdown
Contributor

@simtiaz5 simtiaz5 commented Apr 22, 2026

Summary by Sourcery

Integrate Discord event commands with backend organizations identified by guild IDs, add support for event titles and IDs in the data model, and improve event retrieval robustness and coverage.

New Features:

  • Resolve and cache backend organization IDs per guild and automatically create missing bot organizations based on guild metadata.
  • Store and propagate backend event IDs through the event schema to enable direct update and delete operations instead of name-based lookups.
  • Support first-class event titles in backend create/update requests and responses, replacing the previous name-in-description encoding.

Enhancements:

  • Harden backend event fetching with multi-endpoint fallbacks and org-based filtering to work across different bot API deployments.
  • Tighten guild presence validation for event commands to ensure they only operate when the server context is available.
  • Improve announcement handling and registration checks to better track announcement messages and validate permissions.

Tests:

  • Add an extensive test suite for the event extension covering organization resolution, event listing, creation, updating, deletion, announcements, dropdown views, and legacy decoding behavior.
  • Extend database tests to cover bot organization endpoints and validate organization payloads, including guild IDs.
  • Add tests to verify event fetching fallbacks, malformed backend events handling, and error conditions for event CRUD and announcement flows.

@simtiaz5 simtiaz5 self-assigned this Apr 22, 2026
Copilot AI review requested due to automatic review settings April 22, 2026 21:55
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Apr 22, 2026

Reviewer's Guide

This PR refactors the event cog to use backend organization and event identifiers instead of guild IDs and encoded descriptions, adds robust fallbacks for fetching events across multiple backend API variants, and introduces extensive tests for the event extension and new database client endpoints.

Sequence diagram for resolving org id and listing events

sequenceDiagram
    actor User
    participant Discord
    participant EventCog
    participant DatabaseClient

    User->>Discord: /event list
    Discord->>EventCog: handle_list_action(interaction)
    EventCog->>EventCog: _resolve_org_id(guild)
    alt org_id cached
        EventCog-->>EventCog: return cached org_id
    else org_id not cached
        EventCog->>DatabaseClient: get_bot_organization_by_guild_id(guild.id)
        alt organization exists
            DatabaseClient-->>EventCog: OrganizationResponse(oid)
        else HTTP_STATUS_NOT_FOUND
            DatabaseClient-->>EventCog: BackendAPIError(404)
            EventCog->>DatabaseClient: create_bot_organization(guild_id, name)
            DatabaseClient-->>EventCog: OrganizationResponse(oid)
        end
        EventCog->>EventCog: cache org_id by guild.id
    end

    EventCog->>EventCog: _fetch_backend_events(org_id)
    EventCog->>DatabaseClient: list_events_by_organization(org_id)
    alt list_events_by_organization 404
        DatabaseClient-->>EventCog: BackendAPIError(404)
        EventCog->>DatabaseClient: list_organization_events(org_id)
        alt list_organization_events 404
            DatabaseClient-->>EventCog: BackendAPIError(404)
            EventCog->>DatabaseClient: list_events(limit, offset)
            alt list_events fails
                DatabaseClient-->>EventCog: BackendAPIError
                EventCog-->>Discord: log error, return []
            else list_events success
                DatabaseClient-->>EventCog: [EventResponse]
            end
        else list_organization_events success
            DatabaseClient-->>EventCog: [EventResponse]
        end
    else list_events_by_organization success
        DatabaseClient-->>EventCog: [EventResponse]
    end

    EventCog->>EventCog: filter events by org_id
    EventCog->>EventCog: map EventResponse to EventSchema (event_id, title, ...)
    EventCog-->>Discord: send list of events to user
Loading

Sequence diagram for creating, updating, and deleting events with backend ids

sequenceDiagram
    actor User
    participant Discord
    participant EventCog
    participant DatabaseClient

    rect rgb(230,230,250)
        note over User,DatabaseClient: Create event
        User->>Discord: submit event creation modal
        Discord->>EventCog: _handle_event_submit(interaction, event)
        EventCog->>EventCog: _resolve_org_id(guild)
        EventCog->>DatabaseClient: create_event(CreateEventRequest(org_id, title, description, event_time, location))
        DatabaseClient-->>EventCog: EventResponse(eid, ...)
        EventCog-->>Discord: success_embed("Event Created")
    end

    rect rgb(220,245,220)
        note over User,DatabaseClient: Update event
        User->>Discord: open edit, submit updated event
        Discord->>EventCog: _handle_event_update(interaction, original_event, updated_event)
        EventCog->>EventCog: compute event_time_iso
        EventCog->>EventCog: event_id = original_event.event_id
        alt event_id missing
            EventCog-->>Discord: error_embed("Event Not Found")
        else event_id present
            EventCog->>DatabaseClient: update_event(event_id, UpdateEventRequest(title, description, event_time, location))
            DatabaseClient-->>EventCog: EventResponse
            EventCog-->>Discord: success_embed("Event Updated")
        end
    end

    rect rgb(245,230,230)
        note over User,DatabaseClient: Delete event
        User->>Discord: select event to delete
        Discord->>EventCog: _on_delete_select(interaction, selected_event)
        EventCog->>EventCog: event_id = selected_event.event_id
        alt event_id missing
            EventCog-->>Discord: error_embed("Event Not Found")
        else event_id present
            EventCog->>DatabaseClient: delete_event(event_id)
            DatabaseClient-->>EventCog: success
            EventCog-->>Discord: success_embed("Event Deleted")
        end
    end
Loading

Entity relationship diagram for organizations, guilds, and events

erDiagram
    ORGANIZATION {
        string oid
        string name
        int guild_id
        string date_created
        string date_modified
    }

    EVENT {
        string eid
        string org_id
        string title
        string description
        string event_time
        string location
        string date_created
        string date_modified
    }

    DISCORD_GUILD {
        int guild_id
        string name
    }

    DISCORD_GUILD ||--o| ORGANIZATION : maps_to
    ORGANIZATION ||--o{ EVENT : owns
Loading

Class diagram for updated event schemas and database client

classDiagram
    class EventSchema {
        +str event_id
        +str event_name
        +date event_date
        +time event_time
        +str description
        +str location
    }

    class CreateEventRequest {
        +str org_id
        +str title
        +str description
        +str event_time
        +str location
    }

    class UpdateEventRequest {
        +str title
        +str description
        +str event_time
        +str location
    }

    class EventResponse {
        +str eid
        +str title
        +str description
        +str event_time
        +str location
        +str org_id
        +str date_created
        +str date_modified
    }

    class CreateOrganizationRequest {
        +str name
        +str creator_uid
    }

    class BotCreateOrganizationRequest {
        +int guild_id
        +str name
    }

    class OrganizationResponse {
        +str oid
        +str name
        +int guild_id
        +str date_created
        +str date_modified
    }

    class DatabaseClient {
        +async get_bot_organization_by_guild_id(guild_id int) OrganizationResponse
        +async create_organization(data CreateOrganizationRequest) OrganizationResponse
        +async create_bot_organization(data BotCreateOrganizationRequest) OrganizationResponse
        +async list_events_by_organization(org_id str) list~EventResponse~
        +async list_organization_events(org_id str) list~EventResponse~
        +async list_events(limit int, offset int) list~EventResponse~
        +async create_event(data CreateEventRequest) EventResponse
        +async update_event(event_id str, data UpdateEventRequest) EventResponse
        +async delete_event(event_id str) None
    }

    class EventCog {
        -dict~int, dict~str, int~~ event_announcements
        -dict~int, str~ _guild_org_ids
        +async _resolve_org_id(guild discord_Guild) str
        +async _fetch_backend_events(org_id str) list~EventSchema~
        +_from_backend_event(backend_event EventResponse) EventSchema
    }

    EventCog ..> DatabaseClient : uses
    EventCog ..> EventSchema : manages
    EventCog ..> EventResponse : converts
    DatabaseClient ..> CreateEventRequest : creates
    DatabaseClient ..> UpdateEventRequest : updates
    DatabaseClient ..> EventResponse : returns
    DatabaseClient ..> CreateOrganizationRequest : creates
    DatabaseClient ..> BotCreateOrganizationRequest : creates bot org
    DatabaseClient ..> OrganizationResponse : returns
    EventSchema ..> EventResponse : built_from
Loading

File-Level Changes

Change Details Files
Resolve and cache backend organization IDs per guild and use them for all event operations instead of raw guild IDs.
  • Add _guild_org_ids cache on the Event cog to map guild IDs to backend organization IDs.
  • Implement _resolve_org_id to lookup or lazily create a bot-managed organization via get_bot_organization_by_guild_id/create_bot_organization, handling HTTP 404 as a create trigger and validating returned org ID.
  • Update event list, myevents, dropdown, create, and update flows to call _resolve_org_id(guild) and pass org_id to backend APIs instead of using str(guild_id).
  • Add guild presence checks in handlers that rely on the guild object, returning appropriate error embeds when missing.
capy_discord/exts/event/event.py
capy_discord/database.py
tests/capy_discord/exts/test_event.py
Use backend event IDs and first-class title fields to manage events instead of encoding names in descriptions and re-discovering IDs.
  • Extend EventSchema with an optional event_id used to carry backend eid through UI flows and hide it from the form.
  • Populate event_id when converting from backend events in _from_backend_event and include it in edit initial data, update request selection, and delete selection logic.
  • Switch create/update request payloads to use title and description fields directly, removing the _encode_event_description helper and relying primarily on the backend title field while keeping _decode_event_description as a legacy fallback.
  • Simplify update and delete logic to rely on the selected EventSchema.event_id instead of re-listing events and matching on encoded description names, adding explicit error handling when event_id is missing.
capy_discord/exts/event/event.py
capy_discord/exts/event/_schemas.py
capy_discord/database.py
tests/capy_discord/exts/test_event.py
Harden event fetching against varying backend APIs and filter results to the target organization.
  • Refactor _fetch_backend_events to use list_events_by_organization as the primary call and fall back to list_organization_events on 404, then to a global list_events(limit=100, offset=0), with logging and failure handling for other errors.
  • After fetching, filter events so that only records with matching org_id or no org_id are kept, and drop malformed events whose event_time cannot be parsed into a valid EventSchema.
  • Add multiple tests that exercise the primary path, both fallback paths, non-404 error handling, and malformed event skipping, as well as end-to-end usage via show/edit actions that rely on fallback fetching.
  • Ensure events are sorted by _event_datetime after transformation for stable ordering in dropdowns and listings.
capy_discord/exts/event/event.py
tests/capy_discord/exts/test_event.py
Extend the database client with bot-specific organization endpoints and richer event/organization types, and cover them with tests.
  • Add BotCreateOrganizationRequest TypedDict and extend OrganizationResponse with guild_id to model bot-managed organizations.
  • Implement get_bot_organization_by_guild_id and create_bot_organization methods on DatabaseClient, both hitting /organizations (or /organizations/guilds/{guild_id}) with appropriate expected statuses.
  • Update event-related TypedDicts to include title on create/update requests and responses so callers can stop encoding names into descriptions.
  • Add and update tests for organization endpoints to include guild_id in payloads and validate request URLs, bodies, and response mapping, including new bot organization endpoints.
capy_discord/database.py
tests/capy_discord/test_database.py
Add a comprehensive test suite for the Event cog UI flows, registration logic, and edge cases.
  • Introduce tests for resolving org IDs, fetching events across primary/fallback routes, listing/sorting events, and the /event command routing to specific handlers.
  • Test event creation, update, and deletion flows including success cases, missing IDs, backend failures, and modal response behavior with different interaction states.
  • Cover announcement behavior (channel lookup, member cache, permissions, forbidden/HTTP failures) and RSVP registration detection via reaction scanning.
  • Add tests for dropdown and confirmation views (selection, confirm, cancel, timeout) and helper methods like _decode_event_description and _from_backend_event error handling.
  • Verify that setup adds the Event cog to the bot on extension load.
tests/capy_discord/exts/test_event.py
capy_discord/exts/event/event.py

Possibly linked issues

  • #Test event DB calls: The PR adds comprehensive tests for event-related database calls and behaviors, directly implementing the issue’s request.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The nested try/except flow in _fetch_backend_events has a lot of repeated error handling and early returns; consider refactoring the fallback logic into smaller helpers or a loop over strategies so the control flow and logging paths are easier to follow and reason about.
  • The _guild_org_ids cache never expires or updates if the guild metadata changes (e.g., rename or reassociation in the backend); if that’s a realistic scenario, consider either adding an invalidation path or storing only the oid while always relying on the backend as the source of truth for mutable fields like name.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The nested try/except flow in `_fetch_backend_events` has a lot of repeated error handling and early returns; consider refactoring the fallback logic into smaller helpers or a loop over strategies so the control flow and logging paths are easier to follow and reason about.
- The `_guild_org_ids` cache never expires or updates if the guild metadata changes (e.g., rename or reassociation in the backend); if that’s a realistic scenario, consider either adding an invalidation path or storing only the `oid` while always relying on the backend as the source of truth for mutable fields like `name`.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown

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 updates the Discord event extension to use backend organization IDs (resolved from guild IDs) and to rely on first-class backend event identifiers/titles, while adding focused tests around the new backend call paths.

Changes:

  • Add org-id resolution + caching (guild_id -> oid) and use it for event list/show/edit/delete flows.
  • Switch events to use backend eid as event_id and backend title instead of encoding names into description.
  • Expand backend client and tests to support bot organization endpoints and event API fallbacks.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/capy_discord/test_database.py Adds bot organization endpoint tests and validates guild_id presence in org payloads.
tests/capy_discord/exts/test_event.py Adds comprehensive tests for event cog org resolution, event fetching fallbacks, and UI flows.
capy_discord/exts/event/event.py Implements org-id resolution/caching, event-id plumbing, and fallback backend event listing logic.
capy_discord/exts/event/_schemas.py Adds hidden event_id field to the EventSchema to support edit/delete by backend id.
capy_discord/database.py Extends typed payloads with title and adds bot organization client methods + guild_id on org responses.
Comments suppressed due to low confidence (1)

capy_discord/exts/event/event.py:283

  • handle_myevents_action calls _fetch_backend_events(...) before interaction.response.defer(...). If the backend call takes > ~3s, Discord may time out the interaction. Consider deferring immediately after the guild checks (like the other handlers) and then using interaction.followup.send(...) for the "No Events" case too.
        events = await self._fetch_backend_events(await self._resolve_org_id(guild))

        if not events:
            embed = error_embed("No Events", "No events found in this server.")
            await interaction.response.send_message(embed=embed, ephemeral=True)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

2 participants