Skip to content

fix(locations): send venue/room ids when resyncing a room to the materializer - #1026

Merged
smarcet merged 2 commits into
masterfrom
fix/resync-room-id-route
Aug 3, 2026
Merged

fix(locations): send venue/room ids when resyncing a room to the materializer#1026
smarcet merged 2 commits into
masterfrom
fix/resync-room-id-route

Conversation

@caseylocker

@caseylocker caseylocker commented Jul 28, 2026

Copy link
Copy Markdown

ref: https://app.clickup.com/t/86bb4r5df

What's broken

The Resync Room action on the Locations page has been 404ing in production since the materializer's destination-path restructure deployed.

We send the venue and room by name:

POST /api/v1/sync/materialize/{summitId}/{encodeURIComponent(venueName)}/{encodeURIComponent(roomName)}/

The materializer only accepts integers:

sync/materialize/<int:summit_id>/<int:venue_id>/<int:room_id>/

Django's int converter refuses a non-numeric segment, so the URL does not resolve. There is no name-based fallback. uicore's authErrorHandler renders a "Not Found" dialog on the 404, so the user sees an error and the resync never runs.

How it happened

dropbox-materializer PR #24 (5bb2d5e, merged 2026-06-15) replaced <str:venue>/<str:room> with <int:venue_id>/<int:room_id>. Its own commit message asked for this change:

"Resync Room route now takes integer IDs and builds a 'venue_id/room_id' room_filter to match the ID-keyed schedule. summit-admin's Resync Room button needs a coordinated change to call the new route."

It was never made. This repo last touched that URL in PR #833, months before the contract changed. It stayed invisible because production ran pre-#24 materializer code until recently; the deployed container now has the ID-based build, so the mismatch is live.

The change

LocationForm was already handed the room id and threw it away to look the room's name back up, so no new data is needed.

  • location-form.js::handleRoomResync now passes this.props.entity.id and roomId. Reading the venue id off props (not state) keeps an unsaved rename in the form from becoming the target, matching what the old code did with persistedVenueName. Guarded so a never-saved venue (id: 0) does not fire.
  • edit-location-page.js::handleRoomResync and dropbox-sync-actions.js::resyncRoom renamed to venueId/roomId; the URL drops the encodeURIComponent name interpolation.

Prettier reflowed the body of resyncRoom because the shortened signature now fits on one line. That is lint-staged's own output, not a hand edit.

No materializer change is needed. It has accepted and tested integer ids since #24: MaterializeRoomView builds room_filter = "{venue_id}/{room_id}", which matches build_schedule's keys.

Tests

The existing action tests only asserted that postRequest was called, never the URL — which is exactly why they stayed green through this regression. Added:

  • an action test pinning the built URL to .../sync/materialize/1/12/345/
  • a LocationForm test that opens the Rooms panel, clicks the real resync action, and asserts onRoomResync receives the two ids

Both were mutation-tested: restoring the old name-based behavior fails them (Expected: 781, 5002 / Received: "International Barcelona Convention Center", "Room A").

The existing inline props in location-form.test.js were lifted into a buildProps() factory so both describes share them; the pre-existing test is otherwise untouched.

Full suite: 154 suites, 1410 tests, green.

Worth a manual check

Nothing automated crosses the two repos, so a click-through on staging after merge would be worth it.

Summary by CodeRabbit

  • Bug Fixes
    • Improved room resynchronization by using numeric venue and room IDs, ensuring requests reach the correct sync endpoint.
    • Fixed the location editing and Rooms panel resync flow so the correct room is consistently selected and synchronized.
    • Improved handling of unsuccessful resync requests so loading indicators settle correctly without unexpected errors.

Convention pass

Run against skills/react-frontend.md §§1–7 and patterns/show-admin/summit-admin-testing-patterns.md, by grep rather than eyeball.

Clean on §1 (no URL/query built in a component — this change moves toward it, deleting the name lookup from the form), §2 (no new utils/ files), §3/§4 (no reimplemented formatters, no hand-rolled date/money, no new strings), §5 magic numbers (the only literals are VENUE_ID/ROOM_ID named constants in a test), §6 (no .css), §7 (full suite, not just the focused files).

