From 02c0c281aab28825545b8dedd6eb7aac58f07016 Mon Sep 17 00:00:00 2001 From: Benjamin Goering <171782+gobengo@users.noreply.github.com> Date: Fri, 3 Mar 2023 14:30:01 -0800 Subject: [PATCH] fix: delegations model tries to handle if row.bytes is Array not Buffer (e.g. cloudflare) (#478) ## Rationale This is the error I get from staging atm (on `access/claim`) ``` claimResult { name: 'HandlerExecutionError', cause: { row: { cid: 'bafyreihqswtsrxvsp2pqf5a2jjiyb6zks3hynbfkhga7odwbat4ptxrf5e', bytes: [Array], issuer: 'did:mailto:dag.house:bengo', audience: 'did:key:z6MkkUymFxGqXWRqFNiaU4jCa2efexfDCNADqD7sQK3RbU2j', expiration: null, updated_at: '2023-03-03T16:41:54.999Z', inserted_at: '2023-03-03T16:41:54.999Z' }, name: 'UnexpectedDelegation', stack: 'UnexpectedDelegation: failed to create delegation from row\n' + ``` Notably `bytes` is `Array` here. Locally and in node.js, I think miniflare gives back a `Buffer` and (in nodejs) [this catches it](https://github.com/web3-storage/w3protocol/blob/660f773c5d52eaaba9b7177932edb0e92942dde7/packages/access-api/src/utils/d1.js#L77). But on cloudflare. This PR would cast the bytes column on delegations model to Uint8Array if the db/kysely hand it back as an Array. --------- Co-authored-by: Irakli Gozalishvili --- packages/access-api/src/models/delegations.js | 17 +++++++++++++++++ .../access-api/src/service/voucher-redeem.js | 2 +- packages/access-api/src/utils/context.js | 13 +++++++++++-- packages/access-api/src/utils/d1.js | 4 +++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/access-api/src/models/delegations.js b/packages/access-api/src/models/delegations.js index 3acc5cef7..b18efa166 100644 --- a/packages/access-api/src/models/delegations.js +++ b/packages/access-api/src/models/delegations.js @@ -162,3 +162,20 @@ export function createDelegationRowUpdate(d) { bytes: delegationsToBytes([d]), } } + +/** + * @param {Array | Buffer | unknown} sqlValue - value from kysely 'bytes' table - in node it could be a Buffer. In cloudflare it might be an Array + * @returns {ArrayBuffer|undefined} - undefined if unable to convert + */ +export function delegationsTableBytesToArrayBuffer(sqlValue) { + if (ArrayBuffer.isView(sqlValue)) { + return new Uint8Array( + sqlValue.buffer, + sqlValue.byteOffset, + sqlValue.byteLength + ) + } + if (Array.isArray(sqlValue)) { + return Uint8Array.from(sqlValue) + } +} diff --git a/packages/access-api/src/service/voucher-redeem.js b/packages/access-api/src/service/voucher-redeem.js index 6579a4e73..788302dcf 100644 --- a/packages/access-api/src/service/voucher-redeem.js +++ b/packages/access-api/src/service/voucher-redeem.js @@ -46,7 +46,7 @@ export function voucherRedeemProvider(ctx) { ) if (error) { - if (error.code === 'SQLITE_CONSTRAINT_PRIMARYKEY') { + if ('code' in error && error.code === 'SQLITE_CONSTRAINT_PRIMARYKEY') { return new Failure(`Space ${capability.nb.space} already registered.`) } else { throw error diff --git a/packages/access-api/src/utils/context.js b/packages/access-api/src/utils/context.js index 26a957f89..ed886de8f 100644 --- a/packages/access-api/src/utils/context.js +++ b/packages/access-api/src/utils/context.js @@ -9,7 +9,10 @@ import { Validations } from '../models/validations.js' import * as Email from './email.js' import { createUploadApiConnection } from '../service/upload-api-proxy.js' import { DID } from '@ucanto/core' -import { DbDelegationsStorage } from '../models/delegations.js' +import { + DbDelegationsStorage, + delegationsTableBytesToArrayBuffer, +} from '../models/delegations.js' import { createD1Database } from './d1.js' /** @@ -64,7 +67,13 @@ export function getContext(request, env, ctx) { config, url, models: { - delegations: new DbDelegationsStorage(createD1Database(config.DB)), + delegations: new DbDelegationsStorage( + createD1Database(config.DB, { + bytes: (v) => { + return delegationsTableBytesToArrayBuffer(v) ?? v + }, + }) + ), spaces: new Spaces(config.DB), validations: new Validations(config.VALIDATIONS), accounts: new Accounts(config.DB), diff --git a/packages/access-api/src/utils/d1.js b/packages/access-api/src/utils/d1.js index b4b615fb6..a8ab9b60c 100644 --- a/packages/access-api/src/utils/d1.js +++ b/packages/access-api/src/utils/d1.js @@ -142,9 +142,10 @@ export class D1Error extends Error { /** * @template S * @param {D1Database} d1 + * @param {Record unknown>} [resultTransforms] * @returns {import('../types/database.js').Database} */ -export function createD1Database(d1) { +export function createD1Database(d1, resultTransforms = {}) { /** @type {Kysely} */ const kdb = new Kysely({ dialect: new D1Dialect({ database: d1 }), @@ -154,6 +155,7 @@ export function createD1Database(d1) { expires_at: (v) => (typeof v === 'string' ? new Date(v) : null), inserted_at: (v) => new Date(v), updated_at: (v) => new Date(v), + ...resultTransforms, }), ], })