Skip to content

Commit

Permalink
feat: part.type for easy type narrowing (#422)
Browse files Browse the repository at this point in the history
* feat: part.type for easy type narrowing

* Update test/index.test-d.ts

Co-authored-by: Uzlopak <[email protected]>

* Update test/index.test-d.ts

Co-authored-by: Uzlopak <[email protected]>

* Update test/index.test-d.ts

Co-authored-by: Uzlopak <[email protected]>

* Update test/index.test-d.ts

Co-authored-by: Uzlopak <[email protected]>

---------

Co-authored-by: Uzlopak <[email protected]>
  • Loading branch information
nadhifikbarw and Uzlopak authored Mar 13, 2023
1 parent bbe5094 commit 3042c08
Show file tree
Hide file tree
Showing 9 changed files with 35 additions and 14 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,10 @@ fastify.post('/', async function (req, reply) {
fastify.post('/upload/raw/any', async function (req, reply) {
const parts = req.parts()
for await (const part of parts) {
if (part.file) {
if (part.type === 'file') {
await pump(part.file, fs.createWriteStream(part.filename))
} else {
// part.type === 'field
console.log(part)
}
}
Expand Down Expand Up @@ -177,6 +178,7 @@ This will store all files in the operating system default directory for temporar
fastify.post('/upload/files', async function (req, reply) {
// stores files to tmp dir and return files
const files = await req.saveRequestFiles()
files[0].type // "file"
files[0].filepath
files[0].fieldname
files[0].filename
Expand Down
3 changes: 2 additions & 1 deletion examples/example.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ fastify.post('/upload/stream/files', async function (req, reply) {
fastify.post('/upload/raw/any', async function (req, reply) {
const parts = req.parts()
for await (const part of parts) {
if (part.file) {
if (part.type === 'file') {
await pump(part.file, fs.createWriteStream(part.filename))
} else {
// part.type === 'field'
console.log(part)
}
}
Expand Down
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ declare namespace fastifyMultipart {
export type Multipart = MultipartFile | MultipartValue;

export interface MultipartFile {
type: 'file';
toBuffer: () => Promise<Buffer>;
file: BusboyFileStream;
fieldname: string;
Expand All @@ -92,6 +93,7 @@ declare namespace fastifyMultipart {
}

export interface MultipartValue<T = unknown> {
type: 'field';
value: T;
fieldname: string;
mimetype: string;
Expand Down
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ function fastifyMultipart (fastify, options, done) {
}

const value = {
type: 'field',
fieldname: name,
mimetype: contentType,
encoding,
Expand Down Expand Up @@ -444,6 +445,7 @@ function fastifyMultipart (fastify, options, done) {
: defaultThrowFileSizeLimit

const value = {
type: 'file',
fieldname: name,
filename,
encoding,
Expand Down
3 changes: 2 additions & 1 deletion test/big.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const sendToWormhole = require('stream-wormhole')

// skipping on Github Actions because it takes too long
test('should upload a big file in constant memory', { skip: process.env.CI }, function (t) {
t.plan(9)
t.plan(10)

const fastify = Fastify()
const hashInput = crypto.createHash('sha256')
Expand All @@ -32,6 +32,7 @@ test('should upload a big file in constant memory', { skip: process.env.CI }, fu

for await (const part of req.parts()) {
if (part.file) {
t.equal(part.type, 'file')
t.equal(part.fieldname, 'upload')
t.equal(part.filename, 'random-data')
t.equal(part.encoding, '7bit')
Expand Down
5 changes: 4 additions & 1 deletion test/fix-313.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const { once } = EventEmitter
const filePath = path.join(__dirname, '../README.md')

test('should store file on disk, remove on response when attach fields to body is true', async function (t) {
t.plan(22)
t.plan(25)

const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
Expand All @@ -29,18 +29,21 @@ test('should store file on disk, remove on response when attach fields to body i
const files = await req.saveRequestFiles()

t.ok(files[0].filepath)
t.equal(files[0].type, 'file')
t.equal(files[0].fieldname, 'upload')
t.equal(files[0].filename, 'README.md')
t.equal(files[0].encoding, '7bit')
t.equal(files[0].mimetype, 'text/markdown')
t.ok(files[0].fields.upload)
t.ok(files[1].filepath)
t.equal(files[1].type, 'file')
t.equal(files[1].fieldname, 'upload')
t.equal(files[1].filename, 'README.md')
t.equal(files[1].encoding, '7bit')
t.equal(files[1].mimetype, 'text/markdown')
t.ok(files[1].fields.upload)
t.ok(files[2].filepath)
t.equal(files[2].type, 'file')
t.equal(files[2].fieldname, 'other')
t.equal(files[2].filename, 'README.md')
t.equal(files[2].encoding, '7bit')
Expand Down
11 changes: 7 additions & 4 deletions test/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ const runServer = async () => {
app.post('/', async (req, reply) => {
const data = await req.file()
if (data == null) throw new Error('missing file')


expectType<'file'>(data.type)
expectType<BusboyFileStream>(data.file)
expectType<boolean>(data.file.truncated)
expectType<MultipartFields>(data.fields)
Expand All @@ -69,7 +70,7 @@ const runServer = async () => {
// field missing from the request
} else if (Array.isArray(field)) {
// multiple fields with the same name
} else if ('file' in field) {
} else if (field.type === 'file') {
// field containing a file
field.file.resume()
} else {
Expand All @@ -85,10 +86,11 @@ const runServer = async () => {
// Multiple fields including scalar values
app.post<{Body: {file: MultipartFile, foo: MultipartValue<string>}}>('/upload/stringvalue', async (req, reply) => {
expectError(req.body.foo.file);
expectType<'field'>(req.body.foo.type)
expectType<string>(req.body.foo.value);

expectType<BusboyFileStream>(req.body.file.file)
expectError(req.body.file.value);
expectType<'file'>(req.body.file.type);
reply.send();
})

Expand Down Expand Up @@ -123,7 +125,7 @@ const runServer = async () => {
app.post('/upload/raw/any', async function (req, reply) {
const parts = req.parts()
for await (const part of parts) {
if ('file' in part) {
if (part.type === 'file') {
await pump(part.file, fs.createWriteStream(part.filename))
} else {
console.log(part.value)
Expand All @@ -145,6 +147,7 @@ const runServer = async () => {
app.post('/upload/files', async function (req, reply) {
// stores files to tmp dir and return files
const files = await req.saveRequestFiles()
files[0].type // "file"
files[0].filepath
files[0].fieldname
files[0].filename
Expand Down
3 changes: 2 additions & 1 deletion test/multipart-body-schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const fs = require('fs')
const filePath = path.join(__dirname, '../README.md')

test('should be able to use JSON schema to validate request', function (t) {
t.plan(6)
t.plan(7)

const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
Expand Down Expand Up @@ -39,6 +39,7 @@ test('should be able to use JSON schema to validate request', function (t) {
const content = await req.body.upload.toBuffer()

t.equal(content.toString(), original)
t.equal(req.body.hello.type, 'field')
t.equal(req.body.hello.value, 'world')

reply.code(200).send()
Expand Down
16 changes: 11 additions & 5 deletions test/multipart.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const sendToWormhole = require('stream-wormhole')
const filePath = path.join(__dirname, '../README.md')

test('should parse forms', function (t) {
t.plan(8)
t.plan(9)

const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
Expand All @@ -27,6 +27,7 @@ test('should parse forms', function (t) {
fastify.post('/', async function (req, reply) {
for await (const part of req.parts()) {
if (part.file) {
t.equal(part.type, 'file')
t.equal(part.fieldname, 'upload')
t.equal(part.filename, 'README.md')
t.equal(part.encoding, '7bit')
Expand Down Expand Up @@ -76,7 +77,7 @@ test('should parse forms', function (t) {
})

test('should respond when all files are processed', function (t) {
t.plan(4)
t.plan(6)

const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
Expand All @@ -87,6 +88,7 @@ test('should respond when all files are processed', function (t) {
const parts = req.files()
for await (const part of parts) {
t.ok(part.file)
t.equal(part.type, 'file')
await sendToWormhole(part.file)
}
reply.code(200).send()
Expand Down Expand Up @@ -327,6 +329,7 @@ test('should be able to configure limits globally with plugin register options',
const parts = req.files()
for await (const part of parts) {
t.ok(part.file)
t.equal(part.type, 'file')
await sendToWormhole(part.file)
}
reply.code(200).send()
Expand Down Expand Up @@ -469,7 +472,7 @@ test('should throw error due to partsLimit (The max number of parts (fields + fi
})

test('should throw error due to file size limit exceed (Default: true)', function (t) {
t.plan(4)
t.plan(6)

const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
Expand All @@ -481,6 +484,7 @@ test('should throw error due to file size limit exceed (Default: true)', functio
const parts = req.files()
for await (const part of parts) {
t.ok(part.file)
t.equal(part.type, 'file')
await sendToWormhole(part.file)
}
reply.code(200).send()
Expand Down Expand Up @@ -516,7 +520,7 @@ test('should throw error due to file size limit exceed (Default: true)', functio
})

test('should not throw error due to file size limit exceed - files setting (Default: true)', function (t) {
t.plan(3)
t.plan(5)

const fastify = Fastify()
t.teardown(fastify.close.bind(fastify))
Expand All @@ -527,6 +531,7 @@ test('should not throw error due to file size limit exceed - files setting (Defa
const parts = req.files({ limits: { fileSize: 1 } })
for await (const part of parts) {
t.ok(part.file)
t.equal(part.type, 'file')
await sendToWormhole(part.file)
}
reply.code(200).send()
Expand Down Expand Up @@ -557,7 +562,7 @@ test('should not throw error due to file size limit exceed - files setting (Defa
})

test('should not miss fields if part handler takes much time than formdata parsing', async function (t) {
t.plan(11)
t.plan(12)

const original = fs.readFileSync(filePath, 'utf8')
const immediate = util.promisify(setImmediate)
Expand All @@ -576,6 +581,7 @@ test('should not miss fields if part handler takes much time than formdata parsi

for await (const part of req.parts()) {
if (part.file) {
t.equal(part.type, 'file')
t.equal(part.fieldname, 'upload')
t.equal(part.filename, 'README.md')
t.equal(part.encoding, '7bit')
Expand Down

0 comments on commit 3042c08

Please sign in to comment.