Two notes for the reviewer, both deliberate:

The bare return guard in resyncRoom is not a §5 violation. §5 asks thunks to return a thenable on every branch, and the diff shows if (!baseUrl || !summitId) return; as an added line — but only because prettier reindented the body when the shortened signature collapsed onto one line. Every thunk in this file is async, so a bare return already yields a resolved promise; the rule's concern (undefined returned synchronously while the happy path returns a Promise) does not arise. I confirmed this by mutation: changing it to Promise.resolve() and back leaves behavior and tests identical. Left as-is rather than adding a no-op.

The new component test queries by data-tip, not role/name. The testing playbook prefers getByRole("button", { name }). uicore's Table renders custom row actions as <a href="" data-tip={tooltip}><i className="fa fa-refresh" /></a> — icon-only, no accessible name — so role/name is not reachable without adding an aria-label to production markup purely for the test, which the same playbook rules out. The selector is commented in place.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The room resync flow now uses integer venue and room IDs throughout LocationForm, EditLocationPage, and resyncRoom. The action builds the materializer URL with those IDs. Sync and allowlist tests now cover updated action filtering, loading, failure, pagination, and aggregation behavior.

Changes

Sync action updates

Layer / File(s) Summary
Materializer route contract
src/actions/dropbox-sync-actions.js, src/actions/__tests__/dropbox-sync-actions.test.js
resyncRoom uses integer IDs in the materializer URL. Tests verify exact routing and settled failure behavior.
UI ID propagation
src/components/forms/location-form.js, src/pages/locations/edit-location-page.js, src/components/forms/__tests__/location-form.test.js
LocationForm sends persisted venue and room IDs. EditLocationPage forwards them to the action. UI tests verify the callback arguments.
Sync and allowlist action coverage
src/actions/__tests__/dropbox-sync-actions.test.js
Tests filter internal DUMMY actions and cover loading boundaries, missing summit behavior, pagination error handlers, and aggregator action types.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant LocationForm
  participant EditLocationPage
  participant resyncRoom
  participant MaterializerEndpoint
  User->>LocationForm: Click room resync action
  LocationForm->>EditLocationPage: venueId, roomId
  EditLocationPage->>resyncRoom: venueId, roomId
  resyncRoom->>MaterializerEndpoint: POST materializer URL
  MaterializerEndpoint-->>resyncRoom: success or error
Loading

Possibly related PRs

Suggested reviewers: smarcet, tomrndom

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: sending venue and room IDs when resyncing rooms to the materializer.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/resync-room-id-route

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes the “Resync Room” action on the Locations page by aligning summit-admin’s request shape with the materializer’s updated route contract (integer venue/room IDs instead of names), preventing production 404s after the materializer’s destination-path restructure.

Changes:

  • Updated the resync call chain (LocationForm → EditLocationPage → Redux action) to pass venueId/roomId instead of venueName/roomName.
  • Updated the materializer URL construction to use integer IDs (and removed encodeURIComponent name interpolation).
  • Added tests to pin the exact resync URL and verify the UI callback receives the correct IDs.

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
src/pages/locations/edit-location-page.js Updates handleRoomResync to forward venue/room IDs to the thunk.
src/components/forms/location-form.js Sends persisted venue ID + room ID when invoking onRoomResync (no name lookup).
src/components/forms/tests/location-form.test.js Adds UI test asserting the resync action calls onRoomResync(venueId, roomId).
src/actions/dropbox-sync-actions.js Changes resyncRoom thunk signature and URL to /materialize/{summitId}/{venueId}/{roomId}/.
src/actions/tests/dropbox-sync-actions.test.js Adds assertion pinning the constructed resync URL to the integer-ID route.

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

@caseylocker
caseylocker requested a review from smarcet July 28, 2026 17:07
…rializer

The Resync Room button sent venue and room NAMES. The materializer's per-room
route stopped accepting names in dropbox-materializer PR #24 (merged
2026-06-15), which replaced

  sync/materialize/<int:summit_id>/<str:venue>/<str:room>/

with

  sync/materialize/<int:summit_id>/<int:venue_id>/<int:room_id>/

