Skip to content

Commit

Permalink
feat: store proposal, vote and follower count on space (#292)
Browse files Browse the repository at this point in the history
* feat: store proposal, vote and follower count on space

* fix: ensure positive counters

* fix: fix missing arg

* fix: use total number of votes, instead of number of valid votes

* fix: add init script to populate spaces counters

* chore: add tests for follow and unfollow

* Update scripts/refresh_spaces_counters.ts

Co-authored-by: Chaitanya <[email protected]>

* fix: fix proposals counter being skipped when no votes

* fix: fix missing follows_count initializer

---------

Co-authored-by: Wan Qi Chen <[email protected]>
Co-authored-by: Chaitanya <[email protected]>
  • Loading branch information
3 people authored Jul 8, 2024
1 parent daa929f commit 2869080
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 11 deletions.
40 changes: 40 additions & 0 deletions scripts/refresh_spaces_counters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'dotenv/config';
import db from '../src/helpers/mysql';

// Usage: yarn ts-node scripts/refresh_spaces_counters.ts
async function main() {
const spaces = await db.queryAsync(`SELECT id FROM spaces`);

console.log(`Found ${spaces.length} spaces`);

for (const i in spaces) {
const stats = await db.queryAsync(
`SELECT COUNT(voter) as vote_count FROM votes WHERE space = ?`,
[spaces[i].id]
);
const stat = stats[0];
await db.queryAsync(`UPDATE spaces SET vote_count = ? WHERE id = ?`, [
stat.vote_count,
spaces[i].id
]);
console.log(`${i} / ${spaces.length}`);
}

await db.queryAsync(
'UPDATE spaces SET proposal_count = (SELECT count(id) from proposals WHERE space = spaces.id)'
);

await db.queryAsync(
'UPDATE spaces SET follower_count = (SELECT count(id) from follows WHERE space = spaces.id)'
);
}

(async () => {
try {
await main();
process.exit(0);
} catch (e) {
console.error(e);
process.exit(1);
}
})();
6 changes: 5 additions & 1 deletion src/writer/delete-proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export async function verify(body): Promise<any> {
export async function action(body): Promise<void> {
const msg = jsonParse(body.msg);
const proposal = await getProposal(msg.space, msg.payload.proposal);

const voters = await db.queryAsync(`SELECT voter FROM votes WHERE proposal = ?`, [
msg.payload.proposal
]);
Expand All @@ -33,9 +34,12 @@ export async function action(body): Promise<void> {
SET proposal_count = GREATEST(proposal_count - 1, 0)
WHERE user = ? AND space = ?
LIMIT 1;
UPDATE spaces
SET proposal_count = GREATEST(proposal_count - 1, 0), vote_count = GREATEST(vote_count - ?, 0)
WHERE id = ?;
`;

const parameters = [id, id, proposal.author, msg.space];
const parameters = [id, id, proposal.author, msg.space, voters.length, msg.space];

if (voters.length > 0) {
queries += `
Expand Down
13 changes: 11 additions & 2 deletions src/writer/follow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export const getFollowsCount = async (follower: string): Promise<number> => {
};

export async function verify(message): Promise<any> {
const query = `SELECT * FROM follows WHERE follower = ? AND space = ? LIMIT 1`;
const follows = await db.queryAsync(query, [message.from, message.space]);

if (follows.length !== 0) return Promise.reject('you are already following this space');

const count = await getFollowsCount(message.from);

if (count >= FOLLOWS_LIMIT_PER_USER) {
Expand All @@ -24,7 +29,11 @@ export async function verify(message): Promise<any> {
return true;
}

export async function action(message, ipfs, receipt, id): Promise<void> {
export async function action(message, ipfs, _receipt, id): Promise<void> {
const query = `
INSERT INTO follows SET ?;
UPDATE spaces SET follower_count = follower_count + 1 WHERE id = ?;
`;
const params = {
id,
ipfs,
Expand All @@ -34,5 +43,5 @@ export async function action(message, ipfs, receipt, id): Promise<void> {
created: message.timestamp
};

await db.queryAsync('INSERT INTO follows SET ?', params);
await db.queryAsync(query, [params, message.space]);
}
7 changes: 4 additions & 3 deletions src/writer/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,10 @@ export async function action(body, ipfs, receipt, id): Promise<void> {
const query = `
INSERT INTO proposals SET ?;
INSERT INTO leaderboard (space, user, proposal_count)
VALUES(?, ?, 1)
ON DUPLICATE KEY UPDATE proposal_count = proposal_count + 1
VALUES(?, ?, 1)
ON DUPLICATE KEY UPDATE proposal_count = proposal_count + 1;
UPDATE spaces SET proposal_count = proposal_count + 1 WHERE id = ?;
`;

await db.queryAsync(query, [proposal, space, author]);
await db.queryAsync(query, [proposal, space, author, space]);
}
22 changes: 19 additions & 3 deletions src/writer/unfollow.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import db from '../helpers/mysql';
import { DEFAULT_NETWORK_ID } from '../helpers/utils';

export async function verify(): Promise<any> {
export async function verify(message): Promise<any> {
const query = `SELECT * FROM follows WHERE follower = ? AND space = ? AND network = ? LIMIT 1`;
const follows = await db.queryAsync(query, [
message.from,
message.space,
message.network || DEFAULT_NETWORK_ID
]);

if (follows.length === 0) return Promise.reject('you can only unfollow a space you follow');

return true;
}

export async function action(message): Promise<void> {
const query = 'DELETE FROM follows WHERE follower = ? AND space = ? AND network = ? LIMIT 1';
await db.queryAsync(query, [message.from, message.space, message.network || DEFAULT_NETWORK_ID]);
const query = `
DELETE FROM follows WHERE follower = ? AND space = ? AND network = ? LIMIT 1;
UPDATE spaces SET follower_count = GREATEST(follower_count - 1, 0) WHERE id = ?;`;
await db.queryAsync(query, [
message.from,
message.space,
message.network || DEFAULT_NETWORK_ID,
message.space
]);
}
5 changes: 3 additions & 2 deletions src/writer/vote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,10 @@ export async function action(body, ipfs, receipt, id, context): Promise<void> {
INSERT INTO votes SET ?;
INSERT INTO leaderboard (space, user, vote_count, last_vote)
VALUES(?, ?, 1, ?)
ON DUPLICATE KEY UPDATE vote_count = vote_count + 1, last_vote = ?
ON DUPLICATE KEY UPDATE vote_count = vote_count + 1, last_vote = ?;
UPDATE spaces SET vote_count = vote_count + 1 WHERE id = ?;
`,
[params, msg.space, voter, created, created]
[params, msg.space, voter, created, created, msg.space]
);
}

Expand Down
27 changes: 27 additions & 0 deletions test/integration/writer/follows.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { verify, action } from '../../../src/writer/follow';
import { FOLLOWS_LIMIT_PER_USER } from '../../../src/helpers/limits';
import db, { sequencerDB } from '../../../src/helpers/mysql';
import { spacesSqlFixtures } from '../../fixtures/space';

describe('writer/follow', () => {
const TEST_PREFIX = 'test-follow-';
const space = spacesSqlFixtures[1];

afterAll(async () => {
await db.queryAsync('DELETE FROM follows');
await db.queryAsync('DELETE FROM spaces WHERE id = ?', [`${TEST_PREFIX}-${space.id}`]);
await db.endAsync();
await sequencerDB.endAsync();
});
Expand Down Expand Up @@ -102,5 +107,27 @@ describe('writer/follow', () => {
]);
});
});

it('should increment the follower count of the space', async () => {
await db.queryAsync('INSERT INTO spaces SET ?', {
...space,
id: `${TEST_PREFIX}-${space.id}`,
settings: JSON.stringify(space.settings)
});

const id = '3';
const ipfs = '4';
const message = {
from: '0x4',
space: `${TEST_PREFIX}-${space.id}`,
timestamp: 1
};

await action(message, ipfs, 1, id);

return expect(
db.queryAsync('SELECT follower_count FROM spaces WHERE id = ?', [message.space])
).resolves.toEqual([{ follower_count: 1 }]);
});
});
});
84 changes: 84 additions & 0 deletions test/integration/writer/unfollows.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { verify, action } from '../../../src/writer/unfollow';
import db, { sequencerDB } from '../../../src/helpers/mysql';
import { spacesSqlFixtures } from '../../fixtures/space';

describe('writer/unfollow', () => {
const TEST_PREFIX = 'test-unfollow-';
const space = spacesSqlFixtures[0];
const followerId = '0x0';

afterAll(async () => {
await db.queryAsync('DELETE FROM follows');
await db.queryAsync('DELETE FROM spaces WHERE id LIKE ?', [`${TEST_PREFIX}-%`]);
await db.endAsync();
await sequencerDB.endAsync();
});

describe('verify()', () => {
beforeAll(async () => {
let i = 0;
const promises: Promise<any>[] = [];

while (i <= 2) {
promises.push(
db.queryAsync(
'INSERT INTO follows SET id = ?, ipfs = ?, follower = ?, space = ?, network = ?, created = ?',
[i, i, followerId, `${TEST_PREFIX}-test-${i}.eth`, 's', i]
)
);

i++;
}

await Promise.all(promises);
});

it('rejects when the user has not followed the space', () => {
return expect(verify({ from: '0x1', space: `${TEST_PREFIX}-test-2.eth` })).rejects.toEqual(
'you can only unfollow a space you follow'
);
});

it('returns true when the user has followed the space', () => {
return expect(
verify({ from: followerId, space: `${TEST_PREFIX}-test-0.eth`, network: 's' })
).resolves.toEqual(true);
});
});

describe('action()', () => {
it('should unfollow and decrement the follower count of the space', async () => {
await db.queryAsync('INSERT INTO spaces SET ?', {
...space,
id: `${TEST_PREFIX}-test-0.eth`,
settings: JSON.stringify(space.settings),
follower_count: 2
});

const message = {
from: followerId,
space: `${TEST_PREFIX}-test-0.eth`,
network: 's'
};

expect(
db.queryAsync('SELECT id FROM follows WHERE follower = ? AND space = ?', [
message.from,
message.space
])
).resolves.not.toEqual([]);

await action(message);

expect(
db.queryAsync('SELECT * FROM follows WHERE follower = ? AND space = ?', [
message.from,
message.space
])
).resolves.toEqual([]);
return expect(
db.queryAsync('SELECT follower_count FROM spaces WHERE id = ?', [message.space])
).resolves.toEqual([{ follower_count: 1 }]);
});
});
});

0 comments on commit 2869080

Please sign in to comment.