Skip to content

Commit

Permalink
feat: add endpoint for leaderboards (#839)
Browse files Browse the repository at this point in the history
* feat: add endpoint for leaderboards

* refactor: return User and Space object, instead of ID

* feat: add `proposal_count` and `vote_count` to User

* fix: rename column for consistency

* fix: fix collation error

* fix: fix wrong filter field name

* fix: update schema

* refactor: return space and user as simple string

* fix: add `lastVote` to User queries

* fix: remove obsolete BINARY casting

* fix: remove duplicate definition

* fix: format `user` input as address
  • Loading branch information
wa0x6e authored Jul 12, 2024
1 parent 5b3c96e commit 53827e1
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 6 deletions.
4 changes: 3 additions & 1 deletion src/graphql/operations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import vp from './vp';
import messages from './messages';
import ranking from './ranking';
import roles from './roles';
import leaderboards from './leaderboards';

export default {
space,
Expand All @@ -45,5 +46,6 @@ export default {
statement,
vp,
messages,
roles
roles,
leaderboards
};
45 changes: 45 additions & 0 deletions src/graphql/operations/leaderboards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import db from '../../helpers/mysql';
import log from '../../helpers/log';
import { buildWhereQuery, checkLimits } from '../helpers';
import { capture } from '@snapshot-labs/snapshot-sentry';

export default async function (parent, args) {
const { first, skip, where = {} } = args;

checkLimits(args, 'leaderboards');

const fields = {
user: 'EVMAddress',
space: 'string',
vote_count: 'number',
proposal_count: 'number'
};
const whereQuery = buildWhereQuery(fields, 'l', where);
const defaultOrder = 'votesCount DESC, proposalsCount DESC';

const orderBy = Object.keys(fields).includes(args.orderBy)
? args.orderBy
: null;
let orderDirection = (args.orderDirection || 'desc').toUpperCase();
if (!['ASC', 'DESC'].includes(orderDirection)) orderDirection = 'DESC';

const query = `
SELECT l.*,
l.vote_count as votesCount,
l.proposal_count as proposalsCount,
l.last_vote as lastVote
FROM leaderboard l
WHERE 1=1 ${whereQuery.query}
ORDER BY ${
orderBy ? `l.${orderBy} ${orderDirection}` : defaultOrder
} LIMIT ?, ?
`;

try {
return db.queryAsync(query, [...whereQuery.params, skip, first]);
} catch (e) {
log.error(`[graphql] leaderboards, ${JSON.stringify(e)}`);
capture(e, { args });
return Promise.reject(new Error('request failed'));
}
}
11 changes: 10 additions & 1 deletion src/graphql/operations/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ import { capture } from '@snapshot-labs/snapshot-sentry';

export default async function (parent, args) {
const id = formatAddress(args.id);
const query = `SELECT u.* FROM users u WHERE id = ? LIMIT 1`;
const query = `
SELECT
u.*,
SUM(l.vote_count) as votesCount,
SUM(l.proposal_count) as proposalsCount,
MAX(l.last_vote) as lastVote
FROM users u
LEFT JOIN leaderboard l ON l.user = u.id
WHERE id = ?
LIMIT 1`;
try {
const users = await db.queryAsync(query, id);
if (users.length === 1) return formatUser(users[0]);
Expand Down
13 changes: 9 additions & 4 deletions src/graphql/operations/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,21 @@ export default async function (parent, args) {
orderDirection = orderDirection.toUpperCase();
if (!['ASC', 'DESC'].includes(orderDirection)) orderDirection = 'DESC';

let users: any[] = [];

const query = `
SELECT u.* FROM users u
SELECT
u.*,
SUM(l.vote_count) as votesCount,
SUM(l.proposal_count) as proposalsCount,
MAX(l.last_vote) as lastVote
FROM users u
INNER JOIN leaderboard l ON l.user = u.id
WHERE 1=1 ${queryStr}
GROUP BY u.id
ORDER BY ${orderBy} ${orderDirection} LIMIT ?, ?
`;
params.push(skip, first);
try {
users = await db.queryAsync(query, params);
const users = await db.queryAsync(query, params);
return users.map(user => formatUser(user));
} catch (e: any) {
log.error(`[graphql] users, ${JSON.stringify(e)}`);
Expand Down
58 changes: 58 additions & 0 deletions src/graphql/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ type Query {
orderBy: String
orderDirection: OrderDirection
): [Message]

leaderboards(
first: Int! = 20
skip: Int! = 0
where: LeaderboardsWhere
orderBy: String
orderDirection: OrderDirection
): [Leaderboard]
}

input SpaceWhere {
Expand Down Expand Up @@ -317,6 +325,18 @@ input UsersWhere {
created_gte: Int
created_lt: Int
created_lte: Int
vote_count: Int
vote_count_in: [Int]
vote_count_gt: Int
vote_count_gte: Int
vote_count_lt: Int
vote_count_lte: Int
proposal_count: Int
proposal_count_in: [Int]
proposal_count_gt: Int
proposal_count_gte: Int
proposal_count_lt: Int
proposal_count_lte: Int
}

input StatementsWhere {
Expand All @@ -336,6 +356,33 @@ input StatementsWhere {
created_lte: Int
}

input LeaderboardsWhere {
space: String
space_in: [String]
space_not: String
space_not_in: [String]
user: String
user_in: [String]
user_not: String
user_not_in: [String]
proposal_count: Int
proposal_count_in: [Int]
proposal_count_not: Int
proposal_count_not_in: [Int]
proposal_count_gt: [Int]
proposal_count_gte: [Int]
proposal_count_lt: [Int]
proposal_count_lte: [Int]
vote_count: Int
vote_count_in: [Int]
vote_count_not: Int
vote_count_not_in: [Int]
vote_count_gt: [Int]
vote_count_gte: [Int]
vote_count_lt: [Int]
vote_count_lte: [Int]
}

enum OrderDirection {
asc
desc
Expand Down Expand Up @@ -529,6 +576,9 @@ type User {
lens: String
farcaster: String
created: Int!
votesCount: Int
proposalsCount: Int
lastVote: Int
}

type Statement {
Expand Down Expand Up @@ -576,3 +626,11 @@ type Vp {
vp_by_strategy: [Float]
vp_state: String
}

type Leaderboard {
space: String
user: String
proposalsCount: Int
votesCount: Int
lastVote: Int
}

0 comments on commit 53827e1

Please sign in to comment.