From 199355d41b7bb4da450d67dc7ae3a2e747e9ee3b Mon Sep 17 00:00:00 2001 From: Liran Cohen Date: Mon, 1 Jul 2024 21:05:25 -0400 Subject: [PATCH] upgrade dwn-sdk-js to latest v0.4.0 (#141) - upgrade `dwn-sdk-js` to `v0.4.0` - extract the data for JSON RPC response from both `RecordsRead` and `MessagesRead` - release `v0.4.0` of the `dwn-server` package --- package-lock.json | 22 ++-- package.json | 6 +- src/json-rpc-handlers/dwn/process-message.ts | 15 ++- tests/dwn-process-message.spec.ts | 102 ++++++++++++++++++- 4 files changed, 125 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 80ccacd..d60ac6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@web5/dwn-server", - "version": "0.3.1", + "version": "0.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@web5/dwn-server", - "version": "0.3.1", + "version": "0.4.0", "dependencies": { - "@tbd54566975/dwn-sdk-js": "0.3.10", - "@tbd54566975/dwn-sql-store": "0.5.2", + "@tbd54566975/dwn-sdk-js": "0.4.0", + "@tbd54566975/dwn-sql-store": "0.6.0", "better-sqlite3": "^8.5.0", "body-parser": "^1.20.2", "bytes": "3.1.2", @@ -605,9 +605,9 @@ "dev": true }, "node_modules/@tbd54566975/dwn-sdk-js": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@tbd54566975/dwn-sdk-js/-/dwn-sdk-js-0.3.10.tgz", - "integrity": "sha512-Ky59hx7Diw2dp0rQdIuk6b/ige3C0mRatQiQNwCWvq6gedkKBP+efqp+1l2xhjKiEanwrOJi39gWkK02jkngmg==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@tbd54566975/dwn-sdk-js/-/dwn-sdk-js-0.4.0.tgz", + "integrity": "sha512-eBDjIZQEsxAagKwDbHKzML00/jXlnRN9FLnV9Qx/4UkxZdKRM7IXghFnTRE7aYkwQS8nveAVcijBw46ARSPKcw==", "dependencies": { "@ipld/dag-cbor": "9.0.3", "@js-temporal/polyfill": "0.4.4", @@ -671,12 +671,12 @@ } }, "node_modules/@tbd54566975/dwn-sql-store": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@tbd54566975/dwn-sql-store/-/dwn-sql-store-0.5.2.tgz", - "integrity": "sha512-0NiJraazqgtsLWqju/sQSPoVBv/PbTPkBUMVpcJ64RlCarZA+u7IjJL3/rnJNhUyARjLMyNIBjk33o7jU1zPMQ==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@tbd54566975/dwn-sql-store/-/dwn-sql-store-0.6.0.tgz", + "integrity": "sha512-9o9W2A/gXsmj+n+H5debmOrQelybS9g2sPxFcB46zvT8Zpe9OAKUB9j8s7o1XEeUflyqAW1gf6+q3KKO/UhPHQ==", "dependencies": { "@ipld/dag-cbor": "9.0.5", - "@tbd54566975/dwn-sdk-js": "0.3.10", + "@tbd54566975/dwn-sdk-js": "0.4.0", "kysely": "0.26.3", "multiformats": "12.0.1", "readable-stream": "4.4.2" diff --git a/package.json b/package.json index 73b4ef2..db0ba46 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@web5/dwn-server", "type": "module", - "version": "0.3.1", + "version": "0.4.0", "files": [ "dist", "src" @@ -26,8 +26,8 @@ "url": "https://github.com/TBD54566975/dwn-server/issues" }, "dependencies": { - "@tbd54566975/dwn-sdk-js": "0.3.10", - "@tbd54566975/dwn-sql-store": "0.5.2", + "@tbd54566975/dwn-sdk-js": "0.4.0", + "@tbd54566975/dwn-sql-store": "0.6.0", "better-sqlite3": "^8.5.0", "body-parser": "^1.20.2", "bytes": "3.1.2", diff --git a/src/json-rpc-handlers/dwn/process-message.ts b/src/json-rpc-handlers/dwn/process-message.ts index cac2090..6d66c66 100644 --- a/src/json-rpc-handlers/dwn/process-message.ts +++ b/src/json-rpc-handlers/dwn/process-message.ts @@ -79,12 +79,17 @@ export const handleDwnProcessMessage: JsonRpcHandler = async ( subscriptionHandler: subscriptionRequest?.subscriptionHandler, }); - const { record } = reply; - // RecordsRead messages return record data as a stream to for accommodate large amounts of data + + const { record, entry } = reply; + // RecordsRead or MessagesRead messages optionally return data as a stream to accommodate large amounts of data + // we remove the data stream from the reply that will be serialized and return it as a separate property in the response payload. let recordDataStream: IsomorphicReadable; if (record !== undefined && record.data !== undefined) { recordDataStream = reply.record.data; delete reply.record.data; // not serializable via JSON + } else if (entry !== undefined && entry.data !== undefined) { + recordDataStream = entry.data; + delete reply.entry.data; // not serializable via JSON } if (subscriptionRequest && reply.subscription) { @@ -107,15 +112,15 @@ export const handleDwnProcessMessage: JsonRpcHandler = async ( } return responsePayload; - } catch (e) { + } catch (error) { const jsonRpcResponse = createJsonRpcErrorResponse( requestId, JsonRpcErrorCodes.InternalError, - e.message, + error.message, ); // log the unhandled error response - log.error('handleDwnProcessMessage error', jsonRpcResponse, dwnRequest, e); + log.error('handleDwnProcessMessage error', jsonRpcResponse, dwnRequest, error); return { jsonRpcResponse } as HandlerResponse; } }; diff --git a/tests/dwn-process-message.spec.ts b/tests/dwn-process-message.spec.ts index fe4f3d1..d65c264 100644 --- a/tests/dwn-process-message.spec.ts +++ b/tests/dwn-process-message.spec.ts @@ -7,7 +7,7 @@ import type { RequestContext } from '../src/lib/json-rpc-router.js'; import { JsonRpcErrorCodes, createJsonRpcRequest } from '../src/lib/json-rpc.js'; import { getTestDwn } from './test-dwn.js'; import { createRecordsWriteMessage } from './utils.js'; -import { TestDataGenerator } from '@tbd54566975/dwn-sdk-js'; +import { DataStream, Jws, Message, MessagesRead, RecordsRead, TestDataGenerator } from '@tbd54566975/dwn-sdk-js'; describe('handleDwnProcessMessage', function () { it('returns a JSON RPC Success Response when DWN returns a 2XX status code', async function () { @@ -64,6 +64,106 @@ describe('handleDwnProcessMessage', function () { await dwn.close(); }); + it('should extract data stream from DWN response and return it as a separate property in the JSON RPC response for RecordsRead', async function () { + // scenario: Write a record with some data, and then read the record to get the data back + const alice = await TestDataGenerator.generateDidKeyPersona(); + + // Write a record to later read + const { recordsWrite, dataStream, dataBytes } = await TestDataGenerator.generateRecordsWrite({ author: alice }); + const requestId = uuidv4(); + const dwnRequest = createJsonRpcRequest(requestId, 'dwn.processMessage', { + message: recordsWrite.toJSON(), + target: alice.did, + }); + + const dwn = await getTestDwn(); + const context: RequestContext = { dwn, transport: 'http', dataStream }; + + const { jsonRpcResponse } = await handleDwnProcessMessage( + dwnRequest, + context, + ); + + expect(jsonRpcResponse.error).to.not.exist; + const { reply } = jsonRpcResponse.result; + expect(reply.status.code).to.equal(202); + + + // Read the record to get the data back + const readRequestId = uuidv4(); + const recordsRead = await RecordsRead.create({ + signer: Jws.createSigner(alice), + filter: { recordId: recordsWrite.message.recordId }, + }); + + const readRequest = createJsonRpcRequest(readRequestId, 'dwn.processMessage', { + message: recordsRead.toJSON(), + target: alice.did, + }); + + const { jsonRpcResponse: recordsReadResponse, dataStream: responseDataStream } = await handleDwnProcessMessage(readRequest, { dwn, transport: 'http' }); + expect(recordsReadResponse.error).to.not.exist; + const { reply: readReply } = recordsReadResponse.result; + expect(readReply.status.code).to.equal(200); + expect(responseDataStream).to.not.be.undefined; + + // Compare the data stream bytes to ensure they are the same + const responseDataBytes = await DataStream.toBytes(responseDataStream as any) + expect(responseDataBytes).to.deep.equal(dataBytes); + await dwn.close(); + }); + + it('should extract data stream from DWN response and return it as a separate property in the JSON RPC response for MessagesRead', async function () { + // scenario: Write a record with some data, and then read the message to get the data back + + const alice = await TestDataGenerator.generateDidKeyPersona(); + + // Create a record to read + const { recordsWrite, dataStream, dataBytes } = await TestDataGenerator.generateRecordsWrite({ author: alice }); + const requestId = uuidv4(); + const dwnRequest = createJsonRpcRequest(requestId, 'dwn.processMessage', { + message: recordsWrite.toJSON(), + target: alice.did, + }); + + const dwn = await getTestDwn(); + const context: RequestContext = { dwn, transport: 'http', dataStream }; + + const { jsonRpcResponse } = await handleDwnProcessMessage( + dwnRequest, + context, + ); + + expect(jsonRpcResponse.error).to.not.exist; + const { reply } = jsonRpcResponse.result; + expect(reply.status.code).to.equal(202); + + const messageCid = await Message.getCid(recordsWrite.message); + + // read the message + const readRequestId = uuidv4(); + const messageRead = await MessagesRead.create({ + signer: Jws.createSigner(alice), + messageCid, + }); + + const readRequest = createJsonRpcRequest(readRequestId, 'dwn.processMessage', { + message: messageRead.toJSON(), + target: alice.did, + }); + + const { jsonRpcResponse: recordsReadResponse, dataStream: responseDataStream } = await handleDwnProcessMessage(readRequest, { dwn, transport: 'http' }); + expect(recordsReadResponse.error).to.not.exist; + const { reply: readReply } = recordsReadResponse.result; + expect(readReply.status.code).to.equal(200); + expect(responseDataStream).to.not.be.undefined; + + // Compare the data stream bytes to ensure they are the same + const responseDataBytes = await DataStream.toBytes(responseDataStream as any) + expect(responseDataBytes).to.deep.equal(dataBytes); + await dwn.close(); + }); + it('should fail if no subscriptionRequest context exists for a `Subscribe` message', async function () { const requestId = uuidv4(); const dwnRequest = createJsonRpcRequest(requestId, 'dwn.processMessage', {