-
Notifications
You must be signed in to change notification settings - Fork 117
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
[IND-494]: Create trading_rewards postgres table #822
Changes from 4 commits
d41d94a
972bfe3
69004a0
41f2954
cc6c573
560e615
2c989d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { TradingRewardFromDatabase } from '../../src/types'; | ||
import { clearData, migrate, teardown } from '../../src/helpers/db-helpers'; | ||
import { defaultTradingReward } from '../helpers/constants'; | ||
import * as TradingRewardTable from '../../src/stores/trading-reward-table'; | ||
|
||
describe('TradingReward store', () => { | ||
beforeAll(async () => { | ||
await migrate(); | ||
}); | ||
|
||
afterEach(async () => { | ||
await clearData(); | ||
}); | ||
|
||
afterAll(async () => { | ||
await teardown(); | ||
}); | ||
|
||
it('Successfully creates a TradingReward', async () => { | ||
await TradingRewardTable.create(defaultTradingReward); | ||
}); | ||
|
||
it('Successfully finds all TradingRewards', async () => { | ||
await Promise.all([ | ||
TradingRewardTable.create(defaultTradingReward), | ||
TradingRewardTable.create({ | ||
...defaultTradingReward, | ||
blockHeight: '20', | ||
}), | ||
]); | ||
|
||
const tradingRewards: TradingRewardFromDatabase[] = await TradingRewardTable.findAll( | ||
{}, | ||
[], | ||
{ readReplica: true }, | ||
); | ||
|
||
expect(tradingRewards.length).toEqual(2); | ||
expect(tradingRewards[0]).toEqual(expect.objectContaining({ | ||
...defaultTradingReward, | ||
blockHeight: '20', | ||
})); | ||
expect(tradingRewards[1]).toEqual(expect.objectContaining(defaultTradingReward)); | ||
}); | ||
|
||
it('Successfully finds a TradingReward', async () => { | ||
await TradingRewardTable.create(defaultTradingReward); | ||
|
||
const tradingReward: TradingRewardFromDatabase | undefined = await TradingRewardTable.findById( | ||
TradingRewardTable.uuid(defaultTradingReward.address, defaultTradingReward.blockHeight), | ||
); | ||
|
||
expect(tradingReward).toEqual(expect.objectContaining(defaultTradingReward)); | ||
Comment on lines
+51
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test case 'Successfully finds a TradingReward' correctly asserts the expected result. However, it would be beneficial to also verify that the |
||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import * as Knex from 'knex'; | ||
|
||
export async function up(knex: Knex): Promise<void> { | ||
return knex | ||
.schema | ||
.createTable('trading_rewards', (table) => { | ||
table.uuid('id').primary(); | ||
table.string('address').notNullable(); | ||
table.timestamp('blockTime').notNullable(); | ||
table.bigInteger('blockHeight').notNullable(); | ||
table.decimal('amount').notNullable(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider specifying the precision and scale for the 'amount' column if the decimal values have a fixed format, to ensure data consistency and avoid potential issues with arithmetic operations on these values. |
||
|
||
// Foreign | ||
table.foreign('address').references('wallet.address'); | ||
|
||
// Indices | ||
table.index(['address']); | ||
table.index(['blockTime']); | ||
table.index(['blockHeight']); | ||
}); | ||
} | ||
|
||
export async function down(knex: Knex): Promise<void> { | ||
return knex.schema.dropTableIfExists('trading_rewards'); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ const layer1Tables = [ | |
'liquidity_tiers', | ||
'wallets', | ||
'compliance_data', | ||
'trading_rewards', | ||
]; | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import path from 'path'; | ||
|
||
import { Model } from 'objection'; | ||
|
||
import { IntegerPattern, NonNegativeNumericPattern } from '../lib/validators'; | ||
import UpsertQueryBuilder from '../query-builders/upsert'; | ||
|
||
export default class TradingRewardModel extends Model { | ||
static get tableName() { | ||
return 'trading_rewards'; | ||
} | ||
|
||
static get idColumn() { | ||
return 'id'; | ||
} | ||
|
||
static relationMappings = { | ||
wallet: { | ||
relation: Model.BelongsToOneRelation, | ||
modelClass: path.join(__dirname, 'wallet-model'), | ||
join: { | ||
from: 'trading_rewards.address', | ||
to: 'wallets.address', | ||
}, | ||
}, | ||
}; | ||
|
||
static get jsonSchema() { | ||
return { | ||
type: 'object', | ||
required: [ | ||
'id', | ||
'address', | ||
'blockTime', | ||
'blockHeight', | ||
'amount', | ||
], | ||
properties: { | ||
id: { type: 'string', format: 'uuid' }, | ||
address: { type: 'string' }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
blockTime: { type: 'string', format: 'date-time' }, | ||
blockHeight: { type: 'string', pattern: IntegerPattern }, | ||
amount: { type: 'string', pattern: NonNegativeNumericPattern }, | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* A mapping from column name to JSON conversion expected. | ||
* See getSqlConversionForDydxModelTypes for valid conversions. | ||
* | ||
* TODO(IND-239): Ensure that jsonSchema() / sqlToJsonConversions() / model fields match. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
*/ | ||
static get sqlToJsonConversions() { | ||
return { | ||
id: 'string', | ||
address: 'string', | ||
blockTime: 'date-time', | ||
blockHeight: 'string', | ||
amount: 'string', | ||
}; | ||
} | ||
|
||
QueryBuilderType!: UpsertQueryBuilder<this>; | ||
|
||
id!: string; | ||
|
||
address!: string; | ||
|
||
blockTime!: string; | ||
|
||
blockHeight!: string; | ||
|
||
amount!: string; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { QueryBuilder } from 'objection'; | ||
|
||
import { BUFFER_ENCODING_UTF_8, DEFAULT_POSTGRES_OPTIONS } from '../constants'; | ||
import { setupBaseQuery, verifyAllRequiredFields } from '../helpers/stores-helpers'; | ||
import Transaction from '../helpers/transaction'; | ||
import { getUuid } from '../helpers/uuid'; | ||
import TradingRewardModel from '../models/trading-reward-model'; | ||
import { | ||
Options, | ||
Ordering, | ||
QueryableField, | ||
QueryConfig, | ||
TradingRewardColumns, | ||
TradingRewardCreateObject, | ||
TradingRewardFromDatabase, | ||
TradingRewardQueryConfig, | ||
} from '../types'; | ||
|
||
export function uuid(address: string, blockHeight: string): string { | ||
// TODO(IND-483): Fix all uuid string substitutions to use Array.join. | ||
return getUuid(Buffer.from(`${address}-${blockHeight}`, BUFFER_ENCODING_UTF_8)); | ||
} | ||
|
||
export async function findAll( | ||
{ | ||
address, | ||
blockHeight, | ||
blockTimeBeforeOrAt, | ||
limit, | ||
}: TradingRewardQueryConfig, | ||
requiredFields: QueryableField[], | ||
options: Options = DEFAULT_POSTGRES_OPTIONS, | ||
): Promise<TradingRewardFromDatabase[]> { | ||
verifyAllRequiredFields( | ||
{ | ||
address, | ||
blockHeight, | ||
blockTimeBeforeOrAt, | ||
limit, | ||
} as QueryConfig, | ||
requiredFields, | ||
); | ||
|
||
let baseQuery: QueryBuilder<TradingRewardModel> = setupBaseQuery<TradingRewardModel>( | ||
TradingRewardModel, | ||
options, | ||
); | ||
|
||
if (address) { | ||
baseQuery = baseQuery.where(TradingRewardColumns.address, address); | ||
} | ||
|
||
if (blockHeight) { | ||
baseQuery = baseQuery.where(TradingRewardColumns.blockHeight, blockHeight); | ||
} | ||
|
||
if (blockTimeBeforeOrAt) { | ||
baseQuery = baseQuery.where(TradingRewardColumns.blockTime, '<=', blockTimeBeforeOrAt); | ||
} | ||
|
||
if (options.orderBy !== undefined) { | ||
for (const [column, order] of options.orderBy) { | ||
baseQuery = baseQuery.orderBy( | ||
column, | ||
order, | ||
); | ||
} | ||
} else { | ||
baseQuery = baseQuery.orderBy( | ||
TradingRewardColumns.blockHeight, | ||
Ordering.DESC, | ||
).orderBy( | ||
TradingRewardColumns.address, | ||
Ordering.DESC, | ||
); | ||
} | ||
|
||
if (limit) { | ||
baseQuery = baseQuery.limit(limit); | ||
} | ||
|
||
return baseQuery.returning('*'); | ||
} | ||
|
||
export async function create( | ||
tradingRewardToCreate: TradingRewardCreateObject, | ||
options: Options = { txId: undefined }, | ||
): Promise<TradingRewardFromDatabase> { | ||
return TradingRewardModel.query( | ||
Transaction.get(options.txId), | ||
).insert({ | ||
id: uuid(tradingRewardToCreate.address, tradingRewardToCreate.blockHeight), | ||
...tradingRewardToCreate, | ||
}).returning('*'); | ||
} | ||
|
||
export async function findById( | ||
address: string, | ||
options: Options = DEFAULT_POSTGRES_OPTIONS, | ||
): Promise<TradingRewardFromDatabase | undefined> { | ||
const baseQuery: QueryBuilder<TradingRewardModel> = setupBaseQuery<TradingRewardModel>( | ||
TradingRewardModel, | ||
options, | ||
); | ||
return baseQuery | ||
.findById(address) | ||
.returning('*'); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { IsoString } from './utility-types'; | ||
|
||
export interface TradingRewardCreateObject { | ||
address: string; | ||
blockTime: IsoString; | ||
blockHeight: string; | ||
amount: string; | ||
} | ||
|
||
export enum TradingRewardColumns { | ||
id = 'id', | ||
address = 'address', | ||
blockTime = 'blockTime', | ||
blockHeight = 'blockHeight', | ||
amount = 'amount', | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test case 'Successfully creates a TradingReward' does not assert the result of the
create
operation. It is important to verify that thecreate
method returns the expected result to ensure that the TradingReward was created successfully.