Skip to content

Commit

Permalink
fix: DbDelegationsStorage#find throws UnexpectedDelegation w/ { row }…
Browse files Browse the repository at this point in the history
… if failed bytesToDelegations (#476)

Motivation:
* aid in debugging #471 
* hope this
* confirms that this error is coming from `DbDelegationsStorage#find` (I
see '.find' in the hard-to-read sentry stack trace)
* gives insight into what the db row is like (esp `row.bytes`) that
can't be parsed
  • Loading branch information
gobengo authored Mar 3, 2023
1 parent a205ad1 commit a6dafcb
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 4 deletions.
37 changes: 34 additions & 3 deletions packages/access-api/src/models/delegations.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
* @typedef {import("../types/database").Database<Tables>} DelegationsDatabase
*/

export const delegationsTable = /** @type {const} */ ('delegations_v2')

/**
* DelegationsStorage that persists using SQL.
* * should work with cloudflare D1
Expand All @@ -26,7 +28,7 @@ export class DbDelegationsStorage {
/** @type {DelegationsDatabase} */
#db
#tables = {
delegations: /** @type {const} */ ('delegations_v2'),
delegations: delegationsTable,
}

/**
Expand Down Expand Up @@ -105,12 +107,41 @@ export class DbDelegationsStorage {
}
}

/**
* indicates that processing failed due to encountering an unexpected delegation.
* e.g. if a delegation could not be parsed from underlying storage
*/
class UnexpectedDelegation extends Error {
name = 'UnexpectedDelegation'
}

/**
* @param {Pick<DelegationRow, 'bytes'>} row
* @returns {Ucanto.Delegation}
*/
function rowToDelegation(row) {
const delegations = bytesToDelegations(row.bytes)
/** @type {Ucanto.Delegation[]} */
let delegations = []
try {
delegations = bytesToDelegations(row.bytes)
} catch (error) {
if (
typeof error === 'object' &&
error &&
error.toString() === 'TypeError: Input should be a non-empty Uint8Array.'
) {
throw Object.assign(
new UnexpectedDelegation(`failed to create delegation from row`, {
cause: error,
}),
// adding these so they appear in sentry et al and can aid debugging
{
row,
}
)
}
throw error
}
if (delegations.length !== 1) {
throw new Error(
`unexpected number of delegations from bytes: ${delegations.length}`
Expand All @@ -123,7 +154,7 @@ function rowToDelegation(row) {
* @param {Ucanto.Delegation} d
* @returns {DelegationRowUpdate}
*/
function createDelegationRowUpdate(d) {
export function createDelegationRowUpdate(d) {
return {
cid: d.cid.toV1().toString(),
audience: d.audience.did(),
Expand Down
44 changes: 43 additions & 1 deletion packages/access-api/test/delegations-storage.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { context } from './helpers/context.js'
import { DbDelegationsStorage } from '../src/models/delegations.js'
import {
createDelegationRowUpdate,
DbDelegationsStorage,
delegationsTable,
} from '../src/models/delegations.js'
import { createD1Database } from '../src/utils/d1.js'
import * as assert from 'node:assert'
import { createSampleDelegation } from '../src/utils/ucan.js'
Expand Down Expand Up @@ -65,6 +69,44 @@ describe('DbDelegationsStorage', () => {
)
assert.deepEqual(carolDelegations.length, 0)
})

it('find throws UnexpectedDelegation when encountering row with empty bytes', async () => {
const { d1, issuer } = await context()
const db = createD1Database(d1)
const delegations = new DbDelegationsStorage(db)
const row = createDelegationRowUpdate(
await ucanto.delegate({
issuer,
audience: issuer,
capabilities: [{ can: '*', with: 'ucan:*' }],
})
)
// insert row with empty bytes
await db
.insertInto(delegationsTable)
.values([
{
...row,
bytes: Uint8Array.from([]),
},
])
.onConflict((oc) => oc.column('cid').doNothing())
.execute()
// now try to find
const find = () => collect(delegations.find({ audience: issuer.did() }))
let findError
try {
await find()
} catch (error) {
findError = error
}
assert.ok(findError && typeof findError === 'object')
assert.deepEqual(
'name' in findError && findError?.name,
'UnexpectedDelegation'
)
assert.ok('row' in findError, 'UnexpectedDelegation error contains row')
})
})

/**
Expand Down

0 comments on commit a6dafcb

Please sign in to comment.