Skip to content

Commit 14d42f5

Browse files
authored
@tus/server: save decoded metadata in Upload model (#376)
1 parent e82fdac commit 14d42f5

File tree

10 files changed

+23
-47
lines changed

10 files changed

+23
-47
lines changed

packages/eslint-config-custom/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module.exports = {
1616
'@typescript-eslint/no-explicit-any': 'error',
1717
'@typescript-eslint/ban-ts-comment': 'warn',
1818
'@typescript-eslint/no-empty-function': 'off',
19+
'@typescript-eslint/no-unused-vars': 'error',
1920
'capitalized-comments': 'off',
2021
},
2122
ignorePatterns: ['eslint-config-custom', 'demo', 'dist', 'node_modules'],

packages/s3-store/index.ts

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -135,27 +135,6 @@ export class S3Store extends DataStore {
135135
return this.cache.get(id) as MetadataValue
136136
}
137137

138-
/**
139-
* Parses the Base64 encoded metadata received from the client.
140-
*/
141-
private parseMetadataString(str?: string) {
142-
const pairs: Record<string, {encoded: string; decoded?: string}> = {}
143-
144-
if (!str) {
145-
return pairs
146-
}
147-
148-
for (const pair of str.split(',')) {
149-
const [key, encoded] = pair.split(' ')
150-
pairs[key] = {
151-
encoded,
152-
decoded: encoded ? Buffer.from(encoded, 'base64').toString('ascii') : undefined,
153-
}
154-
}
155-
156-
return pairs
157-
}
158-
159138
private partKey(id: string, isIncomplete = false) {
160139
if (isIncomplete) {
161140
id += '.part'
@@ -412,7 +391,6 @@ export class S3Store extends DataStore {
412391
*/
413392
public async create(upload: Upload) {
414393
log(`[${upload.id}] initializing multipart upload`)
415-
const parsedMetadata = this.parseMetadataString(upload.metadata)
416394
type CreateRequest = Omit<aws.S3.Types.CreateMultipartUploadRequest, 'Metadata'> & {
417395
Metadata: Record<string, string>
418396
}
@@ -430,16 +408,8 @@ export class S3Store extends DataStore {
430408
file.isSizeDeferred = 'true'
431409
}
432410

433-
if (upload.metadata !== undefined) {
434-
file.metadata = upload.metadata
435-
}
436-
437-
if (parsedMetadata.contentType) {
438-
request.ContentType = parsedMetadata.contentType.decoded
439-
}
440-
441-
if (parsedMetadata.filename) {
442-
request.Metadata.original_name = parsedMetadata.filename.encoded
411+
if (upload.metadata?.contentType) {
412+
request.ContentType = upload.metadata.contentType
443413
}
444414

445415
// TODO: rename `file` to `upload` to align with the codebase

packages/server/src/handlers/HeadHandler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {BaseHandler} from './BaseHandler'
22

33
import {ERRORS} from '../constants'
4+
import {Metadata} from '../models'
45

56
import type http from 'node:http'
67

@@ -48,7 +49,7 @@ export class HeadHandler extends BaseHandler {
4849
if (file.metadata !== undefined) {
4950
// If the size of the upload is known, the Server MUST include
5051
// the Upload-Length header in the response.
51-
res.setHeader('Upload-Metadata', file.metadata)
52+
res.setHeader('Upload-Metadata', Metadata.stringify(file.metadata))
5253
}
5354

5455
return res.end()

packages/server/src/handlers/PostHandler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import debug from 'debug'
22

33
import {BaseHandler} from './BaseHandler'
4-
import {Upload, Uid} from '../models'
4+
import {Upload, Uid, Metadata} from '../models'
55
import {RequestValidator} from '../validators/RequestValidator'
66
import {EVENTS, ERRORS} from '../constants'
77

@@ -64,7 +64,7 @@ export class PostHandler extends BaseHandler {
6464
id,
6565
size: upload_length ? Number.parseInt(upload_length, 10) : undefined,
6666
offset: 0,
67-
metadata: upload_metadata,
67+
metadata: Metadata.parse(upload_metadata),
6868
})
6969

7070
if (this.options.onUploadCreate) {

packages/server/src/models/Metadata.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {Upload} from './Upload'
2+
13
const ASCII_SPACE = ' '.codePointAt(0)
24
const ASCII_COMMA = ','.codePointAt(0)
35
const BASE64_REGEX = /^[\d+/A-Za-z]*={0,2}$/
@@ -29,9 +31,13 @@ export function validateValue(value: string) {
2931
return BASE64_REGEX.test(value)
3032
}
3133

32-
export function parse(str: string) {
34+
export function parse(str?: string) {
3335
const meta: Record<string, string | undefined> = {}
3436

37+
if (!str) {
38+
return {}
39+
}
40+
3541
for (const pair of str.split(',')) {
3642
const tokens = pair.split(' ')
3743
const [key, value] = tokens
@@ -52,8 +58,8 @@ export function parse(str: string) {
5258
return meta
5359
}
5460

55-
export function stringify(obj: Record<string, string | undefined>) {
56-
return Object.entries(obj)
61+
export function stringify(metadata: NonNullable<Upload['metadata']>) {
62+
return Object.entries(metadata)
5763
.map(([key, value]) => {
5864
if (value === undefined) {
5965
return key

packages/server/src/models/Upload.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ type TUpload = {
22
id: string
33
size?: number
44
offset: number
5-
metadata?: string
5+
metadata?: Record<string, string | undefined | never>
66
creation_date?: string
77
}
88

99
export class Upload {
1010
id: TUpload['id']
11-
metadata?: TUpload['metadata']
11+
metadata: TUpload['metadata']
1212
size?: TUpload['size']
1313
offset: TUpload['offset']
1414
creation_date: TUpload['creation_date']
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export {DataStore} from './DataStore'
22
export {MemoryConfigstore} from './MemoryConfigstore'
3-
export {parse, stringify} from './Metadata'
3+
export * as Metadata from './Metadata'
44
export {StreamSplitter} from './StreamSplitter'
55
export {Uid} from './Uid'
66
export {Upload} from './Upload'

packages/server/test/HeadHandler.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ describe('HeadHandler', () => {
4343
id: '1234',
4444
offset: 0,
4545
size: 512,
46-
metadata: 'filename d29ybGRfZG9taW5hdGlvbl9wbGFuLnBkZg==,is_confidential',
4746
})
4847
fake_store.getUpload.resolves(file)
4948
await handler.send(req, res)
@@ -55,7 +54,6 @@ describe('HeadHandler', () => {
5554
const file = new Upload({
5655
id: '1234',
5756
offset: 0,
58-
metadata: 'filename d29ybGRfZG9taW5hdGlvbl9wbGFuLnBkZg==,is_confidential',
5957
})
6058
fake_store.getUpload.resolves(file)
6159
await handler.send(req, res)
@@ -67,11 +65,11 @@ describe('HeadHandler', () => {
6765
const file = new Upload({
6866
id: '1234',
6967
offset: 0,
70-
metadata: 'filename d29ybGRfZG9taW5hdGlvbl9wbGFuLnBkZg==,is_confidential',
68+
metadata: {is_confidential: undefined, foo: 'bar'},
7169
})
7270
fake_store.getUpload.resolves(file)
7371
await handler.send(req, res)
74-
assert.equal(res.getHeader('Upload-Metadata'), file.metadata)
72+
assert.equal(res.getHeader('Upload-Metadata'), 'is_confidential,foo YmFy')
7573
})
7674

7775
it('should resolve without metadata', async () => {

packages/server/test/Server.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ describe('Server', () => {
201201
.post(server.options.path)
202202
.set('Tus-Resumable', TUS_RESUMABLE)
203203
.set('Upload-Length', '300')
204-
.set('Upload-Metadata', 'foo aGVsbG8=, bar d29ynGQ=')
204+
.set('Upload-Metadata', 'is_confidential')
205205
.set('Content-Type', 'application/false')
206206
.expect(201, {}, (err, res) => {
207207
res.headers.should.have.property('location')

packages/server/test/Upload.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe('Upload', () => {
1717
const id = Uid.rand()
1818
const size = 1234
1919
const offset = 0
20-
const metadata = 'metadata'
20+
const metadata = {foo: 'bar'}
2121
const upload = new Upload({id, size, offset, metadata})
2222
assert.equal(upload.id, id)
2323
assert.equal(upload.size, size)

0 commit comments

Comments
 (0)