@netlify/blobs
Advanced tools
Comparing version
@@ -22,2 +22,5 @@ interface APICredentials { | ||
} | ||
interface SetOptions { | ||
ttl?: Date | number; | ||
} | ||
type BlobInput = ReadableStream | string | ArrayBuffer | Blob; | ||
@@ -31,2 +34,3 @@ export declare class Blobs { | ||
private getFinalRequest; | ||
private static getTTLHeaders; | ||
private isConfigured; | ||
@@ -48,7 +52,5 @@ private makeStoreRequest; | ||
}): Promise<string>; | ||
set(key: string, data: BlobInput, { ttl }?: { | ||
ttl?: Date | number; | ||
}): Promise<void>; | ||
setJSON(key: string, data: unknown): Promise<void>; | ||
set(key: string, data: BlobInput, { ttl }?: SetOptions): Promise<void>; | ||
setJSON(key: string, data: unknown, { ttl }?: SetOptions): Promise<void>; | ||
} | ||
export {}; |
@@ -41,3 +41,2 @@ var HTTPMethod; | ||
async getFinalRequest(key, method) { | ||
const finalMethod = method; | ||
if ('contextURL' in this.authentication) { | ||
@@ -48,3 +47,2 @@ return { | ||
}, | ||
method: finalMethod, | ||
url: `${this.authentication.contextURL}/${this.siteID}/${this.context}/${key}`, | ||
@@ -61,6 +59,21 @@ }; | ||
return { | ||
method: finalMethod, | ||
url, | ||
}; | ||
} | ||
static getTTLHeaders(ttl) { | ||
if (typeof ttl === 'number') { | ||
return { | ||
[EXPIRY_HEADER]: (Date.now() + ttl).toString(), | ||
}; | ||
} | ||
if (ttl instanceof Date) { | ||
return { | ||
[EXPIRY_HEADER]: ttl.getTime().toString(), | ||
}; | ||
} | ||
if (ttl === undefined) { | ||
return {}; | ||
} | ||
throw new TypeError(`'ttl' value must be a number or a Date, ${typeof ttl} found.`); | ||
} | ||
isConfigured() { | ||
@@ -73,3 +86,3 @@ return Boolean(this.authentication?.token) && Boolean(this.siteID); | ||
} | ||
const { headers: baseHeaders = {}, method: finalMethod, url } = await this.getFinalRequest(key, method); | ||
const { headers: baseHeaders = {}, url } = await this.getFinalRequest(key, method); | ||
const headers = { | ||
@@ -82,9 +95,8 @@ ...baseHeaders, | ||
} | ||
const res = await this.fetcher(url, { body, headers, method: finalMethod }); | ||
if (res.status === 404 && finalMethod === HTTPMethod.Get) { | ||
const res = await this.fetcher(url, { body, headers, method }); | ||
if (res.status === 404 && method === HTTPMethod.Get) { | ||
return null; | ||
} | ||
if (res.status !== 200) { | ||
const details = await res.text(); | ||
throw new Error(`${method} operation has failed: ${details}`); | ||
throw new Error(`${method} operation has failed: store returned a ${res.status} response`); | ||
} | ||
@@ -127,17 +139,9 @@ return res; | ||
async set(key, data, { ttl } = {}) { | ||
const headers = {}; | ||
if (typeof ttl === 'number') { | ||
headers[EXPIRY_HEADER] = (Date.now() + ttl).toString(); | ||
} | ||
else if (ttl instanceof Date) { | ||
headers[EXPIRY_HEADER] = ttl.getTime().toString(); | ||
} | ||
else if (ttl !== undefined) { | ||
throw new TypeError(`'ttl' value must be a number or a Date, ${typeof ttl} found.`); | ||
} | ||
const headers = Blobs.getTTLHeaders(ttl); | ||
await this.makeStoreRequest(key, HTTPMethod.Put, headers, data); | ||
} | ||
async setJSON(key, data) { | ||
async setJSON(key, data, { ttl } = {}) { | ||
const payload = JSON.stringify(data); | ||
const headers = { | ||
...Blobs.getTTLHeaders(ttl), | ||
'content-type': 'application/json', | ||
@@ -144,0 +148,0 @@ }; |
@@ -114,3 +114,3 @@ import { version as nodeVersion } from 'process'; | ||
}); | ||
expect(async () => await blobs.get(key)).rejects.toThrowError('get operation has failed: Something went wrong'); | ||
expect(async () => await blobs.get(key)).rejects.toThrowError('get operation has failed: store returned a 401 response'); | ||
}); | ||
@@ -268,2 +268,60 @@ test('Returns `null` when the blob entry contains an expiry date in the past', async () => { | ||
}); | ||
describe('setJSON', () => { | ||
test('Writes to the blob store using API credentials', async () => { | ||
expect.assertions(5); | ||
const fetcher = async (...args) => { | ||
const [url, options] = args; | ||
const headers = options?.headers; | ||
expect(options?.method).toBe('put'); | ||
if (url === `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`) { | ||
const data = JSON.stringify({ url: signedURL }); | ||
expect(headers.authorization).toBe(`Bearer ${apiToken}`); | ||
return new Response(data); | ||
} | ||
if (url === signedURL) { | ||
expect(options?.body).toBe(JSON.stringify({ value })); | ||
expect(headers['cache-control']).toBe('max-age=0, stale-while-revalidate=60'); | ||
return new Response(value); | ||
} | ||
throw new Error(`Unexpected fetch call: ${url}`); | ||
}; | ||
const blobs = new Blobs({ | ||
authentication: { | ||
token: apiToken, | ||
}, | ||
fetcher, | ||
siteID, | ||
}); | ||
await blobs.setJSON(key, { value }); | ||
}); | ||
test('Accepts a TTL parameter', async () => { | ||
expect.assertions(6); | ||
const ttl = new Date(Date.now() + 15000); | ||
const fetcher = async (...args) => { | ||
const [url, options] = args; | ||
const headers = options?.headers; | ||
expect(options?.method).toBe('put'); | ||
if (url === `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`) { | ||
const data = JSON.stringify({ url: signedURL }); | ||
expect(headers.authorization).toBe(`Bearer ${apiToken}`); | ||
return new Response(data); | ||
} | ||
if (url === signedURL) { | ||
expect(options?.body).toBe(JSON.stringify({ value })); | ||
expect(headers['cache-control']).toBe('max-age=0, stale-while-revalidate=60'); | ||
expect(headers['x-nf-expires-at']).toBe(ttl.getTime().toString()); | ||
return new Response(value); | ||
} | ||
throw new Error(`Unexpected fetch call: ${url}`); | ||
}; | ||
const blobs = new Blobs({ | ||
authentication: { | ||
token: apiToken, | ||
}, | ||
fetcher, | ||
siteID, | ||
}); | ||
await blobs.setJSON(key, { value }, { ttl }); | ||
}); | ||
}); | ||
describe('delete', () => { | ||
@@ -270,0 +328,0 @@ test('Deletes from the blob store using API credentials', async () => { |
{ | ||
"name": "@netlify/blobs", | ||
"version": "1.3.0", | ||
"version": "1.4.0", | ||
"description": "A JavaScript client for the Netlify Blob Store", | ||
@@ -5,0 +5,0 @@ "type": "module", |
31730
8.72%596
12.03%