From 86231890033b5ff01cbcc63671c3203b04b53ebc Mon Sep 17 00:00:00 2001 From: Bregwin Jogi Date: Sat, 8 Jun 2024 12:45:42 -0400 Subject: [PATCH] handle text/plain content type in get.js, added additional tests for post --- src/model/fragment.js | 2 +- src/routes/api/get.js | 45 +++++++++++++++++++++++------------------ src/routes/api/post.js | 14 ++++++++----- tests/unit/get.test.js | 43 +++++++++++++++++++++++++++++++++++++++ tests/unit/post.test.js | 28 ++++++++++++++++++++++++- 5 files changed, 105 insertions(+), 27 deletions(-) diff --git a/src/model/fragment.js b/src/model/fragment.js index bd452a8..01b3697 100644 --- a/src/model/fragment.js +++ b/src/model/fragment.js @@ -140,7 +140,7 @@ class Fragment { * @returns {Array} list of supported mime types */ get formats() { - return ['text/plain']; + return ['text/plain']; } /** diff --git a/src/routes/api/get.js b/src/routes/api/get.js index 297ecfb..3f975a0 100644 --- a/src/routes/api/get.js +++ b/src/routes/api/get.js @@ -10,40 +10,46 @@ const logger = require('../../logger'); module.exports = (req, res, next) => { const expand = req.query.expand; const ownerId = req.user; - const id = req.params.id; - + const idExtension = req.params.id; - if (id) { + if (idExtension) { + logger.info(`GET /fragments/${idExtension} for user ${ownerId}`); - logger.info(`GET /fragments/${id} for user ${ownerId}`) + let id = ''; + let extension = ''; + if (idExtension.includes('.')) { + extension = idExtension.split('.')[1]; + id = idExtension.split('.')[0]; + } else { + id = idExtension; + } - let extension = ''; - if (id.includes('.')) { - extension = id.split('.').pop(); - } + logger.info(`Extension: ${extension}`); + logger.info(`ID: ${id}`); Fragment.byId(ownerId, id, expand) - .then( - (fragment) => { + .then((fragment) => { fragment.getData().then((data) => { - - if (extension === 'txt') { + if (extension) { // Check if the fragment is text/plain - if (fragment.contentType === 'text/plain') { - res.status(200) + logger.info(`Fragment content type: ${fragment.mimeType}`); + if (fragment.mimeType === 'text/plain' && extension === 'txt') { + res + .status(200) .header('Content-Type', 'text/plain') .header('Content-Disposition', 'attachment; filename="fragment.txt"') .send(data.toString()); } else { - res.status(415).json(createErrorResponse(415, 'Not allowed to convert to specified format')); + res + .status(415) + .json(createErrorResponse(415, 'Not allowed to convert to specified format')); } } else { // Return the data with original content type it had - res.status(200).send(data); + res.status(200).send(data.toString()); } - - - })}) + }); + }) .catch((err) => { logger.error(err); next(err); @@ -63,5 +69,4 @@ module.exports = (req, res, next) => { res.status(500).json(createErrorResponse(500, 'ERROR: ' + err)); }); } - }; diff --git a/src/routes/api/post.js b/src/routes/api/post.js index 230f719..b75a257 100644 --- a/src/routes/api/post.js +++ b/src/routes/api/post.js @@ -5,17 +5,21 @@ const { Fragment } = require('../../model/fragment'); const logger = require('../../logger'); module.exports = (req, res) => { + + if (Fragment.isSupportedType(req.get('Content-Type')) === false) { + const type = req.get('Content-Type'); + logger.error(`unsupported media type: ${type}`); + res.status(415).json(createErrorResponse(415, 'unsupported media type')); + return; + } + if (!Buffer.isBuffer(req.body)) { logger.error('no buffer found in request body'); res.status(400).json(createErrorResponse(400, 'no buffer found in request body')); return; } - if (Fragment.isSupportedType(req.get('Content-Type')) === false) { - logger.error(`unsupported media type: ${fragment.type}`); - res.status(400).json(createErrorResponse(415, 'unsupported media type')); - return; - } + const fragment = new Fragment({ ownerId: req.user, type: req.get('Content-Type') }); diff --git a/tests/unit/get.test.js b/tests/unit/get.test.js index e346b3f..e9e7495 100644 --- a/tests/unit/get.test.js +++ b/tests/unit/get.test.js @@ -20,5 +20,48 @@ describe('GET /v1/fragments', () => { expect(Array.isArray(res.body.fragments)).toBe(true); }); + // Test for getting a fragment by id + test('authenticated users can get a fragment by id', async () => { + + const postRequest = await request(app) + .post('/v1/fragments') + .auth('user1@email.com', 'password1') + .set('Content-Type', 'text/plain') + .send(Buffer.from('hello world')); + + const res = await request(app).get(`/v1/fragments/${postRequest.body.fragment.id}`).auth('user1@email.com', 'password1'); + + expect(res.statusCode).toBe(200); + expect(res.text).toBe('hello world'); + }); + + test('authenticated users can get a fragment by id with .txt extension', async () => { + const postRequest = await request(app) + .post('/v1/fragments') + .auth('user1@email.com', 'password1') + .set('Content-Type', 'text/plain') + .send(Buffer.from('test 123')); + + const res = await request(app).get(`/v1/fragments/${postRequest.body.fragment.id}.txt`).auth('user1@email.com', 'password1'); + + expect(res.statusCode).toBe(200); + expect(res.headers['content-type']).toBe('text/plain; charset=utf-8'); + expect(res.headers['content-disposition']).toBe('attachment; filename="fragment.txt"'); + expect(res.text).toBe('test 123'); + }); + + test('error when fragment cannot be converted to .txt', async () => { + const postRequest = await request(app) + .post('/v1/fragments') + .auth('user1@email.com', 'password1') + .set('Content-Type', 'text/plain') + .send(Buffer.from('hihihi')); + + const res = await request(app).get(`/v1/fragments/${postRequest.body.fragment.id}.html`).auth('user1@email.com', 'password1'); + expect(res.statusCode).toBe(415); + expect(res.body.status).toBe('error'); + expect(res.body.error.message).toBe('Not allowed to convert to specified format'); + }); + }); diff --git a/tests/unit/post.test.js b/tests/unit/post.test.js index 94aec51..efbdc2d 100644 --- a/tests/unit/post.test.js +++ b/tests/unit/post.test.js @@ -4,7 +4,7 @@ const request = require('supertest'); const app = require('../../src/app'); -describe('GET /v1/fragments', () => { +describe('POST /v1/fragments', () => { // If the request is missing the Authorization header, it should be forbidden test('unauthenticated requests are denied', () => request(app).post('/v1/fragments').expect(401)); @@ -40,5 +40,31 @@ describe('GET /v1/fragments', () => { expect(res.body.status).toBe('error'); }); + // If the Content-Type is not supported, it should return a 415 error + test('unsupported media type', async () => { + const res = await request(app) + .post('/v1/fragments') + .auth('user1@email.com', 'password1') + .set('Content-Type', 'application/unsupported') + .send(Buffer.from('hello world')); + + expect(res.statusCode).toBe(415); + expect(res.body.status).toBe('error'); + expect(res.body.error.message).toBe('unsupported media type'); + }); + + // Location header is being set correctly + test('successful fragment creation sets Location header', async () => { + const res = await request(app) + .post('/v1/fragments') + .auth('user1@email.com', 'password1') + .set('Content-Type', 'text/plain') + .send(Buffer.from('hello world')); + + expect(res.statusCode).toBe(201); + expect(res.body.status).toBe('ok'); + expect(res.header.location).toBeTruthy(); + }); + });