Skip to content

Commit

Permalink
feat: API for deals tracked per allocator (#196)
Browse files Browse the repository at this point in the history
* deps: upgrade spark-api to latest

Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>

* feat: API for deals tracked per allocator

Example request:

```
GET /allocator/f03015751/deals/eligible/summary
```

Example response:

```json
{
  "allocatorId": "f03019831",
  "dealCount": 117596,
  "clients": [
    {
      "clientId": "f02056762",
      "dealCount": 64674
    },
    {
      "clientId": "f03146638",
      "dealCount": 21405
    }
  ]
}
```

Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>

---------

Signed-off-by: Miroslav Bajtoš <oss@bajtos.net>
  • Loading branch information
bajtos authored Aug 8, 2024
1 parent 12077e4 commit 1fb4ae1
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 4 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ Base URL: http://stats.filspark.com/

http://stats.filspark.com/client/f0215074/deals/eligible/summary

- `GET /allocator/:id/deals/eligible/summary`

http://stats.filspark.com/allocator/f03015751/deals/eligible/summary

- `GET /participants/daily?from=<day>&to=<day>`

http://stats.filspark.com/participants/daily
Expand Down
2 changes: 1 addition & 1 deletion db/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"dependencies": {
"pg": "^8.12.0",
"postgrator": "^7.2.0",
"spark-api": "https://github.com/filecoin-station/spark-api/archive/3de5c3325ef6cc40df02769b96957d33279d38c1.tar.gz",
"spark-api": "https://github.com/filecoin-station/spark-api/archive/7075fb55b253d48d5d5eb4846f13a3f688d80437.tar.gz",
"spark-evaluate": "filecoin-station/spark-evaluate#main"
},
"standard": {
Expand Down
5 changes: 2 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions stats/lib/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ const handler = async (req, res, pgPools) => {
await getRetrievableDealsForMiner(req, res, pgPools.api, segs[1])
} else if (req.method === 'GET' && segs[0] === 'client' && segs[1] && segs[2] === 'deals' && segs[3] === 'eligible' && segs[4] === 'summary') {
await getRetrievableDealsForClient(req, res, pgPools.api, segs[1])
} else if (req.method === 'GET' && segs[0] === 'allocator' && segs[1] && segs[2] === 'deals' && segs[3] === 'eligible' && segs[4] === 'summary') {
await getRetrievableDealsForAllocator(req, res, pgPools.api, segs[1])
} else if (await handlePlatformRoutes(req, res, pgPools)) {
// no-op, request was handled by handlePlatformRoute
} else if (req.method === 'GET' && url === '/') {
Expand Down Expand Up @@ -195,3 +197,30 @@ const getRetrievableDealsForClient = async (_req, res, client, clientId) => {
}
json(res, body)
}

const getRetrievableDealsForAllocator = async (_req, res, client, allocatorId) => {
/** @type {{rows: {client_id: string; deal_count: number}[]}} */
const { rows } = await client.query(`
SELECT ac.client_id, COUNT(cid)::INTEGER as deal_count
FROM allocator_clients ac
LEFT JOIN retrievable_deals rd ON ac.client_id = rd.client_id
WHERE ac.allocator_id = $1 AND expires_at > now()
GROUP BY ac.client_id
ORDER BY deal_count DESC, ac.client_id ASC
`, [
allocatorId
])

// Cache the response for 6 hours
res.setHeader('cache-control', `max-age=${6 * 3600}`)

const body = {
allocatorId,
dealCount: rows.reduce((sum, row) => sum + row.deal_count, 0),
clients: rows.map(
// eslint-disable-next-line camelcase
({ client_id, deal_count }) => ({ clientId: client_id, dealCount: deal_count })
)
}
json(res, body)
}
38 changes: 38 additions & 0 deletions stats/test/handler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,15 @@ describe('HTTP request handler', () => {
('bafyexpired', 'f0230', 'f0800', '2020-01-01')
ON CONFLICT DO NOTHING
`)

await pgPools.api.query(`
INSERT INTO allocator_clients (allocator_id, client_id)
VALUES
('f0500', 'f0800'),
('f0500', 'f0810'),
('f0520', 'f0820')
ON CONFLICT DO NOTHING
`)
})

describe('GET /miner/{id}/deals/eligible/summary', () => {
Expand Down Expand Up @@ -509,6 +518,35 @@ describe('HTTP request handler', () => {
})
})
})

describe('GET /allocator/{id}/deals/eligible/summary', () => {
it('returns deal counts grouped by client id', async () => {
const res = await fetch(new URL('/allocator/f0500/deals/eligible/summary', baseUrl))
await assertResponseStatus(res, 200)
assert.strictEqual(res.headers.get('cache-control'), 'max-age=21600')
const body = await res.json()
assert.deepStrictEqual(body, {
allocatorId: 'f0500',
dealCount: 6,
clients: [
{ clientId: 'f0800', dealCount: 4 },
{ clientId: 'f0810', dealCount: 2 }
]
})
})

it('returns an empty array for miners with no deals in our DB', async () => {
const res = await fetch(new URL('/allocator/f0000/deals/eligible/summary', baseUrl))
await assertResponseStatus(res, 200)
assert.strictEqual(res.headers.get('cache-control'), 'max-age=21600')
const body = await res.json()
assert.deepStrictEqual(body, {
allocatorId: 'f0000',
dealCount: 0,
clients: []
})
})
})
})

describe('GET /deals/daily', () => {
Expand Down

0 comments on commit 1fb4ae1

Please sign in to comment.