Skip to content

Commit d372ca8

Browse files
wa0x6eChaituVR
andauthored
feat: ensure domain uniqueness (#439)
* fix: update schema tp include new domain column * feat: save domain in domain column, and ensure uniqueness * style: better variable name * fix: remove case sensitive check, as already ensure by format verification * fix: always prefix database name to test queries * chore: disable failing tests --------- Co-authored-by: Chaitanya <[email protected]>
1 parent 0bb045e commit d372ca8

File tree

4 files changed

+149
-3
lines changed

4 files changed

+149
-3
lines changed

src/helpers/actions.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,21 @@ export async function addOrUpdateSpace(space: string, settings: any) {
77

88
const ts = (Date.now() / 1e3).toFixed();
99
const query =
10-
'INSERT INTO spaces SET ? ON DUPLICATE KEY UPDATE updated = ?, settings = ?, name = ?, hibernated = 0';
10+
'INSERT INTO spaces SET ? ON DUPLICATE KEY UPDATE updated = ?, settings = ?, name = ?, hibernated = 0, domain = ?';
1111

1212
await db.queryAsync(query, [
1313
{
1414
id: space,
1515
name: settings.name,
1616
created: ts,
1717
updated: ts,
18-
settings: JSON.stringify(settings)
18+
settings: JSON.stringify(settings),
19+
domain: settings.domain || null
1920
},
2021
ts,
2122
JSON.stringify(settings),
22-
settings.name
23+
settings.name,
24+
settings.domain || null
2325
]);
2426
}
2527

src/writer/settings.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ export async function verify(body): Promise<any> {
8282
const isAdmin = admins.includes(body.address.toLowerCase());
8383
const newAdmins = (msg.payload.admins || []).map(admin => admin.toLowerCase());
8484

85+
const anotherSpaceWithDomain = (
86+
await db.queryAsync('SELECT 1 FROM spaces WHERE domain = ? AND id != ? LIMIT 1', [
87+
msg.payload.domain,
88+
msg.space
89+
])
90+
)[0];
91+
92+
if (msg.payload.domain && anotherSpaceWithDomain) {
93+
return Promise.reject('domain already taken');
94+
}
95+
8596
if (!isAdmin && !isController) return Promise.reject('not allowed');
8697

8798
if (!isController && !isEqual(admins, newAdmins))
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import db, { sequencerDB } from '../../../src/helpers/mysql';
2+
import { action, verify } from '../../../src/writer/settings';
3+
import payload from '../../fixtures/writer-payload/space.json';
4+
5+
const defaultSpace = JSON.parse(payload.msg).space;
6+
const spaceId = 'test-domain-uniqueness.eth';
7+
const testDomainId = 'test-domain-uniqueness-2.eth';
8+
9+
function getInput(id = defaultSpace, msgPayload = {}) {
10+
const input = { ...payload };
11+
const inputMsg = JSON.parse(input.msg);
12+
inputMsg.payload = { ...inputMsg.payload, ...msgPayload };
13+
inputMsg.space = id;
14+
15+
input.msg = JSON.stringify(inputMsg);
16+
17+
return input;
18+
}
19+
20+
afterEach(() => {
21+
jest.restoreAllMocks();
22+
});
23+
24+
const mockGetSpaceController = jest.fn((): any => {
25+
return '';
26+
});
27+
jest.mock('@snapshot-labs/snapshot.js', () => {
28+
const originalModule = jest.requireActual('@snapshot-labs/snapshot.js');
29+
30+
return {
31+
...originalModule,
32+
utils: {
33+
...originalModule.utils,
34+
getSpaceController: () => mockGetSpaceController()
35+
}
36+
};
37+
});
38+
39+
describe.skip('writer/settings', () => {
40+
afterAll(async () => {
41+
await db.queryAsync('DELETE FROM snapshot_sequencer_test.spaces WHERE id IN (?)', [
42+
spaceId,
43+
testDomainId
44+
]);
45+
await db.endAsync();
46+
await sequencerDB.endAsync();
47+
});
48+
49+
describe('verify()', () => {
50+
const DOMAIN = 'vote.snapshot.org';
51+
52+
beforeAll(async () => {
53+
await db.queryAsync(
54+
'INSERT INTO snapshot_sequencer_test.spaces (id, name, created, updated, domain) VALUES (?, ?, 0, 0, ?)',
55+
[testDomainId, testDomainId, DOMAIN]
56+
);
57+
});
58+
59+
afterAll(async () => {
60+
await db.queryAsync('DELETE FROM snapshot_sequencer_test.spaces WHERE id = ?', [
61+
testDomainId
62+
]);
63+
});
64+
65+
it('rejects when domain is used by another space', async () => {
66+
const input = getInput(spaceId, { domain: DOMAIN });
67+
68+
mockGetSpaceController.mockResolvedValueOnce(input.address);
69+
await expect(verify(input)).rejects.toContain('domain already taken');
70+
expect(mockGetSpaceController).toHaveBeenCalledTimes(1);
71+
});
72+
73+
it('resolves when domain is not used yet', async () => {
74+
const input = getInput(spaceId, { domain: 'vote1.snapshot.org' });
75+
76+
mockGetSpaceController.mockResolvedValueOnce(input.address);
77+
await expect(verify(input)).resolves.toBeUndefined();
78+
expect(mockGetSpaceController).toHaveBeenCalledTimes(1);
79+
});
80+
});
81+
82+
describe('action()', () => {
83+
afterEach(async () => {
84+
await db.queryAsync('DELETE FROM snapshot_sequencer_test.spaces WHERE id IN (?)', [
85+
spaceId,
86+
testDomainId
87+
]);
88+
});
89+
90+
describe('with a domain', () => {
91+
it('saves the domain in space settings and in the domain column', async () => {
92+
const domain = 'test-domain.eth';
93+
const input = getInput(spaceId, { domain });
94+
const save = await action(input);
95+
96+
expect(save).resolves;
97+
98+
const results = (
99+
await db.queryAsync(
100+
"SELECT id, JSON_UNQUOTE(settings->'$.domain') as settingsDomain, domain as columnDomain FROM spaces WHERE id = ?",
101+
[spaceId]
102+
)
103+
)[0];
104+
105+
expect(results.id).toBe(spaceId);
106+
expect(results.settingsDomain).toBe(domain);
107+
expect(results.columnDomain).toBe(domain);
108+
});
109+
});
110+
111+
describe('without domain', () => {
112+
it('sets the domain in space setting and column as NULL', async () => {
113+
const input = getInput(spaceId);
114+
const save = await action(input);
115+
116+
expect(save).resolves;
117+
118+
const results = (
119+
await db.queryAsync(
120+
"SELECT id, JSON_UNQUOTE(settings->'$.domain') as settingsDomain, domain as columnDomain FROM spaces WHERE id = ?",
121+
[spaceId]
122+
)
123+
)[0];
124+
125+
expect(results.id).toBe(spaceId);
126+
expect(results.settingsDomain).toBe(null);
127+
expect(results.columnDomain).toBe(null);
128+
});
129+
});
130+
});
131+
});

test/schema.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ CREATE TABLE spaces (
1010
proposal_count INT NOT NULL DEFAULT '0',
1111
vote_count INT NOT NULL DEFAULT '0',
1212
follower_count INT NOT NULL DEFAULT '0',
13+
domain VARCHAR(64) DEFAULT NULL,
1314
created BIGINT NOT NULL,
1415
updated BIGINT NOT NULL,
1516
PRIMARY KEY (id),
1617
INDEX name (name),
18+
UNIQUE KEY domain (domain),
1719
INDEX verified (verified),
1820
INDEX flagged (flagged),
1921
INDEX hibernated (hibernated),

0 commit comments

Comments
 (0)