Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

256 refactor to enable strict mode #266

Merged
merged 9 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading