Skip to content

Commit 07bcddb

Browse files
authored
MessagesGet (#313)
* implement `MessagesGet`
1 parent 149c713 commit 07bcddb

File tree

16 files changed

+705
-6
lines changed

16 files changed

+705
-6
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Decentralized Web Node (DWN) SDK
44

55
Code Coverage
6-
![Statements](https://img.shields.io/badge/statements-93.69%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-93.17%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-91.36%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-93.69%25-brightgreen.svg?style=flat)
6+
![Statements](https://img.shields.io/badge/statements-93.86%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-93.36%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-91.54%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-93.86%25-brightgreen.svg?style=flat)
77

88
## Introduction
99

build/compile-validators.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import EventsGet from '../json-schemas/events/events-get.json' assert { type: 'j
2222
import GeneralJwk from '../json-schemas/jwk/general-jwk.json' assert { type: 'json' };
2323
import GeneralJws from '../json-schemas/general-jws.json' assert { type: 'json' };
2424
import HooksWrite from '../json-schemas/hooks/hooks-write.json' assert { type: 'json' };
25-
import JwkVerificationMethod from '../json-schemas/jwk-verification-method.json' assert {type: 'json'};
25+
import JwkVerificationMethod from '../json-schemas/jwk-verification-method.json' assert { type: 'json' };
26+
import MessagesGet from '../json-schemas/messages/messages-get.json' assert { type: 'json' };
2627
import PermissionsDefinitions from '../json-schemas/permissions/definitions.json' assert { type: 'json' };
2728
import PermissionsGrant from '../json-schemas/permissions/permissions-grant.json' assert { type: 'json' };
2829
import PermissionsRequest from '../json-schemas/permissions/permissions-request.json' assert { type: 'json' };
@@ -46,6 +47,7 @@ const schemas = {
4647
GeneralJws,
4748
HooksWrite,
4849
JwkVerificationMethod,
50+
MessagesGet,
4951
PermissionsDefinitions,
5052
PermissionsGrant,
5153
PermissionsRequest,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://identity.foundation/dwn/json-schemas/messages-get.json",
4+
"type": "object",
5+
"additionalProperties": false,
6+
"required": [
7+
"authorization",
8+
"descriptor"
9+
],
10+
"properties": {
11+
"authorization": {
12+
"$ref": "https://identity.foundation/dwn/json-schemas/general-jws.json"
13+
},
14+
"descriptor": {
15+
"type": "object",
16+
"additionalProperties": false,
17+
"required": [
18+
"interface",
19+
"method"
20+
],
21+
"properties": {
22+
"interface": {
23+
"enum": [
24+
"Messages"
25+
],
26+
"type": "string"
27+
},
28+
"method": {
29+
"enum": [
30+
"Get"
31+
],
32+
"type": "string"
33+
},
34+
"messageCids": {
35+
"type": "array",
36+
"items": {
37+
"type": "string"
38+
},
39+
"minItems": 1
40+
}
41+
}
42+
}
43+
}
44+
}

karma.conf.cjs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,23 @@
55
const playwright = require('playwright');
66
const esbuildBrowserConfig = require('./build/esbuild-browser-config.cjs');
77

8-
// set playwright as run-target for webkit tests
8+
// use playwright chrome exec path as run target for chromium tests
9+
process.env.CHROME_BIN = playwright.chromium.executablePath();
10+
11+
// use playwright webkit exec path as run target for safari tests
912
process.env.WEBKIT_HEADLESS_BIN = playwright.webkit.executablePath();
1013

11-
module.exports = function(config) {
14+
// use playwright firefox exec path as run target for firefox tests
15+
process.env.FIREFOX_BIN = playwright.firefox.executablePath();
16+
17+
/** @typedef {import('karma').Config} KarmaConfig */
18+
19+
/**
20+
*
21+
* @param {KarmaConfig} config
22+
*/
23+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
24+
module.exports = function configure(config) {
1225
config.set({
1326
plugins: [
1427
require('karma-chrome-launcher'),
@@ -23,7 +36,6 @@ module.exports = function(config) {
2336
// available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
2437
frameworks: ['mocha'],
2538

26-
2739
// list of files / patterns to load in the browser
2840
files: [
2941
{ pattern: 'tests/**/*.spec.ts', watched: false }

package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,16 @@
7777
"multiformats": "11.0.2",
7878
"randombytes": "2.1.0",
7979
"readable-stream": "4.3.0",
80-
"ulid": "2.3.0",
8180
"secp256k1": "5.0.0",
81+
"ulid": "2.3.0",
8282
"uuid": "8.3.2",
8383
"varint": "6.0.0"
8484
},
8585
"devDependencies": {
8686
"@types/chai": "4.3.0",
8787
"@types/chai-as-promised": "7.1.5",
8888
"@types/flat": "^5.0.2",
89+
"@types/karma": "^6.3.3",
8990
"@types/lodash": "4.14.179",
9091
"@types/mocha": "9.1.0",
9192
"@types/randombytes": "2.0.0",

src/core/message.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { validateJsonSchema } from '../schema-validator.js';
1111
export enum DwnInterfaceName {
1212
Events = 'Events',
1313
Hooks = 'Hooks',
14+
Messages = 'Messages',
1415
Permissions = 'Permissions',
1516
Protocols = 'Protocols',
1617
Records = 'Records'

src/dwn.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { MessageStore } from './store/message-store.js';
55
import type { MethodHandler } from './interfaces/types.js';
66
import type { Readable } from 'readable-stream';
77
import type { TenantGate } from './core/tenant-gate.js';
8+
import type { MessagesGetMessage, MessagesGetReply } from './interfaces/messages/types.js';
89
import type { RecordsReadMessage, RecordsReadReply } from './interfaces/records/types.js';
910

1011
import { AllowAllTenantGate } from './core/tenant-gate.js';
@@ -13,6 +14,7 @@ import { DidResolver } from './did/did-resolver.js';
1314
import { EventLogLevel } from './event-log/event-log-level.js';
1415
import { EventsGetHandler } from './interfaces/events/handlers/events-get.js';
1516
import { MessageReply } from './core/message-reply.js';
17+
import { MessagesGetHandler } from './interfaces/messages/handlers/messages-get.js';
1618
import { MessageStoreLevel } from './store/message-store-level.js';
1719
import { PermissionsRequestHandler } from './interfaces/permissions/handlers/permissions-request.js';
1820
import { ProtocolsConfigureHandler } from './interfaces/protocols/handlers/protocols-configure.js';
@@ -40,6 +42,7 @@ export class Dwn {
4042

4143
this.methodHandlers = {
4244
[DwnInterfaceName.Events + DwnMethodName.Get] : new EventsGetHandler(this.didResolver, this.eventLog),
45+
[DwnInterfaceName.Messages + DwnMethodName.Get] : new MessagesGetHandler(this.didResolver, this.messageStore, this.dataStore),
4346
[DwnInterfaceName.Permissions + DwnMethodName.Request] : new PermissionsRequestHandler(this.didResolver, this.messageStore, this.dataStore),
4447
[DwnInterfaceName.Protocols + DwnMethodName.Configure] : new ProtocolsConfigureHandler(
4548
this.didResolver, this.messageStore, this.dataStore, this.eventLog),
@@ -126,6 +129,14 @@ export class Dwn {
126129
return reply as RecordsReadReply;
127130
}
128131

132+
/**
133+
* Handles a `MessagesGet` message.
134+
*/
135+
public async handleMessagesGet(tenant: string, message: MessagesGetMessage): Promise<MessagesGetReply> {
136+
const reply = await this.processMessage(tenant, message);
137+
return reply as MessagesGetReply;
138+
}
139+
129140
public async dump(): Promise<void> {
130141
console.group('didResolver');
131142
await this.didResolver['dump']?.();

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export type { DwnServiceEndpoint, ServiceEndpoint, DidDocument, DidResolutionRes
77
export type { EventLog, Event } from './event-log/event-log.js';
88
export type { EventsGetMessage, EventsGetReply } from './interfaces/events/types.js';
99
export type { HooksWriteMessage } from './interfaces/hooks/types.js';
10+
export type { MessagesGetMessage, MessagesGetReply } from './interfaces/messages/types.js';
1011
export type { ProtocolDefinition, ProtocolRuleSet, ProtocolsConfigureMessage, ProtocolsQueryMessage } from './interfaces/protocols/types.js';
1112
export type { RecordsDeleteMessage, RecordsQueryMessage, RecordsWriteMessage } from './interfaces/records/types.js';
1213
export { AllowAllTenantGate, TenantGate } from './core/tenant-gate.js';
@@ -31,6 +32,7 @@ export { EncryptionInput, KeyEncryptionInput, RecordsWrite, RecordsWriteOptions,
3132
export { HooksWrite, HooksWriteOptions } from './interfaces/hooks/messages/hooks-write.js';
3233
export { Jws } from './utils/jws.js';
3334
export { KeyMaterial, PrivateJwk, PublicJwk } from './jose/types.js';
35+
export { MessagesGet, MessagesGetOptions } from './interfaces/messages/messages/messages-get.js';
3436
export { MessageReply } from './core/message-reply.js';
3537
export { MessageStore } from './store/message-store.js';
3638
export { MessageStoreLevel } from './store/message-store-level.js';
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import type { DataStore } from '../../../store/data-store.js';
2+
import type { DidResolver } from '../../../did/did-resolver.js';
3+
import type { MessageStore } from '../../../store/message-store.js';
4+
import type { MethodHandler } from '../../types.js';
5+
import type { MessagesGetMessage, MessagesGetReply, MessagesGetReplyEntry } from '../types.js';
6+
7+
import { DataStream } from '../../../utils/data-stream.js';
8+
import { DwnConstant } from '../../../core/dwn-constant.js';
9+
import { Encoder } from '../../../utils/encoder.js';
10+
import { MessageReply } from '../../../core/message-reply.js';
11+
import { MessagesGet } from '../messages/messages-get.js';
12+
import { authenticate, authorize } from '../../../core/auth.js';
13+
import { DwnInterfaceName, DwnMethodName, Message } from '../../../core/message.js';
14+
15+
type HandleArgs = { tenant: string, message: MessagesGetMessage };
16+
17+
export class MessagesGetHandler implements MethodHandler {
18+
constructor(private didResolver: DidResolver, private messageStore: MessageStore, private dataStore: DataStore) {}
19+
20+
public async handle({ tenant, message }: HandleArgs): Promise<MessagesGetReply> {
21+
let messagesGet: MessagesGet;
22+
23+
try {
24+
messagesGet = await MessagesGet.parse(message);
25+
} catch (e) {
26+
return MessageReply.fromError(e, 400);
27+
}
28+
29+
try {
30+
await authenticate(message.authorization, this.didResolver);
31+
await authorize(tenant, messagesGet);
32+
} catch (e) {
33+
return MessageReply.fromError(e, 401);
34+
}
35+
36+
const promises: Promise<MessagesGetReplyEntry>[] = [];
37+
const messageCids = new Set(message.descriptor.messageCids);
38+
39+
for (const messageCid of messageCids) {
40+
const promise = this.messageStore.get(tenant, messageCid)
41+
.then(message => {
42+
return { messageCid, message };
43+
})
44+
.catch(_ => {
45+
return { messageCid, message: undefined, error: `Failed to get message ${messageCid}` };
46+
});
47+
48+
promises.push(promise);
49+
}
50+
51+
const messages = await Promise.all(promises);
52+
53+
// for every message, include associated data as `encodedData` IF:
54+
// * its a RecordsWrite
55+
// * the data size is equal or smaller than the size threshold
56+
//! NOTE: this is somewhat duplicate code that also exists in `StorageController.query`.
57+
for (const entry of messages) {
58+
const { message } = entry;
59+
60+
if (!message) {
61+
continue;
62+
}
63+
64+
const { interface: messageInterface, method } = message.descriptor;
65+
if (messageInterface !== DwnInterfaceName.Records || method !== DwnMethodName.Write) {
66+
continue;
67+
}
68+
69+
const dataCid = message.descriptor.dataCid;
70+
const dataSize = message.descriptor.dataSize;
71+
72+
if (dataCid !== undefined && dataSize! <= DwnConstant.maxDataSizeAllowedToBeEncoded) {
73+
const messageCid = await Message.getCid(message);
74+
const result = await this.dataStore.get(tenant, messageCid, dataCid);
75+
76+
if (result) {
77+
const dataBytes = await DataStream.toBytes(result.dataStream);
78+
entry.encodedData = Encoder.bytesToBase64Url(dataBytes);
79+
}
80+
}
81+
}
82+
83+
return {
84+
status: { code: 200, detail: 'OK' },
85+
messages
86+
};
87+
}
88+
}

0 commit comments

Comments
 (0)