-
Notifications
You must be signed in to change notification settings - Fork 23
fix(blobs): return ETags and handle conditional reads locally #772
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -554,6 +554,36 @@ test('Handles conditional writes', async () => { | |
| await fs.rm(directory.path, { force: true, recursive: true }) | ||
| }) | ||
|
|
||
| test('Returns ETags and handles conditional reads', async () => { | ||
| const directory = await tmp.dir() | ||
| const server = new BlobsServer({ | ||
| directory: directory.path, | ||
| token, | ||
| }) | ||
| const { port } = await server.start() | ||
| const store = getStore({ | ||
| edgeURL: `http://localhost:${port}`, | ||
| name: 'my-store', | ||
| token, | ||
| siteID, | ||
| }) | ||
| const key = 'conditional-key' | ||
| const value = 'value' | ||
| const metadata = { name: 'test-metadata', } | ||
|
|
||
| const writeResult = await store.set(key, value, { metadata }) | ||
| const etag = writeResult.etag | ||
|
|
||
| expect(etag).toBeTypeOf('string') | ||
| expect(await store.getWithMetadata(key)).toEqual({ data: value, etag, metadata }) | ||
| expect(await store.getMetadata(key)).toEqual({ etag, metadata }) | ||
| expect(await store.getWithMetadata(key, { etag: '"stale-etag"' })).toEqual({ data: value, etag, metadata }) | ||
| expect(await store.getWithMetadata(key, { etag })).toEqual({ data: null, etag, metadata }) | ||
|
|
||
| await server.stop() | ||
| await fs.rm(directory.path, { force: true, recursive: true }) | ||
|
Comment on lines
+583
to
+584
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Run cleanup in a If setup, a request, or an assertion fails, the cleanup calls are skipped. The HTTP server and temporary directory can remain active and affect later tests. Move both cleanup calls into 🤖 Prompt for AI Agents |
||
| }) | ||
|
|
||
| test('Deletes all blobs from a store', async () => { | ||
| const directory = await tmp.dir() | ||
| const server = new BlobsServer({ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -210,7 +210,8 @@ export class BlobsServer { | |
|
|
||
| this.dispatchOnRequestEvent(Operation.GET, url) | ||
|
|
||
| const headers: Record<string, string> = {} | ||
| const etag = await BlobsServer.generateETag(dataPath) | ||
| const headers: Record<string, string> = { etag } | ||
|
Comment on lines
+213
to
+214
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift Bind the ETag to the bytes returned.
🤖 Prompt for AI Agents |
||
|
|
||
| try { | ||
| const rawData = await fs.readFile(metadataPath, 'utf8') | ||
|
|
@@ -226,6 +227,10 @@ export class BlobsServer { | |
| } | ||
| } | ||
|
|
||
| if (req.headers.get('if-none-match') === etag) { | ||
| return new Response(null, { headers, status: 304 }) | ||
|
Comment on lines
+230
to
+231
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Parse Clients can send 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| try { | ||
| const fileStream = createReadStream(dataPath) | ||
| const chunks: Buffer[] = [] | ||
|
|
@@ -260,9 +265,11 @@ export class BlobsServer { | |
| const rawData = await fs.readFile(metadataPath, 'utf8') | ||
| const metadata = JSON.parse(rawData) | ||
| const encodedMetadata = encodeMetadata(metadata) | ||
| const etag = await BlobsServer.generateETag(dataPath) | ||
|
|
||
| return new Response(null, { | ||
| headers: { | ||
| etag, | ||
| [METADATA_HEADER_INTERNAL]: encodedMetadata ?? '', | ||
| }, | ||
| }) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Assert that the ETag is valid, not only that it is a string.
BlobsServer.generateETagreturns an empty string when stat fails. This assertion accepts that value, and the later expectations reuse it, so the test can pass without a usable ETag or conditional validator. Assert a non-empty quoted entity-tag.🤖 Prompt for AI Agents