Skip to content

fix: Places likes retrocompatibility & entities support#802

Merged
LautaroPetaccio merged 2 commits intomasterfrom
fix/use-entity-id-on-places-favorites
Feb 17, 2026
Merged

fix: Places likes retrocompatibility & entities support#802
LautaroPetaccio merged 2 commits intomasterfrom
fix/use-entity-id-on-places-favorites

Conversation

@LautaroPetaccio
Copy link
Contributor

The PATCH /places/:entity_id/favorites endpoint was broken. It accepted an entity_id route parameter but internally called getPlace(ctx), which validated against a different schema requiring place_id as a UUID. This caused a runtime error:

Error validating input: - must have required property 'place_id'

Beyond this immediate bug, the /places/:entity_id/favorites and /places/:entity_id/likes endpoints were hardcoded to query only PlaceModel, meaning any world ID sent through these endpoints would silently fail with a 404. Older clients rely on these /places/ endpoints for both places and worlds, so this broke world favoriting and liking for those clients.

Approach

Unified entity lookup via ID format detection

Rather than duplicating place-vs-world branching in every route handler, we introduced findEntityByIdWithAggregates in src/entities/shared/entityInteractions.ts. This function inspects the entity ID format to decide which model to query:

  • UUIDs (e.g. d5c08816-6745-...) are looked up via PlaceModel
  • Non-UUID strings (e.g. myworld.dcl.eth) are looked up via WorldModel

This lets any endpoint that receives a generic entity ID resolve it without needing the caller to specify the type upfront.

Updated route handlers to use the shared lookup

The four mutation endpoints now use findEntityByIdWithAggregates for the initial entity fetch:

  • PATCH /places/:entity_id/favorites — was calling getPlace(ctx), now uses the shared lookup and dispatches updateFavorites to PlaceModel or WorldModel via isWorld(entity)
  • PATCH /places/:entity_id/likes — was querying PlaceModel directly, same fix
  • PATCH /worlds/:world_id/favorites — was querying WorldModel directly, now uses the shared lookup (still only calls WorldModel.updateFavorites since this is a world-specific endpoint)
  • PATCH /worlds/:world_id/likes — same as above for likes

The /places/ endpoints have comments explaining why they handle world entities: older clients may send world IDs through these endpoints, and the dedicated /worlds/:world_id/favorites and /worlds/:world_id/likes endpoints exist for newer clients.

Removed UUID-only validation from /places/ schemas

The entity_id parameter in UserFavorite/schema.ts and UserLikes/schema.ts had format: "uuid", which rejected world names like myworld.dcl.eth at the validation layer before the handler even ran. This was removed so both UUIDs and world names pass validation — the actual entity type resolution happens in findEntityByIdWithAggregates.

Exported route handlers for testability

updateFavorites and updateLike were private functions. They are now exported, consistent with the world route handlers (updateWorldFavorites, updateWorldLikes) which were already exported.

Integration tests

Added 28 integration tests across 4 files in test/integration/, using supertest against a real Express app backed by a PostgreSQL test database. Only external services (auth, Snapshot score, Slack, hot scenes, scene stats, worlds live data) are mocked — the models execute real SQL.

Test file Tests What it covers
updatePlaceFavorites.test.ts 5 Place 404, add/remove favorite, no-op idempotency, DB persistence via GET
updatePlaceLikes.test.ts 7 Place 404, like/dislike, switch like-to-dislike, remove (null), no-op, DB persistence
updateWorldFavorites.test.ts 7 World 404, add/remove favorite, no-op, DB persistence, retro-compatibility via /places/ endpoint
updateWorldLikes.test.ts 9 World 404, like/dislike, switch, remove, no-op, DB persistence, retro-compatibility via /places/ endpoint

The retro-compatibility tests verify that world IDs sent through the /places/:entity_id/favorites and /places/:entity_id/likes endpoints correctly persist to the worlds table — the key behavior this PR preserves.

Supporting changes to the test infrastructure:

  • test/setup/server.ts — mounted userFavoriteRoute and userLikesRoute on the test Express app
  • test/setup/db.ts — added user_favorites and user_likes to the tables truncated between tests

@github-actions
Copy link

github-actions bot commented Feb 17, 2026

Pull Request Test Coverage Report for Build 22105385711

Details

  • 17 of 30 (56.67%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-2.1%) to 89.404%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/entities/shared/entityInteractions.ts 17 30 56.67%
Totals Coverage Status
Change from base Build 22067325409: -2.1%
Covered Lines: 15066
Relevant Lines: 16616

💛 - Coveralls

Copy link
Collaborator

@braianj braianj left a comment

Choose a reason for hiding this comment

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

LGTM!

})

if (!world) {
if (!entity) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we can also validate if this is a world, WDYT?

Suggested change
if (!entity) {
if (!entity || !isWorld(entity)) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! I have just changed the code to prevent places from being used in the worlds endpoints.

@LautaroPetaccio LautaroPetaccio merged commit a323425 into master Feb 17, 2026
3 checks passed
@LautaroPetaccio LautaroPetaccio deleted the fix/use-entity-id-on-places-favorites branch February 17, 2026 16:43
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