Skip to content

Commit

Permalink
hub: Move drop requests and more to Prisma (#3114)
Browse files Browse the repository at this point in the history
This changes email_card_drop_requests code to use Prisma. Interval
calculations being used in the now-removed queries file were
mysteriously incorrect so they now use purely-numeric comparison using
epoch.

This also removes the now-empty queries directory and the WHERE clause
utilities formerly used there. There are a some db.query calls remaining
in the codebase but this should be fine indefinitely.
  • Loading branch information
backspace authored Jul 27, 2022
1 parent 35dfa40 commit 2de9dd8
Show file tree
Hide file tree
Showing 16 changed files with 298 additions and 417 deletions.
2 changes: 0 additions & 2 deletions packages/hub/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ import ChecklyWebhookRoute from './routes/checkly-webhook';
import PagerdutyIncidentsWebhookRoute from './routes/pagerduty-incidents-webhook';
import { KnownRoutes, registerRoutes } from '@cardstack/hub/routes';
import { registerServices } from '@cardstack/hub/services';
import { registerQueries } from './queries';
import EmailCardDropRouter from './services/email-card-drop-router';
import EmailCardDropRequestsRoute from './routes/email-card-drop-requests';
import EmailCardDropRequestSerializer from './services/serializers/email-card-drop-request-serializer';
Expand Down Expand Up @@ -187,7 +186,6 @@ export function createRegistry(): Registry {

registerServices(registry);
registerRoutes(registry);
registerQueries(registry);

return registry;
}
Expand Down
142 changes: 76 additions & 66 deletions packages/hub/node-tests/routes/email-card-drop-request-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ let unclaimedEoa: EmailCardDropRequest = {
requestedAt: new Date(),
};

let fakeTime = 1650440847689;
let fakeTime = 1650440847689; // 2022-04-20 07:47:27

let fakeTimeString = new Date(fakeTime).toISOString();
class FrozenClock extends Clock {
now() {
Expand Down Expand Up @@ -69,16 +70,13 @@ describe('GET /api/email-card-drop-requests', function () {
mockPrepaidCardQuantity = 50;
});

let { getContainer, getPrisma, request } = setupHub(this);
let { getPrisma, request } = setupHub(this);
let prisma: ExtendedPrismaClient;

this.beforeEach(async function () {
prisma = await getPrisma();

let emailCardDropRequestsQueries = await getContainer().lookup('email-card-drop-requests', { type: 'query' });
await emailCardDropRequestsQueries.insert(unclaimedEmailForClaimedEoa);
await emailCardDropRequestsQueries.insert(claimedEoa);
await emailCardDropRequestsQueries.insert(unclaimedEoa);
await prisma.emailCardDropRequest.createMany({ data: [unclaimedEmailForClaimedEoa, claimedEoa, unclaimedEoa] });
});

it('returns claimed as true if a known EOA has a claim timestamp for its card drop request', async function () {
Expand Down Expand Up @@ -114,16 +112,17 @@ describe('GET /api/email-card-drop-requests', function () {
// Since these values are returned as strings, quantity '50' < reservations '100' is false, checking for true number comparison
let reservationCount = 100;

let emailCardDropRequestsQueries = await getContainer().lookup('email-card-drop-requests', { type: 'query' });
let insertionTimeBeforeExpiry = new Date(fakeTime - (emailVerificationLinkExpiryMinutes / 2) * 60 * 1000);

for (let i = 0; i < reservationCount; i++) {
await emailCardDropRequestsQueries.insert({
ownerAddress: `0xother${i}`,
emailHash: `other-email-hash-${i}`,
verificationCode: 'x',
id: shortUUID.uuid(),
requestedAt: insertionTimeBeforeExpiry,
await prisma.emailCardDropRequest.create({
data: {
ownerAddress: `0xother${i}`,
emailHash: `other-email-hash-${i}`,
verificationCode: 'x',
id: shortUUID.uuid(),
requestedAt: insertionTimeBeforeExpiry,
},
});
}

Expand Down Expand Up @@ -222,26 +221,29 @@ describe('POST /api/email-card-drop-requests', function () {
mockPrepaidCardQuantity = 50;
});

let { getPrisma, getContainer, request } = setupHub(this);
let { getContainer, getPrisma, request } = setupHub(this);
let clock: Clock;
let prisma: ExtendedPrismaClient;

this.beforeEach(async function () {
clock = await getContainer().lookup('clock');
prisma = await getPrisma();
});

it('persists an email card drop request and triggers jobs', async function () {
let emailCardDropRequestsQueries = await getContainer().lookup('email-card-drop-requests', { type: 'query' });
let insertionTimeBeyondExpiry = fakeTime - emailVerificationLinkExpiryMinutes * 2 * 60 * 1000;

// Create no-longer-active reservations

for (let i = 0; i < mockPrepaidCardQuantity + 1; i++) {
await emailCardDropRequestsQueries.insert({
ownerAddress: `0xother${i}`,
emailHash: `other-email-hash-${i}`,
verificationCode: 'x',
id: shortUUID.uuid(),
requestedAt: new Date(insertionTimeBeyondExpiry),
await prisma.emailCardDropRequest.create({
data: {
ownerAddress: `0xother${i}`,
emailHash: `other-email-hash-${i}`,
verificationCode: 'x',
id: shortUUID.uuid(),
requestedAt: new Date(insertionTimeBeyondExpiry),
},
});
}

Expand Down Expand Up @@ -269,7 +271,9 @@ describe('POST /api/email-card-drop-requests', function () {
resourceId = res.body.data.id;
});

let emailCardDropRequest = (await emailCardDropRequestsQueries.query({ ownerAddress: stubUserAddress }))[0];
let emailCardDropRequest = (
await prisma.emailCardDropRequest.findManyWithExpiry({ where: { ownerAddress: stubUserAddress } }, clock)
)[0];

expect(emailCardDropRequest.ownerAddress).to.equal(stubUserAddress);
expect(emailCardDropRequest.verificationCode).to.match(verificationCodeRegex);
Expand Down Expand Up @@ -316,8 +320,6 @@ describe('POST /api/email-card-drop-requests', function () {
});

it('persists a new request for a given EOA and runs jobs if a request is present but has not been claimed', async function () {
let emailCardDropRequestsQueries = await getContainer().lookup('email-card-drop-requests', { type: 'query' });

let email = '[email protected]';
let email2 = '[email protected]';

Expand All @@ -327,15 +329,19 @@ describe('POST /api/email-card-drop-requests', function () {
let emailHash2 = crypto.createHmac('sha256', config.get('emailHashSalt')).update(email2).digest('hex');

let insertionTimeInMs = fakeTime - 60 * 1000;
await emailCardDropRequestsQueries.insert({
ownerAddress: stubUserAddress,
emailHash,
verificationCode: 'x',
id: '2850a954-525d-499a-a5c8-3c89192ad40e',
requestedAt: new Date(insertionTimeInMs),
await prisma.emailCardDropRequest.create({
data: {
ownerAddress: stubUserAddress,
emailHash,
verificationCode: 'x',
id: '2850a954-525d-499a-a5c8-3c89192ad40e',
requestedAt: new Date(insertionTimeInMs),
},
});

expect((await emailCardDropRequestsQueries.query({ ownerAddress: stubUserAddress })).length).to.equal(1);
expect(
(await prisma.emailCardDropRequest.findManyWithExpiry({ where: { ownerAddress: stubUserAddress } }, clock)).length
).to.equal(1);

const payload = {
data: {
Expand All @@ -359,8 +365,13 @@ describe('POST /api/email-card-drop-requests', function () {
resourceId = res.body.data.id;
});

let allRequests = await emailCardDropRequestsQueries.query({ ownerAddress: stubUserAddress });
let latestRequest = await emailCardDropRequestsQueries.latestRequest(stubUserAddress);
let allRequests = await prisma.emailCardDropRequest.findManyWithExpiry(
{
where: { ownerAddress: stubUserAddress },
},
clock
);
let latestRequest = await prisma.emailCardDropRequest.latestRequestForOwner(stubUserAddress, clock);

expect(allRequests.length).to.equal(2);
expect(allRequests.find((v) => v.id === '2850a954-525d-499a-a5c8-3c89192ad40e')).to.not.be.undefined;
Expand Down Expand Up @@ -398,16 +409,17 @@ describe('POST /api/email-card-drop-requests', function () {

let reservationCount = mockPrepaidCardQuantity + 1;

let emailCardDropRequestsQueries = await getContainer().lookup('email-card-drop-requests', { type: 'query' });
let insertionTimeBeforeExpiry = Date.now() - (emailVerificationLinkExpiryMinutes / 2) * 60 * 1000;
let insertionTimeBeforeExpiry = clock.now() - (emailVerificationLinkExpiryMinutes / 2) * 60 * 1000;

for (let i = 0; i < reservationCount; i++) {
await emailCardDropRequestsQueries.insert({
ownerAddress: `0xother${i}`,
emailHash: `other-email-hash-${i}`,
verificationCode: 'x',
id: shortUUID.uuid(),
requestedAt: new Date(insertionTimeBeforeExpiry),
await prisma.emailCardDropRequest.create({
data: {
ownerAddress: `0xother${i}`,
emailHash: `other-email-hash-${i}`,
verificationCode: 'x',
id: shortUUID.uuid(),
requestedAt: new Date(insertionTimeBeforeExpiry),
},
});
}

Expand Down Expand Up @@ -506,15 +518,15 @@ describe('POST /api/email-card-drop-requests', function () {
});

it('rejects and triggers the rate limit if enough drops have happened in the interval', async function () {
let emailCardDropRequestsQueries = await getContainer().lookup('email-card-drop-requests', { type: 'query' });

let { count, periodMinutes } = config.get('cardDrop.email.rateLimit');

let timeWithinRateLimitWindow = fakeTime - (periodMinutes / 2) * 60 * 1000; // 2022-04-20 07:37:27

for (let i = 0; i <= count; i++) {
let claim = Object.assign({}, claimedEoa);
claim.claimedAt = new Date(fakeTime - periodMinutes * 60 * 1000);
claim.claimedAt = new Date(timeWithinRateLimitWindow);
claim.id = shortUUID.uuid();
await emailCardDropRequestsQueries.insert(claim);
await prisma.emailCardDropRequest.create({ data: claim });
}

const payload = {
Expand Down Expand Up @@ -558,15 +570,13 @@ describe('POST /api/email-card-drop-requests', function () {
});

it('does not trigger the rate limit when the claims are outside the rate limit period', async function () {
let emailCardDropRequestsQueries = await getContainer().lookup('email-card-drop-requests', { type: 'query' });

let { count, periodMinutes } = config.get('cardDrop.email.rateLimit');

for (let i = 0; i <= count * 2; i++) {
let claim = Object.assign({}, claimedEoa);
claim.claimedAt = new Date(fakeTime - 2 * periodMinutes * 60 * 1000);
claim.id = shortUUID.uuid();
await emailCardDropRequestsQueries.insert(claim);
await prisma.emailCardDropRequest.create({ data: claim });
}

const payload = {
Expand Down Expand Up @@ -617,15 +627,15 @@ describe('POST /api/email-card-drop-requests', function () {
});

it('rejects when the owner address has already claimed', async function () {
let emailCardDropRequestsQueries = await getContainer().lookup('email-card-drop-requests', { type: 'query' });

await emailCardDropRequestsQueries.insert({
ownerAddress: stubUserAddress,
emailHash: 'abc123',
verificationCode: 'I4I.FX8OUx',
id: '2850a954-525d-499a-a5c8-3c89192ad40e',
requestedAt: new Date(),
claimedAt: new Date(),
await prisma.emailCardDropRequest.create({
data: {
ownerAddress: stubUserAddress,
emailHash: 'abc123',
verificationCode: 'I4I.FX8OUx',
id: '2850a954-525d-499a-a5c8-3c89192ad40e',
requestedAt: new Date(),
claimedAt: new Date(),
},
});

let email = '[email protected]';
Expand Down Expand Up @@ -657,21 +667,21 @@ describe('POST /api/email-card-drop-requests', function () {
});

it('rejects when the email has already claimed', async function () {
let emailCardDropRequestsQueries = await getContainer().lookup('email-card-drop-requests', { type: 'query' });

let email = '[email protected]';

let hash = crypto.createHmac('sha256', config.get('emailHashSalt'));
hash.update(email);
let emailHash = hash.digest('hex');

await emailCardDropRequestsQueries.insert({
ownerAddress: '0xanother-address',
emailHash,
verificationCode: 'I4I.FX8OUx',
id: '2850a954-525d-499a-a5c8-3c89192ad40e',
requestedAt: new Date(),
claimedAt: new Date(),
await prisma.emailCardDropRequest.create({
data: {
ownerAddress: '0xanother-address',
emailHash,
verificationCode: 'I4I.FX8OUx',
id: '2850a954-525d-499a-a5c8-3c89192ad40e',
requestedAt: new Date(),
claimedAt: new Date(),
},
});

const payload = {
Expand Down
Loading

0 comments on commit 2de9dd8

Please sign in to comment.