Skip to content

Commit

Permalink
Add FirebaseNotificationToken to Postgres
Browse files Browse the repository at this point in the history
This reverts commit 730d099.
  • Loading branch information
adamfraser committed Sep 3, 2024
1 parent e5db10f commit f564fd6
Show file tree
Hide file tree
Showing 11 changed files with 302 additions and 1 deletion.
2 changes: 1 addition & 1 deletion indexer/packages/postgres/__tests__/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ export const defaultAffiliateInfo1: AffiliateInfoCreateObject = {

// ============== Tokens =============

export const defaultToken = {
export const defaultFirebaseNotificationToken = {
token: 'DEFAULT_TOKEN',
address: defaultAddress,
language: 'en',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as MarketTable from '../../src/stores/market-table';
import * as PerpetualMarketTable from '../../src/stores/perpetual-market-table';
import * as SubaccountTable from '../../src/stores/subaccount-table';
import * as TendermintEventTable from '../../src/stores/tendermint-event-table';
import * as TokenTable from '../../src/stores/firebase-notification-token-table';
import * as WalletTable from '../../src/stores/wallet-table';
import {
defaultAsset,
Expand All @@ -26,6 +27,7 @@ import {
defaultTendermintEvent2,
defaultTendermintEvent3,
defaultTendermintEvent4,
defaultFirebaseNotificationToken,
defaultWallet,
isolatedMarket,
isolatedMarket2,
Expand Down Expand Up @@ -78,4 +80,7 @@ export async function seedData() {
await Promise.all([
WalletTable.create(defaultWallet),
]);
await Promise.all([
TokenTable.create(defaultFirebaseNotificationToken),
]);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { FirebaseNotificationTokenFromDatabase } from '../../src/types';
import { clearData, migrate, teardown } from '../../src/helpers/db-helpers';
import { defaultAddress2, defaultFirebaseNotificationToken, defaultWallet } from '../helpers/constants';
import * as FirebaseNotificationTokenTable from '../../src/stores/firebase-notification-token-table';
import * as WalletTable from '../../src/stores/wallet-table';

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

beforeEach(async () => {
// Default wallet is required in the DB for token creation
// As token has a foreign key constraint on wallet
await WalletTable.create(defaultWallet);
});

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

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

it('Successfully creates a Token', async () => {
await FirebaseNotificationTokenTable.create(defaultFirebaseNotificationToken);
const token = await FirebaseNotificationTokenTable.findByToken(defaultFirebaseNotificationToken.token);
expect(token).toEqual(expect.objectContaining(defaultFirebaseNotificationToken));
});

it('Successfully upserts a Token multiple times', async () => {
await FirebaseNotificationTokenTable.upsert(defaultFirebaseNotificationToken);
let token: FirebaseNotificationTokenFromDatabase | undefined = await FirebaseNotificationTokenTable.findByToken(
defaultFirebaseNotificationToken.token,
);

expect(token).toEqual(expect.objectContaining(defaultFirebaseNotificationToken));

// Upsert again to test update functionality
const updatedToken = { ...defaultFirebaseNotificationToken, updatedAt: new Date().toISOString(), language: 'es' };
await FirebaseNotificationTokenTable.upsert(updatedToken);
token = await FirebaseNotificationTokenTable.findByToken(defaultFirebaseNotificationToken.token);

expect(token).toEqual(expect.objectContaining(updatedToken));
});

it('Successfully finds all Tokens', async () => {
await WalletTable.create({ ...defaultWallet, address: defaultAddress2 });
const additionalToken = {
token: 'fake_token',
address: defaultAddress2,
language: 'en',
updatedAt: new Date().toISOString(),
};

await Promise.all([
FirebaseNotificationTokenTable.create(defaultFirebaseNotificationToken),
FirebaseNotificationTokenTable.create(additionalToken),
]);

const tokens: FirebaseNotificationTokenFromDatabase[] = await FirebaseNotificationTokenTable.findAll(
{},
[],
{ readReplica: true },
);

expect(tokens.length).toEqual(2);
expect(tokens[0]).toEqual(expect.objectContaining(defaultFirebaseNotificationToken));
expect(tokens[1]).toEqual(expect.objectContaining(additionalToken));
});

it('Successfully finds a Token by token', async () => {
await FirebaseNotificationTokenTable.create(defaultFirebaseNotificationToken);

const token: FirebaseNotificationTokenFromDatabase | undefined = await FirebaseNotificationTokenTable.findByToken(
defaultFirebaseNotificationToken.token,
);

expect(token).toEqual(expect.objectContaining(defaultFirebaseNotificationToken));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as Knex from 'knex';

export async function up(knex: Knex): Promise<void> {
return knex.schema.createTable('firebase_notification_tokens', (table) => {
table.increments('id').primary();
table.string('token').notNullable().unique();
table.string('address').notNullable();
table.foreign('address').references('wallets.address').onDelete('CASCADE');
table.string('language').notNullable();
table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now());
});
}

export async function down(knex: Knex): Promise<void> {
return knex.schema.dropTable('firebase_notification_tokens');
}
1 change: 1 addition & 0 deletions indexer/packages/postgres/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export * as TradingRewardAggregationTable from './stores/trading-reward-aggregat
export * as LeaderboardPnlTable from './stores/leaderboard-pnl-table';
export * as SubaccountUsernamesTable from './stores/subaccount-usernames-table';
export * as PersistentCacheTable from './stores/persistent-cache-table';
export * as FirebaseNotificationTokenTable from './stores/firebase-notification-token-table';

export * as perpetualMarketRefresher from './loops/perpetual-market-refresher';
export * as assetRefresher from './loops/asset-refresher';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Model } from 'objection';

import { IsoString } from '../types';
import WalletModel from './wallet-model';

class FirebaseNotificationTokenModel extends Model {
static tableName = 'firebase_notification_tokens';

id!: number;
token!: string;
address!: string;
updatedAt!: IsoString;
language!: string;

static relationMappings = {
wallet: {
relation: Model.BelongsToOneRelation,
modelClass: WalletModel,
join: {
from: 'firebase_notification_tokens.address',
to: 'wallets.address',
},
},
};
}

export default FirebaseNotificationTokenModel;
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { DateTime } from 'luxon';
import { PartialModelObject, QueryBuilder } from 'objection';

import { DEFAULT_POSTGRES_OPTIONS } from '../constants';
import { setupBaseQuery, verifyAllRequiredFields } from '../helpers/stores-helpers';
import Transaction from '../helpers/transaction';
import TokenModel from '../models/firebase-notification-token-model';
import {
Options,
Ordering,
QueryableField,
QueryConfig,
FirebaseNotificationTokenColumns,
FirebaseNotificationTokenCreateObject,
FirebaseNotificationTokenFromDatabase,
FirebaseNotificationTokenQueryConfig,
FirebaseNotificationTokenUpdateObject,
} from '../types';

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

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

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

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

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

return baseQuery.returning('*');
}

export async function create(
tokenToCreate: FirebaseNotificationTokenCreateObject,
options: Options = { txId: undefined },
): Promise<FirebaseNotificationTokenFromDatabase> {
return TokenModel.query(
Transaction.get(options.txId),
).insert(tokenToCreate).returning('*');
}

export async function update(
{
token,
...fields
}: FirebaseNotificationTokenUpdateObject,
options: Options = { txId: undefined },
): Promise<FirebaseNotificationTokenFromDatabase> {
const existingToken = await TokenModel.query(
Transaction.get(options.txId),
).findOne({ token });
const updatedToken = await existingToken.$query().patch(fields as PartialModelObject<TokenModel>).returning('*');
return updatedToken as unknown as FirebaseNotificationTokenFromDatabase;
}

export async function upsert(
tokenToUpsert: FirebaseNotificationTokenCreateObject,
options: Options = { txId: undefined },
): Promise<FirebaseNotificationTokenFromDatabase> {
const existingToken = await TokenModel.query(
Transaction.get(options.txId),
).findOne({ token: tokenToUpsert.token });

if (existingToken) {
return update(tokenToUpsert, options);
} else {
return create(tokenToUpsert, options);
}
}

export async function findByToken(
token: string,
options: Options = DEFAULT_POSTGRES_OPTIONS,
): Promise<FirebaseNotificationTokenFromDatabase | undefined> {
const baseQuery: QueryBuilder<TokenModel> = setupBaseQuery<TokenModel>(
TokenModel,
options,
);
return baseQuery
.findOne({ token })
.returning('*');
}

export async function registerToken(
token: string,
address: string,
language: string,
options: Options = { txId: undefined },
): Promise<FirebaseNotificationTokenFromDatabase> {
return upsert(
{
token,
address,
updatedAt: DateTime.now().toISO(),
language,
},
options,
);
}
7 changes: 7 additions & 0 deletions indexer/packages/postgres/src/types/db-model-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ export interface AffiliateReferredUserFromDatabase {
referredAtBlock: string,
}

export interface FirebaseNotificationTokenFromDatabase {
address: WalletFromDatabase['address'],
token: string,
updatedAt: IsoString,
language: string,
}

export type SubaccountAssetNetTransferMap = { [subaccountId: string]:
{ [assetId: string]: string }, };
export type SubaccountToPerpetualPositionsMap = { [subaccountId: string]:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* ------- TOKEN TYPES ------- */

type IsoString = string;

export interface FirebaseNotificationTokenCreateObject {
token: string,
address: string,
language: string,
updatedAt: IsoString,
}

export interface FirebaseNotificationTokenUpdateObject {
token: string,
address: string,
language: string,
updatedAt: IsoString,
}

export enum FirebaseNotificationTokenColumns {
token = 'token',
address = 'address',
language = 'language',
updatedAt = 'updatedAt',
}
1 change: 1 addition & 0 deletions indexer/packages/postgres/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ export * from './leaderboard-pnl-types';
export * from './affiliate-referred-users-types';
export * from './persistent-cache-types';
export * from './affiliate-info-types';
export * from './firebase-notification-token-types';
export { PositionSide } from './position-types';
6 changes: 6 additions & 0 deletions indexer/packages/postgres/src/types/query-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export enum QueryableField {
REFEREE_ADDRESS = 'refereeAddress',
KEY = 'key',
IS_WHITELIST_AFFILIATE = 'isWhitelistAffiliate',
TOKEN = 'token',
}

export interface QueryConfig {
Expand Down Expand Up @@ -339,3 +340,8 @@ export interface PersistentCacheQueryConfig extends QueryConfig {
export interface AffiliateInfoQueryConfig extends QueryConfig {
[QueryableField.ADDRESS]?: string,
}

export interface FirebaseNotificationTokenQueryConfig extends QueryConfig {
[QueryableField.ADDRESS]?: string;
[QueryableField.TOKEN]?: string;
}

0 comments on commit f564fd6

Please sign in to comment.