Skip to content

Commit

Permalink
256 refactor to enable strict mode (#266)
Browse files Browse the repository at this point in the history
* chore: enable strict mode

* fix: matching ipfs metadata string

* fix: improve handling of undefined get services

* fix: enhanced in memory snapshotting

* fix: currency initialization

* chore: upgrade indexer version

* fix: exceptions handling blockHandlers

* fix: snapshotters tests
  • Loading branch information
filo87 authored Nov 13, 2024
1 parent 60e9608 commit 268d1c9
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 184 deletions.
54 changes: 16 additions & 38 deletions src/helpers/stateSnapshot.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SubstrateBlock } from '@subql/types'
import { PoolService } from '../mappings/services/poolService'
import { substrateStateSnapshotter } from './stateSnapshot'
import { Pool, PoolSnapshot } from '../types'
import { BlockInfo, statesSnapshotter } from './stateSnapshot'
import { PoolSnapshot } from '../types'
import { getPeriodStart } from './timekeeperService'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -25,60 +25,37 @@ describe('Given a populated pool,', () => {
set.mockReset()
getByFields.mockReset()
getByFields.mockReturnValue([pool])
await substrateStateSnapshotter('periodId', periodId, Pool, PoolSnapshot, block)
expect(store.getByFields).toHaveBeenCalledWith('Pool', [['blockchainId', '=', '0']], expect.anything())
const blockInfo: BlockInfo = { timestamp, number: blockNumber }
await statesSnapshotter('periodId', periodId, [pool], PoolSnapshot, blockInfo)
expect(store.set).toHaveBeenCalledTimes(2)
expect(store.set).toHaveBeenNthCalledWith(1, 'Pool', poolId, expect.anything())
expect(store.set).toHaveBeenNthCalledWith(2, 'PoolSnapshot', `${poolId}-11246`, expect.anything())
expect(store.set).toHaveBeenNthCalledWith(2, 'PoolSnapshot', `${poolId}-${blockNumber}`, expect.anything())
})

test('when a snapshot is taken, then the timestamp and blocknumber are added', async () => {
set.mockReset()
getByFields.mockReset()
getByFields.mockReturnValue([pool])
await substrateStateSnapshotter('periodId', periodId, Pool, PoolSnapshot, block)
const blockInfo: BlockInfo = { timestamp, number: blockNumber }
await statesSnapshotter('periodId', periodId, [pool], PoolSnapshot, blockInfo)
expect(store.set).toHaveBeenNthCalledWith(
2,
'PoolSnapshot',
`${poolId}-11246`,
expect.objectContaining({ timestamp: block.timestamp, blockNumber: 11246 })
)
})

test('when filters are specified, then the correct values are fetched', async () => {
set.mockReset()
getByFields.mockReset()
getByFields.mockReturnValue([pool])
await substrateStateSnapshotter<Pool, PoolSnapshot>('periodId', periodId, Pool, PoolSnapshot, block, [
['isActive', '=', true],
])
expect(store.getByFields).toHaveBeenNthCalledWith(
1,
'Pool',
[
['blockchainId', '=', '0'],
['isActive', '=', true],
],
expect.anything()
`${poolId}-${blockNumber}`,
expect.objectContaining({ timestamp: block.timestamp, blockNumber: blockNumber })
)
})

