Skip to content

Commit

Permalink
remove unecessary Event Filters
Browse files Browse the repository at this point in the history
  • Loading branch information
LiranCohen committed Jun 19, 2024
1 parent c2d6ede commit d80bc12
Show file tree
Hide file tree
Showing 10 changed files with 64 additions and 1,648 deletions.
80 changes: 1 addition & 79 deletions json-schemas/interface-methods/events-filter.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"properties": {
"interface": {
"enum": [
"Permissions",
"Protocols",
"Records"
],
Expand All @@ -17,40 +16,14 @@
"enum": [
"Configure",
"Delete",
"Grant",
"Revoke",
"Write"
],
"type": "string"
},
"protocol": {
"type": "string"
},
"protocolPath": {
"type": "string"
},
"recipient": {
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/did"
},
"contextId": {
"type": "string"
},
"schema": {
"type": "string"
},
"recordId": {
"type": "string"
},
"parentId": {
"type": "string"
},
"dataFormat": {
"type": "string"
},
"dataSize": {
"$ref": "https://identity.foundation/dwn/json-schemas/number-range-filter.json"
},
"dateCreated": {
"messageTimestamp": {
"type": "object",
"minProperties": 1,
"additionalProperties": false,
Expand All @@ -62,57 +35,6 @@
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/date-time"
}
}
},
"datePublished": {
"type": "object",
"minProperties": 1,
"additionalProperties": false,
"properties": {
"from": {
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/date-time"
},
"to": {
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/date-time"
}
}
},
"dateUpdated": {
"type": "object",
"minProperties": 1,
"additionalProperties": false,
"properties": {
"from": {
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/date-time"
},
"to": {
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/date-time"
}
}
}
},
"dependencies": {
"datePublished": {
"oneOf": [
{
"properties": {
"published": {
"enum": [
true
]
}
},
"required": [
"published"
]
},
{
"not": {
"required": [
"published"
]
}
}
]
}
}
}
3 changes: 0 additions & 3 deletions src/interfaces/events-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ export class EventsQuery extends AbstractMessage<EventsQueryMessage>{
if ('protocol' in filter && filter.protocol !== undefined) {
validateProtocolUrlNormalized(filter.protocol);
}
if ('schema' in filter && filter.schema !== undefined) {
validateSchemaUrlNormalized(filter.schema);
}
}

return new EventsQuery(message);
Expand Down
3 changes: 0 additions & 3 deletions src/interfaces/events-subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ export class EventsSubscribe extends AbstractMessage<EventsSubscribeMessage> {
if ('protocol' in filter && filter.protocol !== undefined) {
validateProtocolUrlNormalized(filter.protocol);
}
if ('schema' in filter && filter.schema !== undefined) {
validateSchemaUrlNormalized(filter.schema);
}
}

