mirror of
https://github.com/asadbek064/hyparquet.git
synced 2025-12-15 02:16:37 +00:00
pass requestInit to fetch utils (#34)
* pass requestInit to fetch utils It will allow authentication * add tests
This commit is contained in:
parent
302ba655c3
commit
6ec836dac5
2
demo/bundle.min.js
vendored
2
demo/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -78,7 +78,7 @@ export async function asyncBufferFrom(from: AsyncBufferFrom): Promise<AsyncBuffe
|
||||
const key = JSON.stringify(from)
|
||||
const cached = cache.get(key)
|
||||
if (cached) return cached
|
||||
const asyncBuffer = asyncBufferFromUrl(from.url, from.byteLength).then(cachedAsyncBuffer)
|
||||
const asyncBuffer = asyncBufferFromUrl(from).then(cachedAsyncBuffer)
|
||||
cache.set(key, asyncBuffer)
|
||||
return asyncBuffer
|
||||
} else {
|
||||
|
||||
2
demo/workers/worker.min.js
vendored
2
demo/workers/worker.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
20
src/utils.js
20
src/utils.js
@ -40,10 +40,11 @@ export function concat(aaa, bbb) {
|
||||
* Get the byte length of a URL using a HEAD request.
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {RequestInit} [requestInit] fetch options
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
export async function byteLengthFromUrl(url) {
|
||||
return await fetch(url, { method: 'HEAD' })
|
||||
export async function byteLengthFromUrl(url, requestInit) {
|
||||
return await fetch(url, { ...requestInit, method: 'HEAD' })
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error(`fetch head failed ${res.status}`)
|
||||
const length = res.headers.get('Content-Length')
|
||||
@ -56,21 +57,24 @@ export async function byteLengthFromUrl(url) {
|
||||
* Construct an AsyncBuffer for a URL.
|
||||
*
|
||||
* @typedef {import('./types.js').AsyncBuffer} AsyncBuffer
|
||||
* @param {string} url
|
||||
* @param {number} [byteLength]
|
||||
* @param {object} options
|
||||
* @param {string} options.url
|
||||
* @param {number} [options.byteLength]
|
||||
* @param {RequestInit} [options.requestInit]
|
||||
* @returns {Promise<AsyncBuffer>}
|
||||
*/
|
||||
export async function asyncBufferFromUrl(url, byteLength) {
|
||||
export async function asyncBufferFromUrl({ url, byteLength, requestInit }) {
|
||||
// byte length from HEAD request
|
||||
byteLength ||= await byteLengthFromUrl(url)
|
||||
byteLength ||= await byteLengthFromUrl(url, requestInit)
|
||||
const init = requestInit || {}
|
||||
return {
|
||||
byteLength,
|
||||
async slice(start, end) {
|
||||
// fetch byte range from url
|
||||
const headers = new Headers()
|
||||
const headers = new Headers(init.headers)
|
||||
const endStr = end === undefined ? '' : end - 1
|
||||
headers.set('Range', `bytes=${start}-${endStr}`)
|
||||
const res = await fetch(url, { headers })
|
||||
const res = await fetch(url, { ...init, headers })
|
||||
if (!res.ok || !res.body) throw new Error(`fetch failed ${res.status}`)
|
||||
return res.arrayBuffer()
|
||||
},
|
||||
|
||||
@ -67,6 +67,28 @@ describe('byteLengthFromUrl', () => {
|
||||
|
||||
await expect(byteLengthFromUrl('https://example.com')).rejects.toThrow('missing content length')
|
||||
})
|
||||
|
||||
|
||||
it ('passes authentication headers', async () => {
|
||||
global.fetch = vi.fn().mockImplementation((url, options) => {
|
||||
if (new Headers(options.headers).get('Authorization') !== 'Bearer token') {
|
||||
return Promise.resolve({
|
||||
ok: false,
|
||||
status: 401,
|
||||
})}
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
headers: new Map([['Content-Length', '1024']]),
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
const result = await byteLengthFromUrl('https://example.com', { headers: { Authorization: 'Bearer token' } } )
|
||||
expect(result).toBe(1024)
|
||||
expect(fetch).toHaveBeenCalledWith('https://example.com', { method: 'HEAD', headers: { Authorization: 'Bearer token' } })
|
||||
|
||||
await expect(byteLengthFromUrl('https://example.com')).rejects.toThrow('fetch head failed 401')
|
||||
})
|
||||
})
|
||||
|
||||
describe('asyncBufferFromUrl', () => {
|
||||
@ -77,12 +99,12 @@ describe('asyncBufferFromUrl', () => {
|
||||
headers: new Map([['Content-Length', '1024']]),
|
||||
})
|
||||
|
||||
const buffer = await asyncBufferFromUrl('https://example.com')
|
||||
const buffer = await asyncBufferFromUrl({ url: 'https://example.com' })
|
||||
expect(buffer.byteLength).toBe(1024)
|
||||
})
|
||||
|
||||
it('uses provided byte length if given', async () => {
|
||||
const buffer = await asyncBufferFromUrl('https://example.com', 2048)
|
||||
const buffer = await asyncBufferFromUrl({ url: 'https://example.com', byteLength: 2048 })
|
||||
expect(buffer.byteLength).toBe(2048)
|
||||
expect(fetch).toHaveBeenCalledOnce()
|
||||
})
|
||||
@ -95,7 +117,7 @@ describe('asyncBufferFromUrl', () => {
|
||||
arrayBuffer: () => Promise.resolve(mockArrayBuffer),
|
||||
})
|
||||
|
||||
const buffer = await asyncBufferFromUrl('https://example.com', 1024)
|
||||
const buffer = await asyncBufferFromUrl({ url: 'https://example.com', byteLength: 1024 })
|
||||
const result = await buffer.slice(0, 100)
|
||||
|
||||
expect(result).toBe(mockArrayBuffer)
|
||||
@ -112,7 +134,7 @@ describe('asyncBufferFromUrl', () => {
|
||||
arrayBuffer: () => Promise.resolve(mockArrayBuffer),
|
||||
})
|
||||
|
||||
const buffer = await asyncBufferFromUrl('https://example.com', 1024)
|
||||
const buffer = await asyncBufferFromUrl({ url: 'https://example.com', byteLength: 1024 })
|
||||
await buffer.slice(100)
|
||||
|
||||
expect(fetch).toHaveBeenCalledWith('https://example.com', {
|
||||
@ -126,7 +148,58 @@ describe('asyncBufferFromUrl', () => {
|
||||
status: 404,
|
||||
})
|
||||
|
||||
const buffer = await asyncBufferFromUrl('https://example.com', 1024)
|
||||
const buffer = await asyncBufferFromUrl({ url: 'https://example.com', byteLength: 1024 })
|
||||
await expect(buffer.slice(0, 100)).rejects.toThrow('fetch failed 404')
|
||||
})
|
||||
|
||||
it('passes authentication headers to get the byteLength', async () => {
|
||||
global.fetch = vi.fn().mockImplementation((url, options) => {
|
||||
if (new Headers(options.headers).get('Authorization') !== 'Bearer token') {
|
||||
return Promise.resolve({
|
||||
ok: false,
|
||||
status: 401,
|
||||
})
|
||||
}
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
headers: new Map([['Content-Length', '1024']]),
|
||||
})
|
||||
})
|
||||
|
||||
expect(asyncBufferFromUrl({ url: 'https://example.com' })).rejects.toThrow('fetch head failed 401')
|
||||
|
||||
const buffer = await asyncBufferFromUrl({ url: 'https://example.com', requestInit: { headers: { Authorization: 'Bearer token' } } } )
|
||||
expect(buffer.byteLength).toBe(1024)
|
||||
})
|
||||
|
||||
it ('passes authentication headers to fetch byte range', async () => {
|
||||
const mockArrayBuffer = new ArrayBuffer(100)
|
||||
global.fetch = vi.fn().mockImplementation((url, options) => {
|
||||
if (new Headers(options.headers).get('Authorization') !== 'Bearer token') {
|
||||
return Promise.resolve({
|
||||
ok: false,
|
||||
status: 401,
|
||||
})
|
||||
}
|
||||
if (options.headers.get('Range') !== 'bytes=0-99') {
|
||||
return Promise.resolve({
|
||||
ok: false,
|
||||
status: 404,
|
||||
})
|
||||
}
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
body: {},
|
||||
arrayBuffer: () => Promise.resolve(mockArrayBuffer),
|
||||
})
|
||||
})
|
||||
|
||||
const noHeaders = await asyncBufferFromUrl({ url: 'https://example.com', byteLength: 1024 })
|
||||
expect(noHeaders.slice(0, 100)).rejects.toThrow('fetch failed 401')
|
||||
|
||||
const withHeaders = await asyncBufferFromUrl({ url: 'https://example.com', byteLength: 1024, requestInit: { headers: { Authorization: 'Bearer token' } } } )
|
||||
expect (await withHeaders.slice(0, 100)).toBe(mockArrayBuffer)
|
||||
|
||||
expect (withHeaders.slice(0, 10)).rejects.toThrow('fetch failed 404')
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user