Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use new API endpoints #147

Merged
merged 1 commit into from Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/client.ts
Expand Up @@ -97,14 +97,12 @@ export class Client {
}

const apiHeaders: Record<string, string> = { authorization: `Bearer ${this.token}` }
const url = new URL(`/api/v1/sites/${this.siteID}/blobs`, this.apiURL ?? 'https://api.netlify.com')
const url = new URL(`/api/v1/blobs/${this.siteID}/${storeName}`, this.apiURL ?? 'https://api.netlify.com')

for (const key in parameters) {
url.searchParams.set(key, parameters[key])
}

url.searchParams.set('context', storeName)

// If there is no key, we're dealing with the list endpoint, which is
// implemented directly in the Netlify API.
if (key === undefined) {
Expand Down
20 changes: 10 additions & 10 deletions src/list.test.ts
Expand Up @@ -59,7 +59,7 @@ describe('list', () => {
next_cursor: 'cursor_1',
}),
),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs?context=${storeName}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/${storeName}`,
})
.get({
headers: { authorization: `Bearer ${apiToken}` },
Expand All @@ -83,7 +83,7 @@ describe('list', () => {
next_cursor: 'cursor_2',
}),
),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs?cursor=cursor_1&context=${storeName}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/${storeName}?cursor=cursor_1`,
})
.get({
headers: { authorization: `Bearer ${apiToken}` },
Expand All @@ -100,7 +100,7 @@ describe('list', () => {
directories: [],
}),
),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs?cursor=cursor_2&context=${storeName}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/${storeName}?cursor=cursor_2`,
})

globalThis.fetch = mockStore.fetch
Expand Down Expand Up @@ -148,7 +148,7 @@ describe('list', () => {
next_cursor: 'cursor_1',
}),
),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs?directories=true&context=${storeName}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/${storeName}?directories=true`,
})
.get({
headers: { authorization: `Bearer ${apiToken}` },
Expand All @@ -172,7 +172,7 @@ describe('list', () => {
next_cursor: 'cursor_2',
}),
),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs?directories=true&cursor=cursor_1&context=${storeName}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/${storeName}?directories=true&cursor=cursor_1`,
})
.get({
headers: { authorization: `Bearer ${apiToken}` },
Expand All @@ -189,7 +189,7 @@ describe('list', () => {
directories: ['dir3'],
}),
),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs?directories=true&cursor=cursor_2&context=${storeName}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/${storeName}?directories=true&cursor=cursor_2`,
})
.get({
headers: { authorization: `Bearer ${apiToken}` },
Expand All @@ -206,7 +206,7 @@ describe('list', () => {
directories: [],
}),
),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs?prefix=dir2%2F&directories=true&context=${storeName}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/${storeName}?prefix=dir2%2F&directories=true`,
})

globalThis.fetch = mockStore.fetch
Expand Down Expand Up @@ -258,7 +258,7 @@ describe('list', () => {
],
}),
),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs?prefix=group%2F&context=${storeName}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/${storeName}?prefix=group%2F`,
})

globalThis.fetch = mockStore.fetch
Expand Down Expand Up @@ -303,7 +303,7 @@ describe('list', () => {
next_cursor: 'cursor_2',
}),
),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs?context=${storeName}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/${storeName}`,
})
.get({
headers: { authorization: `Bearer ${apiToken}` },
Expand All @@ -319,7 +319,7 @@ describe('list', () => {
],
}),
),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs?cursor=cursor_2&context=${storeName}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/${storeName}?cursor=cursor_2`,
})

