Skip to content

Commit

Permalink
Upgrade api and agent (#500)
Browse files Browse the repository at this point in the history
- Upgrade `dwn-sdk-js` to `v0.3.1`
- Upgrade `dwn-server` to `v0.2.1`
- Upgrade `@web5/dids` to `v1.0.1` in `agent` and `api`

---

This PR updates `dwn-sdk-js` to address CVEs, as well as include the newest version of `@web5/dids` which does not cache a DID resolution with an error.

New feature for `dwn` is the ability to add and query by `tags`
  • Loading branch information
LiranCohen committed Apr 26, 2024
1 parent 277a201 commit 41ac378
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 49 deletions.
12 changes: 12 additions & 0 deletions .changeset/few-oranges-compare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@web5/identity-agent": patch
"@web5/proxy-agent": patch
"@web5/user-agent": patch
"@web5/agent": patch
"@web5/api": patch
---

Upgrade DWN SDK with newest features

- remove `Permissions` interface and replace permissions with a first-class protocol representing it
- adding `RecordsTags` functionality
12 changes: 6 additions & 6 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@

# These are owners of any file in the `agent`, `user-agent`, `proxy-agent`, `identity-agent`, and
# `api` packages and their sub-directories.
/packages/agent @lirancohen @frankhinek @csuwildcat @mistermoe
/packages/proxy-agent @lirancohen @frankhinek @csuwildcat @mistermoe
/packages/user-agent @lirancohen @frankhinek @csuwildcat @mistermoe
/packages/identity-agent @lirancohen @frankhinek @csuwildcat @mistermoe
/packages/api @lirancohen @frankhinek @csuwildcat @mistermoe

/packages/agent @lirancohen @frankhinek @csuwildcat @mistermoe @shamilovtim
/packages/proxy-agent @lirancohen @frankhinek @csuwildcat @mistermoe @shamilovtim
/packages/user-agent @lirancohen @frankhinek @csuwildcat @mistermoe @shamilovtim
/packages/identity-agent @lirancohen @frankhinek @csuwildcat @mistermoe @shamilovtim
/packages/api @lirancohen @frankhinek @csuwildcat @mistermoe @shamilovtim

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@changesets/cli": "^2.27.1",
"@npmcli/package-json": "5.0.0",
"@typescript-eslint/eslint-plugin": "6.4.0",
"@web5/dwn-server": "0.1.17",
"@web5/dwn-server": "0.2.1",
"eslint-plugin-mocha": "10.1.0",
"npkill": "0.11.3"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@
"dependencies": {
"@noble/ciphers": "0.4.1",
"@scure/bip39": "1.2.2",
"@tbd54566975/dwn-sdk-js": "0.2.22",
"@tbd54566975/dwn-sdk-js": "0.3.1",
"@web5/common": "1.0.0",
"@web5/crypto": "1.0.0",
"@web5/dids": "1.0.0",
"@web5/dids": "1.0.1",
"abstract-level": "1.0.4",
"ed25519-keygen": "0.4.11",
"level": "8.0.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@
"@web5/agent": "0.3.1",
"@web5/common": "1.0.0",
"@web5/crypto": "1.0.0",
"@web5/dids": "1.0.0",
"@web5/dids": "1.0.1",
"@web5/user-agent": "0.3.1"
},
"devDependencies": {
"@playwright/test": "1.40.1",
"@tbd54566975/dwn-sdk-js": "0.2.22",
"@tbd54566975/dwn-sdk-js": "0.3.1",
"@types/chai": "4.3.6",
"@types/eslint": "8.44.2",
"@types/mocha": "10.0.1",
Expand Down
19 changes: 16 additions & 3 deletions packages/api/src/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {
} from '@web5/agent';

import { DwnInterface } from '@web5/agent';
import { Convert, NodeStream, removeUndefinedProperties, Stream } from '@web5/common';
import { Convert, isEmptyObject, NodeStream, removeUndefinedProperties, Stream } from '@web5/common';

import { dataToBlob, SendCache } from './utils.js';

Expand Down Expand Up @@ -105,6 +105,9 @@ export type RecordUpdateParams = {

/** The published status of the record. */
published?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['published'];

/** The tags associated with the updated record */
tags?: DwnMessageDescriptor[DwnInterface.RecordsWrite]['tags'];
}

/**
Expand Down Expand Up @@ -247,6 +250,8 @@ export class Record implements RecordModel {
/** Record's published status (true/false) */
get published() { return this._descriptor.published; }

get tags() { return this._descriptor.tags; }

/**
* Returns a copy of the raw `RecordsWriteMessage` that was used to create the current `Record` instance.
*/
Expand Down Expand Up @@ -540,7 +545,8 @@ export class Record implements RecordModel {
published : this.published,
recipient : this.recipient,
recordId : this.id,
schema : this.schema
schema : this.schema,
tags : this.tags,
};
}

Expand Down Expand Up @@ -586,6 +592,13 @@ export class Record implements RecordModel {
recordId : this._recordId
};

// NOTE: The original Record's tags are copied to the update message, so that the tags are not lost.
// However if a user passes new tags in the `RecordUpdateParams` object, they will overwrite the original tags.
// If the updated tag object is empty or set to null, we remove the tags property to avoid schema validation errors in the DWN SDK.
if (isEmptyObject(updateMessage.tags) || updateMessage.tags === null) {
delete updateMessage.tags;
}

let dataBlob: Blob;
if (data !== undefined) {
// If `data` is being updated then `dataCid` and `dataSize` must be undefined and the `data`
Expand All @@ -598,7 +611,7 @@ export class Record implements RecordModel {

// Throw an error if an attempt is made to modify immutable properties.
// Note: `data` and `dateModified` have already been handled.
const mutableDescriptorProperties = new Set(['data', 'dataCid', 'dataSize', 'datePublished', 'messageTimestamp', 'published']);
const mutableDescriptorProperties = new Set(['data', 'dataCid', 'dataSize', 'datePublished', 'messageTimestamp', 'published', 'tags']);
Record.verifyPermittedMutation(Object.keys(params), mutableDescriptorProperties);

// If `published` is set to false, ensure that `datePublished` is undefined. Otherwise, DWN SDK's schema validation
Expand Down
164 changes: 162 additions & 2 deletions packages/api/tests/dwn-api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ describe('DwnApi', () => {
const response = await dwnAlice.protocols.query({
from : bobDid.uri,
message : {
permissionsGrantId : 'bafyreiduimprbncdo2oruvjrvmfmwuyz4xx3d5biegqd2qntlryvuuosem',
filter : {
permissionGrantId : 'bafyreiduimprbncdo2oruvjrvmfmwuyz4xx3d5biegqd2qntlryvuuosem',
filter : {
protocol: 'https://doesnotexist.com/protocol'
}
}
Expand Down Expand Up @@ -238,6 +238,32 @@ describe('DwnApi', () => {
expect(await result.record?.data.text()).to.equal(dataString);
});

it('creates a record with tags', async () => {
const result = await dwnAlice.records.create({
data : 'some data',
message : {
schema : 'foo/bar',
dataFormat : 'text/plain',
tags : {
foo : 'bar',
count : 2,
bool : true
}
}
});

expect(result.status.code).to.equal(202);
expect(result.status.detail).to.equal('Accepted');
expect(result.record).to.exist;
expect(result.record?.tags).to.exist;
expect(result.record?.tags).to.deep.equal({
foo : 'bar',
count : 2,
bool : true
});

});

it('creates a record with JSON data', async () => {
const dataJson = { hello: 'world!'};
const result = await dwnAlice.records.create({
Expand Down Expand Up @@ -841,6 +867,69 @@ describe('DwnApi', () => {
expect(publishedDescResults.records!.length).to.equal(3);
expect(publishedDescResults.records.map(r => r.id)).to.eql([...publishedItems].reverse());
});

it('queries for records matching tags', async () => {

// Write a record to the agent's local DWN that includes a tag `foo` with value `bar`
const { status, record } = await dwnAlice.records.write({
data : 'Hello, world!',
message : {
schema : 'foo/bar',
dataFormat : 'text/plain',
tags : {
foo: 'bar',
}
}
});
expect(status.code).to.equal(202);

// Write a record to the agent's local DWN that includes a tag `foo` with value `baz`
const { status: status2 } = await dwnAlice.records.write({
data : 'Hello, world!',
message : {
schema : 'foo/bar',
dataFormat : 'text/plain',
tags : {
foo: 'baz',
}
}
});
expect(status2.code).to.equal(202);

// Control: query the agent's local DWN for the record without any tag filters
const result = await dwnAlice.records.query({
message: {
filter: {
schema: 'foo/bar'
}
}
});

// should return both records
expect(result.status.code).to.equal(200);
expect(result.records).to.exist;
expect(result.records!.length).to.equal(2);


// Query the agent's local DWN for the record using the tags.
const fooBarResult = await dwnAlice.records.query({
message: {
filter: {
schema : 'foo/bar',
tags : {
foo: 'bar',
}
}
}
});

// should only return the record with the tag `foo` and value `bar`
expect(fooBarResult.status.code).to.equal(200);
expect(fooBarResult.records).to.exist;
expect(fooBarResult.records!.length).to.equal(1);
expect(fooBarResult.records![0].id).to.equal(record.id);
expect(fooBarResult.records![0].tags).to.deep.equal({ foo: 'bar' });
});
});

describe('from: did', () => {
Expand Down Expand Up @@ -947,6 +1036,77 @@ describe('DwnApi', () => {
const [ recordOnBobsDwn ] = bobQueryResult.records;
expect(recordOnBobsDwn.author).to.equal(aliceDid.uri);
});

it('queries for records matching tags', async () => {

// Write a record to alice's remote DWN that includes a tag `foo` with value `bar`
const { status, record } = await dwnAlice.records.write({
store : false,
data : 'Hello, world!',
message : {
schema : 'foo/bar',
dataFormat : 'text/plain',
tags : {
foo: 'bar',
}
}
});
expect(status.code).to.equal(202);
const { status: sendFooBarStatus } = await record.send(aliceDid.uri);
expect(sendFooBarStatus.code).to.equal(202);

// Write a record to alice's remote DWN that includes a tag `foo` with value `baz`
const { status: status2, record: record2 } = await dwnAlice.records.write({
store : false,
data : 'Hello, world!',
message : {
schema : 'foo/bar',
dataFormat : 'text/plain',
tags : {
foo: 'baz',
}
}
});
expect(status2.code).to.equal(202);
const { status: sendFooBazStatus } = await record2.send(aliceDid.uri);
expect(sendFooBazStatus.code).to.equal(202);

// Control: query the agent's local DWN for the record without any tag filters
const result = await dwnAlice.records.query({
from : aliceDid.uri,
message : {
filter: {
schema: 'foo/bar'
}
}
});

// should return both records
expect(result.status.code).to.equal(200);
expect(result.records).to.exist;
expect(result.records!.length).to.equal(2);


// Query the agent's local DWN for the record using the tags.
const fooBarResult = await dwnAlice.records.query({
from : aliceDid.uri,
message : {
filter: {
schema : 'foo/bar',
tags : {
foo: 'bar',
}
}
}
});

// should only return the record with the tag `foo` and value `bar`
expect(fooBarResult.status.code).to.equal(200);
expect(fooBarResult.records).to.exist;
expect(fooBarResult.records!.length).to.equal(1);
expect(fooBarResult.records![0].id).to.equal(record.id);
expect(fooBarResult.records![0].tags).to.deep.equal({ foo: 'bar' });
});
});
});

Expand Down
Loading

0 comments on commit 41ac378

Please sign in to comment.