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

exports working

dholms 8a8b6188 d66ceb43

Changed files
+41 -25
packages
+15 -6
packages/lib/src/client.ts
···
-
import { cidForCbor } from '@atproto/common'
+
import { check, cidForCbor } from '@atproto/common'
import { Keypair } from '@atproto/crypto'
import axios from 'axios'
-
import { didForCreateOp, signOperation } from './operations'
+
import { didForCreateOp, normalizeOp, signOperation } from './operations'
import * as t from './types'
export class Client {
···
async getOperationLog(
did: string,
includeNull = false,
-
): Promise<t.Operation[]> {
+
): Promise<t.CompatibleOpOrTombstone[]> {
let url = `${this.url}/${encodeURIComponent(did)}/log`
if (includeNull) {
url += '?includeNull=true'
···
return `${this.url}/${encodeURIComponent(did)}`
}
-
async getLastOp(did: string): Promise<t.Operation> {
+
async getLastOp(did: string): Promise<t.CompatibleOpOrTombstone> {
const res = await axios.get(`${this.url}/${encodeURIComponent(did)}/last`)
return res.data
}
···
key: Keypair,
) {
const lastOp = await this.getLastOp(did)
+
if (check.is(lastOp, t.def.tombstone)) {
+
throw new Error('Cannot apply op to tombstone')
+
}
const prev = await cidForCbor(lastOp)
-
const { signingKey, rotationKeys, handles, services } = lastOp
+
const { signingKey, rotationKeys, handles, services } = normalizeOp(lastOp)
const op = await signOperation(
{
signingKey,
···
return did
}
-
async sendOperation(did: string, op: t.Operation) {
+
async sendOperation(did: string, op: t.OpOrTombstone) {
await axios.post(this.postOpUrl(did), op)
+
}
+
+
async export(): Promise<t.ExportedOp[]> {
+
const res = await axios.get(`${this.url}/export`)
+
const lines = res.data.split('\n')
+
return lines.map((l) => JSON.parse(l))
}
async health() {
+8
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 didDocVerificationMethod = z.object({
id: z.string(),
type: z.string(),
+3 -6
packages/server/src/db/index.ts
···
-
import { Kysely, Migrator, PostgresDialect, Selectable, sql } from 'kysely'
+
import { Kysely, Migrator, PostgresDialect, sql } from 'kysely'
import { Pool as PgPool, types as pgTypes } from 'pg'
import { CID } from 'multiformats/cid'
import { cidForCbor } from '@atproto/common'
import * as plc from '@did-plc/lib'
import { ServerError } from '../error'
import * as migrations from '../migrations'
-
import { DatabaseSchema, OperationsTable, PlcDatabase } from './types'
+
import { DatabaseSchema, PlcDatabase } from './types'
import MockDatabase from './mock'
export * from './mock'
···
return res?.operation ?? null
}
-
async exportOps(
-
count: number,
-
after?: Date,
-
): Promise<Selectable<OperationsTable>[]> {
+
async exportOps(count: number, after?: Date): Promise<plc.ExportedOp[]> {
let builder = this.db
.selectFrom('operations')
.selectAll()
+2 -6
packages/server/src/db/mock.ts
···
import { cidForCbor, check } from '@atproto/common'
import * as plc from '@did-plc/lib'
-
import { Selectable } from 'kysely'
import { ServerError } from '../error'
-
import { OperationsTable, PlcDatabase } from './types'
+
import { PlcDatabase } from './types'
type Contents = Record<string, plc.IndexedOperation[]>
···
}
// disabled in mocks
-
async exportOps(
-
_count: number,
-
_after?: Date,
-
): Promise<Selectable<OperationsTable>[]> {
+
async exportOps(_count: number, _after?: Date): Promise<plc.ExportedOp[]> {
return []
}
}
+2 -2
packages/server/src/db/types.ts
···
import * as plc from '@did-plc/lib'
-
import { Generated, Selectable } from 'kysely'
+
import { Generated } from 'kysely'
export interface PlcDatabase {
close(): Promise<void>
···
includeNull?: boolean,
): Promise<plc.IndexedOperation[]>
lastOpForDid(did: string): Promise<plc.CompatibleOpOrTombstone | null>
-
exportOps(count: number, after?: Date): Promise<Selectable<OperationsTable>[]>
+
exportOps(count: number, after?: Date): Promise<plc.ExportedOp[]>
}
export interface OperationsTable {
+5 -3
packages/server/src/routes.ts
···
const ops = await ctx.db.exportOps(count, after)
res.setHeader('content-type', 'application/jsonlines')
res.status(200)
-
for (const op of ops) {
-
const line = JSON.stringify(op)
+
for (let i = 0; i < ops.length; i++) {
+
if (i > 0) {
+
res.write('\n')
+
}
+
const line = JSON.stringify(ops[i])
res.write(line)
-
res.write('\n')
}
res.end()
})
+6 -2
packages/server/tests/server.test.ts
···
import { EcdsaKeypair } from '@atproto/crypto'
import * as plc from '@did-plc/lib'
import { CloseFn, runTestServer } from './_util'
-
import { cidForCbor } from '@atproto/common'
+
import { check, cidForCbor } from '@atproto/common'
import { AxiosError } from 'axios'
import { Database } from '../src'
import { signOperation } from '@did-plc/lib'
···
const newKey = await EcdsaKeypair.create()
const ops = await client.getOperationLog(did)
const forkPoint = ops.at(-2)
-
if (!forkPoint) {
+
if (!check.is(forkPoint, plc.def.operation)) {
throw new Error('Could not find fork point')
}
const forkCid = await cidForCbor(forkPoint)
···
const ops = await client.getOperationLog(did)
await plc.validateOperationLog(did, ops)
+
})
+
+
it('exports the data set', async () => {
+
await client.export()
})
it('healthcheck succeeds when database is available.', async () => {