From 94b106c12f1f0db1304e4e7f0f9ecd2c3e6effa7 Mon Sep 17 00:00:00 2001 From: Devin Zuczek Date: Wed, 14 Oct 2026 00:00:24 -0400 Subject: [PATCH] [econ] stub for consumables that do not exist here --- apps/econ/src/econ.app.ts | 29 ++++++++++++++++++++++ apps/econ/src/openapi.ts | 11 ++++++++ apps/econ/src/test/integration/api.test.ts | 14 +++++++++++ 3 files changed, 54 insertions(+) diff --git a/apps/econ/src/econ.app.ts b/apps/econ/src/econ.app.ts index f15c35d..0ca8c65 100644 --- a/apps/econ/src/econ.app.ts +++ b/apps/econ/src/econ.app.ts @@ -134,6 +134,7 @@ import { PurchaseRoomCurrencyRequest, PurchaseRoomKeyWithCurrencyRequest, PurchaseRoomKeyWithCurrencyResponse, + QueryConsumablesRequest, ReferralProgressResponse, RemoveInfluencerBody, RoomConsumableDto, @@ -4992,6 +4993,34 @@ const app = new Hono({ strict: false }) (c) => c.json(consumableDefinitions) ) + // STUB. The client's lookup of consumable definitions by descriptor + // (`?configurationVersion=2`), form-encoded with one repeated key: + // `descriptors=xfeDIDboPES-AS90CCn2xQ&descriptors=test_bad`. The answer is an array of + // definitions — `{ ConsumableItemDesc, InitialCount, CreatedAt, ThumbnailImageName, + // FriendlyName, RoomieCategory, ConsumableToolName, ConfigurationData }`, with + // `ConfigurationData` JSON escaped into a string as on `/all` — holding only the + // descriptors that resolve. Nothing here can resolve one yet, so every query answers []. + .post( + '/api/consumables/v1/query/bulk', + describeRoute({ + tags: ['Consumables'], + summary: 'Look up consumable definitions by descriptor (stub)', + description: [ + 'STUB. Takes a form body repeating `descriptors` once per consumable descriptor and', + 'answers the definitions that resolve. There is no lookup behind it yet, so the', + 'answer is always []. configurationVersion is ignored.', + ].join(' '), + requestBody: form(QueryConsumablesRequest, 'The descriptors to look up'), + responses: { 200: json(JsonArray, 'The matching definitions (always empty for now)') }, + }), + async (c) => { + const body = await c.req.parseBody({ all: true }).catch(() => ({}) as Record) + const descriptors = body['descriptors'] + logger.info('consumable query/bulk (stub, nothing resolved)', { descriptors }) + return c.json([]) + } + ) + // Unlocked consumables. [Authorize]. The consumables the player has bought (from // `buyItem`, stored in the `consumable` table), grouped by item into the client's // unlocked-consumable DTO. A player who has bought none gets an empty list. diff --git a/apps/econ/src/openapi.ts b/apps/econ/src/openapi.ts index 8e2f6d8..5e1bcca 100644 --- a/apps/econ/src/openapi.ts +++ b/apps/econ/src/openapi.ts @@ -1299,6 +1299,17 @@ export const ConsumeConsumableRequest = z.object({ DeltaCount: z.int().optional().describe('How many to spend; defaults to 1'), }) +/** + * `POST /api/consumables/v1/query/bulk` form body — `descriptors` repeated once per + * consumable to look up. + */ +export const QueryConsumablesRequest = z.object({ + descriptors: z + .union([z.string(), z.array(z.string())]) + .optional() + .describe('A ConsumableItemDesc; the key repeats for each one asked about'), +}) + /** * `POST /api/consumables/v1/updateActive` JSON body — the client marking one of its * consumables active or not. Accepted and NOT acted on yet; see the route. diff --git a/apps/econ/src/test/integration/api.test.ts b/apps/econ/src/test/integration/api.test.ts index 9474941..d456c01 100644 --- a/apps/econ/src/test/integration/api.test.ts +++ b/apps/econ/src/test/integration/api.test.ts @@ -3798,6 +3798,19 @@ describe('econ endpoints', () => { expect(await res.json()).toEqual([]) }) + test('POST /api/consumables/v1/query/bulk is a stub: resolves nothing, answers []', async () => { + const res = await exports.default.fetch( + `${ORIGIN}/api/consumables/v1/query/bulk?configurationVersion=2`, + { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: 'descriptors=xfeDIDboPES-AS90CCn2xQ&descriptors=test_bad', + } + ) + expect(res.status).toBe(200) + expect(await res.json()).toEqual([]) + }) + test('POST /api/consumables/v1/updateActive is a stub: auth-gated, stores nothing, answers nothing', async () => { const before = await env.DB.prepare('SELECT * FROM consumable ORDER BY id').all() const body = { Id: 7550, IsActive: true, ActivatedByRoomie: false } @@ -7391,6 +7404,7 @@ describe('econ endpoints', () => { 'POST /api/checklist/v1/complete', 'POST /api/checklist/v2/complete', 'POST /api/consumables/v1/consume', + 'POST /api/consumables/v1/query/bulk', 'POST /api/consumables/v1/updateActive', 'POST /api/distanceMovedRewardTracks/distance', 'POST /api/equipment/v1/update',