From bd9f3ec2e82367271d8f6035f53489c749be4b9f Mon Sep 17 00:00:00 2001 From: Liran Cohen Date: Fri, 24 Nov 2023 16:41:20 -0500 Subject: [PATCH] update EventsFilter types, error handling, convertFilter/parse --- src/core/dwn-error.ts | 1 + src/interfaces/events-query.ts | 74 +++++++++++++++-- src/interfaces/protocols-query.ts | 2 +- src/interfaces/records-query.ts | 13 ++- src/store/index-level.ts | 2 + src/types/event-types.ts | 18 +++-- src/types/query-types.ts | 12 +++ src/types/records-types.ts | 14 +--- src/utils/filter.ts | 23 +++++- src/utils/records.ts | 40 +++------- tests/handlers/events-query.spec.ts | 105 ------------------------- tests/interfaces/events-query.spec.ts | 6 +- tests/interfaces/records-query.spec.ts | 3 + tests/utils/test-data-generator.ts | 4 +- 14 files changed, 150 insertions(+), 167 deletions(-) diff --git a/src/core/dwn-error.ts b/src/core/dwn-error.ts index d9e94a84f..4d79465df 100644 --- a/src/core/dwn-error.ts +++ b/src/core/dwn-error.ts @@ -82,6 +82,7 @@ export enum DwnErrorCode { ProtocolsQueryUnauthorized = 'ProtocolsQueryUnauthorized', RecordsDecryptNoMatchingKeyEncryptedFound = 'RecordsDecryptNoMatchingKeyEncryptedFound', RecordsDeleteAuthorizationFailed = 'RecordsDeleteAuthorizationFailed', + RecordsFilterPublishedSortInvalid = 'RecordsFilterPublishedSortInvalid', RecordsGrantAuthorizationConditionPublicationProhibited = 'RecordsGrantAuthorizationConditionPublicationProhibited', RecordsGrantAuthorizationConditionPublicationRequired = 'RecordsGrantAuthorizationConditionPublicationRequired', RecordsGrantAuthorizationScopeContextIdMismatch = 'RecordsGrantAuthorizationScopeContextIdMismatch', diff --git a/src/interfaces/events-query.ts b/src/interfaces/events-query.ts index ef8977a75..7e1205589 100644 --- a/src/interfaces/events-query.ts +++ b/src/interfaces/events-query.ts @@ -1,9 +1,12 @@ import type { Filter } from '../types/query-types.js'; +import type { ProtocolsQueryFilter } from '../types/protocols-types.js'; import type { Signer } from '../types/signer.js'; -import type { EventsFilter, EventsQueryDescriptor, EventsQueryMessage } from '../types/event-types.js'; +import type { EventsFilter, EventsQueryDescriptor, EventsQueryFilter, EventsQueryMessage, EventsRecordsFilter } from '../types/event-types.js'; import { AbstractMessage } from '../core/abstract-message.js'; +import { FilterUtility } from '../utils/filter.js'; import { Message } from '../core/message.js'; +import { ProtocolsQuery } from '../interfaces/protocols-query.js'; import { Records } from '../utils/records.js'; import { removeUndefinedProperties } from '../utils/object.js'; import { Time } from '../utils/time.js'; @@ -11,7 +14,7 @@ import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.j export type EventsQueryOptions = { signer: Signer; - filters: EventsFilter[]; + filters: EventsQueryFilter[]; cursor?: string; messageTimestamp?: string; }; @@ -44,19 +47,74 @@ export class EventsQuery extends AbstractMessage{ return new EventsQuery(message); } - private static normalizeFilters(filters: EventsFilter[]): EventsFilter[] { - // currently all normalization filters are shared with `Records`. - return filters.map(filter => Records.normalizeFilter(filter)); + private static normalizeFilters(filters: EventsQueryFilter[]): EventsQueryFilter[] { + + const eventsQueryFilters: EventsQueryFilter[] = []; + + // normalize each filter individually by the type of filter it is. + for (const filter of filters) { + if (this.isRecordsFilter(filter)) { + eventsQueryFilters.push(Records.normalizeFilter(filter)); + } else if (this.isProtocolsFilter(filter)) { + const protocolFilter = ProtocolsQuery.normalizeFilter(filter); + eventsQueryFilters.push(protocolFilter!); + } else { + eventsQueryFilters.push(filter as EventsFilter); + } + } + + return eventsQueryFilters; } + /** * Converts an incoming array of EventsFilter into a Filter usable by EventLog. * * @param filters An array of EventsFilter * @returns {Filter[]} an array of generic Filter able to be used when querying. */ - public static convertFilters(filters: EventsFilter[]): Filter[] { - //currently only the range criterion for Records need to be converted - return filters.map(filter => Records.convertFilter(filter)); + public static convertFilters(filters: EventsQueryFilter[]): Filter[] { + + const eventsQueryFilters: Filter[] = []; + + // normalize each filter individually by the type of filter it is. + for (const filter of filters) { + if (this.isRecordsFilter(filter)) { + eventsQueryFilters.push(Records.convertFilter(filter)); + } else if (this.isProtocolsFilter(filter)) { + eventsQueryFilters.push({ ...filter }); + } else { + eventsQueryFilters.push(this.convertFilter(filter)); + } + } + + return eventsQueryFilters; + } + + private static convertFilter(filter: EventsFilter): Filter { + const filterCopy = { ...filter } as Filter; + + const { messageTimestamp } = filter; + const messageTimestampFilter = messageTimestamp ? FilterUtility.convertRangeCriterion(messageTimestamp) : undefined; + if (messageTimestampFilter) { + filterCopy.messageTimestamp = messageTimestampFilter; + } + + return filterCopy as Filter; + } + + private static isProtocolsFilter(filter: EventsQueryFilter): filter is ProtocolsQueryFilter { + return 'protocol' in filter; + } + + private static isRecordsFilter(filter: EventsQueryFilter): filter is EventsRecordsFilter { + return 'dateCreated' in filter || + 'dataFormat' in filter || + 'parentId' in filter || + 'recordId' in filter || + 'schema' in filter || + 'protocolPath' in filter || // explicitly ignore `protocol` as it will be handled by the protocol filter + 'recipient' in filter; } + } \ No newline at end of file diff --git a/src/interfaces/protocols-query.ts b/src/interfaces/protocols-query.ts index ff337667d..dfdf9cc42 100644 --- a/src/interfaces/protocols-query.ts +++ b/src/interfaces/protocols-query.ts @@ -65,7 +65,7 @@ export class ProtocolsQuery extends AbstractMessage { return protocolsQuery; } - private static normalizeFilter(filter: ProtocolsQueryFilter | undefined): ProtocolsQueryFilter | undefined { + static normalizeFilter(filter: ProtocolsQueryFilter | undefined): ProtocolsQueryFilter | undefined { if (filter === undefined) { return undefined; } diff --git a/src/interfaces/records-query.ts b/src/interfaces/records-query.ts index 2ea600bb9..30fb73323 100644 --- a/src/interfaces/records-query.ts +++ b/src/interfaces/records-query.ts @@ -1,9 +1,10 @@ import type { DelegatedGrantMessage } from '../types/delegated-grant-message.js'; import type { Pagination } from '../types/message-types.js'; import type { Signer } from '../types/signer.js'; -import type { DateSort, RecordsFilter, RecordsQueryDescriptor, RecordsQueryMessage } from '../types/records-types.js'; +import type { RecordsFilter, RecordsQueryDescriptor, RecordsQueryMessage } from '../types/records-types.js'; import { AbstractMessage } from '../core/abstract-message.js'; +import { DateSort } from '../types/records-types.js'; import { Message } from '../core/message.js'; import { Records } from '../utils/records.js'; import { removeUndefinedProperties } from '../utils/object.js'; @@ -47,6 +48,16 @@ export class RecordsQuery extends AbstractMessage { ); } } + + if (message.descriptor.filter.published === false) { + if (message.descriptor.dateSort === DateSort.PublishedAscending || message.descriptor.dateSort === DateSort.PublishedDescending) { + throw new DwnError( + DwnErrorCode.RecordsFilterPublishedSortInvalid, + `queries must not filter for \`published:false\` and sort by ${message.descriptor.dateSort}` + ); + } + } + if (message.descriptor.filter.protocol !== undefined) { validateProtocolUrlNormalized(message.descriptor.filter.protocol); } diff --git a/src/store/index-level.ts b/src/store/index-level.ts index 826666b72..6f6d5cb6d 100644 --- a/src/store/index-level.ts +++ b/src/store/index-level.ts @@ -293,6 +293,8 @@ export class IndexLevel { * * @param matchFilters the filters passed to the parent query. * @param searchFilters the modified filters used for the LevelDB query to search for a subset of items to match against. + * + * @throws {DwnErrorCode.IndexLevelInMemoryInvalidSortProperty} if an invalid sort property is provided. */ async queryWithInMemoryPaging( tenant: string, diff --git a/src/types/event-types.ts b/src/types/event-types.ts index feb529cc7..9421e3de4 100644 --- a/src/types/event-types.ts +++ b/src/types/event-types.ts @@ -1,15 +1,21 @@ import type { GenericMessageReply } from '../core/message-reply.js'; +import type { ProtocolsQueryFilter } from './protocols-types.js'; +import type { RangeCriterion } from './query-types.js'; import type { RecordsFilter } from './records-types.js'; import type { AuthorizationModel, GenericMessage } from './message-types.js'; import type { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js'; -export type EventsFilter = RecordsFilter & { - /** optional array of methods to filter */ - method?: string[]; - /** optional array of interfaces to filter */ - interface?: string[]; +export type EventsFilter = { + method?: string; + interface?: string; + messageTimestamp?: RangeCriterion; }; +// We only allow filtering for events by immutable properties, the omitted properties could be different per subsequent writes. +export type EventsRecordsFilter = Omit; + +export type EventsQueryFilter = EventsFilter | EventsRecordsFilter | ProtocolsQueryFilter; + export type EventsGetDescriptor = { interface : DwnInterfaceName.Events; method: DwnMethodName.Get; @@ -30,7 +36,7 @@ export type EventsQueryDescriptor = { interface: DwnInterfaceName.Events; method: DwnMethodName.Query; messageTimestamp: string; - filters: EventsFilter[]; + filters: EventsQueryFilter[]; cursor?: string; }; diff --git a/src/types/query-types.ts b/src/types/query-types.ts index 1015dff3b..642327937 100644 --- a/src/types/query-types.ts +++ b/src/types/query-types.ts @@ -37,4 +37,16 @@ export type FilterValue = EqualFilter | OneOfFilter | RangeFilter; export type Filter = { [property: string]: FilterValue; +}; + +export type RangeCriterion = { + /** + * Inclusive starting date-time. + */ + from?: string; + + /** + * Inclusive end date-time. + */ + to?: string; }; \ No newline at end of file diff --git a/src/types/records-types.ts b/src/types/records-types.ts index 5882c96a8..19d375ce8 100644 --- a/src/types/records-types.ts +++ b/src/types/records-types.ts @@ -3,10 +3,10 @@ import type { GeneralJws } from './jws-types.js'; import type { GenericMessageReply } from '../core/message-reply.js'; import type { KeyDerivationScheme } from '../utils/hd-key.js'; import type { PublicJwk } from './jose-types.js'; -import type { RangeFilter } from './query-types.js'; import type { Readable } from 'readable-stream'; import type { AuthorizationModel, GenericMessage, GenericSignaturePayload, Pagination } from './message-types.js'; import type { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js'; +import type { RangeCriterion, RangeFilter } from './query-types.js'; export enum DateSort { CreatedAscending = 'createdAscending', @@ -122,18 +122,6 @@ export type RecordsFilter = { dateUpdated?: RangeCriterion; }; -export type RangeCriterion = { - /** - * Inclusive starting date-time. - */ - from?: string; - - /** - * Inclusive end date-time. - */ - to?: string; -}; - export type RecordsWriteAttestationPayload = { descriptorCid: string; }; diff --git a/src/utils/filter.ts b/src/utils/filter.ts index 6df59242d..61ab6f4ae 100644 --- a/src/utils/filter.ts +++ b/src/utils/filter.ts @@ -1,4 +1,4 @@ -import type { EqualFilter, Filter, FilterValue, KeyValues, OneOfFilter, QueryOptions, RangeFilter } from '../types/query-types.js'; +import type { EqualFilter, Filter, FilterValue, KeyValues, OneOfFilter, QueryOptions, RangeCriterion, RangeFilter } from '../types/query-types.js'; import { isEmptyObject } from './object.js'; @@ -135,6 +135,26 @@ export class FilterUtility { }; return false; } + + static convertRangeCriterion(inputFilter: RangeCriterion): RangeFilter | undefined { + let rangeFilter: RangeFilter | undefined; + if (inputFilter.to !== undefined && inputFilter.from !== undefined) { + rangeFilter = { + gte : inputFilter.from, + lt : inputFilter.to, + }; + } else if (inputFilter.to !== undefined) { + rangeFilter = { + lt: inputFilter.to, + }; + } else if (inputFilter.from !== undefined) { + rangeFilter = { + gte: inputFilter.from, + }; + } + return rangeFilter; + } + } export class FilterSelector { @@ -158,7 +178,6 @@ export class FilterSelector { }); } - //TODO: return a single filter, this may have to change where/how this method is used. private static checkForIdSearches(filters: Filter[]): { searchFilters: Filter[], remainingFilters: Filter[] } { const searchFilters: Filter[] = []; const remainingFilters: Filter[] = []; diff --git a/src/utils/records.ts b/src/utils/records.ts index 02b80b28b..51e0c83dc 100644 --- a/src/utils/records.ts +++ b/src/utils/records.ts @@ -1,14 +1,16 @@ import type { DerivedPrivateJwk } from './hd-key.js'; +import type { Filter } from '../types/query-types.js'; import type { GenericSignaturePayload } from '../types/message-types.js'; import type { Readable } from 'readable-stream'; -import type { Filter, RangeFilter } from '../types/query-types.js'; -import type { RangeCriterion, RecordsDeleteMessage, RecordsFilter, RecordsQueryMessage, RecordsReadMessage, RecordsWriteDescriptor, RecordsWriteMessage } from '../types/records-types.js'; +import type { RecordsDeleteMessage, RecordsFilter, RecordsQueryMessage, RecordsReadMessage, RecordsWriteDescriptor, RecordsWriteMessage } from '../types/records-types.js'; import { DateSort } from '../types/records-types.js'; import { Encoder } from './encoder.js'; import { Encryption } from './encryption.js'; +import { FilterUtility } from './filter.js'; import { KeyDerivationScheme } from './hd-key.js'; import { Message } from '../core/message.js'; +import { removeUndefinedProperties } from './object.js'; import { Secp256k1 } from './secp256k1.js'; import { DwnError, DwnErrorCode } from '../core/dwn-error.js'; import { normalizeProtocolUrl, normalizeSchemaUrl } from './url.js'; @@ -236,11 +238,14 @@ export class Records { schema = normalizeSchemaUrl(filter.schema); } - return { + const filterCopy = { ...filter, protocol, schema, }; + + removeUndefinedProperties(filterCopy); + return filterCopy; } /** @@ -253,24 +258,24 @@ export class Records { const filterCopy = { ...filter } as Filter; const { dateCreated, datePublished, dateUpdated } = filter; - const dateCreatedFilter = dateCreated ? this.convertRangeCriterion(dateCreated) : undefined; + const dateCreatedFilter = dateCreated ? FilterUtility.convertRangeCriterion(dateCreated) : undefined; if (dateCreatedFilter) { filterCopy.dateCreated = dateCreatedFilter; } - const datePublishedFilter = datePublished ? this.convertRangeCriterion(datePublished): undefined; + const datePublishedFilter = datePublished ? FilterUtility.convertRangeCriterion(datePublished): undefined; if (datePublishedFilter) { // only return published records when filtering with a datePublished range. filterCopy.published = true; filterCopy.datePublished = datePublishedFilter; } - //if sorting by published, results must filter for published - if (dateSort === DateSort.PublishedAscending || dateSort === DateSort.PublishedDescending) { + // if we sort by `PublishedAscending` or `PublishedDescending` we must filter for only published records. + if (filterCopy.published !== true && (dateSort === DateSort.PublishedAscending || dateSort === DateSort.PublishedDescending)) { filterCopy.published = true; } - const messageTimestampFilter = dateUpdated ? this.convertRangeCriterion(dateUpdated) : undefined; + const messageTimestampFilter = dateUpdated ? FilterUtility.convertRangeCriterion(dateUpdated) : undefined; if (messageTimestampFilter) { filterCopy.messageTimestamp = messageTimestampFilter; delete filterCopy.dateUpdated; @@ -278,25 +283,6 @@ export class Records { return filterCopy as Filter; } - private static convertRangeCriterion(inputFilter: RangeCriterion): RangeFilter | undefined { - let rangeFilter: RangeFilter | undefined; - if (inputFilter.to !== undefined && inputFilter.from !== undefined) { - rangeFilter = { - gte : inputFilter.from, - lt : inputFilter.to, - }; - } else if (inputFilter.to !== undefined) { - rangeFilter = { - lt: inputFilter.to, - }; - } else if (inputFilter.from !== undefined) { - rangeFilter = { - gte: inputFilter.from, - }; - } - return rangeFilter; - } - /** * Validates the referential integrity regarding delegated grant. * @param signaturePayload Decoded payload of the signature of the message. `undefined` if message is not signed. diff --git a/tests/handlers/events-query.spec.ts b/tests/handlers/events-query.spec.ts index e0db844a5..c73a1616e 100644 --- a/tests/handlers/events-query.spec.ts +++ b/tests/handlers/events-query.spec.ts @@ -267,111 +267,6 @@ export function testEventsQueryHandler(): void { expect(reply4.events?.length).to.equal(0); }); - it('returns events filtered by a given author', async () => { - // scenario: alice and bob both write messages to alice's DWN - // alice is able to filter for events by author across different message types - - const alice = await DidKeyResolver.generate(); - const bob = await DidKeyResolver.generate(); - - // create a proto1 - const protoConf = await TestDataGenerator.generateProtocolsConfigure({ - author : alice, - protocolDefinition : { ...contributionReward, protocol: 'proto1' } - }); - const protoConfResponse = await dwn.processMessage(alice.did, protoConf.message); - expect(protoConfResponse.status.code).equals(202); - - // alice writes a message - const aliceWrite1 = await TestDataGenerator.generateRecordsWrite({ author: alice, schema: 'contribution', protocol: 'proto1', protocolPath: 'contribution' }); - const aliceWrite1Response = await dwn.processMessage(alice.did, aliceWrite1.message, aliceWrite1.dataStream); - expect(aliceWrite1Response.status.code).equals(202); - - // bob writes messages - const bobWrite1 = await TestDataGenerator.generateRecordsWrite({ author: bob, schema: 'contribution', protocol: 'proto1', protocolPath: 'contribution' }); - const bobWrite1Response = await dwn.processMessage(alice.did, bobWrite1.message, bobWrite1.dataStream); - expect(bobWrite1Response.status.code).equals(202); - - const bobWrite2 = await TestDataGenerator.generateRecordsWrite({ author: bob, schema: 'contribution', protocol: 'proto1', protocolPath: 'contribution' }); - const bobWrite2Response = await dwn.processMessage(alice.did, bobWrite2.message, bobWrite2.dataStream); - expect(bobWrite2Response.status.code).equals(202); - - // alice writes another message - const aliceWrite2 = await TestDataGenerator.generateRecordsWrite({ author: alice, schema: 'contribution', protocol: 'proto1', protocolPath: 'contribution' }); - const aliceWrite2Response = await dwn.processMessage(alice.did, aliceWrite2.message, aliceWrite2.dataStream); - expect(aliceWrite2Response.status.code).equals(202); - - // alice queries for events authored by alice - let aliceEvents = await TestDataGenerator.generateEventsQuery({ - author : alice, - filters : [{ author: alice.did }], - }); - let aliceEventsReply = await dwn.processMessage(alice.did, aliceEvents.message); - expect(aliceEventsReply.status.code).to.equal(200); - expect(aliceEventsReply.events?.length).to.equal(3); - const aliceProtocolCid = await Message.getCid(protoConf.message); - const aliceWrite1Cid = await Message.getCid(aliceWrite1.message); - const aliceWrite2Cid = await Message.getCid(aliceWrite2.message); - expect(aliceEventsReply.events).to.eql([ aliceProtocolCid, aliceWrite1Cid, aliceWrite2Cid ]); - - // alice queries for events authored by bob - const bobsEvents = await TestDataGenerator.generateEventsQuery({ - author : alice, - filters : [{ author: bob.did }], - }); - const bobsEventsReply = await dwn.processMessage(alice.did, bobsEvents.message); - expect(bobsEventsReply.status.code).to.equal(200); - expect(bobsEventsReply.events?.length).to.equal(2); - const bobWrite1Cid = await Message.getCid(bobWrite1.message); - const bobWrite2Cid = await Message.getCid(bobWrite2.message); - expect(bobsEventsReply.events).to.eql([ bobWrite1Cid, bobWrite2Cid ]); - - // alice writes another message - const aliceWrite3 = await TestDataGenerator.generateRecordsWrite({ author: alice, schema: 'contribution', protocol: 'proto1', protocolPath: 'contribution' }); - const aliceWrite3Response = await dwn.processMessage(alice.did, aliceWrite3.message, aliceWrite3.dataStream); - expect(aliceWrite3Response.status.code).equals(202); - - // bob writes another message - const bobWrite3 = await TestDataGenerator.generateRecordsWrite({ author: bob, schema: 'contribution', protocol: 'proto1', protocolPath: 'contribution' }); - const bobWrite3Response = await dwn.processMessage(alice.did, bobWrite3.message, bobWrite3.dataStream); - expect(bobWrite3Response.status.code).equals(202); - - // alice issues a grant - const grant = await TestDataGenerator.generatePermissionsGrant({ author: alice }); - const grantResponse = await dwn.processMessage(alice.did, grant.message); - const grantId = await Message.getCid(grant.message); - expect(grantResponse.status.code).to.equal(202); - - // alice revokes grant - const grantRevoke = await TestDataGenerator.generatePermissionsRevoke({ author: alice, permissionsGrantId: grantId }); - const grantRevokeResponse = await dwn.processMessage(alice.did, grantRevoke.message); - expect(grantRevokeResponse.status.code).to.equal(202); - - // alice configures another protocol - const protoConf2 = await TestDataGenerator.generateProtocolsConfigure({ - author : alice, - protocolDefinition : { ...contributionReward, protocol: 'proto2' } - }); - const protoConf2Response = await dwn.processMessage(alice.did, protoConf2.message); - expect(protoConf2Response.status.code).equals(202); - - // query events after cursor - aliceEvents = await TestDataGenerator.generateEventsQuery({ - cursor : aliceWrite2Cid, - author : alice, - filters : [{ author: alice.did }], - }); - aliceEventsReply = await dwn.processMessage(alice.did, aliceEvents.message); - expect(aliceEventsReply.status.code).to.equal(200); - expect(aliceEventsReply.events?.length).to.equal(4); - expect(aliceEventsReply.events).to.eql([ - await Message.getCid(aliceWrite3.message), - grantId, - await Message.getCid(grantRevoke.message), - await Message.getCid(protoConf2.message), - ]); - }); - it('returns a 401 if tenant is not author', async () => { const alice = await DidKeyResolver.generate(); const bob = await DidKeyResolver.generate(); diff --git a/tests/interfaces/events-query.spec.ts b/tests/interfaces/events-query.spec.ts index f99f01335..c3fbe88b2 100644 --- a/tests/interfaces/events-query.spec.ts +++ b/tests/interfaces/events-query.spec.ts @@ -1,4 +1,6 @@ import type { EventsQueryMessage } from '../../src/types/event-types.js'; +import type { ProtocolsQueryFilter } from '../../src/types/protocols-types.js'; +import type { RecordsFilter } from '../../src/types/records-types.js'; import { EventsQuery } from '../../src/interfaces/events-query.js'; import { Jws } from '../../src/utils/jws.js'; @@ -38,7 +40,7 @@ describe('EventsQuery Message', () => { const message = eventsQuery.message as EventsQueryMessage; expect(message.descriptor.filters.length).to.equal(1); - expect(message.descriptor.filters[0].protocol).to.eq('http://example.com'); + expect((message.descriptor.filters[0] as ProtocolsQueryFilter).protocol).to.eq('http://example.com'); }); it('should auto-normalize schema URL', async () => { @@ -54,7 +56,7 @@ describe('EventsQuery Message', () => { const message = eventsQuery.message as EventsQueryMessage; expect(message.descriptor.filters.length).to.equal(1); - expect(message.descriptor.filters[0].schema).to.eq('http://example.com'); + expect((message.descriptor.filters[0] as RecordsFilter).schema).to.eq('http://example.com'); }); it('throws an exception if message has no filters', async () => { diff --git a/tests/interfaces/records-query.spec.ts b/tests/interfaces/records-query.spec.ts index 43bdfaf3a..29ef9fdf3 100644 --- a/tests/interfaces/records-query.spec.ts +++ b/tests/interfaces/records-query.spec.ts @@ -26,6 +26,9 @@ describe('RecordsQuery', () => { await expect(recordQueryRejected).to.eventually.be.rejectedWith('descriptor/filter/published: must be equal to one of the allowed values'); }); + xit('should not allow published to be set to false with a dateSort set to sorting by `PublishedAscending` or `PublishedDescending`', async () => { + }); + it('should use `messageTimestamp` as is if given', async () => { const alice = await TestDataGenerator.generatePersona(); diff --git a/tests/utils/test-data-generator.ts b/tests/utils/test-data-generator.ts index 803a92f27..2c81cee0e 100644 --- a/tests/utils/test-data-generator.ts +++ b/tests/utils/test-data-generator.ts @@ -4,7 +4,7 @@ import type { GeneralJws } from '../../src/types/jws-types.js'; import type { Readable } from 'readable-stream'; import type { RecordsFilter } from '../../src/types/records-types.js'; import type { AuthorizationModel, Pagination } from '../../src/types/message-types.js'; -import type { EventsFilter, EventsQueryMessage } from '../../src/types/event-types.js'; +import type { EventsQueryFilter, EventsQueryMessage } from '../../src/types/event-types.js'; import type { CreateFromOptions, @@ -250,7 +250,7 @@ export type GenerateEventsGetOutput = { export type GenerateEventsQueryInput = { author?: Persona; - filters: EventsFilter[]; + filters: EventsQueryFilter[]; cursor?: string; };