Skip to content
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: get receipt support in client #1135

Merged
merged 4 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/w3up-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"test:browser": "playwright-test --runner mocha 'test/**/!(*.node).test.js'",
"mock": "run-p mock:*",
"mock:bucket-200": "PORT=9200 STATUS=200 node test/helpers/bucket-server.js",
"mock:receipts-server": "PORT=9201 node test/helpers/receipts-server.js",
"rc": "npm version prerelease --preid rc",
"docs": "npm run build && typedoc --out docs-generated",
"docs:markdown": "npm run build && docusaurus generate-typedoc"
Expand Down
4 changes: 3 additions & 1 deletion packages/w3up-client/src/base.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Agent } from '@web3-storage/access/agent'
import { serviceConf } from './service.js'
import { serviceConf, receiptsEndpoint } from './service.js'

export class Base {
/**
Expand All @@ -18,6 +18,7 @@ export class Base {
* @param {import('@web3-storage/access').AgentData} agentData
* @param {object} [options]
* @param {import('./types.js').ServiceConf} [options.serviceConf]
* @param {URL} [options.receiptsEndpoint]
*/
constructor(agentData, options = {}) {
this._serviceConf = options.serviceConf ?? serviceConf
Expand All @@ -27,6 +28,7 @@ export class Base {
url: this._serviceConf.access.channel.url,
connection: this._serviceConf.access,
})
this._receiptsEndpoint = options.receiptsEndpoint ?? receiptsEndpoint
}

/**
Expand Down
32 changes: 32 additions & 0 deletions packages/w3up-client/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Store as StoreCapabilities,
Upload as UploadCapabilities,
} from '@web3-storage/capabilities'
import { CAR } from '@ucanto/transport'
import { Base } from './base.js'
import * as Account from './account.js'
import { Space } from './space.js'
Expand Down Expand Up @@ -36,6 +37,7 @@ export class Client extends Base {
* @param {import('@web3-storage/access').AgentData} agentData
* @param {object} [options]
* @param {import('./types.js').ServiceConf} [options.serviceConf]
* @param {URL} [options.receiptsEndpoint]
*/
constructor(agentData, options) {
super(agentData, options)
Expand Down Expand Up @@ -142,6 +144,36 @@ export class Client extends Base {
return uploadCAR(conf, car, options)
}

/**
* Get a receipt for an executed task by its CID.
*
* @param {import('multiformats').UnknownLink} taskCid
*/
async getReceipt(taskCid) {
// Fetch receipt from endpoint
const workflowResponse = await fetch(
new URL(taskCid.toString(), this._receiptsEndpoint)
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle 404

/* c8 ignore start */
if (!workflowResponse.ok) {
throw new Error(
`no receipt available for requested task ${taskCid.toString()}`
)
}
/* c8 ignore stop */
// Get receipt from Message Archive
const agentMessageBytes = new Uint8Array(
await workflowResponse.arrayBuffer()
)
// Decode message
const agentMessage = await CAR.request.decode({
body: agentMessageBytes,
headers: {},
})
// Get receipt from the potential multiple receipts in the message
return agentMessage.receipts.get(taskCid.toString())
}

/**
* Return the default provider.
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/w3up-client/src/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,5 @@ export const serviceConf = {
upload: uploadServiceConnection,
filecoin: filecoinServiceConnection,
}

export const receiptsEndpoint = 'https://up.web3.storage/receipt/'
4 changes: 4 additions & 0 deletions packages/w3up-client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export interface ClientFactoryOptions {
* here an error will be thrown.
*/
principal?: Signer<DID<'key'>>
/**
* URL configuration of endpoint where receipts from UCAN Log can be read from.
*/
receiptsEndpoint?: URL
}

export type ClientFactory = (options?: ClientFactoryOptions) => Promise<Client>
Expand Down
23 changes: 22 additions & 1 deletion packages/w3up-client/test/client.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import assert from 'assert'
import { Delegation, create as createServer, provide } from '@ucanto/server'
import {
Delegation,
create as createServer,
parseLink,
provide,
} from '@ucanto/server'
import * as CAR from '@ucanto/transport/car'
import * as Signer from '@ucanto/principal/ed25519'
import * as StoreCapabilities from '@web3-storage/capabilities/store'
Expand Down Expand Up @@ -264,6 +269,22 @@ describe('Client', () => {
})
})

describe('getReceipt', () => {
it('should find a receipt', async () => {
const taskCid = parseLink(
'bafyreibo6nqtvp67daj7dkmeb5c2n6bg5bunxdmxq3lghtp3pmjtzpzfma'
)
const alice = new Client(await AgentData.create(), {
receiptsEndpoint: new URL('http://localhost:9201'),
})
const receipt = await alice.getReceipt(taskCid)
// This is a real `piece/accept` receipt exported as fixture
assert(receipt)
assert.ok(receipt.ran.link().equals(taskCid))
assert.ok(receipt.out.ok)
})
})

describe('currentSpace', () => {
it('should return undefined or space', async () => {
const alice = new Client(await AgentData.create())
Expand Down
Binary file added packages/w3up-client/test/fixtures/workflow.car
Binary file not shown.
30 changes: 30 additions & 0 deletions packages/w3up-client/test/helpers/receipts-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createServer } from 'http'
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'

const port = process.env.PORT ?? 9201
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const fixtureName = process.env.FIXTURE_NAME || 'workflow.car'

const server = createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', '*')
res.setHeader('Access-Control-Allow-Headers', '*')

fs.readFile(
path.resolve(`${__dirname}`, '..', 'fixtures', fixtureName),
(error, content) => {
if (error) {
res.writeHead(500)
res.end()
}
res.writeHead(200, {
'Content-disposition': 'attachment; filename=' + fixtureName,
})
res.end(content)
}
)
})

server.listen(port, () => console.log(`Listening on :${port}`))
Loading