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

fix up export & auditability routes

dholms 9ffb23a5 8a8b6188

Changed files
+76 -34
packages
+19 -12
packages/lib/src/client.ts
···
return res.data
}
-
async getOperationLog(
-
did: string,
-
includeNull = false,
-
): Promise<t.CompatibleOpOrTombstone[]> {
-
let url = `${this.url}/${encodeURIComponent(did)}/log`
-
if (includeNull) {
-
url += '?includeNull=true'
-
}
-
const res = await axios.get(url)
-
return res.data.log
+
async getOperationLog(did: string): Promise<t.CompatibleOpOrTombstone[]> {
+
const res = await axios.get(`${this.url}/${encodeURIComponent(did)}/log`)
+
return res.data
+
}
+
+
async getAuditableLog(did: string): Promise<t.ExportedOp[]> {
+
const res = await axios.get(
+
`${this.url}/${encodeURIComponent(did)}/auditable`,
+
)
+
return res.data
}
postOpUrl(did: string): string {
···
await axios.post(this.postOpUrl(did), op)
}
-
async export(): Promise<t.ExportedOp[]> {
-
const res = await axios.get(`${this.url}/export`)
+
async export(after?: string, count?: number): Promise<t.ExportedOp[]> {
+
const url = new URL(`${this.url}/export`)
+
if (after) {
+
url.searchParams.append('after', after)
+
}
+
if (count !== undefined) {
+
url.searchParams.append('count', count.toString(10))
+
}
+
const res = await axios.get(url.toString())
const lines = res.data.split('\n')
return lines.map((l) => JSON.parse(l))
}
+10 -7
packages/lib/src/types.ts
···
})
export type IndexedOperation = z.infer<typeof indexedOperation>
-
export type ExportedOp = {
-
did: string
-
operation: CompatibleOpOrTombstone
-
cid: string
-
nullified: boolean
-
createdAt: Date
-
}
+
export const exportedOp = z.object({
+
did: z.string(),
+
operation: compatibleOpOrTombstone,
+
cid: z.string(),
+
nullified: z.boolean(),
+
createdAt: z.string(),
+
})
+
export type ExportedOp = z.infer<typeof exportedOp>
export const didDocVerificationMethod = z.object({
id: z.string(),
···
opOrTombstone,
compatibleOp,
compatibleOpOrTombstone,
+
indexedOperation,
+
exportedOp,
didDocument,
}
+1 -1
packages/lib/tests/compatibility.test.ts
···
const result = await assureValidNextOp(did, [indexedLegacy], nextOp)
expect(result.nullified.length).toBe(0)
-
expect(result.prev?.equals(legacyCid))
+
expect(result.prev?.equals(legacyCid)).toBeTruthy()
})
})
+3 -3
packages/lib/tests/recovery.test.ts
···
expect(res.nullified.length).toBe(2)
expect(res.nullified[0].equals(log[1].cid))
expect(res.nullified[1].equals(log[2].cid))
-
expect(res.prev?.equals(createCid))
+
expect(res.prev?.equals(createCid)).toBeTruthy()
log = [log[0], rotate.indexed]
})
···
const res = await data.assureValidNextOp(did, log, rotate.op)
expect(res.nullified.length).toBe(1)
expect(res.nullified[0].equals(log[1].cid))
-
expect(res.prev?.equals(createCid))
+
expect(res.prev?.equals(createCid)).toBeTruthy()
log = [log[0], rotate.indexed]
})
···
)
expect(result.nullified.length).toBe(1)
expect(result.nullified[0].equals(cid))
-
expect(result.prev?.equals(createCid))
+
expect(result.prev?.equals(createCid)).toBeTruthy()
})
})
+6 -2
packages/server/src/db/index.ts
···
let builder = this.db
.selectFrom('operations')
.selectAll()
-
.orderBy('createdAt', 'desc')
+
.orderBy('createdAt', 'asc')
.limit(count)
if (after) {
builder = builder.where('createdAt', '>', after)
}
-
return builder.execute()
+
const res = await builder.execute()
+
return res.map((row) => ({
+
...row,
+
createdAt: row.createdAt.toISOString(),
+
}))
}
}
+18 -7
packages/server/src/routes.ts
···
// Get operation log for a DID
router.get('/:did/log', async function (req, res) {
const { did } = req.params
-
const includeNull = req.query.includeNull === 'true'
+
const log = await ctx.db.opsForDid(did)
+
if (log.length === 0) {
+
throw new ServerError(404, `DID not registered: ${did}`)
+
}
+
res.json(log)
+
})
-
const log = includeNull
-
? await ctx.db.indexedOpsForDid(did, includeNull)
-
: await ctx.db.opsForDid(did)
-
-
if (log.length === 0) {
+
// Get operation log for a DID
+
router.get('/:did/auditable', async function (req, res) {
+
const { did } = req.params
+
const ops = await ctx.db.indexedOpsForDid(did, true)
+
if (ops.length === 0) {
throw new ServerError(404, `DID not registered: ${did}`)
}
-
res.json({ log })
+
const log = ops.map((op) => ({
+
...op,
+
cid: op.cid.toString(),
+
createdAt: op.createdAt.toISOString(),
+
}))
+
+
res.json(log)
})
// Get the most recent operation in the log for a DID
+19 -2
packages/server/tests/server.test.ts
···
expect(doc.services).toEqual({ atpPds })
})
+
it('retrieves the auditable operation log', async () => {
+
const log = await client.getOperationLog(did)
+
const auditable = await client.getAuditableLog(did)
+
// has one nullifed op
+
expect(auditable.length).toBe(log.length + 1)
+
expect(auditable.filter((op) => op.nullified).length).toBe(1)
+
expect(auditable.at(-2)?.nullified).toBe(true)
+
expect(
+
auditable.every((op) => check.is(op, plc.def.exportedOp)),
+
).toBeTruthy()
+
})
+
it('retrieves the did doc', async () => {
const data = await client.getDocumentData(did)
const doc = await client.getDocument(did)
···
})
it('handles concurrent requests to many docs', async () => {
-
const COUNT = 100
+
const COUNT = 50
const keys: EcdsaKeypair[] = []
for (let i = 0; i < COUNT; i++) {
keys.push(await EcdsaKeypair.create())
···
})
it('exports the data set', async () => {
-
await client.export()
+
const data = await client.export()
+
expect(data.every((row) => check.is(row, plc.def.exportedOp))).toBeTruthy()
+
expect(data.length).toBe(58)
+
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 () => {