From 06924d438664fa45988222dc7fa7d33f495050d3 Mon Sep 17 00:00:00 2001 From: Mike Xu Date: Thu, 26 Sep 2019 14:05:40 -0400 Subject: [PATCH 1/5] feat: use axios instead of XMLHttpRequest to handle redirects --- package-lock.json | 85 +++++++++++++++++++--------------- package.json | 10 ++-- src/__tests__/resolver.test.ts | 62 +++++++++++-------------- src/resolver.ts | 46 +++--------------- 4 files changed, 84 insertions(+), 119 deletions(-) diff --git a/package-lock.json b/package-lock.json index e1aa693..817acde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "https-did-resolver", + "name": "web-did-resolver", "version": "1.0.1", "lockfileVersion": 1, "requires": true, @@ -516,6 +516,22 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + } + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -1592,6 +1608,24 @@ "locate-path": "^2.0.0" } }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -1670,8 +1704,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -1692,14 +1725,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1714,20 +1745,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -1844,8 +1872,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -1857,7 +1884,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -1872,7 +1898,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -1880,14 +1905,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -1906,7 +1929,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -1987,8 +2009,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -2000,7 +2021,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -2086,8 +2106,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -2123,7 +2142,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2143,7 +2161,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2187,14 +2204,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -3631,8 +3646,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nan": { "version": "2.11.1", @@ -5722,11 +5736,6 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" - }, "y18n": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", diff --git a/package.json b/package.json index a3d1d7e..afa7997 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "author": "Mike Xu ", "license": "Apache-2.0", "dependencies": { - "did-resolver": "1.0.0", - "xmlhttprequest": "^1.8.0" + "axios": "^0.19.0", + "did-resolver": "1.0.0" }, "scripts": { "build": "tsc", @@ -16,9 +16,6 @@ "dev": "tsc --watch", "format": "prettier" }, - "browser": { - "xmlhttprequest": false - }, "devDependencies": { "@types/jest": "^23.3.10", "jest": "^23.6.0", @@ -28,8 +25,7 @@ "tslint": "^5.12.0", "tslint-config-prettier": "^1.17.0", "tslint-eslint-rules": "^5.4.0", - "typescript": "^3.2.2", - "xhr-mock": "^2.4.1" + "typescript": "^3.2.2" }, "jest": { "transform": { diff --git a/src/__tests__/resolver.test.ts b/src/__tests__/resolver.test.ts index 21cf8a1..ca40bd6 100644 --- a/src/__tests__/resolver.test.ts +++ b/src/__tests__/resolver.test.ts @@ -1,12 +1,14 @@ -import { Resolver, DIDDocument, DIDResolver } from 'did-resolver' +import { Resolver, DIDResolver, DIDDocument } from 'did-resolver' import getResolver from '../resolver' import mock from 'xhr-mock' +import axios from 'axios' +jest.mock('axios') +const mockedAxios = axios as jest.Mocked describe('web did resolver', () => { const did: string = 'did:web:example.com' - const url: string = 'https://example.com/.well-known/did.json' const identity: string = '0x2Cc31912B2b0f3075A87b3640923D45A26cef3Ee' - const validDidDoc: DIDDocument = { + const validResponse: DIDDocument = { '@context': 'https://w3id.org/did/v1', id: did, publicKey: [ @@ -24,23 +26,22 @@ describe('web did resolver', () => { }, ], } - const validResponse: string = JSON.stringify(validDidDoc) - const noContextResponse: string = JSON.stringify({ - id: validDidDoc.id, - publicKey: validDidDoc.publicKey, - authentication: validDidDoc.authentication, - }) - const wrongIdResponse: string = JSON.stringify({ - '@context': validDidDoc['@context'], + const noContextResponse: object = { + id: validResponse.id, + publicKey: validResponse.publicKey, + authentication: validResponse.authentication, + } + const wrongIdResponse: object = { + '@context': validResponse['@context'], id: 'did:web:wrong.com', - publicKey: validDidDoc.publicKey, - authentication: validDidDoc.authentication, - }) - const noPublicKeyResponse: string = JSON.stringify({ - '@context': validDidDoc['@context'], - id: validDidDoc.id, - authentication: validDidDoc.authentication, - }) + publicKey: validResponse.publicKey, + authentication: validResponse.authentication, + } + const noPublicKeyResponse: object = { + '@context': validResponse['@context'], + id: validResponse.id, + authentication: validResponse.authentication, + } let didResolver: Resolver let webDidResolver: { [index: string]: DIDResolver } @@ -53,40 +54,31 @@ describe('web did resolver', () => { afterEach(() => mock.teardown()) it('resolves document', () => { - mock.get(url, { status: 200, body: validResponse }) - return expect(didResolver.resolve(did)).resolves.toEqual(validDidDoc) + mockedAxios.get.mockResolvedValueOnce({ data: validResponse }) + return expect(didResolver.resolve(did)).resolves.toEqual(validResponse) }) it('fails if the did is not a valid https url', () => { - mock.get(url, { status: 404 }) - return expect(didResolver.resolve(did)).rejects.toThrowError( - 'DID must resolve to a valid https URL: Invalid http response status 404', - ) - }) - - it('fails if the did document is not valid json', () => { - mock.get(url, { status: 200, body: 'invalid json' }) - return expect(didResolver.resolve(did)).rejects.toThrowError( - 'DID must resolve to a JSON document', - ) + mockedAxios.get.mockRejectedValueOnce({ response: { status: 404 } }) + return expect(didResolver.resolve(did)).rejects.toThrow() }) it('fails if the did document is missing a context', () => { - mock.get(url, { status: 200, body: noContextResponse }) + mockedAxios.get.mockResolvedValueOnce({ data: noContextResponse }) return expect(didResolver.resolve(did)).rejects.toThrowError( 'DID document missing context', ) }) it('fails if the did document id does not match', () => { - mock.get(url, { status: 200, body: wrongIdResponse }) + mockedAxios.get.mockResolvedValueOnce({ data: wrongIdResponse }) return expect(didResolver.resolve(did)).rejects.toThrowError( 'DID document id does not match requested did', ) }) it('fails if the did document has no public keys', () => { - mock.get(url, { status: 200, body: noPublicKeyResponse }) + mockedAxios.get.mockResolvedValueOnce({ data: noPublicKeyResponse }) return expect(didResolver.resolve(did)).rejects.toThrowError( 'DID document has no public keys', ) diff --git a/src/resolver.ts b/src/resolver.ts index dbb950f..fa83cf7 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -1,40 +1,13 @@ import { ParsedDID, DIDDocument } from 'did-resolver' - -declare global { - interface Window { - XMLHttpRequest: any - } -} -declare var require: any +import axios from 'axios' const DOC_PATH = '/.well-known/did.json' function get(url: string): Promise { - return new Promise((resolve, reject) => { - // declare XMLHttpRequest in here so it can be mocked for tests - const XMLHttpRequest = - typeof window !== 'undefined' - ? window.XMLHttpRequest - : require('xmlhttprequest').XMLHttpRequest - - const request = new XMLHttpRequest() - request.open('GET', url) - request.onreadystatechange = () => { - if (!request || request.readyState !== 4) return - if (request.status === 200) { - resolve(request.responseText) - } else { - reject( - new Error( - `Invalid http response status ${request.status} ${ - request.responseText - }`.trim(), - ), - ) - } - } - request.setRequestHeader('accept', 'application/json') - request.send() + return axios.get(url, { + headers: { + 'Access-Control-Allow-Origin': '*', + }, }) } @@ -52,12 +25,7 @@ export default function getResolver() { throw new Error(`DID must resolve to a valid https URL: ${error.message}`) } - let data: any = null - try { - data = JSON.parse(response) - } catch (error) { - throw new Error('DID must resolve to a JSON document') - } + const { data } = response const hasContext = data['@context'] === 'https://w3id.org/did/v1' if (!hasContext) throw new Error('DID document missing context') @@ -73,5 +41,5 @@ export default function getResolver() { return data } - return { 'web': resolve } + return { web: resolve } } From c1d1010670bb89280fb3b98ac4c2385a6712a3f3 Mon Sep 17 00:00:00 2001 From: Mike Xu Date: Thu, 26 Sep 2019 14:17:26 -0400 Subject: [PATCH 2/5] bump version to publish dev version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index afa7997..ca8f396 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "web-did-resolver", - "version": "1.0.1", + "version": "1.0.2-dev.0", "description": "Resolve DID documents from an https domain", "main": "lib/resolver.js", "types": "lib/resolver.d.ts", From d591bc7d7c7cd61b0e341a93cc45485376a39256 Mon Sep 17 00:00:00 2001 From: Mike Xu Date: Thu, 26 Sep 2019 23:58:26 -0400 Subject: [PATCH 3/5] refactor: replace axios with cross-fetch --- package-lock.json | 123 ++++++--------------------------- package.json | 2 +- src/__tests__/resolver.test.ts | 38 +++++++--- src/resolver.ts | 22 +++--- 4 files changed, 64 insertions(+), 121 deletions(-) diff --git a/package-lock.json b/package-lock.json index 817acde..dd08fee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "web-did-resolver", - "version": "1.0.1", + "version": "1.0.2-dev.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -516,22 +516,6 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, - "axios": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", - "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", - "requires": { - "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" - } - } - }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -1115,6 +1099,15 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "cross-fetch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.4.tgz", + "integrity": "sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw==", + "requires": { + "node-fetch": "2.6.0", + "whatwg-fetch": "3.0.0" + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -1328,12 +1321,6 @@ } } }, - "dom-walk": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", - "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=", - "dev": true - }, "domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -1608,24 +1595,6 @@ "locate-path": "^2.0.0" } }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } - } - }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2279,16 +2248,6 @@ "is-glob": "^2.0.0" } }, - "global": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", - "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", - "dev": true, - "requires": { - "min-document": "^2.19.0", - "process": "~0.5.1" - } - }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", @@ -3589,15 +3548,6 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, - "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", - "dev": true, - "requires": { - "dom-walk": "^0.1.0" - } - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -3646,7 +3596,8 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true }, "nan": { "version": "2.11.1", @@ -3706,6 +3657,11 @@ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -4112,12 +4068,6 @@ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", - "dev": true - }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -4158,12 +4108,6 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, "randomatic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz", @@ -5497,24 +5441,6 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -5615,6 +5541,11 @@ "iconv-lite": "0.4.24" } }, + "whatwg-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + }, "whatwg-mimetype": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz", @@ -5720,16 +5651,6 @@ "async-limiter": "~1.0.0" } }, - "xhr-mock": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/xhr-mock/-/xhr-mock-2.4.1.tgz", - "integrity": "sha1-y1AuPVC4suwxvWF2bOUWv8HdBy8=", - "dev": true, - "requires": { - "global": "^4.3.0", - "url": "^0.11.0" - } - }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", diff --git a/package.json b/package.json index ca8f396..4df4733 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "author": "Mike Xu ", "license": "Apache-2.0", "dependencies": { - "axios": "^0.19.0", + "cross-fetch": "^3.0.4", "did-resolver": "1.0.0" }, "scripts": { diff --git a/src/__tests__/resolver.test.ts b/src/__tests__/resolver.test.ts index ca40bd6..8ba95f3 100644 --- a/src/__tests__/resolver.test.ts +++ b/src/__tests__/resolver.test.ts @@ -1,9 +1,8 @@ import { Resolver, DIDResolver, DIDDocument } from 'did-resolver' import getResolver from '../resolver' -import mock from 'xhr-mock' -import axios from 'axios' -jest.mock('axios') -const mockedAxios = axios as jest.Mocked +import fetch from 'cross-fetch' +jest.mock('cross-fetch') +const mockedFetch = fetch as jest.Mock describe('web did resolver', () => { const did: string = 'did:web:example.com' @@ -50,35 +49,52 @@ describe('web did resolver', () => { webDidResolver = getResolver() didResolver = new Resolver(webDidResolver) }) - beforeEach(() => mock.setup()) - afterEach(() => mock.teardown()) it('resolves document', () => { - mockedAxios.get.mockResolvedValueOnce({ data: validResponse }) + mockedFetch.mockResolvedValueOnce({ + json: () => validResponse, + }) return expect(didResolver.resolve(did)).resolves.toEqual(validResponse) }) it('fails if the did is not a valid https url', () => { - mockedAxios.get.mockRejectedValueOnce({ response: { status: 404 } }) + mockedFetch.mockRejectedValueOnce({ status: 404 }) return expect(didResolver.resolve(did)).rejects.toThrow() }) + it('fails if the did document is not valid json', () => { + mockedFetch.mockResolvedValueOnce({ + json: () => { + throw new Error('unable to parse json') + }, + }) + return expect(didResolver.resolve(did)).rejects.toThrowError( + /unable to parse json/, + ) + }) + it('fails if the did document is missing a context', () => { - mockedAxios.get.mockResolvedValueOnce({ data: noContextResponse }) + mockedFetch.mockResolvedValueOnce({ + json: () => noContextResponse, + }) return expect(didResolver.resolve(did)).rejects.toThrowError( 'DID document missing context', ) }) it('fails if the did document id does not match', () => { - mockedAxios.get.mockResolvedValueOnce({ data: wrongIdResponse }) + mockedFetch.mockResolvedValueOnce({ + json: () => wrongIdResponse, + }) return expect(didResolver.resolve(did)).rejects.toThrowError( 'DID document id does not match requested did', ) }) it('fails if the did document has no public keys', () => { - mockedAxios.get.mockResolvedValueOnce({ data: noPublicKeyResponse }) + mockedFetch.mockResolvedValueOnce({ + json: () => noPublicKeyResponse, + }) return expect(didResolver.resolve(did)).rejects.toThrowError( 'DID document has no public keys', ) diff --git a/src/resolver.ts b/src/resolver.ts index fa83cf7..cd0a53d 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -1,14 +1,18 @@ import { ParsedDID, DIDDocument } from 'did-resolver' -import axios from 'axios' +import fetch from 'cross-fetch' const DOC_PATH = '/.well-known/did.json' -function get(url: string): Promise { - return axios.get(url, { +async function get(url: string): Promise { + const res = await fetch(url, { headers: { 'Access-Control-Allow-Origin': '*', }, }) + if (res.status >= 400) { + throw new Error(`Bad response ${res.statusText}`) + } + return res.json() } export default function getResolver() { @@ -18,15 +22,17 @@ export default function getResolver() { ): Promise { const url: string = `https://${parsed.id}${DOC_PATH}` - let response: any = null + let data: any = null try { - response = await get(url) + data = await get(url) } catch (error) { - throw new Error(`DID must resolve to a valid https URL: ${error.message}`) + throw new Error( + `DID must resolve to a valid https URL containing a JSON document: ${ + error.message + }`, + ) } - const { data } = response - const hasContext = data['@context'] === 'https://w3id.org/did/v1' if (!hasContext) throw new Error('DID document missing context') From c06f78b10cfbfa26ad6b1decfb1fac2f5520a827 Mon Sep 17 00:00:00 2001 From: Mike Xu Date: Fri, 27 Sep 2019 00:05:11 -0400 Subject: [PATCH 4/5] style: remove some trailing commas --- .vscode/settings.json | 2 +- src/__tests__/resolver.test.ts | 34 +++++++++++++++++----------------- src/resolver.ts | 11 ++++++----- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 73d15fb..9c9ab8f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,6 @@ "editor.tabSize": 2, "editor.detectIndentation": false, "[typescript]": { - "editor.formatOnSave": true + "editor.formatOnSave": false } } \ No newline at end of file diff --git a/src/__tests__/resolver.test.ts b/src/__tests__/resolver.test.ts index 8ba95f3..6fa454a 100644 --- a/src/__tests__/resolver.test.ts +++ b/src/__tests__/resolver.test.ts @@ -15,31 +15,31 @@ describe('web did resolver', () => { id: `${did}#owner`, type: 'Secp256k1VerificationKey2018', owner: did, - ethereumAddress: identity, - }, + ethereumAddress: identity + } ], authentication: [ { type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner`, - }, - ], + publicKey: `${did}#owner` + } + ] } const noContextResponse: object = { id: validResponse.id, publicKey: validResponse.publicKey, - authentication: validResponse.authentication, + authentication: validResponse.authentication } const wrongIdResponse: object = { '@context': validResponse['@context'], id: 'did:web:wrong.com', publicKey: validResponse.publicKey, - authentication: validResponse.authentication, + authentication: validResponse.authentication } const noPublicKeyResponse: object = { '@context': validResponse['@context'], id: validResponse.id, - authentication: validResponse.authentication, + authentication: validResponse.authentication } let didResolver: Resolver @@ -52,7 +52,7 @@ describe('web did resolver', () => { it('resolves document', () => { mockedFetch.mockResolvedValueOnce({ - json: () => validResponse, + json: () => validResponse }) return expect(didResolver.resolve(did)).resolves.toEqual(validResponse) }) @@ -66,37 +66,37 @@ describe('web did resolver', () => { mockedFetch.mockResolvedValueOnce({ json: () => { throw new Error('unable to parse json') - }, + } }) return expect(didResolver.resolve(did)).rejects.toThrowError( - /unable to parse json/, + /unable to parse json/ ) }) it('fails if the did document is missing a context', () => { mockedFetch.mockResolvedValueOnce({ - json: () => noContextResponse, + json: () => noContextResponse }) return expect(didResolver.resolve(did)).rejects.toThrowError( - 'DID document missing context', + 'DID document missing context' ) }) it('fails if the did document id does not match', () => { mockedFetch.mockResolvedValueOnce({ - json: () => wrongIdResponse, + json: () => wrongIdResponse }) return expect(didResolver.resolve(did)).rejects.toThrowError( - 'DID document id does not match requested did', + 'DID document id does not match requested did' ) }) it('fails if the did document has no public keys', () => { mockedFetch.mockResolvedValueOnce({ - json: () => noPublicKeyResponse, + json: () => noPublicKeyResponse }) return expect(didResolver.resolve(did)).rejects.toThrowError( - 'DID document has no public keys', + 'DID document has no public keys' ) }) }) diff --git a/src/resolver.ts b/src/resolver.ts index cd0a53d..62fad20 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -6,8 +6,8 @@ const DOC_PATH = '/.well-known/did.json' async function get(url: string): Promise { const res = await fetch(url, { headers: { - 'Access-Control-Allow-Origin': '*', - }, + 'Access-Control-Allow-Origin': '*' + } }) if (res.status >= 400) { throw new Error(`Bad response ${res.statusText}`) @@ -18,7 +18,7 @@ async function get(url: string): Promise { export default function getResolver() { async function resolve( did: string, - parsed: ParsedDID, + parsed: ParsedDID ): Promise { const url: string = `https://${parsed.id}${DOC_PATH}` @@ -29,7 +29,7 @@ export default function getResolver() { throw new Error( `DID must resolve to a valid https URL containing a JSON document: ${ error.message - }`, + }` ) } @@ -37,8 +37,9 @@ export default function getResolver() { if (!hasContext) throw new Error('DID document missing context') const docIdMatchesDid = data.id === did - if (!docIdMatchesDid) + if (!docIdMatchesDid) { throw new Error('DID document id does not match requested did') + } const docHasPublicKey = Array.isArray(data.publicKey) && data.publicKey.length > 0 From e847c566d3d9998b7a9fe6804283a757a52f0c97 Mon Sep 17 00:00:00 2001 From: Mike Xu Date: Tue, 1 Oct 2019 10:42:38 -0400 Subject: [PATCH 5/5] chore: fix trailing comma auto-format --- .prettierrc | 1 - .vscode/settings.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.prettierrc b/.prettierrc index f01b64b..e3c4b95 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,5 @@ { "jsxBracketSameLine": false, - "trailingComma": "all", "tabWidth": 2, "printWidth": 80, "singleQuote": true, diff --git a/.vscode/settings.json b/.vscode/settings.json index 9c9ab8f..73d15fb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,6 @@ "editor.tabSize": 2, "editor.detectIndentation": false, "[typescript]": { - "editor.formatOnSave": false + "editor.formatOnSave": true } } \ No newline at end of file