Fork of github.com/did-method-plc/did-method-plc

upgrade axios + better error handling

dholms 254f179d f0599f71

+1 -1
packages/lib/package.json
···
{
"name": "@did-plc/lib",
"version": "0.0.1",
-
"main": "dist/index.js",
+
"main": "src/index.ts",
"license": "MIT",
"scripts": {
"test": "jest",
+45 -13
packages/lib/src/client.ts
···
import { check, cidForCbor } from '@atproto/common'
import { Keypair } from '@atproto/crypto'
-
import axios from 'axios'
+
import axios, { AxiosError } from 'axios'
import {
atprotoOp,
createUpdateOp,
···
export class Client {
constructor(public url: string) {}
+
private async makeGetReq(url: string) {
+
try {
+
const res = await axios.get(url)
+
return res.data
+
} catch (err) {
+
if (!axios.isAxiosError(err)) {
+
throw err
+
}
+
throw PlcClientError.fromAxiosError(err)
+
}
+
}
+
async getDocument(did: string): Promise<t.DidDocument> {
-
const res = await axios.get(`${this.url}/${encodeURIComponent(did)}`)
-
return res.data
+
return await this.makeGetReq(`${this.url}/${encodeURIComponent(did)}`)
}
async getDocumentData(did: string): Promise<t.DocumentData> {
-
const res = await axios.get(`${this.url}/${encodeURIComponent(did)}/data`)
-
return res.data
+
return await this.makeGetReq(`${this.url}/${encodeURIComponent(did)}/data`)
}
async getOperationLog(did: string): Promise<t.CompatibleOpOrTombstone[]> {
-
const res = await axios.get(`${this.url}/${encodeURIComponent(did)}/log`)
-
return res.data
+
return await this.makeGetReq(`${this.url}/${encodeURIComponent(did)}/log`)
}
async getAuditableLog(did: string): Promise<t.ExportedOp[]> {
-
const res = await axios.get(
+
return await this.makeGetReq(
`${this.url}/${encodeURIComponent(did)}/log/audit`,
)
-
return res.data
}
postOpUrl(did: string): string {
···
}
async getLastOp(did: string): Promise<t.CompatibleOpOrTombstone> {
-
const res = await axios.get(
+
return await this.makeGetReq(
`${this.url}/${encodeURIComponent(did)}/log/last`,
)
-
return res.data
}
async sendOperation(did: string, op: t.OpOrTombstone) {
-
await axios.post(this.postOpUrl(did), op)
+
try {
+
await axios.post(this.postOpUrl(did), op)
+
} catch (err) {
+
if (!axios.isAxiosError(err)) {
+
throw err
+
}
+
throw PlcClientError.fromAxiosError(err)
+
}
}
async export(after?: string, count?: number): Promise<t.ExportedOp[]> {
···
}
async health() {
-
return await axios.get(`${this.url}/_health`)
+
return await this.makeGetReq(`${this.url}/_health`)
+
}
+
}
+
+
export class PlcClientError extends Error {
+
constructor(
+
public status: number,
+
public data: unknown,
+
public message: string,
+
) {
+
super(message)
+
}
+
+
static fromAxiosError(err: AxiosError) {
+
return new PlcClientError(
+
err.response?.status || 500,
+
err.response?.data,
+
err.message,
+
)
}
}
+3 -2
packages/lib/tsconfig.json
···
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
-
"outDir": "./dist", // Your outDir,
+
"outDir": "./dist",
+
"declarationDir": "./dist",
"emitDeclarationOnly": true,
},
-
"include": ["./src", "__tests__/**/**.ts"]
+
"include": ["./src"]
}
+1 -1
packages/server/package.json
···
"@atproto/common": "0.1.0",
"@atproto/crypto": "0.1.0",
"@did-plc/lib": "*",
-
"axios": "^0.27.2",
+
"axios": "^1.3.4",
"cors": "^2.8.5",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
+15 -16
packages/server/tests/server.test.ts
···
import * as plc from '@did-plc/lib'
import { CloseFn, runTestServer } from './_util'
import { check } from '@atproto/common'
-
import { AxiosError } from 'axios'
import { Database } from '../src'
+
import { PlcClientError } from '@did-plc/lib'
describe('PLC server', () => {
let handle = 'at://alice.example.com'
···
'did:key:z6MkjwbBXZnFqL8su24wGL2Fdjti6GSLv9SWdYGswfazUPm9'
const promise = client.updateAtprotoKey(did, rotationKey1, newSigningKey)
-
await expect(promise).rejects.toThrow(AxiosError)
+
await expect(promise).rejects.toThrow(PlcClientError)
const promise2 = client.updateRotationKeys(did, rotationKey1, [
newSigningKey,
])
-
await expect(promise2).rejects.toThrow(AxiosError)
+
await expect(promise2).rejects.toThrow(PlcClientError)
})
it('retrieves the operation log', async () => {
···
})
it('handles concurrent requests to many docs', async () => {
-
const COUNT = 50
+
const COUNT = 20
const keys: EcdsaKeypair[] = []
for (let i = 0; i < COUNT; i++) {
keys.push(await EcdsaKeypair.create())
···
})
it('resolves races into a coherent history with no forks', async () => {
-
const COUNT = 50
+
const COUNT = 20
const keys: EcdsaKeypair[] = []
for (let i = 0; i < COUNT; i++) {
keys.push(await EcdsaKeypair.create())
···
}),
)
expect(successes).toBe(1)
-
expect(failures).toBe(49)
+
expect(failures).toBe(19)
const ops = await client.getOperationLog(did)
await plc.validateOperationLog(did, ops)
···
await client.tombstone(did, rotationKey1)
const promise = client.getDocument(did)
-
await expect(promise).rejects.toThrow(AxiosError)
+
await expect(promise).rejects.toThrow(PlcClientError)
const promise2 = client.getDocumentData(did)
-
await expect(promise2).rejects.toThrow(AxiosError)
+
await expect(promise2).rejects.toThrow(PlcClientError)
})
it('exports the data set', async () => {
const data = await client.export()
expect(data.every((row) => check.is(row, plc.def.exportedOp))).toBeTruthy()
-
expect(data.length).toBe(59)
+
expect(data.length).toBe(29)
for (let i = 1; i < data.length; i++) {
expect(data[i].createdAt >= data[i - 1].createdAt).toBeTruthy()
}
})
it('healthcheck succeeds when database is available.', async () => {
-
const { data, status } = await client.health()
-
expect(status).toEqual(200)
-
expect(data).toEqual({ version: '0.0.0' })
+
const res = await client.health()
+
expect(res).toEqual({ version: '0.0.0' })
})
it('healthcheck fails when database is unavailable.', async () => {
await db.db.destroy()
-
let error: AxiosError
+
let error: PlcClientError
try {
await client.health()
throw new Error('Healthcheck should have failed')
} catch (err) {
-
if (err instanceof AxiosError) {
+
if (err instanceof PlcClientError) {
error = err
} else {
throw err
}
}
-
expect(error.response?.status).toEqual(503)
-
expect(error.response?.data).toEqual({
+
expect(error.status).toEqual(503)
+
expect(error.data).toEqual({
version: '0.0.0',
error: 'Service Unavailable',
})
+7 -1
packages/server/tsconfig.build.json
···
{
"extends": "./tsconfig.json",
-
"exclude": ["**/*.spec.ts", "**/*.test.ts"]
+
"compilerOptions": {
+
"rootDir": "./src",
+
"outDir": "./dist",
+
"declarationDir": "./dist",
+
"emitDeclarationOnly": true,
+
},
+
"include": ["./src"]
}
+1 -9
yarn.lock
···
resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
-
axios@^0.27.2:
-
version "0.27.2"
-
resolved "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz"
-
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
-
dependencies:
-
follow-redirects "^1.14.9"
-
form-data "^4.0.0"
-
axios@^1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024"
···
resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz"
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
-
follow-redirects@^1.14.9, follow-redirects@^1.15.0:
+
follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==