Skip to content

Commit

Permalink
#231 - introduced DataStore as a peer interface to MessageStore (#233)
Browse files Browse the repository at this point in the history
* introduced DataStore as a peer interface to MessageStore
* refactored code such that MessageStoreLevel now has zero knowledge of data store
* refactored code such that there is no need to pass resolver, messageStore, and dataStore for every message handling call, this has been painful for a while especially when it comes to writing/refactoring tests
* kept MessageStore interface as untouched as possible to minimize scope of PR, but might want to add minor tweaks
* moved third party type definitions from devDependencies to dependencies as TypeScript projects are having trouble locating those dependencies on their own
  • Loading branch information
thehenrytsai authored Feb 21, 2023
1 parent 78e33ec commit 576fda4
Show file tree
Hide file tree
Showing 31 changed files with 1,102 additions and 1,077 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ try.ts
# locally used file as a js playground
try.js
# default location for levelDB data storage in a non-browser env
DATASTORE
BLOCKSTORE
# location for levelDB data storage for non-browser tests
TEST-DATASTORE
TEST-BLOCKSTORE
# default location for index specific levelDB data storage in a non-browser env
INDEX
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Decentralized Web Node (DWN) SDK

Code Coverage
![Statements](https://img.shields.io/badge/statements-95.15%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-95.19%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-92.17%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-95.15%25-brightgreen.svg?style=flat)
![Statements](https://img.shields.io/badge/statements-94.36%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-94.95%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-92.3%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-94.36%25-brightgreen.svg?style=flat)

## Introduction

Expand Down
33 changes: 13 additions & 20 deletions package-lock.json

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

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
"@noble/secp256k1": "1.7.1",
"@swc/helpers": "0.3.8",
"@types/ms": "0.7.31",
"@types/node": "^18.13.0",
"@types/readable-stream": "^2.3.15",
"@types/search-index": "3.2.0",
"ajv": "8.11.0",
"cross-fetch": "3.1.5",
"date-fns": "2.28.0",
Expand All @@ -78,8 +81,6 @@
"@types/lodash": "4.14.179",
"@types/mocha": "9.1.0",
"@types/randombytes": "2.0.0",
"@types/readable-stream": "^2.3.15",
"@types/search-index": "3.2.0",
"@types/sinon": "10.0.11",
"@types/varint": "6.0.0",
"@typescript-eslint/eslint-plugin": "5.37.0",
Expand Down
55 changes: 33 additions & 22 deletions src/dwn.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,55 @@
import type { BaseMessage } from './core/types.js';
import type { DidMethodResolver } from './did/did-resolver.js';
import type { DataStore } from './store/data-store.js';
import type { MessageStore } from './store/message-store.js';
import type { MethodHandler } from './interfaces/types.js';
import type { Readable } from 'readable-stream';
import type { TenantGate } from './core/tenant-gate.js';

import { AllowAllTenantGate } from './core/tenant-gate.js';
import { DataStoreLevel } from './store/data-store-level.js';
import { DidResolver } from './did/did-resolver.js';
import { Message } from './core/message.js';
import { MessageReply } from './core/message-reply.js';
import { MessageStoreLevel } from './store/message-store-level.js';
import { PermissionsInterface } from './interfaces/permissions/permissions-interface.js';
import { ProtocolsInterface } from './interfaces/protocols/protocols-interface.js';
import { RecordsInterface } from './interfaces/records/records-interface.js';
import { PermissionsRequestHandler } from './interfaces/permissions/handlers/permissions-request.js';
import { ProtocolsConfigureHandler } from './interfaces/protocols/handlers/protocols-configure.js';
import { ProtocolsQueryHandler } from './interfaces/protocols/handlers/protocols-query.js';
import { RecordsDeleteHandler } from './interfaces/records/handlers/records-delete.js';
import { RecordsQueryHandler } from './interfaces/records/handlers/records-query.js';
import { RecordsWriteHandler } from './interfaces/records/handlers/records-write.js';
import { DwnInterfaceName, DwnMethodName, Message } from './core/message.js';

export class Dwn {
static methodHandlers: { [key:string]: MethodHandler } = {
...RecordsInterface.methodHandlers,
...PermissionsInterface.methodHandlers,
...ProtocolsInterface.methodHandlers
};

private DidResolver: DidResolver;
private methodHandlers: { [key:string]: MethodHandler };
private didResolver: DidResolver;
private messageStore: MessageStore;
private dataStore: DataStore;
private tenantGate: TenantGate;

private constructor(config: DwnConfig) {
this.DidResolver = new DidResolver(config.didMethodResolvers);
this.didResolver = config.didResolver;
this.messageStore = config.messageStore;
this.dataStore = config.dataStore;
this.tenantGate = config.tenantGate;

this.methodHandlers = {
[DwnInterfaceName.Permissions + DwnMethodName.Request] : new PermissionsRequestHandler(this.didResolver, this.messageStore, this.dataStore),
[DwnInterfaceName.Protocols + DwnMethodName.Configure] : new ProtocolsConfigureHandler(this.didResolver, this.messageStore, this.dataStore),
[DwnInterfaceName.Protocols + DwnMethodName.Query] : new ProtocolsQueryHandler(this.didResolver, this.messageStore, this.dataStore),
[DwnInterfaceName.Records + DwnMethodName.Delete] : new RecordsDeleteHandler(this.didResolver, this.messageStore, this.dataStore),
[DwnInterfaceName.Records + DwnMethodName.Query] : new RecordsQueryHandler(this.didResolver, this.messageStore, this.dataStore),
[DwnInterfaceName.Records + DwnMethodName.Write] : new RecordsWriteHandler(this.didResolver, this.messageStore, this.dataStore),
};
}

/**
* Creates an instance of the DWN.
*/
static async create(config?: DwnConfig): Promise<Dwn> {
config ??= { };
config.didResolver ??= new DidResolver();
config.tenantGate ??= new AllowAllTenantGate();
config.messageStore ??= new MessageStoreLevel();
config.dataStore ??= new DataStoreLevel();

const dwn = new Dwn(config);
await dwn.open();
Expand All @@ -46,11 +58,13 @@ export class Dwn {
}

private async open(): Promise<void> {
return this.messageStore.open();
await this.messageStore.open();
await this.dataStore.open();
}

async close(): Promise<void> {
return this.messageStore.close();
this.messageStore.close();
this.dataStore.close();
}

/**
Expand Down Expand Up @@ -83,21 +97,18 @@ export class Dwn {
}

const handlerKey = dwnInterface + dwnMethod;
const interfaceMethodHandler = Dwn.methodHandlers[handlerKey];

const methodHandlerReply = await interfaceMethodHandler({
const methodHandlerReply = await this.methodHandlers[handlerKey].handle({
tenant,
message : rawMessage as BaseMessage,
messageStore : this.messageStore,
didResolver : this.DidResolver,
message: rawMessage as BaseMessage,
dataStream
});
return methodHandlerReply;
}
};

export type DwnConfig = {
didMethodResolvers?: DidMethodResolver[],
didResolver?: DidResolver,
messageStore?: MessageStore;
dataStore?: DataStore;
tenantGate?: TenantGate;
};
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type { HooksWriteMessage } from './interfaces/hooks/types.js';
export type { ProtocolDefinition, ProtocolRuleSet, ProtocolsConfigureMessage, ProtocolsQueryMessage } from './interfaces/protocols/types.js';
export type { RecordsDeleteMessage, RecordsQueryMessage, RecordsWriteMessage } from './interfaces/records/types.js';
export { AllowAllTenantGate, TenantGate } from './core/tenant-gate.js';
export { DataStore } from './store/data-store.js';
export { DateSort } from './interfaces/records/messages/records-query.js';
export { DataStream } from './utils/data-stream.js';
export { DidKeyResolver } from './did/did-key-resolver.js';
Expand Down
58 changes: 31 additions & 27 deletions src/interfaces/permissions/handlers/permissions-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,40 @@ import type { PermissionsRequestMessage } from '../types.js';
import { canonicalAuth } from '../../../core/auth.js';
import { MessageReply } from '../../../core/message-reply.js';
import { PermissionsRequest } from '../messages/permissions-request.js';
import { DataStore, DidResolver, MessageStore } from '../../../index.js';

export const handlePermissionsRequest: MethodHandler = async ({
tenant,
message,
messageStore,
didResolver
}): Promise<MessageReply> => {
const permissionRequest = await PermissionsRequest.parse(message as PermissionsRequestMessage);
const { author } = permissionRequest;
export class PermissionsRequestHandler implements MethodHandler {

if (tenant !== permissionRequest.grantedBy && tenant !== permissionRequest.grantedTo) {
return new MessageReply({
status: { code: 400, detail: 'grantedBy or grantedTo must be the targeted message recipient' }
});
}
constructor(private didResolver: DidResolver, private messageStore: MessageStore,private dataStore: DataStore) { }

await canonicalAuth(tenant, permissionRequest, didResolver);
public async handle({
tenant,
message
}): Promise<MessageReply> {
const permissionRequest = await PermissionsRequest.parse(message as PermissionsRequestMessage);
const { author } = permissionRequest;

if (author !== permissionRequest.grantedTo) {
throw new Error('grantee must be signer');
}
if (tenant !== permissionRequest.grantedBy && tenant !== permissionRequest.grantedTo) {
return new MessageReply({
status: { code: 400, detail: 'grantedBy or grantedTo must be the targeted message recipient' }
});
}

const index = {
tenant,
author,
... message.descriptor
};
await messageStore.put(message, index);
await canonicalAuth(tenant, permissionRequest, this.didResolver);

return new MessageReply({
status: { code: 202, detail: 'Accepted' }
});
};
if (author !== permissionRequest.grantedTo) {
throw new Error('grantee must be signer');
}

const index = {
tenant,
author,
... message.descriptor
};
await this.messageStore.put(message, index);

return new MessageReply({
status: { code: 202, detail: 'Accepted' }
});
};
}
7 changes: 0 additions & 7 deletions src/interfaces/permissions/permissions-interface.ts

This file was deleted.

Loading

0 comments on commit 576fda4

Please sign in to comment.