Time.validateTimestamp(message.descriptor.messageTimestamp);
Expand Down
27 changes: 2 additions & 25 deletions src/types/events-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,13 @@ import type { PaginationCursor, RangeCriterion, RangeFilter } from './query-type
/**
* filters used when filtering for any type of Message across interfaces
*/
export type EventsMessageFilter = {
export type EventsFilter = {
interface?: string;
method?: string;
dateUpdated?: RangeCriterion;
};

/**
* We only allow filtering for events by immutable properties, the omitted properties could be different per subsequent writes.
*/
export type EventsRecordsFilter = {
recipient?: string;
protocol?: string;
protocolPath?: string;
contextId?: string;
schema?: string;
recordId?: string;
parentId?: string;
dataFormat?: string;
dataSize?: RangeFilter;
dateCreated?: RangeCriterion;
dateUpdated?: RangeCriterion;
messageTimestamp?: RangeCriterion;
};


/**
* A union type of the different types of filters a user can use when issuing an EventsQuery or EventsSubscribe
* TODO: simplify the EventsFilters to only the necessary in order to reduce complexity https://github.com/TBD54566975/dwn-sdk-js/issues/663
*/
export type EventsFilter = EventsMessageFilter | EventsRecordsFilter;

export type EventsGetDescriptor = {
interface: DwnInterfaceName.Events;
method: DwnMethodName.Get;
Expand Down
78 changes: 26 additions & 52 deletions src/utils/events.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { EventsFilter } from '../types/events-types.js';
import type { Filter } from '../types/query-types.js';
import type { EventsFilter, EventsMessageFilter, EventsRecordsFilter } from '../types/events-types.js';

import { FilterUtility } from '../utils/filter.js';
import { PermissionsProtocol } from '../protocols/permissions.js';
import { Records } from '../utils/records.js';
import { isEmptyObject, removeUndefinedProperties } from './object.js';
import { normalizeProtocolUrl } from './url.js';

Check failure on line 8 in src/utils/events.ts

View workflow job for this annotation

GitHub Actions / test-with-node (18.16.0)

Expected 'single' syntax before 'multiple' syntax

Check failure on line 8 in src/utils/events.ts

View workflow job for this annotation

GitHub Actions / test-with-node (20.3.0)

Expected 'single' syntax before 'multiple' syntax


/**
Expand All @@ -20,13 +21,13 @@ export class Events {

// normalize each filter individually by the type of filter it is.
for (const filter of filters) {
let eventsFilter: EventsFilter;
if (this.isRecordsFilter(filter)) {
eventsFilter = Records.normalizeFilter(filter);
} else {
// no normalization needed
eventsFilter = filter;
}
// normalize the protocol URL if it exists
const protocol = filter.protocol !== undefined ? normalizeProtocolUrl(filter.protocol) : undefined;

const eventsFilter = {
...filter,
protocol,
};

// remove any empty filter properties and do not add if empty
removeUndefinedProperties(eventsFilter);
Expand All @@ -35,7 +36,6 @@ export class Events {
}
}


return eventsQueryFilters;
}

Expand All @@ -54,77 +54,51 @@ export class Events {
// first we check for `EventsRecordsFilter` fields for conversion
// otherwise it is `EventsMessageFilter` fields for conversion
for (const filter of filters) {
if (this.isRecordsFilter(filter)) {

// extract the protocol tag filter from the incoming event record filter
// this filters for permission grants, requests and revocations associated with a targeted protocol
// since permissions are their own protocol, we add an additional tag index when writing the permission messages, so we can filter on it here
const protocolTagFilter = this.extractProtocolTagFilters(filter);
if (protocolTagFilter) {
eventsQueryFilters.push(protocolTagFilter);
}

eventsQueryFilters.push(Records.convertFilter(filter));
} else {
eventsQueryFilters.push(this.convertFilter(filter));
// extract the protocol tag filter from the incoming event record filter
// this filters for permission grants, requests and revocations associated with a targeted protocol
// since permissions are their own protocol, we add an additional tag index when writing the permission messages, so we can filter on it here
const protocolTagFilter = this.extractProtocolTagFilters(filter);
if (protocolTagFilter) {
eventsQueryFilters.push(protocolTagFilter);
}

eventsQueryFilters.push(this.convertFilter(filter));
}

return eventsQueryFilters;
}

private static extractProtocolTagFilters(filter: EventsRecordsFilter): Filter | undefined {
if (filter.protocol !== undefined) {
private static extractProtocolTagFilters(filter: EventsFilter): Filter | undefined {
const { protocol, messageTimestamp } = filter;
if (protocol !== undefined) {
const taggedFilter = {
protocol: PermissionsProtocol.uri,
...Records.convertTagsFilter({ protocol: filter.protocol })
...Records.convertTagsFilter({ protocol })
} as Filter;

if (filter.dateUpdated != undefined) {
const messageTimestampFilter = filter.dateUpdated ? FilterUtility.convertRangeCriterion(filter.dateUpdated) : undefined;
if (messageTimestamp != undefined) {
const messageTimestampFilter = FilterUtility.convertRangeCriterion(messageTimestamp);
if (messageTimestampFilter) {
taggedFilter.messageTimestamp = messageTimestampFilter;
}
}

if (filter.dateCreated !== undefined) {
const dateCreatedFilter = filter.dateCreated ? FilterUtility.convertRangeCriterion(filter.dateCreated) : undefined;
if (dateCreatedFilter) {
taggedFilter.dateCreated = dateCreatedFilter;
}
}

return taggedFilter;
}
}

/**
* Converts an external-facing filter model into an internal-facing filer model used by data store.
*/
private static convertFilter(filter: EventsMessageFilter): Filter {
private static convertFilter(filter: EventsFilter): Filter {
const filterCopy = { ...filter } as Filter;

const { dateUpdated } = filter;
const messageTimestampFilter = dateUpdated ? FilterUtility.convertRangeCriterion(dateUpdated) : undefined;
const { messageTimestamp } = filter;
const messageTimestampFilter = messageTimestamp ? FilterUtility.convertRangeCriterion(messageTimestamp) : undefined;
if (messageTimestampFilter) {
filterCopy.messageTimestamp = messageTimestampFilter;
delete filterCopy.dateUpdated;
}
return filterCopy as Filter;
}

// we deliberately do not check for `dateUpdated` in this filter.
// if it were the only property that matched, it could be handled by `EventsFilter`
private static isRecordsFilter(filter: EventsFilter): filter is EventsRecordsFilter {
return 'author' in filter ||
'dateCreated' in filter ||
'dataFormat' in filter ||
'dataSize' in filter ||
'parentId' in filter ||
'recordId' in filter ||
'schema' in filter ||
'protocol' in filter ||
'protocolPath' in filter ||
'recipient' in filter;
}
}
8 changes: 4 additions & 4 deletions tests/handlers/events-query.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function testEventsQueryHandler(): void {

const { message } = await TestDataGenerator.generateEventsQuery({
author : alice,
filters : [{ schema: 'schema1' }]
filters : [{ protocol: 'http://example.org/protocol/v1' }]
});
const eventsQueryHandler = new EventsQueryHandler(didResolver, eventLog);
const reply = await eventsQueryHandler.handle({ tenant: bob.did, message });
Expand All @@ -73,7 +73,7 @@ export function testEventsQueryHandler(): void {

const { message } = await TestDataGenerator.generateEventsQuery({
author : alice,
filters : [{ schema: 'schema1' }]
filters : [{ protocol: 'http://example.org/protocol/v1' }]
});
(message['descriptor'] as any)['troll'] = 'hehe';

Expand All @@ -89,7 +89,7 @@ export function testEventsQueryHandler(): void {

const { message } = await TestDataGenerator.generateEventsQuery({
author : alice,
filters : [{ schema: 'schema1' }],
filters : [{ protocol: 'http://example.org/protocol/v1' }],
}); // create with filter to prevent failure on .create()
message.descriptor.filters = []; // remove filters
const eventsQueryHandler = new EventsQueryHandler(didResolver, eventLog);
Expand All @@ -104,7 +104,7 @@ export function testEventsQueryHandler(): void {

const { message } = await TestDataGenerator.generateEventsQuery({
author : alice,
filters : [{ schema: 'schema1' }],
filters : [{ protocol: 'http://example.org/protocol/v1' }],
}); // create with filter to prevent failure on .create()
message.descriptor.filters = [{}]; // empty out filter properties
const eventsQueryHandler = new EventsQueryHandler(didResolver, eventLog);
Expand Down
Loading

0 comments on commit d80bc12

Please sign in to comment.