mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-17 16:50:22 +00:00
Companion option uploadHeaders (#5981)
closes #5921 also improve tests that currently depend on eachother todo - [x] docs https://github.com/transloadit/uppy.io/pull/394
This commit is contained in:
parent
5e166a101d
commit
0c8dd19dc2
10 changed files with 126 additions and 77 deletions
5
.changeset/nine-rice-brake.md
Normal file
5
.changeset/nine-rice-brake.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@uppy/companion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
New Companion option `uploadHeaders` which can be used to include a static set of headers with every request sent to all upload destinations.
|
||||||
|
|
@ -148,10 +148,18 @@ export default class Uploader {
|
||||||
* @property {number} [chunkSize]
|
* @property {number} [chunkSize]
|
||||||
* @property {string} [providerName]
|
* @property {string} [providerName]
|
||||||
*
|
*
|
||||||
* @param {UploaderOptions} options
|
* @param {UploaderOptions} optionsIn
|
||||||
*/
|
*/
|
||||||
constructor(options) {
|
constructor(optionsIn) {
|
||||||
validateOptions(options)
|
validateOptions(optionsIn)
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
...optionsIn,
|
||||||
|
headers: {
|
||||||
|
...optionsIn.headers,
|
||||||
|
...optionsIn.companionOptions.uploadHeaders,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
this.providerName = options.providerName
|
this.providerName = options.providerName
|
||||||
this.options = options
|
this.options = options
|
||||||
|
|
@ -682,7 +690,7 @@ export default class Uploader {
|
||||||
try {
|
try {
|
||||||
const httpMethod =
|
const httpMethod =
|
||||||
(this.options.httpMethod || '').toUpperCase() === 'PUT' ? 'put' : 'post'
|
(this.options.httpMethod || '').toUpperCase() === 'PUT' ? 'put' : 'post'
|
||||||
const runRequest = await got[httpMethod]
|
const runRequest = got[httpMethod]
|
||||||
|
|
||||||
const response = await runRequest(url, reqOptions)
|
const response = await runRequest(url, reqOptions)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,9 @@ const getConfigFromEnv = () => {
|
||||||
process.env.COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS === 'true',
|
process.env.COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS === 'true',
|
||||||
testDynamicOauthCredentialsSecret:
|
testDynamicOauthCredentialsSecret:
|
||||||
process.env.COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS_SECRET,
|
process.env.COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS_SECRET,
|
||||||
|
uploadHeaders: process.env.COMPANION_UPLOAD_HEADERS
|
||||||
|
? JSON.parse(process.env.COMPANION_UPLOAD_HEADERS)
|
||||||
|
: undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import request from 'supertest'
|
import request from 'supertest'
|
||||||
import { afterAll, describe, expect, it, test, vi } from 'vitest'
|
import { afterAll, afterEach, describe, expect, it, test, vi } from 'vitest'
|
||||||
import packageJson from '../package.json' with { type: 'json' }
|
import packageJson from '../package.json' with { type: 'json' }
|
||||||
import * as tokenService from '../src/server/helpers/jwt.js'
|
import * as tokenService from '../src/server/helpers/jwt.js'
|
||||||
import * as defaults from './fixtures/constants.js'
|
import * as defaults from './fixtures/constants.js'
|
||||||
|
|
@ -37,8 +37,10 @@ const authData = {
|
||||||
const token = tokenService.generateEncryptedAuthToken(authData, secret)
|
const token = tokenService.generateEncryptedAuthToken(authData, secret)
|
||||||
const OAUTH_STATE = 'some-cool-nice-encrytpion'
|
const OAUTH_STATE = 'some-cool-nice-encrytpion'
|
||||||
|
|
||||||
afterAll(() => {
|
afterEach(() => {
|
||||||
nock.cleanAll()
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
afterAll(() => {
|
||||||
nock.restore()
|
nock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import request from 'supertest'
|
import request from 'supertest'
|
||||||
import { afterAll, describe, expect, test, vi } from 'vitest'
|
import {
|
||||||
|
afterAll,
|
||||||
|
afterEach,
|
||||||
|
beforeEach,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
test,
|
||||||
|
vi,
|
||||||
|
} from 'vitest'
|
||||||
import * as tokenService from '../src/server/helpers/jwt.js'
|
import * as tokenService from '../src/server/helpers/jwt.js'
|
||||||
import { nockZoomRevoke, expects as zoomExpects } from './fixtures/zoom.js'
|
import { nockZoomRevoke, expects as zoomExpects } from './fixtures/zoom.js'
|
||||||
import { getServer } from './mockserver.js'
|
import { getServer } from './mockserver.js'
|
||||||
|
|
@ -21,32 +29,35 @@ const authData = {
|
||||||
}
|
}
|
||||||
const token = tokenService.generateEncryptedAuthToken(authData, secret)
|
const token = tokenService.generateEncryptedAuthToken(authData, secret)
|
||||||
|
|
||||||
afterAll(() => {
|
afterEach(() => {
|
||||||
nock.cleanAll()
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
afterAll(() => {
|
||||||
nock.restore()
|
nock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('providers requests with remote oauth keys', () => {
|
describe('providers requests with remote oauth keys', () => {
|
||||||
// mocking request module used to fetch custom oauth credentials
|
beforeEach(() => {
|
||||||
nock('http://localhost:2111')
|
// mocking request module used to fetch custom oauth credentials
|
||||||
.post('/zoom-keys')
|
nock('http://localhost:2111')
|
||||||
// @ts-ignore
|
.post('/zoom-keys')
|
||||||
.reply((uri, { provider, parameters }) => {
|
// @ts-ignore
|
||||||
if (provider !== 'zoom' || parameters !== 'ZOOM-CREDENTIALS-PARAMS')
|
.reply((uri, { provider, parameters }) => {
|
||||||
return [400]
|
if (provider !== 'zoom' || parameters !== 'ZOOM-CREDENTIALS-PARAMS')
|
||||||
|
return [400]
|
||||||
|
|
||||||
return [
|
return [
|
||||||
200,
|
200,
|
||||||
{
|
{
|
||||||
credentials: {
|
credentials: {
|
||||||
key: remoteZoomKey,
|
key: remoteZoomKey,
|
||||||
secret: remoteZoomSecret,
|
secret: remoteZoomSecret,
|
||||||
verificationToken: remoteZoomVerificationToken,
|
verificationToken: remoteZoomVerificationToken,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
]
|
||||||
]
|
})
|
||||||
})
|
})
|
||||||
.persist()
|
|
||||||
|
|
||||||
test('zoom logout with remote oauth keys happy path', async () => {
|
test('zoom logout with remote oauth keys happy path', async () => {
|
||||||
nockZoomRevoke({ key: remoteZoomKey, secret: remoteZoomSecret })
|
nockZoomRevoke({ key: remoteZoomKey, secret: remoteZoomSecret })
|
||||||
|
|
@ -69,6 +80,8 @@ describe('providers requests with remote oauth keys', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('zoom logout with wrong credentials params', async () => {
|
test('zoom logout with wrong credentials params', async () => {
|
||||||
|
nockZoomRevoke({ key: remoteZoomKey, secret: remoteZoomSecret })
|
||||||
|
|
||||||
const params = { params: 'WRONG-ZOOM-CREDENTIALS-PARAMS' }
|
const params = { params: 'WRONG-ZOOM-CREDENTIALS-PARAMS' }
|
||||||
const encodedParams = Buffer.from(
|
const encodedParams = Buffer.from(
|
||||||
JSON.stringify(params),
|
JSON.stringify(params),
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import request from 'supertest'
|
import request from 'supertest'
|
||||||
import { afterAll, describe, test, vi } from 'vitest'
|
import { afterAll, afterEach, beforeEach, describe, test, vi } from 'vitest'
|
||||||
import { getServer } from './mockserver.js'
|
import { getServer } from './mockserver.js'
|
||||||
|
|
||||||
vi.mock('express-prom-bundle')
|
vi.mock('express-prom-bundle')
|
||||||
|
|
||||||
afterAll(() => {
|
afterEach(() => {
|
||||||
nock.cleanAll()
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
afterAll(() => {
|
||||||
nock.restore()
|
nock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('handle deauthorization callback', () => {
|
describe('handle deauthorization callback', () => {
|
||||||
nock('https://api.zoom.us').post('/oauth/data/compliance').reply(200)
|
beforeEach(() => {
|
||||||
|
nock('https://api.zoom.us').post('/oauth/data/compliance').reply(200)
|
||||||
|
})
|
||||||
|
|
||||||
test('providers without support for callback endpoint', async () => {
|
test('providers without support for callback endpoint', async () => {
|
||||||
return request(await getServer())
|
return request(await getServer())
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import { afterAll, describe, expect, test } from 'vitest'
|
import { afterAll, afterEach, describe, expect, test } from 'vitest'
|
||||||
import {
|
import {
|
||||||
FORBIDDEN_IP_ADDRESS,
|
FORBIDDEN_IP_ADDRESS,
|
||||||
getProtectedGot,
|
getProtectedGot,
|
||||||
} from '../src/server/helpers/request.js'
|
} from '../src/server/helpers/request.js'
|
||||||
|
|
||||||
afterAll(() => {
|
afterEach(() => {
|
||||||
nock.cleanAll()
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
afterAll(() => {
|
||||||
nock.restore()
|
nock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import request from 'supertest'
|
import request from 'supertest'
|
||||||
import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest'
|
import {
|
||||||
|
afterAll,
|
||||||
|
afterEach,
|
||||||
|
beforeEach,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
test,
|
||||||
|
vi,
|
||||||
|
} from 'vitest'
|
||||||
import * as tokenService from '../src/server/helpers/jwt.js'
|
import * as tokenService from '../src/server/helpers/jwt.js'
|
||||||
import * as providerModule from '../src/server/provider/index.js'
|
import * as providerModule from '../src/server/provider/index.js'
|
||||||
import * as defaults from './fixtures/constants.js'
|
import * as defaults from './fixtures/constants.js'
|
||||||
|
|
@ -76,16 +84,17 @@ function nockGetCurrentAccount(times = 1) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeEach(() => {
|
||||||
const url = new URL(defaults.THUMBNAIL_URL)
|
const url = new URL(defaults.THUMBNAIL_URL)
|
||||||
nock(url.origin)
|
nock(url.origin)
|
||||||
.get(url.pathname)
|
.get(url.pathname)
|
||||||
.reply(200, () => '')
|
.reply(200, () => '')
|
||||||
.persist()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(() => {
|
afterEach(() => {
|
||||||
nock.cleanAll()
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
afterAll(() => {
|
||||||
nock.restore()
|
nock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -543,13 +552,6 @@ describe('logout of provider', () => {
|
||||||
await runTest('box')
|
await runTest('box')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('dropbox', async () => {
|
|
||||||
nock('https://api.dropboxapi.com')
|
|
||||||
.post('/2/auth/token/revoke')
|
|
||||||
.reply(200, {})
|
|
||||||
await runTest('dropbox')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('drive', async () => {
|
test('drive', async () => {
|
||||||
nock('https://accounts.google.com')
|
nock('https://accounts.google.com')
|
||||||
.post('/o/oauth2/revoke?token=token+value')
|
.post('/o/oauth2/revoke?token=token+value')
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import { once } from 'node:events'
|
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import { createServer } from 'node:http'
|
|
||||||
import { Readable } from 'node:stream'
|
import { Readable } from 'node:stream'
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import { afterAll, describe, expect, test, vi } from 'vitest'
|
import { afterAll, afterEach, describe, expect, test, vi } from 'vitest'
|
||||||
import Emitter from '../src/server/emitter/index.js'
|
import Emitter from '../src/server/emitter/index.js'
|
||||||
import Uploader, { ValidationError } from '../src/server/Uploader.js'
|
import Uploader, { ValidationError } from '../src/server/Uploader.js'
|
||||||
import standalone from '../src/standalone/index.js'
|
import standalone from '../src/standalone/index.js'
|
||||||
|
|
@ -12,8 +10,10 @@ import * as socketClient from './mocksocket.js'
|
||||||
vi.mock('tus-js-client')
|
vi.mock('tus-js-client')
|
||||||
vi.mock('express-prom-bundle')
|
vi.mock('express-prom-bundle')
|
||||||
|
|
||||||
afterAll(() => {
|
afterEach(() => {
|
||||||
nock.cleanAll()
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
afterAll(() => {
|
||||||
nock.restore()
|
nock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ const { companionOptions } = standalone()
|
||||||
|
|
||||||
const mockReq = {}
|
const mockReq = {}
|
||||||
|
|
||||||
describe('uploader with tus protocol', () => {
|
describe('uploader', () => {
|
||||||
test('uploader respects uploadUrls', async () => {
|
test('uploader respects uploadUrls', async () => {
|
||||||
const opts = {
|
const opts = {
|
||||||
endpoint: 'http://localhost/files',
|
endpoint: 'http://localhost/files',
|
||||||
|
|
@ -207,12 +207,14 @@ describe('uploader with tus protocol', () => {
|
||||||
useFormData,
|
useFormData,
|
||||||
includeSize = true,
|
includeSize = true,
|
||||||
address = 'localhost',
|
address = 'localhost',
|
||||||
|
// @ts-ignore
|
||||||
|
extraCompanionOpts,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const fileContent = Buffer.from('Some file content')
|
const fileContent = Buffer.from('Some file content')
|
||||||
const stream = Readable.from([fileContent])
|
const stream = Readable.from([fileContent])
|
||||||
|
|
||||||
const opts = {
|
const opts = {
|
||||||
companionOptions,
|
companionOptions: { ...companionOptions, ...extraCompanionOpts },
|
||||||
endpoint: `http://${address}`,
|
endpoint: `http://${address}`,
|
||||||
protocol: 'multipart',
|
protocol: 'multipart',
|
||||||
size: includeSize ? fileContent.length : undefined,
|
size: includeSize ? fileContent.length : undefined,
|
||||||
|
|
@ -227,32 +229,33 @@ describe('uploader with tus protocol', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
test('upload functions with xhr protocol', async () => {
|
test('upload functions with xhr protocol', async () => {
|
||||||
let alreadyCalled = false
|
nock('http://localhost').post('/').reply(200, 'OK')
|
||||||
// We are creating our own test server for this test
|
const ret = await runMultipartTest()
|
||||||
// instead of using nock because of a bug when passing a Node.js stream to got.
|
expect(ret).toMatchObject({
|
||||||
// Ref: https://github.com/nock/nock/issues/2595
|
url: null,
|
||||||
const server = createServer((req, res) => {
|
extraData: { response: expect.anything(), bytesUploaded: 17 },
|
||||||
if (alreadyCalled) throw new Error('already called')
|
})
|
||||||
alreadyCalled = true
|
})
|
||||||
if (req.url === '/' && req.method === 'POST') {
|
|
||||||
res.writeHead(200)
|
|
||||||
res.end('OK')
|
|
||||||
}
|
|
||||||
}).listen()
|
|
||||||
try {
|
|
||||||
await once(server, 'listening')
|
|
||||||
|
|
||||||
const ret = await runMultipartTest({
|
test('header companion option gets passed along to destination endpoint', async () => {
|
||||||
// @ts-ignore
|
nock('http://localhost')
|
||||||
address: `localhost:${server.address().port}`,
|
.post('/')
|
||||||
})
|
.matchHeader('header-a', '1')
|
||||||
expect(ret).toMatchObject({
|
.matchHeader('header-b', '2')
|
||||||
url: null,
|
.reply(200, () => '')
|
||||||
extraData: { response: expect.anything(), bytesUploaded: 17 },
|
|
||||||
})
|
const ret = await runMultipartTest({
|
||||||
} finally {
|
// @ts-ignore
|
||||||
server.close()
|
extraCompanionOpts: {
|
||||||
}
|
uploadHeaders: { 'header-a': '1', 'header-b': '2' },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(ret).toMatchObject({
|
||||||
|
url: null,
|
||||||
|
extraData: { response: expect.anything(), bytesUploaded: 17 },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(ret.extraData.response?.headers?.['header-a']).toBeUndefined() // headers sent to destination, not received back
|
||||||
})
|
})
|
||||||
|
|
||||||
const formDataNoMetaMatch =
|
const formDataNoMetaMatch =
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import nock from 'nock'
|
import nock from 'nock'
|
||||||
import request from 'supertest'
|
import request from 'supertest'
|
||||||
import { afterAll, beforeAll, describe, expect, test, vi } from 'vitest'
|
import { afterAll, afterEach, describe, expect, test, vi } from 'vitest'
|
||||||
|
|
||||||
import { getServer } from './mockserver.js'
|
import { getServer } from './mockserver.js'
|
||||||
|
|
||||||
|
|
@ -18,14 +18,15 @@ vi.mock('../src/server/helpers/request.js', async () => {
|
||||||
const getMockServer = async () =>
|
const getMockServer = async () =>
|
||||||
getServer({ COMPANION_CLIENT_SOCKET_CONNECT_TIMEOUT: '0' })
|
getServer({ COMPANION_CLIENT_SOCKET_CONNECT_TIMEOUT: '0' })
|
||||||
|
|
||||||
beforeAll(() => {
|
const nockUrl = () =>
|
||||||
nock('http://url.myendpoint.com')
|
nock('http://url.myendpoint.com')
|
||||||
.get('/files')
|
.get('/files')
|
||||||
.reply(200, () => '')
|
.reply(200, () => '')
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(() => {
|
afterEach(() => {
|
||||||
nock.cleanAll()
|
nock.cleanAll()
|
||||||
|
})
|
||||||
|
afterAll(() => {
|
||||||
nock.restore()
|
nock.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -52,6 +53,8 @@ describe('url meta', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test.each(invalids)('return 400 for invalid url', async (urlCase) => {
|
test.each(invalids)('return 400 for invalid url', async (urlCase) => {
|
||||||
|
nockUrl()
|
||||||
|
|
||||||
return request(await getMockServer())
|
return request(await getMockServer())
|
||||||
.post('/url/meta')
|
.post('/url/meta')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
|
|
@ -65,6 +68,8 @@ describe('url meta', () => {
|
||||||
|
|
||||||
describe('url get', () => {
|
describe('url get', () => {
|
||||||
test('url download gets instanitated', async () => {
|
test('url download gets instanitated', async () => {
|
||||||
|
nockUrl()
|
||||||
|
|
||||||
return request(await getMockServer())
|
return request(await getMockServer())
|
||||||
.post('/url/get')
|
.post('/url/get')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
|
|
@ -80,6 +85,8 @@ describe('url get', () => {
|
||||||
test.each(invalids)(
|
test.each(invalids)(
|
||||||
'downloads are not instantiated for invalid urls',
|
'downloads are not instantiated for invalid urls',
|
||||||
async (urlCase) => {
|
async (urlCase) => {
|
||||||
|
nockUrl()
|
||||||
|
|
||||||
return request(await getMockServer())
|
return request(await getMockServer())
|
||||||
.post('/url/get')
|
.post('/url/get')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue