Skip to content

Commit

Permalink
feat: delegation.toJSON (#186)
Browse files Browse the repository at this point in the history
* fix: toJSON behavior on the ucan.data

* feat: delegation to-json
  • Loading branch information
Gozala authored Dec 15, 2022
1 parent d1ee6b6 commit f8ffa74
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 21 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"devDependencies": {
"mocha": "^10.1.0",
"prettier": "2.7.1",
"typescript": "4.8.3"
"typescript": "^4.8.4"
},
"prettier": {
"trailingComma": "es5",
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/delegation.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ export class Delegation {
iterate() {
return it(this)
}

/**
* @returns {API.DelegationJSON<this>}
*/
toJSON() {
return /** @type {any} */ ({
...this.data.toJSON(),
'/': this.cid.toString(),
prf: this.proofs.map(proof =>
isDelegation(proof) ? proof : { '/': proof.toString() }
),
})
}
}

/**
Expand Down
121 changes: 119 additions & 2 deletions packages/core/test/delegation.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert, test } from './test.js'
import { Delegation, UCAN, isDelegation, parseLink } from '../src/lib.js'
import { alice, bob, mallory, service } from './fixtures.js'
import { Delegation, UCAN, delegate, parseLink } from '../src/lib.js'
import { alice, bob, mallory, service as w3 } from './fixtures.js'
import { base64 } from 'multiformats/bases/base64'
const utf8 = new TextEncoder()

Expand Down Expand Up @@ -132,3 +132,120 @@ test('delegation.data.toJSON with bytes', async () => {
s: { '/': { bytes: base64.baseEncode(ucan.signature) } },
})
})

test('toJSON delegation', async () => {
const ucan = await delegate({
issuer: alice,
audience: w3,
capabilities: [
{
with: alice.did(),
can: 'test/echo',
nb: {
message: 'data:1',
},
},
],
expiration: Infinity,
})

assert.deepEqual(toJSON(ucan), {
'/': ucan.cid.toString(),
v: ucan.version,
iss: alice.did(),
aud: w3.did(),
att: [
{
nb: {
message: 'data:1',
},
can: 'test/echo',
with: alice.did(),
},
],
exp: null,
prf: [],
s: {
'/': { bytes: base64.baseEncode(ucan.signature) },
},
})
})

test('toJSON delegation chain', async () => {
const proof = await delegate({
issuer: bob,
audience: alice,
capabilities: [
{
with: bob.did(),
can: 'test/echo',
},
],
})

const proof2 = await delegate({
issuer: mallory,
audience: alice,
capabilities: [
{
with: mallory.did(),
can: 'test/echo',
},
],
})

const ucan = await delegate({
issuer: alice,
audience: w3,
capabilities: [
{
with: bob.did(),
can: 'test/echo',
nb: {
message: 'data:hi',
},
},
],
proofs: [proof, proof2.cid],
})

assert.deepEqual(toJSON(ucan), {
'/': ucan.cid.toString(),
v: ucan.version,
iss: alice.did(),
aud: w3.did(),
att: [
{
with: bob.did(),
can: 'test/echo',
nb: {
message: 'data:hi',
},
},
],
exp: ucan.expiration,
prf: [
{
'/': proof.cid.toString(),
iss: bob.did(),
aud: alice.did(),
att: [
{
with: bob.did(),
can: 'test/echo',
},
],
exp: proof.expiration,
v: proof.version,
s: { '/': { bytes: base64.baseEncode(proof.signature) } },
prf: [],
},
{
'/': proof2.cid.toString(),
},
],
s: {
'/': { bytes: base64.baseEncode(ucan.signature) },
},
})
})
42 changes: 41 additions & 1 deletion packages/interface/src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ import {
Principal,
MulticodecCode,
SigAlg,
ToJSON,
SignatureJSON,
JSONUnknown,
IntoJSON,
JSONObject,
} from '@ipld/dag-ucan'
import { Link, Block as IPLDBlock } from 'multiformats'
import { Link, UnknownLink, Block as IPLDBlock, ToString } from 'multiformats'
import * as UCAN from '@ipld/dag-ucan'
import {
CanIssue,
Expand Down Expand Up @@ -59,6 +64,10 @@ export type {
MultibaseEncoder,
MulticodecCode,
Principal,
ToJSON,
ToString,
UnknownLink,
JSONUnknown,
}
export * as UCAN from '@ipld/dag-ucan'

Expand Down Expand Up @@ -161,8 +170,39 @@ export interface Delegation<C extends Capabilities = Capabilities> {
iterate(): IterableIterator<Delegation>

signature: Signature
version: UCAN.Version

toJSON(): DelegationJSON<this>
}

export type DelegationJSON<T extends Delegation = Delegation> = ToJSON<
T,
{
'/': ToString<T['cid']>
v: T['version']
iss: DID
aud: DID
att: ToJSON<
T['capabilities'],
T['capabilities'] &
UCAN.Tuple<{ with: UCAN.Resource; can: UCAN.Ability; nb?: JSONObject }>
>
exp: T['expiration']
nbf?: T['notBefore'] & {}
nnc?: T['nonce'] & {}
fct: ToJSON<T['facts']>
prf: ProofJSON[] & JSONUnknown[]
s: SignatureJSON<T['signature']>
}
>

export type ProofJSON = DelegationJSON | LinkJSON<UCANLink>

export type LinkJSON<T extends UnknownLink = UnknownLink> = ToJSON<
T,
{ '/': ToString<T> }
>

/**
* An Invocation represents a UCAN that can be presented to a service provider to
* invoke or "exercise" a {@link Capability}. You can think of invocations as a
Expand Down
28 changes: 11 additions & 17 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f8ffa74

Please sign in to comment.