diff --git a/README.md b/README.md index 8198186..2b63578 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,11 @@ Base URL: http://stats.filspark.com/ http://stats.filspark.com/deals/daily +- `GET /deals/summary?from=2024-01-01&to=2024-01-31` + + http://stats.filspark.com/deals/summary + + ## Development ### Database diff --git a/stats/lib/handler.js b/stats/lib/handler.js index 2b18f0d..f536230 100644 --- a/stats/lib/handler.js +++ b/stats/lib/handler.js @@ -10,7 +10,8 @@ import { fetchParticipantChangeRates, fetchParticipantScheduledRewards, fetchParticipantRewardTransfers, - fetchRetrievalSuccessRate + fetchRetrievalSuccessRate, + fetchDealSummary } from './stats-fetchers.js' import { handlePlatformRoutes } from './platform-routes.js' @@ -83,6 +84,8 @@ const handler = async (req, res, pgPools) => { if (req.method === 'GET' && url === '/deals/daily') { await respond(fetchDailyDealStats) + } else if (req.method === 'GET' && url === '/deals/summary') { + await respond(fetchDealSummary) } else if (req.method === 'GET' && url === '/retrieval-success-rate') { await respond(fetchRetrievalSuccessRate) } else if (req.method === 'GET' && url === '/participants/daily') { diff --git a/stats/lib/stats-fetchers.js b/stats/lib/stats-fetchers.js index 48e7d19..2a61f5d 100644 --- a/stats/lib/stats-fetchers.js +++ b/stats/lib/stats-fetchers.js @@ -46,6 +46,24 @@ export const fetchDailyDealStats = async (pgPools, filter) => { return rows } +/** + * @param {import('@filecoin-station/spark-stats-db').PgPools} pgPools + * @param {import('./typings.js').DateRangeFilter} filter + */ +export const fetchDealSummary = async (pgPools, filter) => { + const { rows: [summary] } = await pgPools.evaluate.query(` + SELECT + SUM(total) as total, + SUM(indexed) as indexed, + SUM(retrievable) as retrievable + FROM daily_deals + WHERE day >= date_trunc('day', $1::DATE) + AND day <= date_trunc('day', $2::DATE) + `, [filter.from, filter.to] + ) + return summary +} + export const fetchDailyParticipants = async (pgPools, filter) => { return await getDailyDistinctCount({ pgPool: pgPools.evaluate, diff --git a/stats/test/handler.test.js b/stats/test/handler.test.js index 9237561..8ba3615 100644 --- a/stats/test/handler.test.js +++ b/stats/test/handler.test.js @@ -457,6 +457,57 @@ describe('HTTP request handler', () => { }) }) + describe('GET /deals/summary', () => { + it('returns deal summary for the given date range (including the end day)', async () => { + await givenDailyDealStats(pgPools.evaluate, { day: '2024-03-12', total: 200, indexed: 52, retrievable: 2 }) + // filter.to - 7 days -> should be excluded + await givenDailyDealStats(pgPools.evaluate, { day: '2024-03-23', total: 300, indexed: 53, retrievable: 3 }) + // last 7 days + await givenDailyDealStats(pgPools.evaluate, { day: '2024-03-24', total: 400, indexed: 54, retrievable: 4 }) + await givenDailyDealStats(pgPools.evaluate, { day: '2024-03-29', total: 500, indexed: 55, retrievable: 5 }) + // `filter.to` (e.g. today) - should be included + await givenDailyDealStats(pgPools.evaluate, { day: '2024-03-30', total: 6000, indexed: 600, retrievable: 60 }) + // after the requested range + await givenDailyDealStats(pgPools.evaluate, { day: '2024-03-31', total: 70000, indexed: 7000, retrievable: 700 }) + + const res = await fetch( + new URL( + '/deals/summary?from=2024-03-24&to=2024-03-30', + baseUrl + ), { + redirect: 'manual' + } + ) + await assertResponseStatus(res, 200) + const stats = await res.json() + + assert.deepStrictEqual(stats, { + total: '6900', + indexed: '709', + retrievable: '69' + }) + }) + + it('handles query for future date with no recorded stats', async () => { + const res = await fetch( + new URL( + '/deals/summary?from=3024-04-24&to=3024-03-30', + baseUrl + ), { + redirect: 'manual' + } + ) + await assertResponseStatus(res, 200) + const stats = await res.json() + + assert.deepStrictEqual(stats, { + total: null, + indexed: null, + retrievable: null + }) + }) + }) + describe('CORS', () => { it('sets CORS headers for requests from Station Desktop in production', async () => { const res = await fetch(new URL('/', baseUrl), {