globalThis.fetch = mockStore.fetch
Expand Down
58 changes: 29 additions & 29 deletions src/main.test.ts
Expand Up @@ -47,7 +47,7 @@ describe('get', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.get({
response: new Response(value),
Expand All @@ -56,7 +56,7 @@ describe('get', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.get({
response: new Response(value),
Expand All @@ -65,7 +65,7 @@ describe('get', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${complexKey}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${complexKey}`,
})
.get({
response: new Response(value),
Expand Down Expand Up @@ -97,7 +97,7 @@ describe('get', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.get({
response: new Response('Something went wrong', { status: 404 }),
Expand All @@ -120,7 +120,7 @@ describe('get', () => {
const mockStore = new MockFetch().get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(null, { status: 401 }),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})

globalThis.fetch = mockStore.fetch
Expand All @@ -142,7 +142,7 @@ describe('get', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.get({
response: new Response('Something went wrong', { status: 401 }),
Expand Down Expand Up @@ -361,7 +361,7 @@ describe('getMetadata', () => {
const mockStore = new MockFetch().head({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(null, { headers }),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})

globalThis.fetch = mockStore.fetch
Expand All @@ -383,7 +383,7 @@ describe('getMetadata', () => {
const mockStore = new MockFetch().head({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(null, { status: 404 }),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})

globalThis.fetch = mockStore.fetch
Expand All @@ -406,7 +406,7 @@ describe('getMetadata', () => {
const mockStore = new MockFetch().head({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(null, { headers }),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})

globalThis.fetch = mockStore.fetch
Expand Down Expand Up @@ -476,7 +476,7 @@ describe('getWithMetadata', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.get({
response: new Response(value, { headers: responseHeaders }),
Expand All @@ -485,7 +485,7 @@ describe('getWithMetadata', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.get({
response: new Response(value, { headers: responseHeaders }),
Expand Down Expand Up @@ -518,7 +518,7 @@ describe('getWithMetadata', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.get({
response: new Response('Something went wrong', { status: 404 }),
Expand Down Expand Up @@ -546,7 +546,7 @@ describe('getWithMetadata', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.get({
response: new Response(value, { headers: responseHeaders }),
Expand Down Expand Up @@ -585,7 +585,7 @@ describe('getWithMetadata', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: `${signedURL}b` })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.get({
headers: { 'if-none-match': etags.wrong },
Expand All @@ -595,7 +595,7 @@ describe('getWithMetadata', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: `${signedURL}a` })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.get({
headers: { 'if-none-match': etags.right },
Expand Down Expand Up @@ -679,7 +679,7 @@ describe('set', () => {
.put({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.put({
body: value,
Expand All @@ -690,7 +690,7 @@ describe('set', () => {
.put({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${complexKey}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${complexKey}`,
})
.put({
body: value,
Expand Down Expand Up @@ -724,7 +724,7 @@ describe('set', () => {
.put({
headers: { authorization: `Bearer ${apiToken}`, 'netlify-blobs-metadata': encodedMetadata },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.put({
body: value,
Expand Down Expand Up @@ -753,7 +753,7 @@ describe('set', () => {
const mockStore = new MockFetch().put({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(null, { status: 401 }),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})

globalThis.fetch = mockStore.fetch
Expand Down Expand Up @@ -795,7 +795,7 @@ describe('set', () => {
.put({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.put({
body: value,
Expand Down Expand Up @@ -949,7 +949,7 @@ describe('setJSON', () => {
.put({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.put({
body: JSON.stringify({ value }),
Expand Down Expand Up @@ -1058,7 +1058,7 @@ describe('delete', () => {
.delete({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.delete({
response: new Response(null),
Expand All @@ -1067,7 +1067,7 @@ describe('delete', () => {
.delete({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${complexKey}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${complexKey}`,
})
.delete({
response: new Response(null),
Expand All @@ -1093,7 +1093,7 @@ describe('delete', () => {
.delete({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})
.delete({
response: new Response(null, { status: 404 }),
Expand All @@ -1117,7 +1117,7 @@ describe('delete', () => {
const mockStore = new MockFetch().delete({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(null, { status: 401 }),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=production`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/production/${key}`,
})

globalThis.fetch = mockStore.fetch
Expand Down Expand Up @@ -1244,7 +1244,7 @@ describe('Deploy scope', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=deploy%3A${deployID}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/deploy:${deployID}/${key}`,
})
.get({
response: new Response(value),
Expand All @@ -1253,7 +1253,7 @@ describe('Deploy scope', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=deploy%3A${deployID}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/deploy:${deployID}/${key}`,
})
.get({
response: new Response(value),
Expand Down Expand Up @@ -1314,7 +1314,7 @@ describe('Deploy scope', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=deploy%3A${deployID}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/deploy:${deployID}/${key}`,
})
.get({
response: new Response(value),
Expand All @@ -1323,7 +1323,7 @@ describe('Deploy scope', () => {
.get({
headers: { authorization: `Bearer ${apiToken}` },
response: new Response(JSON.stringify({ url: signedURL })),
url: `https://api.netlify.com/api/v1/sites/${siteID}/blobs/${key}?context=deploy%3A${deployID}`,
url: `https://api.netlify.com/api/v1/blobs/${siteID}/deploy:${deployID}/${key}`,
})
.get({
response: new Response(value),
Expand Down
4 changes: 3 additions & 1 deletion src/retry.ts
@@ -1,6 +1,8 @@
import { env } from 'node:process'

import type { Fetcher } from './types.ts'

const DEFAULT_RETRY_DELAY = 5000
const DEFAULT_RETRY_DELAY = env.NODE_ENV === 'test' ? 1 : 5000
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Speeds up tests in the CI, as we don't have to wait a few seconds when a test is about to fail.

const MIN_RETRY_DELAY = 1000
const MAX_RETRY = 5
const RATE_LIMIT_HEADER = 'X-RateLimit-Reset'
Expand Down
6 changes: 2 additions & 4 deletions src/server.ts
Expand Up @@ -12,8 +12,7 @@ import { decodeMetadata, encodeMetadata, METADATA_HEADER_INTERNAL } from './meta
import { HTTPMethod } from './types.ts'
import { isNodeError, Logger } from './util.ts'

const API_URL_PATH = /\/api\/v1\/sites\/(?<site_id>[^/]+)\/blobs\/?(?<key>[^?]*)/
const DEFAULT_STORE = 'production'
const API_URL_PATH = /\/api\/v1\/blobs\/(?<site_id>[^/]+)\/(?<store_name>[^/]+)\/?(?<key>[^?]*)/

export enum Operation {
DELETE = 'delete',
Expand Down Expand Up @@ -367,10 +366,9 @@ export class BlobsServer {
return null
}

const fullURL = new URL(req.url, this.address)
const storeName = fullURL.searchParams.get('context') ?? DEFAULT_STORE
const key = apiURLMatch.groups?.key
const siteID = apiURLMatch.groups?.site_id as string
const storeName = apiURLMatch.groups?.store_name as string
const urlPath = [siteID, storeName, key].filter(Boolean) as string[]
const url = new URL(`/${urlPath.join('/')}?signature=${this.tokenHash}`, this.address)

Expand Down