Skip to content

Commit

Permalink
feat: remove files from body (#2)
Browse files Browse the repository at this point in the history
* chore: update deps

* feat: remove files from body
  • Loading branch information
climba03003 authored Jul 11, 2021
1 parent d41d005 commit 679a665
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 22 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ fastify.register(FastifyFormidable, {

See: [`formidable`](https://github.com/node-formidable/formidable#options)

#### options.removeFilesFromBody

This options will add a `preHandler` hooks to remove files from body.

```ts
import FastifyFormidable from 'fastify-formidable'

fastify.register(FastifyFormidable, {
removeFilesFromBody: true
})
```

### Integration

It is a known limitation for `fastify-multipart` integrate with `fastify-swagger` and this plugin provide a relatively simple solution for the integration.
Expand Down
37 changes: 27 additions & 10 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ declare module 'fastify' {
export interface FastifyFormidableOptions {
addContentTypeParser?: boolean
addHooks?: boolean
removeFilesFromBody?: boolean
formidable?: Options
}

Expand All @@ -24,14 +25,14 @@ const plugin: FastifyPluginAsync<FastifyFormidableOptions> = async function (fas
fastify.decorateRequest(kIsMultipart, false)
fastify.decorateRequest('files', null)

fastify.decorateRequest('parseMultipart', async function (this: FastifyRequest, options?: Options) {
fastify.decorateRequest('parseMultipart', async function (this: FastifyRequest, decoratorOptions?: Options) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const request = this

let requestFormidable = formidable

if (typeof options === 'object' && options !== null && !Array.isArray(options)) {
requestFormidable = new IncomingForm(options)
if (typeof decoratorOptions === 'object' && decoratorOptions !== null && !Array.isArray(decoratorOptions)) {
requestFormidable = new IncomingForm(decoratorOptions)
}

return await new Promise(function (resolve, reject) {
Expand All @@ -40,13 +41,15 @@ const plugin: FastifyPluginAsync<FastifyFormidableOptions> = async function (fas
requestFormidable.parse(request.raw, function (err, fields, files) {
if (err as true) reject(err)
request.body = Object.assign({}, fields)
Object.keys(files).forEach(function (key) {
(request.body as any)[key] = Array.isArray(files[key])
? (files[key] as File[]).map(function (file) {
return file.path
})
: (files[key] as File).path
})
if (options.removeFilesFromBody !== true) {
Object.keys(files).forEach(function (key) {
(request.body as any)[key] = Array.isArray(files[key])
? (files[key] as File[]).map(function (file) {
return file.path
})
: (files[key] as File).path
})
}
request.files = files
resolve(request.body)
})
Expand Down Expand Up @@ -101,6 +104,20 @@ const plugin: FastifyPluginAsync<FastifyFormidableOptions> = async function (fas
})
})
}

if (options.removeFilesFromBody === true && (options.addHooks === true || options.addContentTypeParser === true)) {
// we only remove after validation
fastify.addHook('preHandler', function (request, reply, done) {
if (request.files !== null) {
const keys = Object.keys(request.files)
keys.forEach(function (key) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete (request.body as any)[key]
})
}
done()
})
}
}

export const ajvBinaryFormat = function (ajv: Ajv): void {
Expand Down
17 changes: 8 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,23 @@
},
"devDependencies": {
"@rollup/plugin-typescript": "^8.2.1",
"@types/jest": "^26.0.23",
"@types/nodemailer": "^6.4.2",
"@types/jest": "^26.0.24",
"@typescript-eslint/eslint-plugin": "4",
"eslint": "7",
"eslint-config-standard-with-typescript": "^20.0.0",
"eslint-plugin-import": "2",
"eslint-plugin-node": "11",
"eslint-plugin-promise": "5",
"eslint-plugin-standard": "5",
"fastify": "^3.15.1",
"fastify": "^3.19.0",
"fastify-swagger": "^4.8.2",
"form-data": "^4.0.0",
"husky": "^6.0.0",
"jest": "^26.6.3",
"prettier": "^2.3.0",
"rollup": "^2.47.0",
"ts-jest": "^26.5.6",
"typescript": "^4.2.4",
"husky": "^7.0.1",
"jest": "^27.0.6",
"prettier": "^2.3.2",
"rollup": "^2.53.1",
"ts-jest": "^27.0.3",
"typescript": "^4.3.5",
"undici": "^4.1.0"
},
"husky": {
Expand Down
2 changes: 1 addition & 1 deletion test/addHooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import FormData = require('form-data')

const filePath = path.join(__dirname, '../package.json')

describe('addContentTypeParser', function () {
describe('addHooks', function () {
let fastify: FastifyInstance

beforeEach(async function () {
Expand Down
1 change: 0 additions & 1 deletion test/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ describe('integration', function () {

const response = await request(`http://localhost:${(fastify.server.address() as AddressInfo).port}`, form)

console.log(response.body)
expect(response.status).toStrictEqual(200)

const json = await response.json()
Expand Down
2 changes: 1 addition & 1 deletion test/parseMultipart.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import FormData = require('form-data')

const filePath = path.join(__dirname, '../package.json')

describe('addContentTypeParser', function () {
describe('parseMultipart', function () {
let fastify: FastifyInstance

beforeEach(async function () {
Expand Down
113 changes: 113 additions & 0 deletions test/removeFilesFromBody.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import Fastify, { FastifyInstance } from 'fastify'
import * as fs from 'fs'
import { AddressInfo } from 'net'
import * as path from 'path'
import FastifyFormidable from '../lib'
import { request } from './request'
import FormData = require('form-data')

const filePath = path.join(__dirname, '../package.json')

describe('removeFilesFromBody', function () {
let fastify: FastifyInstance

test('with addContentTypeParser', async function () {
fastify = Fastify()
await fastify.register(FastifyFormidable, {
addContentTypeParser: true,
removeFilesFromBody: true
})

fastify.post<{ Body: { foo: String, file: string } }>('/', async function (request, reply) {
return await reply.code(200).send({
body: request.body,
files: request.files
})
})

await fastify.listen(0)

const form = new FormData()
form.append('foo', 'bar')
form.append('file', fs.createReadStream(filePath))

const response = await request(`http://localhost:${(fastify.server.address() as AddressInfo).port}`, form)

expect(response.status).toStrictEqual(200)

const json = await response.json()

expect(json.body.foo).toStrictEqual('bar')
expect(json.body.file).toBeUndefined()
expect(json.files.file).toBeDefined()
expect(json.files.file.name).toStrictEqual('package.json')
})

test('with addHooks', async function () {
fastify = Fastify()
await fastify.register(FastifyFormidable, {
addHooks: true,
removeFilesFromBody: true
})

fastify.post<{ Body: { foo: String, file: string } }>('/', async function (request, reply) {
return await reply.code(200).send({
body: request.body,
files: request.files
})
})

await fastify.listen(0)

const form = new FormData()
form.append('foo', 'bar')
form.append('file', fs.createReadStream(filePath))

const response = await request(`http://localhost:${(fastify.server.address() as AddressInfo).port}`, form)

expect(response.status).toStrictEqual(200)

const json = await response.json()

expect(json.body.foo).toStrictEqual('bar')
expect(json.body.file).toBeUndefined()
expect(json.files.file).toBeDefined()
expect(json.files.file.name).toStrictEqual('package.json')
})

test('with parseMultipart', async function () {
fastify = Fastify()
await fastify.register(FastifyFormidable, {
removeFilesFromBody: true
})

fastify.post<{ Body: { foo: String, file: string } }>('/', async function (request, reply) {
const body = await request.parseMultipart()
return await reply.code(200).send({
body: body,
files: request.files
})
})

await fastify.listen(0)

const form = new FormData()
form.append('foo', 'bar')
form.append('file', fs.createReadStream(filePath))

const response = await request(`http://localhost:${(fastify.server.address() as AddressInfo).port}`, form)

expect(response.status).toStrictEqual(200)

const json = await response.json()

expect(json.body.foo).toStrictEqual('bar')
expect(json.body.file).toBeUndefined()
expect(json.files.file).toBeDefined()
expect(json.files.file.name).toStrictEqual('package.json')
})

afterEach(async function () {
await fastify.close()
})
})

0 comments on commit 679a665

Please sign in to comment.