Skip to content

Allow partial updates to ROM user props via typed payload schema#3124

Merged
gantoine merged 4 commits intomasterfrom
copilot/feature-allow-partial-rom-update
Mar 12, 2026
Merged

Allow partial updates to ROM user props via typed payload schema#3124
gantoine merged 4 commits intomasterfrom
copilot/feature-allow-partial-rom-update

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 12, 2026

The PUT /api/roms/{id}/props endpoint accepted an untyped data: dict[str, Any], making it undocumented in the OpenAPI schema and leaving users to discover the available fields by trial and error. Users were forced to re-send all fields or risk silently clearing them.

Changes

  • Typed RomUserData model — replaces dict[str, Any] with a fully documented Pydantic model; all fields are optional to support partial updates:
    class RomUserData(BaseModel):
        is_main_sibling: bool | None = Field(default=None, ...)
        backlogged: bool | None = Field(default=None, ...)
        rating: int | None = Field(default=None, ...)
        status: RomUserStatus | None = Field(default=None, ...)
        # ...
  • exclude_unset=True — endpoint now uses payload.data.model_dump(exclude_unset=True), so only explicitly provided fields are written; omitted fields are preserved, and status: null still explicitly clears the value as intended
  • Updated RomUserUpdatePayloaddata, update_last_played, and remove_last_played fields now carry descriptions visible in API docs
  • Test — added test_update_rom_user_props_partial_update to assert that a single-field update leaves other previously set fields unchanged
Original prompt

This section details on the original issue you should resolve

<issue_title>[Feature] API doesn't allow partial rom properties update</issue_title>
<issue_description>Is your feature request related to a problem? Please describe.

When updating rom props (/api/roms/{id}/props), one has to pass undocumented "data" object will full list of attributes. I would expect one can PUT only a subset of attributes to get them updated without overriding the rest of the data.

Describe the solution you'd like

API docs should:

  • document the data object and
  • allow partial data to be passed.

Describe alternatives you've considered

None, I'm kind of wiping my data each turn 😅

Additional context

I'm batch updating romm props (here, status) from external sources (igdb, etc)</issue_description>

Comments on the Issue (you are @copilot in this section)


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI changed the title [WIP] [Feature] Allow partial rom properties update in API Allow partial updates to ROM user props via typed payload schema Mar 12, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 12, 2026

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
14249 9637 68% 0% 🟢

New Files

No new covered files...

Modified Files

File Coverage Status
backend/endpoints/roms/_init_.py 61% 🟢
TOTAL 61% 🟢

updated for commit: b1cd003 by action🐍

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 12, 2026

Test Results (postgresql)

991 tests  +991   990 ✅ +990   2m 32s ⏱️ + 2m 32s
  1 suites +  1     1 💤 +  1 
  1 files   +  1     0 ❌ ±  0 

Results for commit b1cd003. ± Comparison against base commit fcfb74a.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 12, 2026

Test Results (mariadb)

991 tests  +1   990 ✅ +1   2m 45s ⏱️ -1s
  1 suites ±0     1 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit b1cd003. ± Comparison against base commit fcfb74a.

♻️ This comment has been updated with latest results.

@gantoine gantoine marked this pull request as ready for review March 12, 2026 02:11
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 12, 2026

Greptile Summary

This PR replaces the untyped dict[str, Any] payload on the PUT /api/roms/{id}/props endpoint with a properly documented RomUserData Pydantic model and adopts model_dump(exclude_unset=True) so that omitted fields are never overwritten — directly addressing the reported data-clearing issue. The change makes the API self-documenting in OpenAPI and correctly handles the "send only what you want to change" contract.

Key changes:

  • New RomUserData model with all fields optional and range constraints (ge/le) on rating, difficulty, and completion
  • RomUserUpdatePayload.data updated from dict[str, Any] to RomUserData; update_last_played / remove_last_played gain OpenAPI descriptions
  • Endpoint logic simplified to payload.data.model_dump(exclude_unset=True)
  • New test test_update_rom_user_props_partial_update verifying that a single-field update leaves other fields intact

Issue to address:

  • The rating, difficulty, and completion fields are typed as int | None, but the ge/le constraints are only enforced when the value is an integer — a None value passes Pydantic validation and reaches the DB as NULL on a NOT NULL column, causing a 500 error. These fields should be typed as plain int (with defaults matching the DB defaults) so that numeric range validation is always applied and None is rejected with a 422.

Confidence Score: 3/5

  • The partial-update logic is sound but int | None fields bypass ge/le validation and risk 500-level DB errors if null is sent.
  • The core exclude_unset=True mechanism is correct and the main feature (partial updates without data loss) works as intended. However, rating, difficulty, and completion being typed as int | None means None bypasses Pydantic's numeric range constraints and propagates to a NOT NULL database column, causing a runtime 500. This is a straightforward but real correctness gap that should be fixed before the endpoint is exposed in production API docs.
  • backend/endpoints/roms/init.py — the int | None field types on rating, difficulty, and completion in RomUserData need attention to prevent None bypassing ge/le validation.

Important Files Changed

Filename Overview
backend/endpoints/roms/init.py Introduces typed RomUserData Pydantic model replacing dict[str, Any] for the PUT /api/roms/{id}/props endpoint; uses exclude_unset=True for proper partial updates; `bool
backend/tests/endpoints/roms/test_rom.py Adds test_update_rom_user_props_partial_update which seeds initial state, performs a single-field update, and asserts unchanged fields are preserved; setup response is now properly asserted (addressing prior thread). Test coverage is focused on backlogged and hidden preservation but does not verify that now_playing or status are preserved after a partial update.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Endpoint as PUT /api/roms/{id}/props
    participant Pydantic as RomUserUpdatePayload
    participant DB as db_rom_handler

    Client->>Endpoint: JSON body (partial fields)
    Endpoint->>Pydantic: Parse into RomUserUpdatePayload
    Note over Pydantic: RomUserData validates each field<br/>(ge/le, enum, optional)
    Pydantic-->>Endpoint: Validated payload

    Endpoint->>Endpoint: payload.data.model_dump(exclude_unset=True)
    Note over Endpoint: Only explicitly provided fields<br/>end up in cleaned_data

    alt update_last_played = true
        Endpoint->>Endpoint: cleaned_data["last_played"] = now()
    else remove_last_played = true
        Endpoint->>Endpoint: cleaned_data["last_played"] = None
    end

    Endpoint->>DB: update_rom_user(id, cleaned_data)
    Note over DB: SQLAlchemy UPDATE with<br/>only the provided columns

    DB-->>Endpoint: Updated RomUser
    Endpoint-->>Client: RomUserSchema (200 OK)
Loading

Last reviewed commit: b1cd003

@gantoine gantoine merged commit f926771 into master Mar 12, 2026
11 checks passed
@gantoine gantoine deleted the copilot/feature-allow-partial-rom-update branch March 12, 2026 02:55
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.

[Feature] API doesn't allow partial rom properties update

2 participants