-
Notifications
You must be signed in to change notification settings - Fork 22
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!: add blob list and remove #1385
Changes from 2 commits
bbc0433
9bbb24e
3d997a4
1331af0
a1486fe
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 |
---|---|---|
|
@@ -11,7 +11,8 @@ | |
* | ||
* @module | ||
*/ | ||
import { capability, Schema } from '@ucanto/validator' | ||
import { equals } from 'uint8arrays/equals' | ||
import { capability, Schema, fail, ok } from '@ucanto/validator' | ||
import { equalBlob, equalWith, SpaceDID } from './utils.js' | ||
|
||
/** | ||
|
@@ -70,6 +71,76 @@ export const add = capability({ | |
derives: equalBlob, | ||
}) | ||
|
||
/** | ||
* Capability can be used to remove the stored Blob from the (memory) | ||
* space identified by `with` field. | ||
*/ | ||
export const remove = capability({ | ||
can: 'blob/remove', | ||
/** | ||
* DID of the (memory) space where Blob is stored. | ||
*/ | ||
with: SpaceDID, | ||
nb: Schema.struct({ | ||
/** | ||
* A multihash digest of the blob payload bytes, uniquely identifying blob. | ||
*/ | ||
content: Schema.bytes(), | ||
}), | ||
derives: (claimed, delegated) => { | ||
if (claimed.with !== delegated.with) { | ||
return fail( | ||
`Expected 'with: "${delegated.with}"' instead got '${claimed.with}'` | ||
) | ||
} else if ( | ||
delegated.nb.content && | ||
!equals(delegated.nb.content, claimed.nb.content) | ||
) { | ||
return fail( | ||
`Link ${ | ||
claimed.nb.content ? `${claimed.nb.content}` : '' | ||
} violates imposed ${delegated.nb.content} constraint.` | ||
) | ||
} | ||
return ok({}) | ||
}, | ||
}) | ||
|
||
/** | ||
* Capability can be invoked to request a list of stored Blobs in the | ||
* (memory) space identified by `with` field. | ||
*/ | ||
export const list = capability({ | ||
can: 'blob/list', | ||
/** | ||
* DID of the (memory) space where Blobs to be listed are stored. | ||
*/ | ||
with: SpaceDID, | ||
nb: Schema.struct({ | ||
/** | ||
* A pointer that can be moved back and forth on the list. | ||
* It can be used to paginate a list for instance. | ||
*/ | ||
cursor: Schema.string().optional(), | ||
/** | ||
* Maximum number of items per page. | ||
*/ | ||
size: Schema.integer().optional(), | ||
/** | ||
* If true, return page of results preceding cursor. Defaults to false. | ||
*/ | ||
pre: Schema.boolean().optional(), | ||
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. Was this removed from the spec or no? 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. oh yeah, removed things in last commit 9bbb24e but missed this one. Good catch |
||
}), | ||
derives: (claimed, delegated) => { | ||
if (claimed.with !== delegated.with) { | ||
return fail( | ||
`Expected 'with: "${delegated.with}"' instead got '${claimed.with}'` | ||
) | ||
} | ||
return ok({}) | ||
}, | ||
}) | ||
|
||
// ⚠️ We export imports here so they are not omitted in generated typedefs | ||
// @see https://github.com/microsoft/TypeScript/issues/51548 | ||
export { Schema } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import * as Server from '@ucanto/server' | ||
import * as Blob from '@web3-storage/capabilities/blob' | ||
import * as API from '../types.js' | ||
|
||
/** | ||
* @param {API.BlobServiceContext} context | ||
* @returns {API.ServiceMethod<API.BlobList, API.BlobListSuccess, API.Failure>} | ||
*/ | ||
export function blobListProvider(context) { | ||
return Server.provide(Blob.list, async ({ capability }) => { | ||
const space = capability.with | ||
const { cursor, size } = capability.nb | ||
return await context.allocationsStorage.list(space, { size, cursor }) | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import * as Server from '@ucanto/server' | ||
import * as Blob from '@web3-storage/capabilities/blob' | ||
import * as API from '../types.js' | ||
|
||
import { RecordNotFoundErrorName } from '../errors.js' | ||
|
||
/** | ||
* @param {API.BlobServiceContext} context | ||
* @returns {API.ServiceMethod<API.BlobRemove, API.BlobRemoveSuccess, API.BlobRemoveFailure>} | ||
*/ | ||
export function blobRemoveProvider(context) { | ||
return Server.provide(Blob.remove, async ({ capability }) => { | ||
const space = capability.with | ||
const { content } = capability.nb | ||
const res = await context.allocationsStorage.remove(space, content) | ||
if (res.error && res.error.name === RecordNotFoundErrorName) { | ||
return { | ||
ok: { | ||
size: 0, | ||
}, | ||
} | ||
} | ||
|
||
return res | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,9 +5,13 @@ import type { | |
Failure, | ||
DID, | ||
} from '@ucanto/interface' | ||
import { BlobMultihash, BlobListItem } from '@web3-storage/capabilities/types' | ||
import { | ||
BlobMultihash, | ||
BlobListItem, | ||
BlobRemoveSuccess, | ||
} from '@web3-storage/capabilities/types' | ||
|
||
import { RecordKeyConflict, ListOptions, ListResponse } from '../types.js' | ||
import { RecordKeyConflict, ListResponse } from '../types.js' | ||
import { Storage } from './storage.js' | ||
|
||
export type TasksStorage = Storage<UnknownLink, Invocation> | ||
|
@@ -29,6 +33,16 @@ export interface AllocationsStorage { | |
space: DID, | ||
options?: ListOptions | ||
) => Promise<Result<ListResponse<BlobListItem>, Failure>> | ||
/** Removes an item from the table but fails if the item does not exist. */ | ||
remove: ( | ||
space: DID, | ||
digest: BlobMultihash | ||
) => Promise<Result<BlobRemoveSuccess, Failure>> | ||
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. We don't have a record not found failure? 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. Not Found should be success with size zero. See https://github.com/w3s-project/specs/blob/main/w3-blob.md#remove-blob-size This would be a general failure, like unexpected response from dynamo/store 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. Ah, ok, can you please change the comment above then? |
||
} | ||
|
||
export interface ListOptions { | ||
size?: number | ||
cursor?: string | ||
} | ||
|
||
export interface BlobModel { | ||
|
@@ -42,8 +56,7 @@ export interface BlobAddInput { | |
blob: BlobModel | ||
} | ||
|
||
export interface BlobAddOutput | ||
extends Omit<BlobAddInput, 'space' | 'cause'> {} | ||
export interface BlobAddOutput extends Omit<BlobAddInput, 'space' | 'cause'> {} | ||
|
||
export interface BlobGetOutput { | ||
blob: { digest: Uint8Array; size: number } | ||
|
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.
I think
content.digest
would be less confusing here and more aligned withadd
operation.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.
sounds a good call, I will change in spec
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.
storacha/specs#124