Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
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-93.56%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-93.09%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-91.24%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-93.56%25-brightgreen.svg?style=flat)
![Statements](https://img.shields.io/badge/statements-93.63%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-93.08%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.63%25-brightgreen.svg?style=flat)

## Introduction

Expand Down
2 changes: 2 additions & 0 deletions build/compile-validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import mkdirp from 'mkdirp';
import standaloneCode from 'ajv/dist/standalone/index.js';

import Definitions from '../json-schemas/definitions.json' assert { type: 'json' };
import EventsGet from '../json-schemas/events/events-get.json' assert { type: 'json' };
import GeneralJwk from '../json-schemas/jwk/general-jwk.json' assert { type: 'json' };
import GeneralJws from '../json-schemas/general-jws.json' assert { type: 'json' };
import HooksWrite from '../json-schemas/hooks/hooks-write.json' assert { type: 'json' };
Expand All @@ -39,6 +40,7 @@ const schemas = {
RecordsDelete,
RecordsQuery,
RecordsWrite,
EventsGet,
Definitions,
GeneralJwk,
GeneralJws,
Expand Down
40 changes: 40 additions & 0 deletions json-schemas/events/events-get.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://identity.foundation/dwn/json-schemas/events-get.json",
"type": "object",
"additionalProperties": false,
"required": [
"authorization",
"descriptor"
],
"properties": {
"authorization": {
"$ref": "https://identity.foundation/dwn/json-schemas/general-jws.json"
},
"descriptor": {
"type": "object",
"additionalProperties": false,
"required": [
"interface",
"method"
],
"properties": {
"interface": {
"enum": [
"Events"
],
"type": "string"
},
"method": {
"enum": [
"Get"
],
"type": "string"
},
"watermark": {
"type": "string"
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/core/message-reply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class MessageReply {
status: Status;

/**
* Resulting message entries returned from the invocation of the corresponding message.
* Resulting message entries or events returned from the invocation of the corresponding message.
* e.g. the resulting messages from a RecordsQuery
* Mutually exclusive with `data`.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/core/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { lexicographicalCompare } from '../utils/string.js';
import { validateJsonSchema } from '../schema-validator.js';

export enum DwnInterfaceName {
Events = 'Events',
Hooks = 'Hooks',
Permissions = 'Permissions',
Protocols = 'Protocols',
Expand All @@ -17,6 +18,7 @@ export enum DwnInterfaceName {

export enum DwnMethodName {
Configure = 'Configure',
Get = 'Get',
Grant = 'Grant',
Query = 'Query',
Read = 'Read',
Expand Down
2 changes: 2 additions & 0 deletions src/dwn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { AllowAllTenantGate } from './core/tenant-gate.js';
import { DataStoreLevel } from './store/data-store-level.js';
import { DidResolver } from './did/did-resolver.js';
import { EventLogLevel } from './event-log/event-log-level.js';
import { EventsGetHandler } from './interfaces/events/handlers/events-get.js';
import { MessageReply } from './core/message-reply.js';
import { MessageStoreLevel } from './store/message-store-level.js';
import { PermissionsRequestHandler } from './interfaces/permissions/handlers/permissions-request.js';
Expand Down Expand Up @@ -38,6 +39,7 @@ export class Dwn {
this.tenantGate = config.tenantGate!;

this.methodHandlers = {
[DwnInterfaceName.Events + DwnMethodName.Get] : new EventsGetHandler(this.didResolver, this.eventLog),
[DwnInterfaceName.Permissions + DwnMethodName.Request] : new PermissionsRequestHandler(this.didResolver, this.messageStore, this.dataStore),
[DwnInterfaceName.Protocols + DwnMethodName.Configure] : new ProtocolsConfigureHandler(
this.didResolver, this.messageStore, this.dataStore, this.eventLog),
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// export everything that we want to be consumable
export type { DwnConfig } from './dwn.js';
export type { DwnServiceEndpoint, ServiceEndpoint, DidDocument, DidResolutionResult, DidResolutionMetadata, DidDocumentMetadata, VerificationMethod } from './did/did-resolver.js';
export type { EventLog, Event } from './event-log/event-log.js';
export type { EventsGetMessage, EventsGetReply } from './interfaces/events/types.js';
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';
Expand All @@ -22,6 +24,8 @@ export { DwnConstant } from './core/dwn-constant.js';
export { DwnError, DwnErrorCode } from './core/dwn-error.js';
export { DwnInterfaceName, DwnMethodName } from './core/message.js';
export { Encoder } from './utils/encoder.js';
export { EventLogLevel } from './event-log/event-log-level.js';
export { EventsGet, EventsGetOptions } from './interfaces/events/messages/events-get.js';
export { Encryption, EncryptionAlgorithm } from './utils/encryption.js';
export { EncryptionInput, KeyEncryptionInput, RecordsWrite, RecordsWriteOptions, CreateFromOptions } from './interfaces/records/messages/records-write.js';
export { HooksWrite, HooksWriteOptions } from './interfaces/hooks/messages/hooks-write.js';
Expand Down
46 changes: 46 additions & 0 deletions src/interfaces/events/handlers/events-get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { DidResolver } from '../../../index.js';
import type { EventLog } from '../../../event-log/event-log.js';
import type { GetEventsOptions } from '../../../event-log/event-log.js';
import type { MethodHandler } from '../../types.js';
import type { EventsGetMessage, EventsGetReply } from '../types.js';

import { EventsGet } from '../messages/events-get.js';
import { MessageReply } from '../../../core/message-reply.js';
import { authenticate, authorize } from '../../../core/auth.js';

type HandleArgs = {tenant: string, message: EventsGetMessage};

export class EventsGetHandler implements MethodHandler {
constructor(private didResolver: DidResolver, private eventLog: EventLog) {}

public async handle({ tenant, message }: HandleArgs): Promise<EventsGetReply> {
let eventsGet: EventsGet;

try {
eventsGet = await EventsGet.parse(message);
} catch (e) {
return MessageReply.fromError(e, 400);
}

try {
await authenticate(message.authorization, this.didResolver);
await authorize(tenant, eventsGet);
} catch (e) {
return MessageReply.fromError(e, 401);
}

// if watermark was provided in message, get all events _after_ the watermark.
// Otherwise, get all events.
let options: GetEventsOptions | undefined;
if (message.descriptor.watermark) {
options = { gt: message.descriptor.watermark };
}

const events = await this.eventLog.getEvents(tenant, options);

return {
status: { code: 200, detail: 'OK' },
events
};
}
}
38 changes: 38 additions & 0 deletions src/interfaces/events/messages/events-get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { SignatureInput } from '../../../jose/jws/general/types.js';
import type { EventsGetDescriptor, EventsGetMessage } from '../types.js';

import { validateAuthorizationIntegrity } from '../../../core/auth.js';
import { DwnInterfaceName, DwnMethodName, Message } from '../../../core/message.js';

export type EventsGetOptions = {
watermark?: string;
authorizationSignatureInput: SignatureInput;
};

export class EventsGet extends Message<EventsGetMessage> {

public static async parse(message: EventsGetMessage): Promise<EventsGet> {
Message.validateJsonSchema(message);
await validateAuthorizationIntegrity(message);

return new EventsGet(message);
}

public static async create(options: EventsGetOptions): Promise<EventsGet> {
const descriptor: EventsGetDescriptor = {
interface : DwnInterfaceName.Events,
method : DwnMethodName.Get,
};

if (options.watermark) {
descriptor.watermark = options.watermark;
}

const authorization = await Message.signAsAuthorization(descriptor, options.authorizationSignatureInput);
const message = { descriptor, authorization };

Message.validateJsonSchema(message);

return new EventsGet(message);
}
}
18 changes: 18 additions & 0 deletions src/interfaces/events/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { BaseMessage } from '../../core/types.js';
import type { BaseMessageReply } from '../../core/message-reply.js';
import type { Event } from '../../event-log/event-log.js';
import type { DwnInterfaceName, DwnMethodName } from '../../core/message.js';

export type EventsGetDescriptor = {
interface : DwnInterfaceName.Events;
method: DwnMethodName.Get;
watermark?: string;
};

export type EventsGetMessage = BaseMessage & {
descriptor: EventsGetDescriptor;
};

export type EventsGetReply = BaseMessageReply & {
events?: Event[];
};
13 changes: 12 additions & 1 deletion tests/dwn.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { TenantGate } from '../src/index.js';
import type { EventsGetReply, TenantGate } from '../src/index.js';

import chaiAsPromised from 'chai-as-promised';
import sinon from 'sinon';
Expand Down Expand Up @@ -86,6 +86,17 @@ describe('DWN', () => {
expect(reply.entries).to.be.empty;
});

it('should process an EventsGet message', async () => {
const alice = await DidKeyResolver.generate();
const { message } = await TestDataGenerator.generateEventsGet({ requester: alice });

const reply: EventsGetReply = await dwn.processMessage(alice.did, message);

expect(reply.status.code).to.equal(200);
expect(reply.events).to.be.empty;
expect(reply['data']).to.not.exist;
});

it('#191 - regression - should run JSON schema validation', async () => {
const invalidMessage = {
descriptor: {
Expand Down
Loading