Skip to content

Commit

Permalink
fix: delegations model tries to handle if row.bytes is Array not Buff…
Browse files Browse the repository at this point in the history
…er (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/a6dafcb0c44b20fa87bbe81d7a982fb13387084e/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 <contact@gozala.io>
  • Loading branch information
gobengo and Gozala committed Mar 3, 2023
1 parent a6dafcb commit 030e7b7
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 4 deletions.
17 changes: 17 additions & 0 deletions packages/access-api/src/models/delegations.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,20 @@ export function createDelegationRowUpdate(d) {
bytes: delegationsToBytes([d]),
}
}

/**
* @param {Array<number> | 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)
}
}
2 changes: 1 addition & 1 deletion packages/access-api/src/service/voucher-redeem.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 11 additions & 2 deletions packages/access-api/src/utils/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'

/**
Expand Down Expand Up @@ -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),
Expand Down
4 changes: 3 additions & 1 deletion packages/access-api/src/utils/d1.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,10 @@ export class D1Error extends Error {
/**
* @template S
* @param {D1Database} d1
* @param {Record<string, (v: unknown) => unknown>} [resultTransforms]
* @returns {import('../types/database.js').Database<S>}
*/
export function createD1Database(d1) {
export function createD1Database(d1, resultTransforms = {}) {
/** @type {Kysely<S>} */
const kdb = new Kysely({
dialect: new D1Dialect({ database: d1 }),
Expand All @@ -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,
}),
],
})
Expand Down

0 comments on commit 030e7b7

Please sign in to comment.