mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
stub the checklist endpoint
This commit is contained in:
@@ -18,6 +18,7 @@ import weeklyChallenge from '../static/weekly-challenge.json'
|
|||||||
import { getAvatar, setAvatar } from './avatar-db'
|
import { getAvatar, setAvatar } from './avatar-db'
|
||||||
import {
|
import {
|
||||||
ALL_PLATFORMS,
|
ALL_PLATFORMS,
|
||||||
|
CurrencyType,
|
||||||
DEFAULT_STARTING_TOKENS,
|
DEFAULT_STARTING_TOKENS,
|
||||||
getBalance,
|
getBalance,
|
||||||
isSpendable,
|
isSpendable,
|
||||||
@@ -40,7 +41,9 @@ import {
|
|||||||
BuyItemResponse,
|
BuyItemResponse,
|
||||||
ChallengeProgressRequest,
|
ChallengeProgressRequest,
|
||||||
ChallengeProgressResponse,
|
ChallengeProgressResponse,
|
||||||
|
ChecklistCompleteResponse,
|
||||||
ChecklistEntry,
|
ChecklistEntry,
|
||||||
|
CompleteChecklistRequest,
|
||||||
ConsumeConsumableRequest,
|
ConsumeConsumableRequest,
|
||||||
ConsumeEnvelope,
|
ConsumeEnvelope,
|
||||||
ConsumeGiftRequest,
|
ConsumeGiftRequest,
|
||||||
@@ -363,6 +366,9 @@ const DEFAULT_CHECKLIST = [
|
|||||||
{ Order: 4, Objective: 6, Count: 1, CreditAmount: 25 }, // CheerAPlayer
|
{ Order: 4, Objective: 6, Count: 1, CreditAmount: 25 }, // CheerAPlayer
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/** The `UpdateResponse` context a checklist reward is reported under. */
|
||||||
|
const CHECKLIST_REWARD_CONTEXT = 303
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A concise `describeRoute` spec for a route that serves an opaque JSON array — either
|
* A concise `describeRoute` spec for a route that serves an opaque JSON array — either
|
||||||
* a static catalog served verbatim or an empty-list stub. `auth` adds the bearer
|
* a static catalog served verbatim or an empty-list stub. `auth` adds the bearer
|
||||||
@@ -579,6 +585,44 @@ const app = new Hono<App>({ strict: false })
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Mark a checklist row done. [Authorize]. Stubbed: there is no objective-progress
|
||||||
|
// table to record the completion in, and no reward ledger to make the 25-token grant
|
||||||
|
// once-only — without one, re-posting the same row would mint tokens indefinitely, so
|
||||||
|
// we grant nothing and report a change of 0. The envelope is still the balance-update
|
||||||
|
// shape the client parses, so the flow completes instead of erroring.
|
||||||
|
.on(
|
||||||
|
'POST',
|
||||||
|
['/api/checklist/v1/complete', '/api/checklist/v2/complete'],
|
||||||
|
describeRoute({
|
||||||
|
tags: ['Econ'],
|
||||||
|
summary: 'Complete a checklist row (stub)',
|
||||||
|
description:
|
||||||
|
'Marks a NUX checklist row done. Stubbed: nothing records the completion (no ' +
|
||||||
|
'objective-progress table) and nothing is granted — a reward is worth 25 XP and 25 ' +
|
||||||
|
'tokens, but making that once-only needs a ledger we do not have, and without one ' +
|
||||||
|
're-posting the same row would mint tokens indefinitely. The response is still the ' +
|
||||||
|
'balance-update envelope, with `Balance` (the change) 0. v1 and v2 behave alike.',
|
||||||
|
security: AUTHED,
|
||||||
|
requestBody: jsonBody(CompleteChecklistRequest, 'Which row was completed — `{ ItemIndex }`'),
|
||||||
|
responses: {
|
||||||
|
200: json(ChecklistCompleteResponse, 'The balance-update envelope, granting nothing'),
|
||||||
|
401: UNAUTHORIZED_RESPONSE,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
async (c) => {
|
||||||
|
const id = await authedId(c)
|
||||||
|
if (id === null) return unauthorized(c)
|
||||||
|
// The body names the row (`{ ItemIndex: 1 }`, or `Id` as a fallback) — read only
|
||||||
|
// once there is somewhere to record it.
|
||||||
|
return c.json({
|
||||||
|
BalanceUpdates: [{ UpdateResponse: CHECKLIST_REWARD_CONTEXT, Data: [] }],
|
||||||
|
Balance: 0,
|
||||||
|
CurrencyType: CurrencyType.RecCenterTokens,
|
||||||
|
BalanceType: -2,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// The player's item wishlist. [Authorize]; empty without a DB binding.
|
// The player's item wishlist. [Authorize]; empty without a DB binding.
|
||||||
.get(
|
.get(
|
||||||
'/api/itemWishlists/v1/wishlist/me',
|
'/api/itemWishlists/v1/wishlist/me',
|
||||||
|
|||||||
@@ -115,6 +115,28 @@ export const AvatarItemV4Dto = z.object({
|
|||||||
isBaseAvatarItem: z.boolean(),
|
isBaseAvatarItem: z.boolean(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `POST /api/checklist/v1|v2/complete` JSON body — which checklist row was finished.
|
||||||
|
* The client posts just `{ "ItemIndex": 1 }`; `Id` is the fallback key read when
|
||||||
|
* `ItemIndex` is absent or 0.
|
||||||
|
*/
|
||||||
|
export const CompleteChecklistRequest = z.object({
|
||||||
|
ItemIndex: z.int().describe('The row’s index — what the client actually sends'),
|
||||||
|
Id: z.int().optional().describe('Fallback row id, read when ItemIndex is absent or 0'),
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `POST /api/checklist/v1|v2/complete` — the balance-update envelope, the same shape
|
||||||
|
* buyItem answers with. `Balance` is the CHANGE applied, so a stubbed (ungranted)
|
||||||
|
* completion reports 0. `UpdateResponse` 303 is the checklist-reward context.
|
||||||
|
*/
|
||||||
|
export const ChecklistCompleteResponse = z.object({
|
||||||
|
BalanceUpdates: z.array(z.object({ UpdateResponse: z.int(), Data: z.array(JsonObject) })),
|
||||||
|
Balance: z.int().describe('The change applied — 0 while completion is stubbed'),
|
||||||
|
CurrencyType: z.int(),
|
||||||
|
BalanceType: z.int().describe('-2 = account-wide'),
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* One row of the new-user checklist (`GET /api/checklist/v1|v2/current`). `Objective` is
|
* One row of the new-user checklist (`GET /api/checklist/v1|v2/current`). `Objective` is
|
||||||
* an `ObjectiveType` ordinal the client matches its own progress events against.
|
* an `ObjectiveType` ordinal the client matches its own progress events against.
|
||||||
|
|||||||
@@ -311,6 +311,37 @@ describe('econ endpoints', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('POST /api/checklist/v1|v2/complete 401s without a token, grants nothing with one', async () => {
|
||||||
|
for (const path of ['/api/checklist/v1/complete', '/api/checklist/v2/complete']) {
|
||||||
|
const anon = await exports.default.fetch(`${ORIGIN}${path}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ ItemIndex: 1 }),
|
||||||
|
})
|
||||||
|
expect(anon.status).toBe(401)
|
||||||
|
|
||||||
|
const res = await exports.default.fetch(`${ORIGIN}${path}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { ...(await bearer('33')), 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ ItemIndex: 1 }),
|
||||||
|
})
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
expect(await res.json()).toEqual({
|
||||||
|
BalanceUpdates: [{ UpdateResponse: 303, Data: [] }],
|
||||||
|
Balance: 0,
|
||||||
|
CurrencyType: 2,
|
||||||
|
BalanceType: -2,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stubbed, so completing rows does not move the balance — re-posting cannot farm
|
||||||
|
// tokens, and the checklist still lists every row.
|
||||||
|
const bal = await exports.default.fetch(`${ORIGIN}/api/storefronts/v4/balance/2`, {
|
||||||
|
headers: await bearer('33'),
|
||||||
|
})
|
||||||
|
expect(await bal.json()).toEqual([{ CurrencyType: 2, Platform: -2, Balance: 10000 }])
|
||||||
|
})
|
||||||
|
|
||||||
test('GET /api/itemWishlists/v1/wishlist/me 401s without a token, returns [] with one', async () => {
|
test('GET /api/itemWishlists/v1/wishlist/me 401s without a token, returns [] with one', async () => {
|
||||||
const anon = await exports.default.fetch(`${ORIGIN}/api/itemWishlists/v1/wishlist/me`)
|
const anon = await exports.default.fetch(`${ORIGIN}/api/itemWishlists/v1/wishlist/me`)
|
||||||
expect(anon.status).toBe(401)
|
expect(anon.status).toBe(401)
|
||||||
@@ -1223,6 +1254,8 @@ describe('econ endpoints', () => {
|
|||||||
'POST /api/avatar/v3/saved/set',
|
'POST /api/avatar/v3/saved/set',
|
||||||
'POST /api/avatar/v4/saved/set',
|
'POST /api/avatar/v4/saved/set',
|
||||||
'POST /api/challenge/v2/updateProgress',
|
'POST /api/challenge/v2/updateProgress',
|
||||||
|
'POST /api/checklist/v1/complete',
|
||||||
|
'POST /api/checklist/v2/complete',
|
||||||
'POST /api/consumables/v1/consume',
|
'POST /api/consumables/v1/consume',
|
||||||
'POST /api/gamerewards/v1/request',
|
'POST /api/gamerewards/v1/request',
|
||||||
'POST /api/objectives/v1/cleargroup',
|
'POST /api/objectives/v1/cleargroup',
|
||||||
|
|||||||
Reference in New Issue
Block a user