Skip to content

Commit 209f578

Browse files
Merge pull request #791 from decentraland/feat/re-discovery-add-ranking-endpoint
feat: add ranking update endpoint for places
2 parents d0cb879 + 5816a1c commit 209f578

File tree

8 files changed

+471
-8
lines changed

8 files changed

+471
-8
lines changed

docs/ai-agent-context.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ The Decentraland Places service is a comprehensive API solution for discovering,
6868

6969
The service exposes a REST API under `/api` with comprehensive documentation in [OpenAPI 3.0 format](openapi.yaml). Key endpoint categories:
7070

71-
- **Places**: `/api/places`, `/api/places/:id`, `/api/places/status`, `/api/places/:id/categories`, `/api/places/:id/rating`, `/api/places/:id/highlight` (admin only) (POST `/api/places` accepts array of place IDs in body)
71+
- **Places**: `/api/places`, `/api/places/:id`, `/api/places/status`, `/api/places/:id/categories`, `/api/places/:id/rating`, `/api/places/:id/ranking` (service token auth via `DATA_TEAM_AUTH_TOKEN` env var), `/api/places/:id/highlight` (admin only) (POST `/api/places` accepts array of place IDs in body)
7272
- **Worlds**: `/api/worlds`, `/api/world_names`
7373
- **Destinations**: `/api/destinations` (GET: combined places + worlds with enhanced filtering including SDK version and LIKE name matching; highlighted items are always returned first, followed by ranking value, then by specified sort order. POST: accepts array of destination IDs in body, maximum 100 IDs per request, supports all GET query parameters for additional filtering)
7474
- **Map**: `/api/map`, `/api/map/places` (coordinate-based queries with higher limits)

docs/openapi.yaml

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,93 @@ paths:
530530
'404':
531531
$ref: '#/components/responses/NotFound'
532532

533+
/places/{place_id}/ranking:
534+
put:
535+
tags:
536+
- Places
537+
summary: Update place ranking 🔒 (Service token)
538+
security:
539+
- ServiceToken: []
540+
description: |
541+
Update the ranking score for a place. **Requires service token authentication.**
542+
543+
This endpoint uses a pre-configured service token for authentication, intended for
544+
service-to-service calls from the data team's systems. The token must be provided
545+
in the Authorization header as `Bearer <token>` or just `<token>`.
546+
547+
The ranking value is used to order places in listings. Higher ranking values appear first
548+
(after highlighted places). This endpoint is intended for the data team to curate
549+
place ordering based on quality metrics or promotional needs.
550+
551+
**Use Cases:**
552+
- Promote high-quality places in listings
553+
- Adjust place ordering based on data analysis
554+
- Set custom ordering for promotional periods
555+
556+
**Authentication:**
557+
Set the `DATA_TEAM_AUTH_TOKEN` environment variable on the server, then use that
558+
token value in the Authorization header when calling this endpoint.
559+
operationId: updatePlaceRanking
560+
parameters:
561+
- name: place_id
562+
in: path
563+
required: true
564+
description: UUID of the place to update ranking
565+
schema:
566+
type: string
567+
format: uuid
568+
requestBody:
569+
required: true
570+
content:
571+
application/json:
572+
schema:
573+
$ref: '#/components/schemas/UpdateRankingBody'
574+
examples:
575+
setRanking:
576+
summary: Set ranking value
577+
value:
578+
ranking: 0.85
579+
removeRanking:
580+
summary: Remove ranking (set to null)
581+
value:
582+
ranking: null
583+
responses:
584+
'200':
585+
description: Ranking updated successfully
586+
content:
587+
application/json:
588+
schema:
589+
$ref: '#/components/schemas/PlaceResponse'
590+
'401':
591+
description: Unauthorized - missing or invalid token
592+
content:
593+
application/json:
594+
schema:
595+
$ref: '#/components/schemas/Error'
596+
example:
597+
ok: false
598+
error: "Authorization required"
599+
'403':
600+
description: Forbidden - invalid authorization token
601+
content:
602+
application/json:
603+
schema:
604+
$ref: '#/components/schemas/Error'
605+
example:
606+
ok: false
607+
error: "Invalid authorization token"
608+
'404':
609+
$ref: '#/components/responses/NotFound'
610+
'500':
611+
description: Internal server error - service authentication not configured
612+
content:
613+
application/json:
614+
schema:
615+
$ref: '#/components/schemas/Error'
616+
example:
617+
ok: false
618+
error: "Service authentication not configured"
619+
533620
/places/{place_id}/highlight:
534621
put:
535622
tags:
@@ -1651,6 +1738,19 @@ components:
16511738
2. Get the auth token from the SDK
16521739
3. Include in requests: `Authorization: Bearer <token>`
16531740
1741+
ServiceToken:
1742+
type: http
1743+
scheme: bearer
1744+
description: |
1745+
Pre-configured service token for service-to-service authentication.
1746+
1747+
This authentication method is used for internal service calls, such as
1748+
the data team's ranking updates. The token value must match the
1749+
`DATA_TEAM_AUTH_TOKEN` environment variable configured on the server.
1750+
1751+
To authenticate:
1752+
Include in requests: `Authorization: Bearer <service-token>` or `Authorization: <service-token>`
1753+
16541754
schemas:
16551755
Place:
16561756
type: object
@@ -2089,6 +2189,19 @@ components:
20892189
description: Optional explanation for the rating change (for moderation log)
20902190
example: "Updated due to new interactive content"
20912191