test('when a foreigh key is set, then the correct foreign key value is set on the snapshot', async () => {
set.mockReset()
getByFields.mockReset()
getByFields.mockReturnValue([pool])
await substrateStateSnapshotter<Pool, PoolSnapshot>(
'periodId',
periodId,
Pool,
PoolSnapshot,
block,
[['type', '=', 'ALL']],
'poolId'
)
const blockInfo: BlockInfo = { timestamp, number: blockNumber }
await statesSnapshotter('periodId', periodId, [pool], PoolSnapshot, blockInfo, 'poolId')
expect(store.set).toHaveBeenNthCalledWith(
2,
'PoolSnapshot',
`${poolId}-11246`,
`${poolId}-${blockNumber}`,
expect.objectContaining({ poolId: poolId })
)
})
Expand All @@ -101,13 +78,14 @@ describe('Given a pool with non zero accumulators, ', () => {

Object.assign(pool, accumulatedProps)

await substrateStateSnapshotter('periodId', periodId, Pool, PoolSnapshot, block)
const blockInfo: BlockInfo = { timestamp, number: 11246 }
await statesSnapshotter('periodId', periodId, [pool], PoolSnapshot, blockInfo)

expect(store.set).toHaveBeenNthCalledWith(1, 'Pool', poolId, expect.objectContaining(zeroedProps))
expect(store.set).toHaveBeenNthCalledWith(
2,
'PoolSnapshot',
`${poolId}-11246`,
`${poolId}-${blockNumber}`,
expect.objectContaining(zeroedProps)
)
})
Expand Down
96 changes: 2 additions & 94 deletions src/helpers/stateSnapshot.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { paginatedGetter } from './paginatedGetter'
import type { Entity, FieldsExpression, FunctionPropertyNames, GetOptions } from '@subql/types-core'
import { EthereumBlock } from '@subql/types-ethereum'
import { SubstrateBlock } from '@subql/types'
/**
* Creates a snapshot of a generic stateModel to a snapshotModel.
* A snapshotModel has the same fields as the originating stateModel, however a timestamp and a blockNumber are added.
Expand All @@ -16,72 +13,6 @@ import { SubstrateBlock } from '@subql/types'
* @param resetPeriodStates - (optional) reset properties ending in ByPeriod to 0 after snapshot (defaults to true).
* @returns A promise resolving when all state manipulations in the DB is completed
*/
async function stateSnapshotter<T extends SnapshottableEntity, U extends SnapshottedEntity<T>>(
relationshipField: StringForeignKeys<SnapshotAdditions>,
relationshipId: string,
stateModel: EntityClass<T>,
snapshotModel: EntityClass<U>,
block: { number: number; timestamp: Date },
filters?: FieldsExpression<EntityProps<T>>[],
fkReferenceField?: StringForeignKeys<U>,
resetPeriodStates = true,
blockchainId = '0'
): Promise<void[]> {
const entitySaves: Promise<void>[] = []
logger.info(`Performing snapshots of ${stateModel.prototype._name} for blockchainId ${blockchainId}`)
const filter = [['blockchainId', '=', blockchainId]] as FieldsExpression<EntityProps<T>>[]
if (filters) filter.push(...filters)
const stateEntities = await paginatedGetter(stateModel, filter)
if (stateEntities.length === 0) logger.info(`No ${stateModel.prototype._name} to snapshot!`)
for (const stateEntity of stateEntities) {
const blockNumber = block.number
const snapshot: SnapshottedEntity<T> = {
...stateEntity,
id: `${stateEntity.id}-${blockNumber}`,
timestamp: block.timestamp,
blockNumber: blockNumber,
[relationshipField]: relationshipId,
}
logger.info(`Snapshotting ${stateModel.prototype._name}: ${stateEntity.id}`)
const snapshotEntity = snapshotModel.create(snapshot as U)
if (fkReferenceField) snapshotEntity[fkReferenceField] = stateEntity.id as U[StringForeignKeys<U>]
const propNames = Object.getOwnPropertyNames(stateEntity)
const propNamesToReset = propNames.filter((propName) => propName.endsWith('ByPeriod')) as ResettableKeys<T>[]
if (resetPeriodStates) {
for (const propName of propNamesToReset) {
logger.debug(`resetting ${stateEntity._name?.toLowerCase()}.${propName} to 0`)
stateEntity[propName] = BigInt(0) as T[ResettableKeys<T>]
}
entitySaves.push(stateEntity.save())
}
entitySaves.push(snapshotEntity.save())
}
return Promise.all(entitySaves)
}
export function evmStateSnapshotter<T extends SnapshottableEntity, U extends SnapshottedEntity<T>>(
relationshipField: StringForeignKeys<SnapshotAdditions>,
relationshipId: string,
stateModel: EntityClass<T>,
snapshotModel: EntityClass<U>,
block: EthereumBlock,
filters?: FieldsExpression<EntityProps<T>>[],
fkReferenceName?: StringForeignKeys<U>,
resetPeriodStates = true
): Promise<void[]> {
const formattedBlock = { number: block.number, timestamp: new Date(Number(block.timestamp) * 1000) }
return stateSnapshotter(
relationshipField,
relationshipId,
stateModel,
snapshotModel,
formattedBlock,
filters,
fkReferenceName,
resetPeriodStates,
'1'
)
}

export async function statesSnapshotter<T extends SnapshottableEntity, U extends SnapshottedEntity<T>>(
relationshipField: StringForeignKeys<SnapshotAdditions>,
relationshipId: string,
Expand Down Expand Up @@ -116,35 +47,12 @@ export async function statesSnapshotter<T extends SnapshottableEntity, U extends
entitySaves.push(stateEntity.save())
}
entitySaves.push(snapshotEntity.save())
//logger.info(JSON.stringify(stateEntity))
//logger.info(JSON.stringify(snapshotEntity))
}
return Promise.all(entitySaves)
}

export function substrateStateSnapshotter<T extends SnapshottableEntity, U extends SnapshottedEntity<T>>(
relationshipField: StringForeignKeys<SnapshotAdditions>,
relationshipId: string,
stateModel: EntityClass<T>,
snapshotModel: EntityClass<U>,
block: SubstrateBlock,
filters?: FieldsExpression<EntityProps<T>>[],
fkReferenceName?: StringForeignKeys<U>,
resetPeriodStates = true
): Promise<void[]> {
if (!block.timestamp) throw new Error('Missing block timestamp')
const formattedBlock = { number: block.block.header.number.toNumber(), timestamp: block.timestamp }
return stateSnapshotter(
relationshipField,
relationshipId,
stateModel,
snapshotModel,
formattedBlock,
filters,
fkReferenceName,
resetPeriodStates,
'0'
)
}

type ResettableKeyFormat = `${string}ByPeriod`
type ForeignKeyFormat = `${string}Id`
type ResettableKeys<T> = { [K in keyof T]: K extends ResettableKeyFormat ? K : never }[keyof T]
Expand Down
Loading

0 comments on commit 268d1c9

Please sign in to comment.