From 77ba7531f3a0922272174e43aacdf9a47cc17889 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 16 Feb 2022 15:26:11 +0800 Subject: [PATCH] add tests for upload --- .../api/src/routers/svc/pdf_attachments.ts | 27 +++++----- .../api/test/routers/pdf_attachments.test.ts | 49 +++++++++++++++++++ ....do.add_delete_cascade_on_upload_files.sql | 14 ++++++ ...ndo.add_delete_cascade_on_upload_files.sql | 13 +++++ packages/inbound-email-handler/package.json | 5 +- packages/inbound-email-handler/src/index.ts | 2 +- yarn.lock | 17 ------- 7 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 packages/api/test/routers/pdf_attachments.test.ts create mode 100755 packages/db/migrations/0068.do.add_delete_cascade_on_upload_files.sql create mode 100755 packages/db/migrations/0068.undo.add_delete_cascade_on_upload_files.sql diff --git a/packages/api/src/routers/svc/pdf_attachments.ts b/packages/api/src/routers/svc/pdf_attachments.ts index 5abbeb927..356589267 100644 --- a/packages/api/src/routers/svc/pdf_attachments.ts +++ b/packages/api/src/routers/svc/pdf_attachments.ts @@ -12,6 +12,7 @@ import { initModels } from '../../server' import { kx } from '../../datalayer/knex_config' import { analytics } from '../../utils/analytics' import normalizeUrl from 'normalize-url' +import { getNewsletterEmail } from '../../services/newsletters' export function pdfAttachmentsRouter() { const router = express.Router() @@ -30,13 +31,13 @@ export function pdfAttachmentsRouter() { return res.status(401).send('UNAUTHORIZED') } - const models = initModels(kx, false) - const user = await models.user.getWhere({ email }) - - if (!user) { + const newsletterEmail = await getNewsletterEmail(email) + if (!newsletterEmail || !newsletterEmail.user) { return res.status(401).send('UNAUTHORIZED') } + const user = newsletterEmail.user + analytics.track({ userId: user.id, event: 'pdf-attachment-upload', @@ -46,6 +47,7 @@ export function pdfAttachmentsRouter() { }) const contentType = 'application/pdf' + const models = initModels(kx, false) const uploadFileData = await models.uploadFile.create({ url: '', userId: user.id, @@ -59,10 +61,10 @@ export function pdfAttachmentsRouter() { uploadFileData.id, fileName ) - const uploadSignedUrl = await generateUploadSignedUrl( - uploadFilePathName, - contentType - ) + const uploadSignedUrl = + env.server.apiEnv === 'prod' + ? await generateUploadSignedUrl(uploadFilePathName, contentType) + : 'http://localhost:3000/uploads/' + uploadFilePathName res.send({ id: uploadFileData.id, url: uploadSignedUrl, @@ -86,13 +88,13 @@ export function pdfAttachmentsRouter() { return res.status(401).send('UNAUTHORIZED') } - const models = initModels(kx, false) - const user = await models.user.getWhere({ email }) - - if (!user) { + const newsletterEmail = await getNewsletterEmail(email) + if (!newsletterEmail || !newsletterEmail.user) { return res.status(401).send('UNAUTHORIZED') } + const user = newsletterEmail.user + analytics.track({ userId: user.id, event: 'pdf-attachment-create-article', @@ -101,6 +103,7 @@ export function pdfAttachmentsRouter() { }, }) + const models = initModels(kx, false) const uploadFile = await models.uploadFile.getWhere({ id: uploadFileId, userId: user.id, diff --git a/packages/api/test/routers/pdf_attachments.test.ts b/packages/api/test/routers/pdf_attachments.test.ts new file mode 100644 index 000000000..ee1ee0152 --- /dev/null +++ b/packages/api/test/routers/pdf_attachments.test.ts @@ -0,0 +1,49 @@ +import { + createTestNewsletterEmail, + createTestUser, + deleteTestUser, +} from '../db' +import { request } from '../util' +import { User } from '../../src/entity/user' +import 'mocha' +import * as jwt from 'jsonwebtoken' +import { expect } from 'chai' + +describe('PDF attachments Router', () => { + const username = 'fakeUser' + const newsletterEmail = 'fakeEmail' + + let user: User + let authToken: string + + before(async () => { + // create test user and login + user = await createTestUser(username) + + await createTestNewsletterEmail(user, newsletterEmail) + authToken = jwt.sign(newsletterEmail, process.env.JWT_SECRET || '') + }) + + after(async () => { + // clean up + await deleteTestUser(username) + }) + + describe('upload', () => { + it('create upload file request and return id and url', async () => { + const testFile = 'testFile.pdf' + + const res = await request + .post('/svc/pdf-attachments/upload') + .send({ + email: newsletterEmail, + fileName: testFile, + }) + .set('Authorization', `${authToken}`) + .expect(200) + + expect(res.body.id).to.be.a('string') + expect(res.body.url).to.be.a('string') + }) + }) +}) diff --git a/packages/db/migrations/0068.do.add_delete_cascade_on_upload_files.sql b/packages/db/migrations/0068.do.add_delete_cascade_on_upload_files.sql new file mode 100755 index 000000000..5cbdc49cd --- /dev/null +++ b/packages/db/migrations/0068.do.add_delete_cascade_on_upload_files.sql @@ -0,0 +1,14 @@ +-- Type: DO +-- Name: add_delete_cascade_on_upload_files +-- Description: Add delete cascade on user_id field on upload_files table + +BEGIN; + +ALTER TABLE omnivore.upload_files + DROP CONSTRAINT upload_files_user_id_fkey, + ADD CONSTRAINT upload_files_user_id_fkey + FOREIGN KEY (user_id) + REFERENCES omnivore.user(id) + ON DELETE CASCADE; + +COMMIT; diff --git a/packages/db/migrations/0068.undo.add_delete_cascade_on_upload_files.sql b/packages/db/migrations/0068.undo.add_delete_cascade_on_upload_files.sql new file mode 100755 index 000000000..a4c0727a7 --- /dev/null +++ b/packages/db/migrations/0068.undo.add_delete_cascade_on_upload_files.sql @@ -0,0 +1,13 @@ +-- Type: UNDO +-- Name: add_delete_cascade_on_upload_files +-- Description: Add delete cascade on user_id field on upload_files table + +BEGIN; + +ALTER TABLE omnivore.upload_files + DROP CONSTRAINT upload_files_user_id_fkey, + ADD CONSTRAINT upload_files_user_id_fkey + FOREIGN KEY (user_id) + REFERENCES omnivore.user(id); + +COMMIT; diff --git a/packages/inbound-email-handler/package.json b/packages/inbound-email-handler/package.json index 704439c60..a3b9231db 100644 --- a/packages/inbound-email-handler/package.json +++ b/packages/inbound-email-handler/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@types/json-bigint": "^1.0.1", "@types/node": "^14.11.2", - "@types/quoted-printable": "^1.0.0", "eslint-plugin-prettier": "^4.0.0" }, "dependencies": { @@ -31,8 +30,8 @@ "@sendgrid/client": "^7.6.0", "@sentry/serverless": "^6.16.1", "axios": "^0.26.0", + "jsonwebtoken": "^8.5.1", "parse-headers": "^2.0.4", - "parse-multipart-data": "^1.2.1", - "quoted-printable": "^1.0.1" + "parse-multipart-data": "^1.2.1" } } diff --git a/packages/inbound-email-handler/src/index.ts b/packages/inbound-email-handler/src/index.ts index 57c4a73ac..5b79b2cfc 100644 --- a/packages/inbound-email-handler/src/index.ts +++ b/packages/inbound-email-handler/src/index.ts @@ -82,7 +82,7 @@ export const inboundEmailHandler = Sentry.GCPFunction.wrapHttpFunction( if (isConfirmationEmail(from)) { console.log('handleConfirmation', from, recipientAddress) await handleConfirmation(recipientAddress, subject) - } else if (parsed['pdf-attachment']) { + } else if (parsed['pdf-attachment-filename']) { console.log('handle PDF attachment', from, recipientAddress) await handlePdfAttachment( recipientAddress, diff --git a/yarn.lock b/yarn.lock index 9ce1db3aa..d020d8059 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4906,11 +4906,6 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== -"@types/quoted-printable@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/quoted-printable/-/quoted-printable-1.0.0.tgz#903f81f5d81698d361c176b5b52970cd2bc9868e" - integrity sha512-hgFjmHmgT5M8SvDVe+tMhiUb3xViwqkEAM/sTpWCpO0B2Z7RGAgwiQaxPcLVk4KLiZmqj7BMXZvaQQdX6uPM6A== - "@types/range-parser@*": version "1.2.4" resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" @@ -14538,13 +14533,6 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -quoted-printable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/quoted-printable/-/quoted-printable-1.0.1.tgz#9eebf5eb3d11eef022b264fd2d2b6b2bb3b84cc3" - integrity sha1-nuv16z0R7vAismT9LStrK7O4TMM= - dependencies: - utf8 "^2.1.0" - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -16991,11 +16979,6 @@ user-home@^1.1.1: resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" integrity sha1-K1viOjK2Onyd640PKNSFcko98ZA= -utf8@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96" - integrity sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY= - util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"