Skip to content

Commit

Permalink
feat: basic caching support
Browse files Browse the repository at this point in the history
  • Loading branch information
pelle authored and mirceanis committed Nov 8, 2019
1 parent 7a4d242 commit a495237
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 7 deletions.
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,42 @@ resolver.resolve('did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX/some/path#fragme
const doc = await resolver.resolve('did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX/some/path#fragment=123')
```

## Caching

Resolving DID Documents can be expensive. It is in most cases best to cache DID documents. By default caching is now enabled in the resolver.

The built in cache uses a WeakMap, but does not have an automatic TTL, so entries don't expire. This is fine in most web, mobile and serverless contexts. If you run a long running process you may want to use an existing configurable caching system.

Here is an example using `js-cache` which has not been tested.

```js
var cache = require('js-cache')
const customCache : DIDCache = (parsed, resolve) => {
// DID spec requires to not cache if no-cache param is set
if (parsed.params && parsed.params['no-cache'] === 'true') return await resolve()
const cached = cache.get(parsed.didUrl)
if (cached !== undefined) return cached
const doc = await resolve()
cache.set(parsed, doc, 60000)
return doc
}

const resolver = new DIDResolver({
ethr,
web
}, customCache)
```

The Cache can also be disabled by passing in a `false` value to the constructor:

```js
const resolver = new DIDResolver({
ethr,
web
}, false)
```


## Implementing a DID method

Each DID method will have it's own methods for looking up an identifier on it's respective blockchain or other decentralized storage mechanism.
Expand All @@ -60,8 +96,6 @@ export default async function myResolver (did, parsed, didResolver) {
})
```



The method resolver should register this so that just requiring it will register the method:

```js
Expand Down
33 changes: 28 additions & 5 deletions src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,30 @@ export interface ParsedDID {
params?: Params
}

export type DIDResolver = (did: string, parsed: ParsedDID, didResolver: Resolver) => Promise<null | DIDDocument>
export type DIDResolver = (did: string, parsed: ParsedDID, resolver: Resolver) => Promise<null | DIDDocument>
export type WrappedResolver = () => Promise<null | DIDDocument>
export type DIDCache = (parsed: ParsedDID, resolve: WrappedResolver) => Promise<null | DIDDocument>

interface ResolverRegistry {
[index: string]: DIDResolver
}

export function inMemoryCache() : DIDCache {
const cache: WeakMap<ParsedDID, DIDDocument|null> = new WeakMap()
return async (parsed, resolve) => {
if (parsed.params && parsed.params['no-cache'] === 'true') return await resolve()
const cached = cache.get(parsed)
if (cached !== undefined) return cached
const doc = await resolve()
cache.set(parsed, doc)
return doc
}
}

export function noCache(parsed: ParsedDID, resolve: WrappedResolver): Promise<null | DIDDocument> {
return resolve()
}

const ID_CHAR = '[a-zA-Z0-9_.-]'
const METHOD = '([a-zA-Z0-9_]+)'
const METHOD_ID = `(${ID_CHAR}+(:${ID_CHAR}+)*)`
Expand Down Expand Up @@ -98,16 +116,21 @@ export function parse(didUrl: string): ParsedDID {

export class Resolver {
private registry: ResolverRegistry
constructor(registry: ResolverRegistry = {}) {
private cache: DIDCache

constructor(registry: ResolverRegistry = {}, cache?: DIDCache|boolean|undefined) {
this.registry = registry
this.cache = cache === true || cache === undefined
? inMemoryCache()
: (cache === false ? noCache : cache)
}

resolve(did: string): Promise<DIDDocument | null> {
resolve(didUrl: string): Promise<DIDDocument | null> {
try {
const parsed = parse(did)
const parsed = parse(didUrl)
const resolver = this.registry[parsed.method]
if (resolver) {
return resolver(did, parsed, this)
return this.cache(parsed, () => resolver(parsed.did, parsed, this))
}
return Promise.reject(new Error(`Unsupported DID method: '${parsed.method}'`));
} catch (error) {
Expand Down

0 comments on commit a495237

Please sign in to comment.