generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
714 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
--- | ||
"@web5/agent": patch | ||
"@web5/identity-agent": patch | ||
"@web5/proxy-agent": patch | ||
"@web5/user-agent": patch | ||
--- | ||
|
||
Add `DwnServerInfoRpc` to `Web5Rpc` for retrieving server specific info. | ||
|
||
Server Info includes: | ||
- maxFileSize | ||
- registrationRequirements | ||
- webSocketSupport | ||
|
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
packages/agent/src/prototyping/clients/dwn-server-info-cache-memory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
|
||
import ms from 'ms'; | ||
import { TtlCache } from '@web5/common'; | ||
import { DwnServerInfoCache, ServerInfo } from './server-info-types.js'; | ||
|
||
/** | ||
* Configuration parameters for creating an in-memory cache for DWN ServerInfo entries. | ||
* | ||
* Allows customization of the cache time-to-live (TTL) setting. | ||
*/ | ||
export type DwnServerInfoCacheMemoryParams = { | ||
/** | ||
* Optional. The time-to-live for cache entries, expressed as a string (e.g., '1h', '15m'). | ||
* Determines how long a cache entry should remain valid before being considered expired. | ||
* | ||
* Defaults to '15m' if not specified. | ||
*/ | ||
ttl?: string; | ||
} | ||
|
||
export class DwnServerInfoCacheMemory implements DwnServerInfoCache { | ||
private cache: TtlCache<string, ServerInfo>; | ||
|
||
constructor({ ttl = '15m' }: DwnServerInfoCacheMemoryParams= {}) { | ||
this.cache = new TtlCache({ ttl: ms(ttl) }); | ||
} | ||
|
||
/** | ||
* Retrieves a DWN ServerInfo entry from the cache. | ||
* | ||
* If the cached item has exceeded its TTL, it's scheduled for deletion and undefined is returned. | ||
* | ||
* @param dwnUrl - The DWN URL endpoint string used as the key for getting the entry. | ||
* @returns The cached DWN ServerInfo entry or undefined if not found or expired. | ||
*/ | ||
public async get(dwnUrl: string): Promise<ServerInfo| undefined> { | ||
return this.cache.get(dwnUrl); | ||
} | ||
|
||
/** | ||
* Stores a DWN ServerInfo entry in the cache with a TTL. | ||
* | ||
* @param dwnUrl - The DWN URL endpoint string used as the key for storing the entry. | ||
* @param value - The DWN ServerInfo entry to be cached. | ||
* @returns A promise that resolves when the operation is complete. | ||
*/ | ||
public async set(dwnUrl: string, value: ServerInfo): Promise<void> { | ||
this.cache.set(dwnUrl, value); | ||
} | ||
|
||
/** | ||
* Deletes a DWN ServerInfo entry from the cache. | ||
* | ||
* @param dwnUrl - The DWN URL endpoint string used as the key for deletion. | ||
* @returns A promise that resolves when the operation is complete. | ||
*/ | ||
public async delete(dwnUrl: string): Promise<void> { | ||
this.cache.delete(dwnUrl); | ||
} | ||
|
||
/** | ||
* Clears all entries from the cache. | ||
* | ||
* @returns A promise that resolves when the operation is complete. | ||
*/ | ||
public async clear(): Promise<void> { | ||
this.cache.clear(); | ||
} | ||
|
||
/** | ||
* This method is a no-op but exists to be consistent with other DWN ServerInfo Cache | ||
* implementations. | ||
* | ||
* @returns A promise that resolves immediately. | ||
*/ | ||
public async close(): Promise<void> { | ||
// No-op since there is no underlying store to close. | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
packages/agent/src/prototyping/clients/server-info-types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { KeyValueStore } from '@web5/common'; | ||
|
||
export type ServerInfo = { | ||
/** the maximum file size the user can request to store */ | ||
maxFileSize: number, | ||
/** | ||
* an array of strings representing the server's registration requirements. | ||
* | ||
* ie. ['proof-of-work-sha256-v0', 'terms-of-service'] | ||
* */ | ||
registrationRequirements: string[], | ||
/** whether web socket support is enabled on this server */ | ||
webSocketSupport: boolean, | ||
} | ||
|
||
export interface DwnServerInfoCache extends KeyValueStore<string, ServerInfo| undefined> {} | ||
|
||
export interface DwnServerInfoRpc { | ||
/** retrieves the DWN Sever info, used to detect features such as WebSocket Subscriptions */ | ||
getServerInfo(url: string): Promise<ServerInfo>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
119 changes: 119 additions & 0 deletions
119
packages/agent/tests/prototyping/clients/dwn-server-info-cache.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import sinon from 'sinon'; | ||
|
||
import { expect } from 'chai'; | ||
|
||
import { DwnServerInfoCache, ServerInfo } from '../../../src/prototyping/clients/server-info-types.js'; | ||
import { DwnServerInfoCacheMemory } from '../../../src/prototyping/clients/dwn-server-info-cache-memory.js'; | ||
import { isNode } from '../../utils/runtimes.js'; | ||
|
||
describe('DwnServerInfoCache', () => { | ||
|
||
describe(`DwnServerInfoCacheMemory`, () => { | ||
let cache: DwnServerInfoCache; | ||
let clock: sinon.SinonFakeTimers; | ||
|
||
const exampleInfo:ServerInfo = { | ||
maxFileSize : 100, | ||
webSocketSupport : true, | ||
registrationRequirements : [] | ||
}; | ||
|
||
after(() => { | ||
sinon.restore(); | ||
}); | ||
|
||
beforeEach(() => { | ||
clock = sinon.useFakeTimers(); | ||
cache = new DwnServerInfoCacheMemory(); | ||
}); | ||
|
||
afterEach(async () => { | ||
await cache.clear(); | ||
await cache.close(); | ||
clock.restore(); | ||
}); | ||
|
||
it('sets server info in cache', async () => { | ||
const key1 = 'some-key1'; | ||
const key2 = 'some-key2'; | ||
await cache.set(key1, { ...exampleInfo }); | ||
await cache.set(key2, { ...exampleInfo, webSocketSupport: false }); // set to false | ||
|
||
const result1 = await cache.get(key1); | ||
expect(result1!.webSocketSupport).to.deep.equal(true); | ||
expect(result1).to.deep.equal(exampleInfo); | ||
|
||
const result2 = await cache.get(key2); | ||
expect(result2!.webSocketSupport).to.deep.equal(false); | ||
}); | ||
|
||
it('deletes from cache', async () => { | ||
const key1 = 'some-key1'; | ||
const key2 = 'some-key2'; | ||
await cache.set(key1, { ...exampleInfo }); | ||
await cache.set(key2, { ...exampleInfo, webSocketSupport: false }); // set to false | ||
|
||
const result1 = await cache.get(key1); | ||
expect(result1!.webSocketSupport).to.deep.equal(true); | ||
expect(result1).to.deep.equal(exampleInfo); | ||
|
||
const result2 = await cache.get(key2); | ||
expect(result2!.webSocketSupport).to.deep.equal(false); | ||
|
||
// delete one of the keys | ||
await cache.delete(key1); | ||
|
||
// check results after delete | ||
const resultAfterDelete = await cache.get(key1); | ||
expect(resultAfterDelete).to.equal(undefined); | ||
|
||
// key 2 still exists | ||
const result2AfterDelete = await cache.get(key2); | ||
expect(result2AfterDelete!.webSocketSupport).to.equal(false); | ||
}); | ||
|
||
it('clears cache', async () => { | ||
const key1 = 'some-key1'; | ||
const key2 = 'some-key2'; | ||
await cache.set(key1, { ...exampleInfo }); | ||
await cache.set(key2, { ...exampleInfo, webSocketSupport: false }); // set to false | ||
|
||
const result1 = await cache.get(key1); | ||
expect(result1!.webSocketSupport).to.deep.equal(true); | ||
expect(result1).to.deep.equal(exampleInfo); | ||
|
||
const result2 = await cache.get(key2); | ||
expect(result2!.webSocketSupport).to.deep.equal(false); | ||
|
||
// delete one of the keys | ||
await cache.clear(); | ||
|
||
// check results after delete | ||
const resultAfterDelete = await cache.get(key1); | ||
expect(resultAfterDelete).to.equal(undefined); | ||
const result2AfterDelete = await cache.get(key2); | ||
expect(result2AfterDelete).to.equal(undefined); | ||
}); | ||
|
||
it('returns undefined after ttl', async function () { | ||
// skip this test in the browser, sinon fake timers don't seem to work here | ||
// with a an await setTimeout in the test, it passes. | ||
if (!isNode) { | ||
this.skip(); | ||
} | ||
|
||
const key = 'some-key1'; | ||
await cache.set(key, { ...exampleInfo }); | ||
|
||
const result = await cache.get(key); | ||
expect(result!.webSocketSupport).to.deep.equal(true); | ||
expect(result).to.deep.equal(exampleInfo); | ||
|
||
// wait until 15m default ttl is up | ||
await clock.tickAsync('15:01'); | ||
|
||
const resultAfter = await cache.get(key); | ||
expect(resultAfter).to.be.undefined; | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.