[api] stub out progression event record

This commit is contained in:
Devin Zuczek
2026-10-15 13:38:31 -04:00
parent 2a9ff1337a
commit e225a8a2f1
3 changed files with 91 additions and 3 deletions
+25 -3
View File
@@ -310,6 +310,22 @@ export const ProgressionDto = z.object({
XP: z.int(),
})
/**
* The caller's progress through one progression event (`/api/progressionEvents/record/:id`).
* A stub for now: every counter is 0 and nothing is collected.
*/
export const ProgressionEventRecordDto = z.object({
AccountId: z.int().describe('The caller'),
ProgressionEventId: z.int().describe('The event requested'),
Xp: z.int(),
GameMinutesToday: z.int(),
RewardsCollected: z.int(),
BonusRewardsCollected: z.int(),
XpBoostLastPurchasedAt: z.string().nullable().describe('ISO-8601 UTC; null when never bought'),
PremiumRewardsCollected: z.int(),
LimitedRewardCollectionState: z.int(),
})
/**
* The form body of `POST /api/PlayerCheer/v1/create`. Nothing here is stored beyond the
* counter the cheer increments: `Anonymous` is spent on the notification it triggers and
@@ -567,7 +583,9 @@ export const InventionV9Dto = z.object({
Name: z.string(),
Description: z.string(),
ImageName: z.string(),
UgcVersion: z.int().describe('The UGC format the blob was written in; 1 when the save named none'),
UgcVersion: z
.int()
.describe('The UGC format the blob was written in; 1 when the save named none'),
CurrentVersionNumber: z.int(),
LatestVersionNumber: z.int().describe('The same as CurrentVersionNumber on a fresh save'),
Accessibility: z.int(),
@@ -760,7 +778,9 @@ export const PublishInventionRequest = z.object({
.int()
.nullable()
.optional()
.describe('Public 1 publishes it into the store; Unlisted 2 publishes it but keeps it out of search and the feeds'),
.describe(
'Public 1 publishes it into the store; Unlisted 2 publishes it but keeps it out of search and the feeds'
),
Price: z
.int()
.nullable()
@@ -1359,7 +1379,9 @@ export const PlayerEventsAll = z.object({
export const PlayerPublicEvents = z.object({
Created: z.array(PlayerEventBaseDto).describe('Public events the player created'),
Responses: z
.array(z.object({ PlayerEvent: PlayerEventBaseDto, PlayerEventResponse: PlayerEventResponseDto }))
.array(
z.object({ PlayerEvent: PlayerEventBaseDto, PlayerEventResponse: PlayerEventResponseDto })
)
.describe('Public events the player is attending, each beside their Going RSVP'),
})
+40
View File
@@ -41,6 +41,7 @@ import {
JsonArray,
JsonObject,
ProgressionDto,
ProgressionEventRecordDto,
ReputationDto,
SetSelectedCheerRequest,
UNAUTHORIZED_RESPONSE,
@@ -632,3 +633,42 @@ export const progressionRoutes = new Hono<App>({ strict: false })
return c.body(event, 200, { 'Content-Type': 'application/json' })
}
)
// The caller's progress through one progression event — the XP, minutes and rewards the
// client draws its event track from.
//
// STUB: every counter 0, for any event id, echoing the id requested. Nothing tracks
// progress through an event yet (see `/active` above). Auth-gated because the record is
// the CALLER's and the path names no player.
.get(
'/api/progressionEvents/record/:id{[0-9]+}',
describeRoute({
tags: ['Progression'],
summary: 'The caller’s progress through a progression event (stub)',
description:
'The caller’s record for the event with this `ProgressionEventId`. Always empty ' +
'for now — 0 XP, nothing collected, `XpBoostLastPurchasedAt` null — for any id: ' +
'nothing on this server tracks progress through an event yet.',
security: AUTHED,
parameters: [idParam('id', 'The `ProgressionEventId`')],
responses: {
200: json(ProgressionEventRecordDto, 'An empty record for the event requested'),
401: UNAUTHORIZED_RESPONSE,
},
}),
async (c) => {
const id = await authedId(c)
if (id === null) return unauthorized(c)
return c.json({
AccountId: id,
ProgressionEventId: Number(c.req.param('id')),
Xp: 0,
GameMinutesToday: 0,
RewardsCollected: 0,
BonusRewardsCollected: 0,
XpBoostLastPurchasedAt: null,
PremiumRewardsCollected: 0,
LimitedRewardCollectionState: 0,
})
}
)
+26
View File
@@ -2119,6 +2119,31 @@ describe('public endpoints', () => {
expect(missing.status).toBe(404)
})
test('GET /api/progressionEvents/record/:id is an empty record for the caller', async () => {
const url = `${ORIGIN}/api/progressionEvents/record/18`
expect((await exports.default.fetch(url)).status).toBe(401)
const res = await exports.default.fetch(url, { headers: await bearer('2') })
expect(res.status).toBe(200)
expect(await res.json()).toStrictEqual({
AccountId: 2,
ProgressionEventId: 18,
Xp: 0,
GameMinutesToday: 0,
RewardsCollected: 0,
BonusRewardsCollected: 0,
XpBoostLastPurchasedAt: null,
PremiumRewardsCollected: 0,
LimitedRewardCollectionState: 0,
})
// The id is echoed, whichever event is asked for.
const other = await exports.default.fetch(`${ORIGIN}/api/progressionEvents/record/7`, {
headers: await bearer('2'),
})
expect(await other.json()).toMatchObject({ ProgressionEventId: 7 })
})
test('GET /api/rooms/v1/filters returns an object with filter arrays', async () => {
const res = await exports.default.fetch(`${ORIGIN}/api/rooms/v1/filters`)
expect(res.status).toBe(200)
@@ -10221,6 +10246,7 @@ describe('openapi', () => {
'GET /api/players/v2/progression/bulk',
'GET /api/progressionEvents/active',
'GET /api/progressionEvents/event/{id}',
'GET /api/progressionEvents/record/{id}',
'GET /api/quickPlay/v1/getandclear',
'GET /api/referee/files',
'GET /api/relationships/mutualfriends',