Skip to content

Commit

Permalink
chore: allow any JSON object to be saved in blob store (#901)
Browse files Browse the repository at this point in the history
The blob store will need to store additional metadata, such as audio
duration, in the future. This changes its `metadata` type to any JSON
object to allow this.

It also adds a test for `blobStore.entry()` to ensure metadata is saved.

This change shouldn't affect the external API because the blob store is
not directly exposed. It's used internally by `BlobApi`, which did not
change.
  • Loading branch information
EvanHahn authored Oct 23, 2024
1 parent 100a973 commit 7dbed14
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 3 deletions.
5 changes: 3 additions & 2 deletions src/blob-store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import util from 'node:util'
import { discoveryKey } from 'hypercore-crypto'
import { TypedEmitter } from 'tiny-typed-emitter'
import { LiveDownload } from './live-download.js'
/** @import { JsonObject } from 'type-fest' */
/** @import { Readable as NodeReadable } from 'node:stream' */
/** @import { Readable as StreamxReadable, Writable } from 'streamx' */
/** @import { BlobId } from '../types.js' */
Expand Down Expand Up @@ -200,7 +201,7 @@ export class BlobStore {
* @param {Omit<BlobId, 'driveId'>} blobId
* @param {Buffer} blob
* @param {object} [options]
* @param {{mimeType: string}} [options.metadata] Metadata to store with the blob
* @param {JsonObject} [options.metadata] Metadata to store with the blob
* @returns {Promise<string>} discovery key as hex string of hyperdrive where blob is stored
*/
async put({ type, variant, name }, blob, options) {
Expand All @@ -212,7 +213,7 @@ export class BlobStore {
/**
* @param {Omit<BlobId, 'driveId'>} blobId
* @param {object} [options]
* @param {{mimeType: string}} [options.metadata] Metadata to store with the blob
* @param {JsonObject} [options.metadata] Metadata to store with the blob
* @returns {Writable & { driveId: string }}
*/
createWriteStream({ type, variant, name }, options) {
Expand Down
1 change: 1 addition & 0 deletions src/fastify-plugins/blobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ async function routes(fastify, options) {
// Extract the 'mimeType' property of the metadata and use it for the response header if found
if (
metadata &&
typeof metadata === 'object' &&
'mimeType' in metadata &&
typeof metadata.mimeType === 'string'
) {
Expand Down
26 changes: 26 additions & 0 deletions test/blob-store/blob-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,32 @@ test('blobStore.createWriteStream(blobId) and blobStore.createReadStream(blobId)
assert.deepEqual(bndlbuf, diskbuf, 'should be equal')
})

test('blobStore.entry includes metadata if present', async () => {
const { blobStore } = testenv()

const blobId = /** @type {const} */ ({
type: 'photo',
variant: 'original',
name: 'test-file',
})
const ws = blobStore.createWriteStream(blobId, {
metadata: {
foo: 'bar',
baz: [1, 2, 3],
},
})
await pipeline(fs.createReadStream(new URL(import.meta.url)), ws)

const entry = await blobStore.entry({
...blobId,
driveId: ws.driveId,
})
assert.deepEqual(entry?.value.metadata, {
foo: 'bar',
baz: [1, 2, 3],
})
})

test('blobStore.createReadStream should not wait', async () => {
const { blobStore } = testenv()
const expected = await readFile(new URL(import.meta.url))
Expand Down
3 changes: 2 additions & 1 deletion types/hyperdrive.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ declare module 'hyperdrive' {
import Hyperblobs, { BlobId } from 'hyperblobs'
import { Readable, Writable } from 'streamx'
import { TypedEmitter } from 'tiny-typed-emitter'
import { JsonValue } from 'type-fest'

interface HyperdriveOptions {
onwait: () => void
Expand Down Expand Up @@ -39,7 +40,7 @@ declare module 'hyperdrive' {
executable: boolean // whether the blob at path is an executable
linkname: null | string // if entry not symlink, otherwise a string to the entry this links to
blob: BlobId // a Hyperblob id that can be used to fetch the blob associated with this entry
metadata: null | object
metadata: JsonValue
}
}
}
Expand Down

0 comments on commit 7dbed14

Please sign in to comment.