mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 23:18:51 -07:00
[inventions] cheer endpoint
This commit is contained in:
@@ -1897,6 +1897,49 @@ export const avatarRoutes = new Hono<App>({ strict: false })
|
||||
return c.json({ success: true, error: '' })
|
||||
}
|
||||
)
|
||||
// The newer client's cheer: the same body as `v1/cheer` and the same write, at a new
|
||||
// path. It answers the `{ Value, Success, Error, error_id }` envelope the other v2+
|
||||
// invention routes use, refusals in-band — an ASSUMPTION: only the request has been
|
||||
// observed, not what the client reads off the reply.
|
||||
.post(
|
||||
'/api/inventions/v2/cheer',
|
||||
describeRoute({
|
||||
tags: ['Inventions'],
|
||||
summary: 'Cheer or un-cheer an invention (v2)',
|
||||
description:
|
||||
'`v1/cheer` for the newer client: persists the caller’s cheer state and resyncs ' +
|
||||
'the invention’s `CheerCount`; repeating the same state is idempotent. Answers ' +
|
||||
'the `{ Value, Success, Error, error_id }` envelope with `Value` null, and a ' +
|
||||
'refusal — a bad body, an unknown invention — is `Success: false` with a message ' +
|
||||
'rather than an error status.',
|
||||
security: AUTHED,
|
||||
requestBody: jsonBody(InventionCheerRequest, 'The invention and new cheer state'),
|
||||
responses: {
|
||||
200: json(InventionDeleteResult, 'The envelope, `Value` null either way'),
|
||||
401: json(InventionDeleteResult, 'The same envelope, refused — not an empty body'),
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
const playerId = await authedId(c)
|
||||
if (playerId === null) return c.json(inventionDeleteResult('Unauthorized'), 401)
|
||||
const body = await c.req
|
||||
.json<{ InventionId?: unknown; Cheer?: unknown }>()
|
||||
.catch(() => ({}) as Record<string, unknown>)
|
||||
const inventionId = body.InventionId
|
||||
if (
|
||||
typeof inventionId !== 'number' ||
|
||||
!Number.isInteger(inventionId) ||
|
||||
typeof body.Cheer !== 'boolean'
|
||||
) {
|
||||
return c.json(inventionDeleteResult('InventionId and Cheer are required'))
|
||||
}
|
||||
if ((await getInventionById(c.env.DB, inventionId)) === null) {
|
||||
return c.json(inventionDeleteResult('No such invention'))
|
||||
}
|
||||
await setInventionCheer(c.env.DB, playerId, inventionId, body.Cheer)
|
||||
return c.json(inventionDeleteResult())
|
||||
}
|
||||
)
|
||||
// Report an invention. Stored in the `report` table the player and event reports use —
|
||||
// same fields, same moderation life — with `invention_id` set. See
|
||||
// migrations/0016_report_invention.sql.
|
||||
|
||||
@@ -3914,6 +3914,50 @@ describe('public endpoints', () => {
|
||||
expect(noId.status).toBe(400)
|
||||
})
|
||||
|
||||
test('POST /api/inventions/v2/cheer is v1/cheer in the newer client’s envelope', async () => {
|
||||
const saved = await exports.default.fetch(`${ORIGIN}/api/inventions/v6/save`, {
|
||||
method: 'POST',
|
||||
headers: { ...(await bearer('8210')), 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: 'Cheerable Sofa', inventionDataFilename: 'cheerable2.inv' }),
|
||||
})
|
||||
const { InventionId } = ((await saved.json()) as InventionSaveResult).Invention
|
||||
const cheer = async (body: unknown, sub?: string) =>
|
||||
exports.default.fetch(`${ORIGIN}/api/inventions/v2/cheer`, {
|
||||
method: 'POST',
|
||||
headers: { ...(sub ? await bearer(sub) : {}), 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
const stored = async (): Promise<number> =>
|
||||
(
|
||||
(await (
|
||||
await exports.default.fetch(`${ORIGIN}/api/inventions/v1?inventionId=${InventionId}`)
|
||||
).json()) as SavedInvention
|
||||
).CheerCount
|
||||
const ok = { Value: null, Success: true, Error: null, error_id: null }
|
||||
|
||||
const anonymous = await cheer({ InventionId, Cheer: true })
|
||||
expect(anonymous.status).toBe(401)
|
||||
expect(await anonymous.json()).toMatchObject({ Value: null, Success: false })
|
||||
|
||||
expect(await (await cheer({ InventionId, Cheer: true }, '44')).json()).toEqual(ok)
|
||||
expect(await (await cheer({ InventionId, Cheer: true }, '44')).json()).toEqual(ok)
|
||||
expect(await stored()).toBe(1)
|
||||
const personal = await exports.default.fetch(
|
||||
`${ORIGIN}/api/inventions/v1/personaldetails/${InventionId}`,
|
||||
{ headers: await bearer('44') }
|
||||
)
|
||||
expect(await personal.json()).toEqual({ IsCheering: true })
|
||||
|
||||
expect(await (await cheer({ InventionId, Cheer: false }, '44')).json()).toEqual(ok)
|
||||
expect(await stored()).toBe(0)
|
||||
|
||||
// Refusals stay in the envelope, at 200.
|
||||
const unknown = await cheer({ InventionId: 999999, Cheer: true }, '44')
|
||||
expect(unknown.status).toBe(200)
|
||||
expect(await unknown.json()).toMatchObject({ Success: false, Error: 'No such invention' })
|
||||
expect(await (await cheer({ InventionId }, '44')).json()).toMatchObject({ Success: false })
|
||||
})
|
||||
|
||||
test('POST /api/inventions/v1/cheer persists and personaldetails reflects it', async () => {
|
||||
const saved = await exports.default.fetch(`${ORIGIN}/api/inventions/v6/save`, {
|
||||
method: 'POST',
|
||||
@@ -10059,6 +10103,7 @@ describe('openapi', () => {
|
||||
'POST /api/inventions/v1/settags',
|
||||
'POST /api/inventions/v1/update',
|
||||
'POST /api/inventions/v1/updateprice',
|
||||
'POST /api/inventions/v2/cheer',
|
||||
'POST /api/inventions/v2/delete',
|
||||
'POST /api/inventions/v2/unpublish',
|
||||
'POST /api/inventions/v2/update',
|
||||
|
||||
Reference in New Issue
Block a user