mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 15:09:18 -07:00
[econ] stub active consumable endpoint (?)
This commit is contained in:
@@ -43,6 +43,7 @@ missing/invalid). `~` = optional auth: served to anyone, personalised for a vali
|
|||||||
| POST | `/api/settings/v2/set` | ✓ | Persist settings (accept-and-ack) |
|
| POST | `/api/settings/v2/set` | ✓ | Persist settings (accept-and-ack) |
|
||||||
| GET | `/api/consumables/v2/getUnlocked` | ✓ | Unlocked consumables |
|
| GET | `/api/consumables/v2/getUnlocked` | ✓ | Unlocked consumables |
|
||||||
| POST | `/api/consumables/v1/consume` | ✓ | Consume an owned consumable |
|
| POST | `/api/consumables/v1/consume` | ✓ | Consume an owned consumable |
|
||||||
|
| POST | `/api/consumables/v1/updateActive` | ✓ | Mark a consumable active (stub, empty) |
|
||||||
| GET | `/api/storefronts/v4/balance/:currencyType` | ✓ | Currency balance |
|
| GET | `/api/storefronts/v4/balance/:currencyType` | ✓ | Currency balance |
|
||||||
| GET | `/api/storefronts/v3/giftdropstore/:id` | | Gift-drop storefront catalog |
|
| GET | `/api/storefronts/v3/giftdropstore/:id` | | Gift-drop storefront catalog |
|
||||||
| POST | `/api/storefronts/v2/buyItem` | ✓ | Buy a storefront item |
|
| POST | `/api/storefronts/v2/buyItem` | ✓ | Buy a storefront item |
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ import {
|
|||||||
UgcPurchasableBulkRequest,
|
UgcPurchasableBulkRequest,
|
||||||
UgcPurchasableItemList,
|
UgcPurchasableItemList,
|
||||||
UNAUTHORIZED_RESPONSE,
|
UNAUTHORIZED_RESPONSE,
|
||||||
|
UpdateActiveConsumableRequest,
|
||||||
UpdateObjectiveRequest,
|
UpdateObjectiveRequest,
|
||||||
UpdateObjectiveResponse,
|
UpdateObjectiveResponse,
|
||||||
UpdateRoomCurrencyRequest,
|
UpdateRoomCurrencyRequest,
|
||||||
@@ -4859,6 +4860,54 @@ const app = new Hono<App>({ strict: false })
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// STUB. The client posts `{ Id, IsActive, ActivatedByRoomie }` when a consumable starts
|
||||||
|
// or stops being used — `Id` is the consumable row id, as on `/consume`. Nothing is
|
||||||
|
// stored: the body is read only to log it, and the client ignores the response entirely,
|
||||||
|
// so none is sent.
|
||||||
|
//
|
||||||
|
// What this most likely means: the consumable is IN USE, and marking it so is what keeps
|
||||||
|
// a player from reusing one before it is over (a second Confetti Cannon while the first
|
||||||
|
// is still firing), and `ActivatedByRoomie` says a roommate set it off rather than its
|
||||||
|
// owner. That is a guess from the field names, not observed behaviour, so no row flips
|
||||||
|
// and `/consume` is unchanged. When the client's reading of it is known, this is where
|
||||||
|
// an `is_active` flag on the consumable row (and a refusal on `/consume`) would go.
|
||||||
|
.post(
|
||||||
|
'/api/consumables/v1/updateActive',
|
||||||
|
describeRoute({
|
||||||
|
tags: ['Consumables'],
|
||||||
|
summary: 'Mark a consumable active or inactive (stub)',
|
||||||
|
description: [
|
||||||
|
'STUB. Accepts `{ Id, IsActive, ActivatedByRoomie }` — the client posting that one',
|
||||||
|
'of its consumables has started or stopped being used — and stores nothing. The',
|
||||||
|
'client ignores the response entirely, so this answers an empty 200.',
|
||||||
|
'',
|
||||||
|
'Believed to mark the consumable as IN USE so it cannot be reused before it is over,',
|
||||||
|
'with `ActivatedByRoomie` saying a roommate set it off; that is a guess from the field',
|
||||||
|
'names, and nothing acts on it yet. `/consume` is unaffected.',
|
||||||
|
].join(' '),
|
||||||
|
security: AUTHED,
|
||||||
|
requestBody: jsonBody(UpdateActiveConsumableRequest, 'The consumable and its new state'),
|
||||||
|
responses: {
|
||||||
|
200: { description: 'Empty body — the client ignores the response entirely' },
|
||||||
|
401: UNAUTHORIZED_RESPONSE,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const id = await authedId(c)
|
||||||
|
if (id === null) return unauthorized(c)
|
||||||
|
const body = await c.req
|
||||||
|
.json<Record<string, unknown>>()
|
||||||
|
.catch(() => ({}) as Record<string, unknown>)
|
||||||
|
logger.info('consumable updateActive (stub, not stored)', {
|
||||||
|
accountId: id,
|
||||||
|
consumableId: body.Id,
|
||||||
|
isActive: body.IsActive,
|
||||||
|
activatedByRoomie: body.ActivatedByRoomie,
|
||||||
|
})
|
||||||
|
return c.body(null, 200)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Currency balance. [Authorize]. The trailing int is a CurrencyType — the client
|
// Currency balance. [Authorize]. The trailing int is a CurrencyType — the client
|
||||||
// fetches `/balance/2` (RecCenterTokens) on load. Backed by the `balance` table; a
|
// fetches `/balance/2` (RecCenterTokens) on load. Backed by the `balance` table; a
|
||||||
// player who has never been granted gets their starting balance on this first read.
|
// player who has never been granted gets their starting balance on this first read.
|
||||||
|
|||||||
@@ -1257,6 +1257,19 @@ export const ConsumeConsumableRequest = z.object({
|
|||||||
DeltaCount: z.int().optional().describe('How many to spend; defaults to 1'),
|
DeltaCount: z.int().optional().describe('How many to spend; defaults to 1'),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `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.
|
||||||
|
*/
|
||||||
|
export const UpdateActiveConsumableRequest = z.object({
|
||||||
|
Id: z.int().describe('The consumable row id'),
|
||||||
|
IsActive: z.boolean().describe('Whether the consumable is now in use'),
|
||||||
|
ActivatedByRoomie: z
|
||||||
|
.boolean()
|
||||||
|
.optional()
|
||||||
|
.describe('Whether a roommate, rather than the owner, activated it'),
|
||||||
|
})
|
||||||
|
|
||||||
/** `POST /api/avatar/v2/gifts/consume` form body (posted with a trailing slash). */
|
/** `POST /api/avatar/v2/gifts/consume` form body (posted with a trailing slash). */
|
||||||
export const ConsumeGiftRequest = z.object({
|
export const ConsumeGiftRequest = z.object({
|
||||||
Id: z.string().describe('The gift-box id to open'),
|
Id: z.string().describe('The gift-box id to open'),
|
||||||
|
|||||||
@@ -3578,6 +3578,30 @@ describe('econ endpoints', () => {
|
|||||||
expect(await res.json()).toEqual([])
|
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 }
|
||||||
|
|
||||||
|
const anon = await exports.default.fetch(`${ORIGIN}/api/consumables/v1/updateActive`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
})
|
||||||
|
expect(anon.status).toBe(401)
|
||||||
|
|
||||||
|
const res = await exports.default.fetch(`${ORIGIN}/api/consumables/v1/updateActive`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { ...(await bearer('314')), 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
})
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
// No body at all: the client ignores the response entirely.
|
||||||
|
expect(await res.text()).toBe('')
|
||||||
|
// Nothing is stored yet.
|
||||||
|
const after = await env.DB.prepare('SELECT * FROM consumable ORDER BY id').all()
|
||||||
|
expect(after.results).toEqual(before.results)
|
||||||
|
})
|
||||||
|
|
||||||
test('POST /api/consumables/v1/consume reduces the count and deletes the row at zero', async () => {
|
test('POST /api/consumables/v1/consume reduces the count and deletes the row at zero', async () => {
|
||||||
// Seed account 313 with two Supreme Pizza instances (counts 3 and 1).
|
// Seed account 313 with two Supreme Pizza instances (counts 3 and 1).
|
||||||
await grantConsumable(env.DB, 313, 'Supreme Pizza', 3)
|
await grantConsumable(env.DB, 313, 'Supreme Pizza', 3)
|
||||||
@@ -6991,6 +7015,7 @@ describe('econ endpoints', () => {
|
|||||||
'POST /api/checklist/v1/complete',
|
'POST /api/checklist/v1/complete',
|
||||||
'POST /api/checklist/v2/complete',
|
'POST /api/checklist/v2/complete',
|
||||||
'POST /api/consumables/v1/consume',
|
'POST /api/consumables/v1/consume',
|
||||||
|
'POST /api/consumables/v1/updateActive',
|
||||||
'POST /api/equipment/v1/update',
|
'POST /api/equipment/v1/update',
|
||||||
'POST /api/gamerewards/v1/request',
|
'POST /api/gamerewards/v1/request',
|
||||||
'POST /api/itemWishlists/v1/isonwishlist/bulk',
|
'POST /api/itemWishlists/v1/isonwishlist/bulk',
|
||||||
|
|||||||
Reference in New Issue
Block a user