and shipped no name-based fallback. That PR's own commit message called for a
coordinated change here; it was never made. Since the materializer deployed,
every click has 404'd and shown a "Not Found" dialog instead of resyncing.

LocationForm already receives the room id and was discarding it to look the
room's name back up, so no new data is needed. It now passes
this.props.entity.id (the persisted venue, so an unsaved rename in the form
cannot become the target) and the room id straight through, guarding against a
venue that has never been saved (id 0).

The existing action tests only asserted that postRequest was called, never the
URL, which is why they stayed green through the regression. Added a test
pinning the built URL, and a LocationForm test that opens the Rooms panel,
clicks the real resync action and asserts the ids reach onRoomResync. Both fail
if the old behavior is restored.

Full suite: 154 suites, 1410 tests, green.

Co-Authored-By: Claude <noreply@anthropic.com>
@caseylocker
caseylocker force-pushed the fix/resync-room-id-route branch from 77a7a88 to 2d6b74e Compare July 30, 2026 15:54
@smarcet
smarcet requested a review from santipalenque July 30, 2026 19:09
@@ -442,7 +458,7 @@ describe("dropbox sync actions", () => {
test("resyncRoom dispatches STOP_LOADING on failure", async () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

please review all tests @caseylocker , tests like this one make no sense

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@santipalenque, fair. I went through all 35 in the file, not just this one. This test and its rebuildSync twin were the worst. They asserted an action sequence production never actually emits, guarding an overlay that uicore's error handler already clears on its own. I rewrote them down to the one thing they really protect (the promise settles after a failed POST, since nothing upstream catches it) and made them prove the POST actually fired.

While I was in there I deleted three more that were duplicates, or literally could not execute their own assertions, folded three near-overlaps into siblings, and replaced the media-upload disjointness test. Its mock threw away the action creators and so could never catch the regression it was named for. That one is mutation-verified now. The rest I kept on purpose: the sync-config failure tests are the only thing clearing the loading flag that disables Save and Rebuild, and each guard test covers a distinct branch. 35 down to 29, all green, in 4edbdc2.

Full pass over the file in response to review. Deleted three tests (a
duplicate single-page summit-switch test, a supersession test strictly
subsumed by the A-B-A test's nothing-was-dispatched assertion, and one
whose distinctive assertions iterated an array just asserted empty).
Folded three near-overlaps into siblings without losing an assertion
(loading bracketing, the snackbarErrorHandler identity pin - now in the
mid-fan-out test so both getRequest call sites are covered, and the
resyncRoom action sequence into the URL pin). Rewrote the rebuildSync/
resyncRoom failure tests to assert the real contract - the dispatch
promise settles and the POST fired - instead of a two-action sequence
production never emits (authErrorHandler stops loading before uicore
rejects). Replaced the media-upload disjointness test with a
creator-dispatching mock across both getRequest sites; the old mock
discarded the creators, so it could not detect the regression it named
(verified by mutation: wiring a media-upload creator in now fails it).
Filtered the inert DUMMY placeholder out of the two exact-sequence
assertions. 35 tests -> 29, every survivor with a nameable regression.

Co-Authored-By: Claude <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/actions/__tests__/dropbox-sync-actions.test.js`:
- Line 446: Update the rejected request mock setup using mockRequestImplReject
so it matches postRequest’s curried invocation: the mock must return a
params-level function that returns a dispatch-level function, which then returns
the rejected Promise. Apply this correction to both affected
postRequest.mockImplementation occurrences so the thunk’s catch handler receives
the rejection instead of a TypeError.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ab79f9ad-d2fe-44e8-b601-9110b996ca1a

📥 Commits

Reviewing files that changed from the base of the PR and between 2d6b74e and 4edbdc2.

📒 Files selected for processing (1)
  • src/actions/__tests__/dropbox-sync-actions.test.js

Comment thread src/actions/__tests__/dropbox-sync-actions.test.js

@santipalenque santipalenque left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM

@smarcet smarcet left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM

@smarcet
smarcet merged commit 55129fe into master Aug 3, 2026
9 checks passed
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.

4 participants