Skip to content

Commit

Permalink
RecordsQuery & RecordsSubscribe supports an array for author and `r…
Browse files Browse the repository at this point in the history
…ecipient` (#777)

Introduce filtering by multiple authors or recipients. The filters are
backward compatible, so that `author` and `recipient` filters now accept
a `string` or `Array<string>`.

NOTE: If an empty array is passed into the filter, it is treated as an
undefined filter.
  • Loading branch information
LiranCohen authored Aug 20, 2024
1 parent 8d574ed commit 9f25759
Show file tree
Hide file tree
Showing 8 changed files with 1,689 additions and 133 deletions.
18 changes: 16 additions & 2 deletions json-schemas/interface-methods/records-filter.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,27 @@
"type": "string"
},
"author": {
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/did"
"oneOf": [{
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/did"
},{
"type": "array",
"items": {
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/did"
}
}]
},
"attester": {
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/did"
},
"recipient": {
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/did"
"oneOf": [{
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/did"
},{
"type": "array",
"items": {
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/$defs/did"
}
}]
},
"contextId": {
"type": "string"
Expand Down
11 changes: 6 additions & 5 deletions src/handlers/records-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,16 +151,17 @@ export class RecordsQueryHandler implements MethodHandler {
}

if (Records.filterIncludesUnpublishedRecords(filter)) {
filters.push(RecordsQueryHandler.buildUnpublishedRecordsByQueryAuthorFilter(recordsQuery));

const recipientFilter = recordsQuery.message.descriptor.filter.recipient;
if (recipientFilter === undefined || recipientFilter === recordsQuery.author) {
filters.push(RecordsQueryHandler.buildUnpublishedRecordsForQueryAuthorFilter(recordsQuery));
if (Records.shouldBuildUnpublishedAuthorFilter(filter, recordsQuery.author!)) {
filters.push(RecordsQueryHandler.buildUnpublishedRecordsByQueryAuthorFilter(recordsQuery));
}

if (Records.shouldProtocolAuthorize(recordsQuery.signaturePayload!)) {
filters.push(RecordsQueryHandler.buildUnpublishedProtocolAuthorizedRecordsFilter(recordsQuery));
}

if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsQuery.author!)) {
filters.push(RecordsQueryHandler.buildUnpublishedRecordsForQueryAuthorFilter(recordsQuery));
}
}

const messageSort = this.convertDateSort(dateSort);
Expand Down
11 changes: 6 additions & 5 deletions src/handlers/records-subscribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,17 @@ export class RecordsSubscribeHandler implements MethodHandler {
}

if (Records.filterIncludesUnpublishedRecords(filter)) {
filters.push(RecordsSubscribeHandler.buildUnpublishedRecordsBySubscribeAuthorFilter(recordsSubscribe));

const recipientFilter = recordsSubscribe.message.descriptor.filter.recipient;
if (recipientFilter === undefined || recipientFilter === recordsSubscribe.author) {
filters.push(RecordsSubscribeHandler.buildUnpublishedRecordsForSubscribeAuthorFilter(recordsSubscribe));
if (Records.shouldBuildUnpublishedAuthorFilter(filter, recordsSubscribe.author!)) {
filters.push(RecordsSubscribeHandler.buildUnpublishedRecordsBySubscribeAuthorFilter(recordsSubscribe));
}

if (Records.shouldProtocolAuthorize(recordsSubscribe.signaturePayload!)) {
filters.push(RecordsSubscribeHandler.buildUnpublishedProtocolAuthorizedRecordsFilter(recordsSubscribe));
}

if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsSubscribe.author!)) {
filters.push(RecordsSubscribeHandler.buildUnpublishedRecordsForSubscribeAuthorFilter(recordsSubscribe));
}
}
return filters;
}
Expand Down
4 changes: 2 additions & 2 deletions src/types/records-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ export type RecordsFilter = {
/**
* The logical author of the record
*/
author?: string;
author?: string | string[];
attester?: string;
recipient?: string;
recipient?: string | string[];
protocol?: string;
protocolPath?: string;
published?: boolean;
Expand Down
40 changes: 40 additions & 0 deletions src/utils/records.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,16 @@ export class Records {
filterCopy.contextId = contextIdPrefixFilter;
}

// if the author filter is an array and it's empty, we should remove it from the filter as it will always return no results.
if (Array.isArray(filterCopy.author) && filterCopy.author.length === 0) {
delete filterCopy.author;
}

// if the recipient filter is an array and it's empty, we should remove it from the filter as it will always return no results.
if (Array.isArray(filterCopy.recipient) && filterCopy.recipient.length === 0) {
delete filterCopy.recipient;
}

return filterCopy as Filter;
}

Expand Down Expand Up @@ -531,4 +541,34 @@ export class Records {

return true;
}

/**
* Checks whether or not the incoming records query filter should build an unpublished recipient MessageStore filter.
*
* @param filter The incoming RecordsFilter to evaluate against.
* @param recipient The recipient to check against the filter, typically the query/subscribe message author.
* @returns {boolean} True if the filter contains the recipient, or if the recipient filter is undefined/empty.
*/
static shouldBuildUnpublishedRecipientFilter(filter: RecordsFilter, recipient: string): boolean {
const { recipient: recipientFilter } = filter;

return Array.isArray(recipientFilter) ?
recipientFilter.length === 0 || recipientFilter.includes(recipient) :
recipientFilter === undefined || recipientFilter === recipient;
}

/**
* Checks whether or not the incoming records query filter should build an unpublished author MessageStore filter.
*
* @param filter The incoming RecordsFilter to evaluate against.
* @param author The author to check against the filter, typically the query/subscribe message author.
* @returns {boolean} True if the filter contains the author, or if the author filter is undefined/empty.
*/
static shouldBuildUnpublishedAuthorFilter(filter: RecordsFilter, author: string): boolean {
const { author: authorFilter } = filter;

return Array.isArray(authorFilter) ?
authorFilter.length === 0 || authorFilter.includes(author) :
authorFilter === undefined || authorFilter === author;
}
}
Loading

0 comments on commit 9f25759

Please sign in to comment.