diff --git a/json-schemas/interface-methods/records-filter.json b/json-schemas/interface-methods/records-filter.json index da4f2fdd8..2ab6c0f2c 100644 --- a/json-schemas/interface-methods/records-filter.json +++ b/json-schemas/interface-methods/records-filter.json @@ -11,6 +11,9 @@ "protocolPath": { "type": "string" }, + "author": { + "$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/definitions/did" + }, "attester": { "$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/definitions/did" }, diff --git a/src/types/records-types.ts b/src/types/records-types.ts index 369116e22..f3587aef7 100644 --- a/src/types/records-types.ts +++ b/src/types/records-types.ts @@ -103,6 +103,8 @@ export type RecordsQueryDescriptor = { }; export type RecordsFilter = { + /**the logical author of the record */ + author?: string; attester?: string; recipient?: string; protocol?: string; diff --git a/tests/handlers/records-query.spec.ts b/tests/handlers/records-query.spec.ts index 2a809e03f..a70204c05 100644 --- a/tests/handlers/records-query.spec.ts +++ b/tests/handlers/records-query.spec.ts @@ -6,6 +6,7 @@ import chaiAsPromised from 'chai-as-promised'; import sinon from 'sinon'; import chai, { expect } from 'chai'; +import freeForAll from '../vectors/protocol-definitions/free-for-all.json' assert { type: 'json' }; import friendRoleProtocolDefinition from '../vectors/protocol-definitions/friend-role.json' assert { type: 'json' }; import threadRoleProtocolDefinition from '../vectors/protocol-definitions/thread-role.json' assert { type: 'json' }; @@ -197,6 +198,72 @@ export function testRecordsQueryHandler(): void { expect(reply3.entries?.length).to.equal(0); }); + it('should be able to query by author', async () => { + // scenario alice and bob both author records into alice's DWN. + // alice is able to filter for records authored by bob. + const alice = await DidKeyResolver.generate(); + const bob = await DidKeyResolver.generate(); + + const protocolDefinition = freeForAll; + + const protocolsConfig = await TestDataGenerator.generateProtocolsConfigure({ + author: alice, + protocolDefinition + }); + const protocolsConfigureReply = await dwn.processMessage(alice.did, protocolsConfig.message); + expect(protocolsConfigureReply.status.code).to.equal(202); + + const aliceAuthorWrite = await TestDataGenerator.generateRecordsWrite({ + author : alice, + protocol : protocolDefinition.protocol, + schema : protocolDefinition.types.post.schema, + dataFormat : protocolDefinition.types.post.dataFormats[0], + protocolPath : 'post' + }); + const aliceAuthorReply = await dwn.processMessage(alice.did, aliceAuthorWrite.message, aliceAuthorWrite.dataStream); + expect(aliceAuthorReply.status.code).to.equal(202); + + const bobAuthorWrite = await TestDataGenerator.generateRecordsWrite({ + author : bob, + protocol : protocolDefinition.protocol, + schema : protocolDefinition.types.post.schema, + dataFormat : protocolDefinition.types.post.dataFormats[0], + protocolPath : 'post' + }); + const bobAuthorReply = await dwn.processMessage(alice.did, bobAuthorWrite.message, bobAuthorWrite.dataStream); + expect(bobAuthorReply.status.code).to.equal(202); + + // alice queries with an empty filter, gets both + let recordsQuery = await TestDataGenerator.generateRecordsQuery({ + author : alice, + filter : { + protocol : protocolDefinition.protocol, + schema : protocolDefinition.types.post.schema, + dataFormat : protocolDefinition.types.post.dataFormats[0], + protocolPath : 'post' + } + }); + let queryReply = await dwn.processMessage(alice.did, recordsQuery.message); + expect(queryReply.status.code).to.equal(200); + expect(queryReply.entries?.length).to.equal(2); + + // filter for bob as author + recordsQuery = await TestDataGenerator.generateRecordsQuery({ + author : alice, + filter : { + author : bob.did, + protocol : protocolDefinition.protocol, + schema : protocolDefinition.types.post.schema, + dataFormat : protocolDefinition.types.post.dataFormats[0], + protocolPath : 'post' + } + }); + queryReply = await dwn.processMessage(alice.did, recordsQuery.message); + expect(queryReply.status.code).to.equal(200); + expect(queryReply.entries?.length).to.equal(1); + expect(queryReply.entries![0].recordId).to.equal(bobAuthorWrite.message.recordId); + }); + it('should be able to query for published records', async () => { const alice = await DidKeyResolver.generate(); const bob = await DidKeyResolver.generate();