Skip to content
This repository has been archived by the owner on Oct 9, 2024. It is now read-only.

Commit

Permalink
HYP-2: promoting example controller to a real token, also pushing as …
Browse files Browse the repository at this point in the history
…a current state, tests need adjusting.
  • Loading branch information
n3op2 committed Nov 24, 2023
1 parent 966545d commit 80f072b
Show file tree
Hide file tree
Showing 19 changed files with 239 additions and 192 deletions.
28 changes: 11 additions & 17 deletions src/controllers/v1/attachment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ import { Readable } from 'node:stream'

import { logger } from '../../../lib/logger'
import Database, { AttachmentRow } from '../../../lib/db'
import type { Attachment } from '../../../models'
import type * as Attachment from '../../../models'
import { BadRequest, NotFound } from '../../../lib/error-handler'
import type { UUID, DATE } from '../../../models/strings'
import Ipfs from '../../../lib/ipfs'
import env from '../../../env'
import { parseDateParam } from '../../../lib/utils/queryParams'

const parseAccept = (acceptHeader: string) =>
acceptHeader
Expand Down Expand Up @@ -87,14 +86,14 @@ export class attachment extends Controller {

@Get('/')
@SuccessResponse(200, 'returns all attachment')
public async get(@Query() updated_since?: DATE): Promise<Attachment[]> {
public async getAll(@Query() createdAt?: DATE): Promise<Attachment.Response> {
this.log.debug('retrieving all attachment')
const where: Record<string, number | string | DATE> = {}
if (createdAt) where.created_at = createdAt

const attachments: AttachmentRow[] = await this.db.get(
'attachment',
updated_since ? { created_at: parseDateParam(updated_since) } : {}
)
return attachments.map(({ ipfs_hash, ...rest }): Attachment => ({ ...rest, createdAt: rest.created_at }))
const attachments = await this.db.get('attachment', where)

Check failure on line 95 in src/controllers/v1/attachment/index.ts

View workflow job for this annotation

GitHub Actions / Run lint

Delete `··`
return { message: 'ok', attachments}

Check failure on line 96 in src/controllers/v1/attachment/index.ts

View workflow job for this annotation

GitHub Actions / Run lint

Insert `·`
}

@Post('/')
Expand All @@ -103,7 +102,7 @@ export class attachment extends Controller {
public async create(
@Request() req: express.Request,
@UploadedFile() file?: Express.Multer.File
): Promise<Attachment> {
): Promise<Attachment.Response | string> {
this.log.debug(`creating an attachment filename: ${file?.originalname || 'json'}`)

if (!req.body && !file) throw new BadRequest('nothing to upload')
Expand All @@ -112,26 +111,21 @@ export class attachment extends Controller {
const fileBlob = new Blob([Buffer.from(file?.buffer || JSON.stringify(req.body))])
const ipfsHash = await this.ipfs.addFile({ blob: fileBlob, filename })

const [{ id, created_at }] = await this.db.insert('attachment', {
const result: AttachmentRow | string = await this.db.insert('attachment', {
filename,
ipfs_hash: ipfsHash,
size: fileBlob.size,
})

return {
id,
filename,
size: fileBlob.size,
createdAt: created_at,
}
return result
}

@Get('/{id}')
@Response<NotFound>(404)
@Produces('application/json')
@Produces('application/octet-stream')
@SuccessResponse(200)
public async getById(@Request() req: express.Request, @Path() id: UUID): Promise<unknown | Readable> {
public async getById(@Request() req: express.Request, @Path() id: UUID): Promise<Attachment.Response> {
this.log.debug(`attempting to retrieve ${id} attachment`)
const [attachment] = await this.db.get('attachment', { id })
if (!attachment) throw new NotFound('attachment')
Expand Down
102 changes: 102 additions & 0 deletions src/controllers/v1/certificate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {
ValidateError,
Controller,
Post,
Get,
Route,
Response,
Body,
SuccessResponse,
Tags,
Security,
Path,
Query,
} from 'tsoa'
import type { Logger } from 'pino'
import { injectable } from 'tsyringe'

import { logger } from '../../../lib/logger'
import Database, { CertificateRow } from '../../../lib/db'
import { BadRequest, NotFound } from '../../../lib/error-handler/index'
import Identity from '../../../lib/services/identity'
import * as Certificate from '../../../models/certificate'
import { DATE, UUID } from '../../../models/strings'
// import { TransactionResponse, TransactionType } from '../../../models/transaction'
import ChainNode from '../../../lib/chainNode'
import env from '../../../env'
import { camelToSnake } from 'src/lib/utils/shared'

@Route('v1/certificate')
@injectable()
@Tags('certificate')
@Security('BearerAuth')
export class CertificateController extends Controller {
log: Logger
db: Database
node: ChainNode

constructor(private identity: Identity) {
super()
this.log = logger.child({ controller: '/certificate' })
this.db = new Database()
this.node = new ChainNode({
host: env.NODE_HOST,
port: env.NODE_PORT,
logger,
userUri: env.USER_URI,
})
this.identity = identity
}

/**
* @summary insert a certificate for initialisation
*/
@Post()
@Response<BadRequest>(400, 'Request was invalid')
@Response<ValidateError>(422, 'Validation Failed')
@SuccessResponse('201')
public async post(@Body() body: Certificate.Request): Promise<Certificate.Response> {
this.log.info({ identity: this.identity, body })

const formatted = Object.keys(body).reduce((out, key) => ({

Check failure on line 61 in src/controllers/v1/certificate/index.ts

View workflow job for this annotation

GitHub Actions / Run lint

Insert `⏎······`
[camelToSnake(key)]: body[key],

Check failure on line 62 in src/controllers/v1/certificate/index.ts

View workflow job for this annotation

GitHub Actions / Run lint

Insert `··`
...out,

Check failure on line 63 in src/controllers/v1/certificate/index.ts

View workflow job for this annotation

GitHub Actions / Run lint

Insert `··`
}), {})

Check failure on line 64 in src/controllers/v1/certificate/index.ts

View workflow job for this annotation

GitHub Actions / Run lint

Replace `····}),·{}` with `······}),⏎······{}⏎····`

const result: CertificateRow[] | string = await this.db.insert('certificate', formatted)

return { message: 'ok', result }
}

/**
* description
* @summary Lists all local certificates
* @param createdAt - is optional and will return certificates based on options provided
* TODO - combine where so with all possible options e.g.
* columns so it's not a default and will be rendered in swagger
*/
@Get('/')
public async getAll(@Query() createdAt?: DATE): Promise<Certificate.Response> {
const where: Record<string, number | string | DATE> = {}
if (createdAt) where.created_at = createdAt
const certificates = await this.db.get('certificate', where)

return { message: 'ok', certificates }
}

/**
* @summary
* @param id The certificate's identifier
*/
@Response<ValidateError>(422, 'Validation Failed')
@Response<NotFound>(404, 'Item not found')
@Get('{id}')
public async getById(@Path() id: UUID): Promise<Certificate.Response> {
if (!id) throw new BadRequest()

return {
message: 'ok',
certificate: await this.db.get('certificate', { id }),
}
}
}
86 changes: 0 additions & 86 deletions src/controllers/v1/example/index.ts

This file was deleted.

18 changes: 10 additions & 8 deletions src/lib/db/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import knex, { Knex } from 'knex'

import { pgConfig } from './knexfile'
import { HEX, UUID } from '../../models/strings'
import { DATE, HEX, UUID } from '../../models/strings'
import { TransactionApiType, TransactionState, TransactionType } from '../../models/transaction'
import { NotFound } from '../error-handler'

const tablesList = ['attachment', 'example', 'transaction', 'processed_blocks'] as const
const tablesList = ['attachment', 'certificate', 'transaction', 'processed_blocks'] as const
type TABLES_TUPLE = typeof tablesList
type TABLE = TABLES_TUPLE[number]

Expand All @@ -21,7 +21,7 @@ export interface AttachmentRow {
filename: string | null
size: number | null
ipfs_hash: string
created_at: Date
created_at: DATE

Check failure on line 24 in src/lib/db/index.ts

View workflow job for this annotation

GitHub Actions / Run lint

Delete `·`
}

export interface TransactionRow {
Expand All @@ -34,12 +34,12 @@ export interface TransactionRow {
updated_at: Date
}

export interface ExampleRow {
export interface CertificateRow {
id: UUID
createdAt: Date
}

export type Entities = ExampleRow | TransactionRow | AttachmentRow
export type Entities = CertificateRow | TransactionRow | AttachmentRow

function restore0x(input: ProcessedBlockTrimmed): ProcessedBlock {
return {
Expand Down Expand Up @@ -73,13 +73,15 @@ export default class Database {
}

// generics methods
insert = async (model: keyof Models<() => QueryBuilder>, record: Record<string, string | number>) => {
insert = async (model: keyof Models<() => QueryBuilder>, record: Record<string, string | number>): Promise<keyof Entities> => {

Check failure on line 76 in src/lib/db/index.ts

View workflow job for this annotation

GitHub Actions / Run lint

Replace `model:·keyof·Models<()·=>·QueryBuilder>,·record:·Record<string,·string·|·number>` with `⏎····model:·keyof·Models<()·=>·QueryBuilder>,⏎····record:·Record<string,·string·|·number>⏎··`
const query = this.db()[model]

// TODO address indexer (create a backlog item)
if (model == 'processed_blocks') {
return query().insert(trim0x(record as ProcessedBlock))
}

return query().insert(record).returning('*')
return query().insert(record)
}

delete = async (model: keyof Models<() => QueryBuilder>, where: Record<string, string | number>) => {
Expand Down Expand Up @@ -115,7 +117,7 @@ export default class Database {

// TODO some methods could be generic as well, e.g. insert/get for event processor indexer
findLocalIdForToken = async (tokenId: number): Promise<UUID | null> => {
const result = (await Promise.all([this.db().example().select(['id']).where({ latest_token_id: tokenId })])) as {
const result = (await Promise.all([this.db().certificate().select(['id']).where({ latest_token_id: tokenId })])) as {

Check failure on line 120 in src/lib/db/index.ts

View workflow job for this annotation

GitHub Actions / Run lint

Replace `this.db().certificate().select(['id']).where({·latest_token_id:·tokenId·})` with `⏎······this.db().certificate().select(['id']).where({·latest_token_id:·tokenId·}),⏎····`
id: UUID
}[][]
const flatten = result.reduce((acc, set) => [...acc, ...set], [])
Expand Down
10 changes: 5 additions & 5 deletions src/lib/db/migrations/20230310111029_initial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ export async function up(knex: Knex): Promise<void> {
def.primary(['id'])
})

await knex.schema.createTable('example', (def) => {
await knex.schema.createTable('certificate', (def) => {
def.uuid('id').defaultTo(knex.raw('uuid_generate_v4()'))
def.string('owner', 48).notNullable()
def
.enum('state', ['created', 'allocated'], {
enumName: 'example_state',
.enum('state', ['initialized', 'issued', 'revoked'], {
enumName: 'certificate_state',
useNative: true,
})
.notNullable()
def.uuid('parameters_attachment_id').notNullable()
def.uuid('parameters_attachment_id').nullable().defaultTo(null)
def.integer('latest_token_id')
def.integer('original_token_id')
def.datetime('created_at').notNullable().defaultTo(now())
Expand Down Expand Up @@ -75,7 +75,7 @@ export async function up(knex: Knex): Promise<void> {

export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTable('attachment')
await knex.schema.dropTable('example')
await knex.schema.dropTable('certificate')
await knex.schema.dropTable('transaction')
await knex.schema.dropTable('processed_blocks')
await knex.raw('DROP EXTENSION "uuid-ossp"')
Expand Down
8 changes: 4 additions & 4 deletions src/lib/indexer/__tests__/eventProcessor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import { expect } from 'chai'
import { TransactionRow } from '../../db'

describe('eventProcessor', function () {
describe('example-create', function () {
describe('process_initiate_cert', function () {
it('should error with version != 1', function () {
let error: Error | null = null
try {
eventProcessors['example-create'](0, null, 'alice', [], [])
eventProcessors['process_initiate_cert'](0, null, 'alice', [], [])
} catch (err) {
error = err instanceof Error ? err : null
}
expect(error).instanceOf(Error)
})

it('should return update to example if transaction exists', function () {
const result = eventProcessors['example-create'](
const result = eventProcessors['process_initiate_cert'](
1,
{ localId: '42' } as TransactionRow,
'alice',
Expand All @@ -33,7 +33,7 @@ describe('eventProcessor', function () {
})

it("should return new attachment and example if transaction doesn't exist", function () {
const result = eventProcessors['example-create'](
const result = eventProcessors['process_initiate_cert'](
1,
null,
'alice',
Expand Down
Loading

0 comments on commit 80f072b

Please sign in to comment.