Skip to content

Commit

Permalink
[OTE-750] Affiliates new affiliate info table (#2179)
Browse files Browse the repository at this point in the history
  • Loading branch information
jerryfan01234 authored Sep 1, 2024
1 parent ffde811 commit f4bb592
Show file tree
Hide file tree
Showing 14 changed files with 331 additions and 11 deletions.
27 changes: 26 additions & 1 deletion indexer/packages/postgres/__tests__/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as TradingRewardAggregationTable from '../../src/stores/trading-reward-
import * as TransactionTable from '../../src/stores/transaction-table';
import * as TransferTable from '../../src/stores/transfer-table';
import {
AffiliateInfoCreateObject,
AffiliateReferredUsersCreateObject,
AssetCreateObject,
AssetPositionCreateObject,
Expand Down Expand Up @@ -947,7 +948,7 @@ export const defaultLeaderboardPnlOneDayToUpsert: LeaderboardPnlCreateObject = {
export const defaultAffiliateReferredUser: AffiliateReferredUsersCreateObject = {
affiliateAddress: defaultAddress,
refereeAddress: defaultAddress2,
referredAtBlock: 1,
referredAtBlock: '1',
};

// ============== Persistent cache Data ==============
Expand All @@ -961,3 +962,27 @@ export const defaultKV2: PersistentCacheCreateObject = {
key: 'otherKey',
value: 'otherValue',
};

// ============== Affiliate Info Data ==============

export const defaultAffiliateInfo: AffiliateInfoCreateObject = {
address: defaultAddress,
affiliateEarnings: '10.00',
referredMakerTrades: 10,
referredTakerTrades: 20,
totalReferredFees: '10.00',
totalReferredUsers: 5,
referredNetProtocolEarnings: '20.00',
firstReferralBlockHeight: '1',
};

export const defaultAffiliateInfo1: AffiliateInfoCreateObject = {
address: defaultAddress2,
affiliateEarnings: '11.00',
referredMakerTrades: 11,
referredTakerTrades: 21,
totalReferredFees: '11.00',
totalReferredUsers: 5,
referredNetProtocolEarnings: '21.00',
firstReferralBlockHeight: '11',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { AffiliateInfoFromDatabase } from '../../src/types';
import { clearData, migrate, teardown } from '../../src/helpers/db-helpers';
import { defaultAffiliateInfo, defaultAffiliateInfo1 } from '../helpers/constants';
import * as AffiliateInfoTable from '../../src/stores/affiliate-info-table';

describe('Affiliate info store', () => {
beforeAll(async () => {
await migrate();
});

afterEach(async () => {
await clearData();
});

afterAll(async () => {
await teardown();
});

it('Successfully creates affiliate info', async () => {
await AffiliateInfoTable.create(defaultAffiliateInfo);
});

it('Cannot create duplicate info for duplicate address', async () => {
await AffiliateInfoTable.create(defaultAffiliateInfo);
await expect(AffiliateInfoTable.create(defaultAffiliateInfo)).rejects.toThrowError();
});

it('Can upsert affiliate info multiple times', async () => {
await AffiliateInfoTable.upsert(defaultAffiliateInfo);
let info: AffiliateInfoFromDatabase | undefined = await AffiliateInfoTable.findById(
defaultAffiliateInfo.address,
);
expect(info).toEqual(expect.objectContaining(defaultAffiliateInfo));

await AffiliateInfoTable.upsert(defaultAffiliateInfo1);
info = await AffiliateInfoTable.findById(defaultAffiliateInfo1.address);
expect(info).toEqual(expect.objectContaining(defaultAffiliateInfo1));
});

it('Successfully finds all affiliate infos', async () => {
await Promise.all([
AffiliateInfoTable.create(defaultAffiliateInfo),
AffiliateInfoTable.create(defaultAffiliateInfo1),
]);

const infos: AffiliateInfoFromDatabase[] = await AffiliateInfoTable.findAll(
{},
[],
{ readReplica: true },
);

expect(infos.length).toEqual(2);
expect(infos).toEqual(expect.arrayContaining([
expect.objectContaining(defaultAffiliateInfo),
expect.objectContaining(defaultAffiliateInfo1),
]));
});

it('Successfully finds an affiliate info', async () => {
await AffiliateInfoTable.create(defaultAffiliateInfo);

const info: AffiliateInfoFromDatabase | undefined = await AffiliateInfoTable.findById(
defaultAffiliateInfo.address,
);

expect(info).toEqual(expect.objectContaining(defaultAffiliateInfo));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,12 @@ describe('AffiliateReferredUsers store', () => {
const entry1: AffiliateReferredUsersCreateObject = {
affiliateAddress: 'affiliate_address1',
refereeAddress: 'referee_address1',
referredAtBlock: 1,
referredAtBlock: '1',
};
const entry2: AffiliateReferredUsersCreateObject = {
affiliateAddress: 'affiliate_address1',
refereeAddress: 'referee_address2',
referredAtBlock: 20,
referredAtBlock: '20',
};

await AffiliateReferredUsersTable.create(entry1);
Expand All @@ -103,12 +103,12 @@ describe('AffiliateReferredUsers store', () => {
const entry1: AffiliateReferredUsersCreateObject = {
affiliateAddress: 'affiliate_address1',
refereeAddress: 'referee_address1',
referredAtBlock: 1,
referredAtBlock: '1',
};
const entry2: AffiliateReferredUsersCreateObject = {
affiliateAddress: 'affiliate_address1',
refereeAddress: 'referee_address2',
referredAtBlock: 20,
referredAtBlock: '20',
};

await AffiliateReferredUsersTable.create(entry1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export async function up(knex: Knex): Promise<void> {
return knex.schema.createTable('affiliate_referred_users', (table) => {
table.string('refereeAddress').primary().notNullable();
table.string('affiliateAddress').notNullable();
table.integer('referredAtBlock').notNullable();
table.bigInteger('referredAtBlock').notNullable();

// Index on affiliateAddress for faster queries
table.index(['affiliateAddress']);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as Knex from 'knex';

export async function up(knex: Knex): Promise<void> {
return knex.schema.createTable('affiliate_info', (table) => {
table.string('address').primary().notNullable();
table.decimal('affiliateEarnings').notNullable();
table.integer('referredMakerTrades').notNullable();
table.integer('referredTakerTrades').notNullable();
table.decimal('totalReferredFees').notNullable();
table.integer('totalReferredUsers').notNullable();
table.decimal('referredNetProtocolEarnings').notNullable();
table.bigInteger('firstReferralBlockHeight').notNullable();
});
}

export async function down(knex: Knex): Promise<void> {
return knex.schema.dropTable('affiliate_info');
}
1 change: 1 addition & 0 deletions indexer/packages/postgres/src/helpers/db-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const layer1Tables = [
'compliance_status',
'affiliate_referred_users',
'persistent_cache',
'affiliate_info',
];

/**
Expand Down
76 changes: 76 additions & 0 deletions indexer/packages/postgres/src/models/affiliate-info-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { NonNegativeNumericPattern } from '../lib/validators';
import UpsertQueryBuilder from '../query-builders/upsert';
import BaseModel from './base-model';

export default class AffiliateInfoModel extends BaseModel {
static get tableName() {
return 'affiliate_info';
}

static get idColumn() {
return 'address';
}

static get jsonSchema() {
return {
type: 'object',
required: [
'address',
'affiliateEarnings',
'referredMakerTrades',
'referredTakerTrades',
'totalReferredFees',
'totalReferredUsers',
'referredNetProtocolEarnings',
'firstReferralBlockHeight',
],
properties: {
address: { type: 'string' },
affiliateEarnings: { type: 'string', pattern: NonNegativeNumericPattern },
referredMakerTrades: { type: 'int' },
referredTakerTrades: { type: 'int' },
totalReferredFees: { type: 'string', pattern: NonNegativeNumericPattern },
totalReferredUsers: { type: 'int' },
referredNetProtocolEarnings: { type: 'string', pattern: NonNegativeNumericPattern },
firstReferralBlockHeight: { 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.
*/
static get sqlToJsonConversions() {
return {
address: 'string',
affiliateEarnings: 'string',
referredMakerTrades: 'int',
referredTakerTrades: 'int',
totalReferredFees: 'string',
totalReferredUsers: 'int',
referredNetProtocolEarnings: 'string',
firstReferralBlockHeight: 'string',
};
}

QueryBuilderType!: UpsertQueryBuilder<this>;

address!: string;

affiliateEarnings!: string;

referredMakerTrades!: number;

referredTakerTrades!: number;

totalReferredFees!: string;

totalReferredUsers!: number;

referredNetProtocolEarnings!: string;

firstReferralBlockHeight!: string;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NonNegativeNumericPattern } from '../lib/validators';
import BaseModel from './base-model';

export default class AffiliateReferredUsersModel extends BaseModel {
Expand All @@ -20,7 +21,7 @@ export default class AffiliateReferredUsersModel extends BaseModel {
properties: {
affiliateAddress: { type: 'string' },
refereeAddress: { type: 'string' },
referredAtBlock: { type: 'integer' },
referredAtBlock: { type: 'string', pattern: NonNegativeNumericPattern },
},
};
}
Expand All @@ -35,13 +36,13 @@ export default class AffiliateReferredUsersModel extends BaseModel {
return {
affiliateAddress: 'string',
refereeAddress: 'string',
referredAtBlock: 'integer',
referredAtBlock: 'string',
};
}

affiliateAddress!: string;

refereeAddress!: string;

referredAtBlock!: number;
referredAtBlock!: string;
}
94 changes: 94 additions & 0 deletions indexer/packages/postgres/src/stores/affiliate-info-table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { QueryBuilder } from 'objection';

import { DEFAULT_POSTGRES_OPTIONS } from '../constants';
import { setupBaseQuery, verifyAllRequiredFields } from '../helpers/stores-helpers';
import Transaction from '../helpers/transaction';
import AffiliateInfoModel from '../models/affiliate-info-model';
import {
Options,
Ordering,
QueryableField,
QueryConfig,
AffiliateInfoColumns,
AffiliateInfoCreateObject,
AffiliateInfoFromDatabase,
AffiliateInfoQueryConfig,
} from '../types';

export async function findAll(
{
address,
limit,
}: AffiliateInfoQueryConfig,
requiredFields: QueryableField[],
options: Options = DEFAULT_POSTGRES_OPTIONS,
): Promise<AffiliateInfoFromDatabase[]> {
verifyAllRequiredFields(
{
address,
limit,
} as QueryConfig,
requiredFields,
);

let baseQuery: QueryBuilder<AffiliateInfoModel> = setupBaseQuery<AffiliateInfoModel>(
AffiliateInfoModel,
options,
);

if (address) {
baseQuery = baseQuery.where(AffiliateInfoColumns.address, address);
}

if (options.orderBy !== undefined) {
for (const [column, order] of options.orderBy) {
baseQuery = baseQuery.orderBy(
column,
order,
);
}
} else {
baseQuery = baseQuery.orderBy(
AffiliateInfoColumns.address,
Ordering.ASC,
);
}

if (limit) {
baseQuery = baseQuery.limit(limit);
}

return baseQuery.returning('*');
}

export async function create(
AffiliateInfoToCreate: AffiliateInfoCreateObject,
options: Options = { txId: undefined },
): Promise<AffiliateInfoFromDatabase> {
return AffiliateInfoModel.query(
Transaction.get(options.txId),
).insert(AffiliateInfoToCreate).returning('*');
}

export async function upsert(
AffiliateInfoToUpsert: AffiliateInfoCreateObject,
options: Options = { txId: undefined },
): Promise<AffiliateInfoFromDatabase> {
const AffiliateInfos: AffiliateInfoModel[] = await AffiliateInfoModel.query(
Transaction.get(options.txId),
).upsert(AffiliateInfoToUpsert).returning('*');
// should only ever be one AffiliateInfo
return AffiliateInfos[0];
}
export async function findById(
address: string,
options: Options = DEFAULT_POSTGRES_OPTIONS,
): Promise<AffiliateInfoFromDatabase | undefined> {
const baseQuery: QueryBuilder<AffiliateInfoModel> = setupBaseQuery<AffiliateInfoModel>(
AffiliateInfoModel,
options,
);
return baseQuery
.findById(address)
.returning('*');
}
Loading

0 comments on commit f4bb592

Please sign in to comment.