2192+
UpdateRankingBody:
2193+
type: object
2194+
required:
2195+
- ranking
2196+
properties:
2197+
ranking:
2198+
type: number
2199+
nullable: true
2200+
description: |
2201+
Ranking score for ordering places. Higher values appear first in listings
2202+
(after highlighted places). Set to null to remove ranking.
2203+
example: 0.85
2204+
20922205
UpdateHighlightBody:
20932206
type: object
20942207
required:

src/api/CommsGatekeeper.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ export default class CommsGatekeeper extends API {
5656
return cached.addresses
5757
}
5858

59-
const { signal, abort } = new AbortController()
60-
const fetchOptions = new Options({ signal })
59+
const controller = new AbortController()
60+
const fetchOptions = new Options({ signal: controller.signal })
6161

6262
const timeoutId = setTimeout(() => {
63-
abort()
63+
controller.abort()
6464
}, Time.Second * 10)
6565

6666
try {
@@ -107,11 +107,11 @@ export default class CommsGatekeeper extends API {
107107
return cached.addresses
108108
}
109109

110-
const { signal, abort } = new AbortController()
111-
const fetchOptions = new Options({ signal })
110+
const controller = new AbortController()
111+
const fetchOptions = new Options({ signal: controller.signal })
112112

113113
const timeoutId = setTimeout(() => {
114-
abort()
114+
controller.abort()
115115
}, Time.Second * 10)
116116

117117
try {

src/entities/Place/routes/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { getPlaceList } from "./getPlaceList"
88
import { getPlaceListById } from "./getPlaceListById"
99
import { getPlaceStatusListById } from "./getPlaceStatusListById"
1010
import { updateHighlight } from "./updateHighlight"
11+
import { updateRanking } from "./updateRanking"
1112
import { updateRating } from "./updateRating"
1213

1314
export const DECENTRALAND_URL = env("DECENTRALAND_URL", "")
@@ -34,8 +35,8 @@ export default routes((router) => {
3435
router.get("/places", getPlaceList)
3536
router.post("/places", getPlaceListById)
3637
router.put("/places/:place_id/rating", updateRating)
38+
router.put("/places/:place_id/ranking", updateRanking)
3739
router.put("/places/:place_id/highlight", updateHighlight)
3840
router.get("/places/:place_id/categories", getPlaceCategories)
39-
router.put("/places/:place_id/rating", updateRating)
4041
router.post("/places/status", getPlaceStatusListById)
4142
}, {})

0 commit comments

Comments
 (0)