diff --git a/build/compile-validators.js b/build/compile-validators.js index 71a40d89d..0efad1fdd 100644 --- a/build/compile-validators.js +++ b/build/compile-validators.js @@ -22,7 +22,6 @@ import AuthorizationDelegatedGrant from '../json-schemas/authorization-delegated import AuthorizationOwner from '../json-schemas/authorization-owner.json' assert { type: 'json' }; import Definitions from '../json-schemas/definitions.json' assert { type: 'json' }; import EventsFilter from '../json-schemas/interface-methods/events-filter.json' assert { type: 'json' }; -import EventsGet from '../json-schemas/interface-methods/events-get.json' assert { type: 'json' }; import EventsQuery from '../json-schemas/interface-methods/events-query.json' assert { type: 'json' }; import EventsSubscribe from '../json-schemas/interface-methods/events-subscribe.json' assert { type: 'json' }; import GeneralJwk from '../json-schemas/jwk/general-jwk.json' assert { type: 'json' }; @@ -64,7 +63,6 @@ const schemas = { RecordsWriteDataEncoded, RecordsWriteUnidentified, EventsFilter, - EventsGet, EventsQuery, EventsSubscribe, Definitions, diff --git a/json-schemas/interface-methods/events-get.json b/json-schemas/interface-methods/events-get.json deleted file mode 100644 index e9b436593..000000000 --- a/json-schemas/interface-methods/events-get.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/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/authorization.json" - }, - "descriptor": { - "type": "object", - "additionalProperties": false, - "required": [ - "interface", - "method", - "messageTimestamp" - ], - "properties": { - "interface": { - "enum": [ - "Events" - ], - "type": "string" - }, - "method": { - "enum": [ - "Get" - ], - "type": "string" - }, - "messageTimestamp": { - "type": "string" - }, - "cursor": { - "$ref": "https://identity.foundation/dwn/json-schemas/pagination-cursor.json" - } - } - } - } -} \ No newline at end of file diff --git a/json-schemas/interface-methods/events-query.json b/json-schemas/interface-methods/events-query.json index a8b5aae2b..be00ac2c8 100644 --- a/json-schemas/interface-methods/events-query.json +++ b/json-schemas/interface-methods/events-query.json @@ -17,8 +17,7 @@ "required": [ "interface", "method", - "messageTimestamp", - "filters" + "messageTimestamp" ], "properties": { "interface": { diff --git a/src/core/message-reply.ts b/src/core/message-reply.ts index decd58758..6de2afd5f 100644 --- a/src/core/message-reply.ts +++ b/src/core/message-reply.ts @@ -18,7 +18,7 @@ export function messageReplyFromError(e: unknown, code: number): GenericMessageR export type UnionMessageReply = GenericMessageReply & { /** * Resulting message entries or events returned from the invocation of the corresponding message. - * e.g. the resulting messages from a RecordsQuery, or array of messageCid strings for EventsGet or EventsQuery + * e.g. the resulting messages from a RecordsQuery, or array of messageCid strings for EventsQuery * Mutually exclusive with `record`. */ entries?: QueryResultEntry[] | ProtocolsConfigureMessage[] | MessagesGetReplyEntry[] | string[]; diff --git a/src/dwn.ts b/src/dwn.ts index c6082998e..1f4f487c2 100644 --- a/src/dwn.ts +++ b/src/dwn.ts @@ -9,14 +9,13 @@ import type { Readable } from 'readable-stream'; import type { ResumableTaskStore } from './types/resumable-task-store.js'; import type { TenantGate } from './core/tenant-gate.js'; import type { UnionMessageReply } from './core/message-reply.js'; -import type { EventsGetMessage, EventsGetReply, EventsQueryMessage, EventsQueryReply, EventsSubscribeMessage, EventsSubscribeMessageOptions, EventsSubscribeReply, MessageSubscriptionHandler } from './types/events-types.js'; +import type { EventsQueryMessage, EventsQueryReply, EventsSubscribeMessage, EventsSubscribeMessageOptions, EventsSubscribeReply, MessageSubscriptionHandler } from './types/events-types.js'; import type { GenericMessage, GenericMessageReply } from './types/message-types.js'; import type { MessagesGetMessage, MessagesGetReply } from './types/messages-types.js'; import type { ProtocolsConfigureMessage, ProtocolsQueryMessage, ProtocolsQueryReply } from './types/protocols-types.js'; import type { RecordsDeleteMessage, RecordsQueryMessage, RecordsQueryReply, RecordsReadMessage, RecordsReadReply, RecordsSubscribeMessage, RecordsSubscribeMessageOptions, RecordsSubscribeReply, RecordSubscriptionHandler, RecordsWriteMessage, RecordsWriteMessageOptions } from './types/records-types.js'; import { AllowAllTenantGate } from './core/tenant-gate.js'; -import { EventsGetHandler } from './handlers/events-get.js'; import { EventsQueryHandler } from './handlers/events-query.js'; import { EventsSubscribeHandler } from './handlers/events-subscribe.js'; import { Message } from './core/message.js'; @@ -67,10 +66,6 @@ export class Dwn { ); this.methodHandlers = { - [DwnInterfaceName.Events + DwnMethodName.Get]: new EventsGetHandler( - this.didResolver, - this.eventLog, - ), [DwnInterfaceName.Events + DwnMethodName.Query]: new EventsQueryHandler( this.didResolver, this.eventLog, @@ -165,7 +160,6 @@ export class Dwn { * Processes the given DWN message and returns with a reply. * @param tenant The tenant DID to route the given message to. */ - public async processMessage(tenant: string, rawMessage: EventsGetMessage): Promise; public async processMessage(tenant: string, rawMessage: EventsQueryMessage): Promise; public async processMessage( tenant: string, rawMessage: EventsSubscribeMessage, options?: EventsSubscribeMessageOptions): Promise; diff --git a/src/handlers/events-get.ts b/src/handlers/events-get.ts deleted file mode 100644 index 03d3d2a98..000000000 --- a/src/handlers/events-get.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { DidResolver } from '@web5/dids'; -import type { EventLog } from '../types/event-log.js'; -import type { MethodHandler } from '../types/method-handler.js'; -import type { EventsGetMessage, EventsGetReply } from '../types/events-types.js'; - -import { EventsGet } from '../interfaces/events-get.js'; -import { messageReplyFromError } from '../core/message-reply.js'; -import { authenticate, authorizeOwner } 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 { - let eventsGet: EventsGet; - - try { - eventsGet = await EventsGet.parse(message); - } catch (e) { - return messageReplyFromError(e, 400); - } - - try { - await authenticate(message.authorization, this.didResolver); - await authorizeOwner(tenant, eventsGet); - } catch (e) { - return messageReplyFromError(e, 401); - } - - // if a cursor was provided in message, get all events _after_ the cursor. - // Otherwise, get all events. - const { cursor: queryCursor } = message.descriptor; - const { events, cursor } = await this.eventLog.getEvents(tenant, queryCursor); - - return { - status : { code: 200, detail: 'OK' }, - entries : events, - cursor - }; - } -} \ No newline at end of file diff --git a/src/handlers/events-query.ts b/src/handlers/events-query.ts index 09eb05782..2b8485758 100644 --- a/src/handlers/events-query.ts +++ b/src/handlers/events-query.ts @@ -32,7 +32,10 @@ export class EventsQueryHandler implements MethodHandler { return messageReplyFromError(e, 401); } - const eventFilters = Events.convertFilters(message.descriptor.filters); + // if no filter is present in the the `EventsQuery` descriptor, we pass an empty array of filters to the `queryEvents` method + // this will return all events in the event log for the given tenant beyond the cursor provided. + // if no cursor is provided, it will return all events + const eventFilters = message.descriptor.filters ? Events.convertFilters(message.descriptor.filters) : []; const { events, cursor } = await this.eventLog.queryEvents(tenant, eventFilters, message.descriptor.cursor); return { diff --git a/src/index.ts b/src/index.ts index 33583609b..802848080 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ // export everything that we want to be consumable export type { DwnConfig } from './dwn.js'; export type { EventLog } from './types/event-log.js'; -export type { EventsGetMessage, EventsGetReply, EventsQueryMessage, EventsQueryReply, EventsSubscribeDescriptor, EventsSubscribeMessage, EventsSubscribeReply, MessageSubscriptionHandler as EventSubscriptionHandler } from './types/events-types.js'; +export type { EventsQueryMessage, EventsQueryReply, EventsSubscribeDescriptor, EventsSubscribeMessage, EventsSubscribeReply, MessageSubscriptionHandler } from './types/events-types.js'; export type { EventListener, EventStream, EventSubscription, MessageEvent, SubscriptionReply } from './types/subscriptions.js'; export type { GenericMessage, GenericMessageReply, MessageSort, MessageSubscription, Pagination, QueryResultEntry } from './types/message-types.js'; export type { MessagesGetMessage, MessagesGetReply, MessagesGetReplyEntry } from './types/messages-types.js'; @@ -23,7 +23,6 @@ export { DwnConstant } from './core/dwn-constant.js'; export { DwnError, DwnErrorCode } from './core/dwn-error.js'; export { DwnInterfaceName, DwnMethodName } from './enums/dwn-interface-method.js'; export { Encoder } from './utils/encoder.js'; -export { EventsGet, EventsGetOptions } from './interfaces/events-get.js'; export { EventsQuery, EventsQueryOptions } from './interfaces/events-query.js'; export { EventsSubscribe, EventsSubscribeOptions } from './interfaces/events-subscribe.js'; export { Encryption, EncryptionAlgorithm } from './utils/encryption.js'; diff --git a/src/interfaces/events-get.ts b/src/interfaces/events-get.ts deleted file mode 100644 index f168268a2..000000000 --- a/src/interfaces/events-get.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { PaginationCursor } from '../types/query-types.js'; -import type { Signer } from '../types/signer.js'; -import type { EventsGetDescriptor, EventsGetMessage } from '../types/events-types.js'; - -import { AbstractMessage } from '../core/abstract-message.js'; -import { Message } from '../core/message.js'; -import { Time } from '../utils/time.js'; -import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js'; - -export type EventsGetOptions = { - cursor?: PaginationCursor; - signer: Signer; - messageTimestamp?: string; -}; - -export class EventsGet extends AbstractMessage { - - public static async parse(message: EventsGetMessage): Promise { - Message.validateJsonSchema(message); - await Message.validateSignatureStructure(message.authorization.signature, message.descriptor); - Time.validateTimestamp(message.descriptor.messageTimestamp); - - return new EventsGet(message); - } - - public static async create(options: EventsGetOptions): Promise { - const descriptor: EventsGetDescriptor = { - interface : DwnInterfaceName.Events, - method : DwnMethodName.Get, - messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(), - }; - - if (options.cursor) { - descriptor.cursor = options.cursor; - } - - const authorization = await Message.createAuthorization({ descriptor, signer: options.signer }); - const message = { descriptor, authorization }; - - Message.validateJsonSchema(message); - - return new EventsGet(message); - } -} \ No newline at end of file diff --git a/src/interfaces/events-query.ts b/src/interfaces/events-query.ts index 9f7fc39f1..df3f1afdd 100644 --- a/src/interfaces/events-query.ts +++ b/src/interfaces/events-query.ts @@ -12,7 +12,7 @@ import { validateProtocolUrlNormalized, validateSchemaUrlNormalized } from '../u export type EventsQueryOptions = { signer: Signer; - filters: EventsFilter[]; + filters?: EventsFilter[]; cursor?: PaginationCursor; messageTimestamp?: string; }; @@ -23,7 +23,7 @@ export class EventsQuery extends AbstractMessage{ Message.validateJsonSchema(message); await Message.validateSignatureStructure(message.authorization.signature, message.descriptor); - for (const filter of message.descriptor.filters) { + for (const filter of message.descriptor.filters || []) { if ('protocol' in filter && filter.protocol !== undefined) { validateProtocolUrlNormalized(filter.protocol); } @@ -39,7 +39,7 @@ export class EventsQuery extends AbstractMessage{ const descriptor: EventsQueryDescriptor = { interface : DwnInterfaceName.Events, method : DwnMethodName.Query, - filters : Events.normalizeFilters(options.filters), + filters : options.filters ? Events.normalizeFilters(options.filters) : undefined, messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(), cursor : options.cursor, }; diff --git a/src/types/events-types.ts b/src/types/events-types.ts index 4994b57bb..733132612 100644 --- a/src/types/events-types.ts +++ b/src/types/events-types.ts @@ -34,23 +34,6 @@ export type EventsRecordsFilter = { */ export type EventsFilter = EventsMessageFilter | EventsRecordsFilter; -export type EventsGetDescriptor = { - interface: DwnInterfaceName.Events; - method: DwnMethodName.Get; - cursor?: PaginationCursor; - messageTimestamp: string; -}; - -export type EventsGetMessage = GenericMessage & { - authorization: AuthorizationModel; // overriding `GenericMessage` with `authorization` being required - descriptor: EventsGetDescriptor; -}; - -export type EventsGetReply = GenericMessageReply & { - entries?: string[]; - cursor?: PaginationCursor; -}; - export type MessageSubscriptionHandler = (event: MessageEvent) => void; export type EventsSubscribeMessageOptions = { @@ -77,7 +60,7 @@ export type EventsQueryDescriptor = { interface: DwnInterfaceName.Events; method: DwnMethodName.Query; messageTimestamp: string; - filters: EventsFilter[]; + filters?: EventsFilter[]; cursor?: PaginationCursor; }; diff --git a/tests/dwn.spec.ts b/tests/dwn.spec.ts index 8069cd407..00a758548 100644 --- a/tests/dwn.spec.ts +++ b/tests/dwn.spec.ts @@ -1,6 +1,6 @@ import type { DidResolver } from '@web5/dids'; import type { EventStream } from '../src/types/subscriptions.js'; -import type { ActiveTenantCheckResult, EventsGetReply, TenantGate } from '../src/index.js'; +import type { ActiveTenantCheckResult, EventsQueryReply, TenantGate } from '../src/index.js'; import type { DataStore, EventLog, MessageStore, ResumableTaskStore } from '../src/index.js'; import chaiAsPromised from 'chai-as-promised'; @@ -78,11 +78,11 @@ export function testDwnClass(): void { expect(reply.entries).to.be.empty; }); - it('should process an EventsGet message', async () => { + it('should process an EventsQuery message', async () => { const alice = await TestDataGenerator.generateDidKeyPersona(); - const { message } = await TestDataGenerator.generateEventsGet({ author: alice }); + const { message } = await TestDataGenerator.generateEventsQuery({ author: alice }); - const reply: EventsGetReply = await dwn.processMessage(alice.did, message); + const reply: EventsQueryReply = await dwn.processMessage(alice.did, message); expect(reply.status.code).to.equal(200); expect(reply.entries).to.be.empty; diff --git a/tests/handlers/events-get.spec.ts b/tests/handlers/events-get.spec.ts deleted file mode 100644 index 1cccb4cb2..000000000 --- a/tests/handlers/events-get.spec.ts +++ /dev/null @@ -1,147 +0,0 @@ -import type { DidResolver } from '@web5/dids'; -import type { EventStream } from '../../src/types/subscriptions.js'; -import type { - DataStore, - EventLog, - EventsGetReply, - MessageStore, - ResumableTaskStore, -} from '../../src/index.js'; - -import { Dwn } from '../../src/index.js'; -import { EventsGetHandler } from '../../src/handlers/events-get.js'; -import { expect } from 'chai'; -import { TestDataGenerator } from '../utils/test-data-generator.js'; - -import { Message } from '../../src/core/message.js'; -import { TestEventStream } from '../test-event-stream.js'; -import { TestStores } from '../test-stores.js'; -import { DidKey, UniversalResolver } from '@web5/dids'; - -export function testEventsGetHandler(): void { - describe('EventsGetHandler.handle()', () => { - let didResolver: DidResolver; - let messageStore: MessageStore; - let dataStore: DataStore; - let resumableTaskStore: ResumableTaskStore; - let eventLog: EventLog; - let eventStream: EventStream; - let dwn: Dwn; - - // important to follow the `before` and `after` pattern to initialize and clean the stores in tests - // so that different test suites can reuse the same backend store for testing - before(async () => { - didResolver = new UniversalResolver({ didResolvers: [DidKey] }); - - const stores = TestStores.get(); - messageStore = stores.messageStore; - dataStore = stores.dataStore; - resumableTaskStore = stores.resumableTaskStore; - eventLog = stores.eventLog; - eventStream = TestEventStream.get(); - - dwn = await Dwn.create({ didResolver, messageStore, dataStore, eventLog, eventStream, resumableTaskStore }); - }); - - beforeEach(async () => { - // clean up before each test rather than after so that a test does not depend on other tests to do the clean up - await messageStore.clear(); - await dataStore.clear(); - await resumableTaskStore.clear(); - await eventLog.clear(); - }); - - after(async () => { - await dwn.close(); - }); - - it('returns a 401 if tenant is not author', async () => { - const alice = await TestDataGenerator.generateDidKeyPersona(); - const bob = await TestDataGenerator.generateDidKeyPersona(); - - const { message } = await TestDataGenerator.generateEventsGet({ author: alice }); - const eventsGetHandler = new EventsGetHandler(didResolver, eventLog); - const reply = await eventsGetHandler.handle({ tenant: bob.did, message }); - - expect(reply.status.code).to.equal(401); - expect(reply.entries).to.not.exist; - }); - - it('returns a 400 if message is invalid', async () => { - const alice = await TestDataGenerator.generateDidKeyPersona(); - - const { message } = await TestDataGenerator.generateEventsGet({ author: alice }); - (message['descriptor'] as any)['troll'] = 'hehe'; - const eventsGetHandler = new EventsGetHandler(didResolver, eventLog); - const reply = await eventsGetHandler.handle({ tenant: alice.did, message }); - - expect(reply.status.code).to.equal(400); - expect(reply.entries).to.not.exist; - }); - - it('returns all events for a tenant if cursor is not provided', async () => { - const alice = await TestDataGenerator.generateDidKeyPersona(); - const expectedCids: string[] = []; - - for (let i = 0; i < 5; i += 1) { - const { message, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice }); - const reply = await dwn.processMessage(alice.did, message, { dataStream }); - - expect(reply.status.code).to.equal(202); - const messageCid = await Message.getCid(message); - expectedCids.push(messageCid); - - } - - const { message } = await TestDataGenerator.generateEventsGet({ author: alice }); - const reply: EventsGetReply = await dwn.processMessage(alice.did, message); - - expect(reply.status.code).to.equal(200); - expect((reply as any).data).to.not.exist; - expect(reply.entries?.length).to.equal(expectedCids.length); - - for (let i = 0; i < reply.entries!.length; i += 1) { - expect(reply.entries![i]).to.equal(expectedCids[i]); - } - }); - - it('returns all events after cursor if provided', async () => { - const alice = await TestDataGenerator.generateDidKeyPersona(); - - for (let i = 0; i < 5; i += 1) { - const { message, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice }); - const reply = await dwn.processMessage(alice.did, message, { dataStream }); - - expect(reply.status.code).to.equal(202); - } - - const { message } = await TestDataGenerator.generateEventsGet({ author: alice }); - let reply: EventsGetReply = await dwn.processMessage(alice.did, message); - expect(reply.status.code).to.equal(200); - expect(reply.entries?.length).to.equal(5); - expect(reply.cursor).to.not.be.undefined; - - const expectedCids: string[] = []; - - for (let i = 0; i < 3; i += 1) { - const { message, dataStream } = await TestDataGenerator.generateRecordsWrite({ author: alice }); - const reply = await dwn.processMessage(alice.did, message, { dataStream }); - - expect(reply.status.code).to.equal(202); - const messageCid = await Message.getCid(message); - expectedCids.push(messageCid); - } - - const { message: m } = await TestDataGenerator.generateEventsGet({ author: alice, cursor: reply.cursor }); - reply = await dwn.processMessage(alice.did, m); - - expect(reply.status.code).to.equal(200); - expect((reply as any).data).to.not.exist; - expect(reply.entries!.length).to.equal(expectedCids.length); - - for (let i = 0; i < reply.entries!.length; i += 1) { - expect(reply.entries![i]).to.equal(expectedCids[i]); - } - }); - }); -} diff --git a/tests/handlers/events-query.spec.ts b/tests/handlers/events-query.spec.ts index e1eb91bde..c721d4394 100644 --- a/tests/handlers/events-query.spec.ts +++ b/tests/handlers/events-query.spec.ts @@ -1,15 +1,19 @@ import type { DidResolver } from '@web5/dids'; +import type { EventsQueryReply } from '../../src/types/events-types.js'; import type { DataStore, EventLog, EventStream, MessageStore, + ProtocolDefinition, ResumableTaskStore, } from '../../src/index.js'; import { Dwn } from '../../src/index.js'; import { EventsQueryHandler } from '../../src/handlers/events-query.js'; import { expect } from 'chai'; +import freeForAll from '../vectors/protocol-definitions/free-for-all.json' assert { type: 'json' }; +import { Message } from '../../src/core/message.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; import { TestEventStream } from '../test-event-stream.js'; import { TestStores } from '../test-stores.js'; @@ -113,5 +117,68 @@ export function testEventsQueryHandler(): void { expect(reply.status.code).to.equal(400); expect(reply.entries).to.not.exist; }); + + it('returns all events for a tenant beyond a provided cursor', async () => { + // scenario: Alice configures a protocol, and writes 5 records. + // Alice queries for events without a cursor, and expects to see all 5 records as well as the protocol configuration message. + // Alice writes an additional record. + // Alice queries for events beyond the cursor, and expects to see only the additional record. + + const alice = await TestDataGenerator.generateDidKeyPersona(); + const expectedCids: string[] = []; + + const protocolDefinition: ProtocolDefinition = { ...freeForAll, published: true }; + + // write a protocol configuration + const { message: protocolMessage } = await TestDataGenerator.generateProtocolsConfigure({ + author: alice, + protocolDefinition, + }); + const { status: configureStatus } = await dwn.processMessage(alice.did, protocolMessage); + expect(configureStatus.code).to.equal(202); + expectedCids.push(await Message.getCid(protocolMessage)); + + for (let i = 0; i < 5; i += 1) { + const { message, dataStream } = await TestDataGenerator.generateRecordsWrite({ + protocol : protocolDefinition.protocol, + protocolPath : 'post', + schema : protocolDefinition.types.post.schema, + author : alice + }); + const reply = await dwn.processMessage(alice.did, message, { dataStream }); + + expect(reply.status.code).to.equal(202); + const messageCid = await Message.getCid(message); + expectedCids.push(messageCid); + } + + const { message } = await TestDataGenerator.generateEventsQuery({ author: alice }); + const reply: EventsQueryReply = await dwn.processMessage(alice.did, message); + + expect(reply.status.code).to.equal(200); + expect((reply as any).data).to.not.exist; + expect(reply.entries?.length).to.equal(expectedCids.length); + + for (let i = 0; i < reply.entries!.length; i += 1) { + expect(reply.entries![i]).to.equal(expectedCids[i]); + } + + // write an additional message + const { message: additionalMessage, dataStream: additionalDataStream } = await TestDataGenerator.generateRecordsWrite({ + protocol : protocolDefinition.protocol, + protocolPath : 'post', + schema : protocolDefinition.types.post.schema, + author : alice + }); + const additionalReply = await dwn.processMessage(alice.did, additionalMessage, { dataStream: additionalDataStream }); + expect(additionalReply.status.code).to.equal(202); + + // query for events beyond the cursor + const { message: messagesAfterCursor } = await TestDataGenerator.generateEventsQuery({ author: alice, cursor: reply.cursor }); + const afterCursorReply = await dwn.processMessage(alice.did, messagesAfterCursor); + expect(afterCursorReply.status.code).to.equal(200); + expect(afterCursorReply.entries!.length).to.equal(1); + expect(afterCursorReply.entries![0]).to.equal(await Message.getCid(additionalMessage)); + }); }); } diff --git a/tests/interfaces/events-get.spec.ts b/tests/interfaces/events-get.spec.ts deleted file mode 100644 index 98395092b..000000000 --- a/tests/interfaces/events-get.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ - -import { EventsGet } from '../../src/interfaces/events-get.js'; -import { expect } from 'chai'; -import { Jws } from '../../src/index.js'; -import { Message } from '../../src/core/message.js'; -import { TestDataGenerator } from '../utils/test-data-generator.js'; - -describe('EventsGet Message', () => { - describe('create', () => { - it('creates an EventsGet message', async () => { - const alice = await TestDataGenerator.generatePersona(); - const eventsGet = await EventsGet.create({ - cursor: { - messageCid : 'messageCid', - value : 'value' - }, - signer: Jws.createSigner(alice) - }); - - const { message } = eventsGet; - expect(message.descriptor).to.exist; - expect(message.descriptor.cursor).to.eql({ messageCid: 'messageCid', value: 'value' }); - expect(message.authorization).to.exist; - }); - - it('does not require a cursor', async () => { - const alice = await TestDataGenerator.generatePersona(); - const eventsGet = await EventsGet.create({ - signer: await Jws.createSigner(alice) - }); - - const message = eventsGet.message; - expect(message.descriptor).to.exist; - expect(message.descriptor.cursor).to.not.exist; - expect(message.authorization).to.exist; - }); - }); - - describe('parse', () => { - it('parses a message into an EventsGet instance', async () => { - const alice = await TestDataGenerator.generatePersona(); - const eventsGet = await EventsGet.create({ - cursor: { - messageCid : 'messageCid', - value : 'value' - }, - signer: Jws.createSigner(alice) - }); - - const parsed = await EventsGet.parse(eventsGet.message); - expect(parsed).to.be.instanceof(EventsGet); - - const expectedMessageCid = await Message.getCid(eventsGet.message); - const messageCid = await Message.getCid(parsed.message); - - expect(messageCid).to.equal(expectedMessageCid); - }); - - it('throws an exception if message is not a valid EventsGet message', async () => { - const alice = await TestDataGenerator.generatePersona(); - const eventsGet = await EventsGet.create({ - cursor: { - messageCid : 'messageCid', - value : 'value' - }, - signer: Jws.createSigner(alice) - }); - - const { message } = eventsGet; - (message as any)['hehe'] = 'troll'; - - try { - await EventsGet.parse(message as any); - expect.fail(); - } catch (e: any) { - expect(e.message).to.include('additional properties'); - } - }); - }); -}); \ No newline at end of file diff --git a/tests/interfaces/events-query.spec.ts b/tests/interfaces/events-query.spec.ts index c9bd9c94e..a7331c37a 100644 --- a/tests/interfaces/events-query.spec.ts +++ b/tests/interfaces/events-query.spec.ts @@ -39,8 +39,8 @@ describe('EventsQuery Message', () => { const eventsQuery = await EventsQuery.create(options); const message = eventsQuery.message as EventsQueryMessage; - expect(message.descriptor.filters.length).to.equal(1); - expect((message.descriptor.filters[0] as ProtocolsQueryFilter).protocol).to.eq('http://example.com'); + expect(message.descriptor.filters?.length).to.equal(1); + expect((message.descriptor.filters![0] as ProtocolsQueryFilter).protocol).to.eq('http://example.com'); }); it('should auto-normalize schema URL', async () => { @@ -55,19 +55,18 @@ describe('EventsQuery Message', () => { const message = eventsQuery.message as EventsQueryMessage; - expect(message.descriptor.filters.length).to.equal(1); - expect((message.descriptor.filters[0] as RecordsFilter).schema).to.eq('http://example.com'); + expect(message.descriptor.filters?.length).to.equal(1); + expect((message.descriptor.filters![0] as RecordsFilter).schema).to.eq('http://example.com'); }); - it('throws an exception if message has no filters', async () => { + it('allows query with no filters', async () => { const alice = await TestDataGenerator.generatePersona(); const currentTime = Time.getCurrentTimestamp(); - const eventsQueryPromise = EventsQuery.create({ - filters : [], + const eventsQueryPromise = await EventsQuery.create({ messageTimestamp : currentTime, signer : Jws.createSigner(alice), }); - await expect(eventsQueryPromise).to.eventually.be.rejectedWith('fewer than 1 items'); + expect(eventsQueryPromise.message.descriptor.filters).to.be.undefined; }); it('removes empty filters', async () => { @@ -88,7 +87,7 @@ describe('EventsQuery Message', () => { messageTimestamp : currentTime, signer : Jws.createSigner(alice), }); - expect(eventsQuery.message.descriptor.filters.length).to.equal(1); + expect(eventsQuery.message.descriptor.filters?.length).to.equal(1); }); }); @@ -128,20 +127,17 @@ describe('EventsQuery Message', () => { await expect(eventsQueryPromise).to.eventually.be.rejectedWith('must NOT have additional properties'); }); - it('throws an exception if message has no filters', async () => { + it('allows query without any filters', async () => { const alice = await TestDataGenerator.generatePersona(); const currentTime = Time.getCurrentTimestamp(); const eventsQuery = await EventsQuery.create({ - filters : [{ schema: 'anything' }], messageTimestamp : currentTime, signer : Jws.createSigner(alice), }); const { message } = eventsQuery; - message.descriptor.filters = []; //empty out the filters - - const eventsQueryPromise = EventsQuery.parse(message); - await expect(eventsQueryPromise).to.eventually.be.rejectedWith('fewer than 1 items'); + const parsedQuery = await EventsQuery.parse(message); + expect(parsedQuery.message.descriptor.filters).to.be.undefined; }); it('throws an exception if message has an empty filter', async () => { @@ -154,7 +150,7 @@ describe('EventsQuery Message', () => { }); const { message } = eventsQuery; - message.descriptor.filters.push({ }); // add an empty filter + message.descriptor.filters!.push({ }); // add an empty filter const eventsQueryPromise = EventsQuery.parse(message); await expect(eventsQueryPromise).to.eventually.be.rejectedWith('must NOT have fewer than 1 properties'); }); diff --git a/tests/test-suite.ts b/tests/test-suite.ts index 262f5daf6..b966fb545 100644 --- a/tests/test-suite.ts +++ b/tests/test-suite.ts @@ -4,7 +4,6 @@ import { testAuthorDelegatedGrant } from './features/author-delegated-grant.spec import { testDwnClass } from './dwn.spec.js'; import { testEndToEndScenarios } from './scenarios/end-to-end-tests.spec.js'; import { testEventLog } from './event-log/event-log.spec.js'; -import { testEventsGetHandler } from './handlers/events-get.spec.js'; import { testEventsQueryHandler } from './handlers/events-query.spec.js'; import { testEventsQueryScenarios } from './scenarios/events-query.spec.js'; import { testEventsSubscribeHandler } from './handlers/events-subscribe.spec.js'; @@ -60,7 +59,6 @@ export class TestSuite { testEventStream(); // handler tests - testEventsGetHandler(); testEventsSubscribeHandler(); testEventsQueryHandler(); testMessagesGetHandler(); diff --git a/tests/utils/test-data-generator.ts b/tests/utils/test-data-generator.ts index 465cd10ed..8a4f4c021 100644 --- a/tests/utils/test-data-generator.ts +++ b/tests/utils/test-data-generator.ts @@ -1,6 +1,5 @@ import type { DerivedPrivateJwk } from '../../src/utils/hd-key.js'; import type { DidResolutionResult } from '@web5/dids'; -import type { EventsGetOptions } from '../../src/interfaces/events-get.js'; import type { EventsQueryOptions } from '../../src/interfaces/events-query.js'; import type { EventsSubscribeOptions } from '../../src/interfaces/events-subscribe.js'; import type { GeneralJws } from '../../src/types/jws-types.js'; @@ -16,7 +15,7 @@ import type { Signer } from '../../src/types/signer.js'; import type { AuthorizationModel, Pagination } from '../../src/types/message-types.js'; import type { CreateFromOptions, EncryptionInput, KeyEncryptionInput, RecordsWriteOptions } from '../../src/interfaces/records-write.js'; import type { DateSort, RecordsDeleteMessage, RecordsFilter, RecordsQueryMessage, RecordsWriteTags } from '../../src/types/records-types.js'; -import type { EventsFilter, EventsGetMessage, EventsQueryMessage, EventsSubscribeMessage } from '../../src/types/events-types.js'; +import type { EventsFilter, EventsQueryMessage, EventsSubscribeMessage } from '../../src/types/events-types.js'; import type { PrivateJwk, PublicJwk } from '../../src/types/jose-types.js'; import type { ProtocolDefinition, ProtocolsConfigureMessage, ProtocolsQueryMessage } from '../../src/types/protocols-types.js'; import type { RecordsSubscribeMessage, RecordsWriteMessage } from '../../src/types/records-types.js'; @@ -28,7 +27,6 @@ import { DidKey } from '@web5/dids'; import { ed25519 } from '../../src/jose/algorithms/signing/ed25519.js'; import { Encoder } from '../../src/utils/encoder.js'; import { Encryption } from '../../src/utils/encryption.js'; -import { EventsGet } from '../../src/interfaces/events-get.js'; import { EventsQuery } from '../../src/interfaces/events-query.js'; import { EventsSubscribe } from '../../src/interfaces/events-subscribe.js'; import { Jws } from '../../src/utils/jws.js'; @@ -192,20 +190,9 @@ export type GenerateRecordsDeleteOutput = { message: RecordsDeleteMessage; }; -export type GenerateEventsGetInput = { - author?: Persona; - cursor?: PaginationCursor; -}; - -export type GenerateEventsGetOutput = { - author: Persona; - eventsGet: EventsGet; - message: EventsGetMessage; -}; - export type GenerateEventsQueryInput = { author?: Persona; - filters: EventsFilter[]; + filters?: EventsFilter[]; cursor?: PaginationCursor; }; @@ -673,24 +660,6 @@ export class TestDataGenerator { }; } - public static async generateEventsGet(input?: GenerateEventsGetInput): Promise { - const author = input?.author ?? await TestDataGenerator.generatePersona(); - const signer = Jws.createSigner(author); - - const options: EventsGetOptions = { signer }; - if (input?.cursor) { - options.cursor = input.cursor; - } - - const eventsGet = await EventsGet.create(options); - - return { - author, - eventsGet, - message: eventsGet.message - }; - } - public static async generateEventsQuery(input: GenerateEventsQueryInput): Promise { const { filters, cursor } = input; const author = input.author ?? await TestDataGenerator.generatePersona();