Skip to content

Commit

Permalink
Add language to Token table
Browse files Browse the repository at this point in the history
  • Loading branch information
adamfraser committed Aug 22, 2024
1 parent ccb1f77 commit 48b1be9
Show file tree
Hide file tree
Showing 21 changed files with 138 additions and 41 deletions.
10 changes: 8 additions & 2 deletions indexer/packages/notifications/__tests__/message.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ describe('sendFirebaseMessage', () => {
});

it('should send a Firebase message successfully', async () => {
await sendFirebaseMessage([defaultToken.token], mockNotification);
await sendFirebaseMessage(
[{ token: defaultToken.token, language: defaultToken.language }],
mockNotification,
);

expect(sendMulticast).toHaveBeenCalled();
expect(logger.info).toHaveBeenCalledWith(expect.objectContaining({
Expand All @@ -63,7 +66,10 @@ describe('sendFirebaseMessage', () => {
const mockedSendMulticast = sendMulticast as jest.MockedFunction<typeof sendMulticast>;
mockedSendMulticast.mockRejectedValueOnce(new Error('Send failed'));

await sendFirebaseMessage([defaultToken.token], mockNotification);
await sendFirebaseMessage(
[{ token: defaultToken.token, language: defaultToken.language }],
mockNotification,
);

expect(logger.error).toHaveBeenCalledWith(expect.objectContaining({
message: 'Failed to send Firebase message',
Expand Down
2 changes: 1 addition & 1 deletion indexer/packages/notifications/src/localizedMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ export const LOCALIZED_MESSAGES: Record<LanguageCode, Record<LocalizationKey, st
[LocalizationKey.ORDER_TRIGGERED_TITLE]: '{MARKET} 订单已触发',
[LocalizationKey.ORDER_TRIGGERED_BODY]: '您的 {AMOUNT} {MARKET} 订单已在 ${PRICE} 触发',
},
};
};
12 changes: 8 additions & 4 deletions indexer/packages/notifications/src/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import {
sendMulticast,
} from './lib/firebase';
import { deriveLocalizedNotificationMessage } from './localization';
import { Notification } from './types';
import { LanguageCode, Notification } from './types';

export async function sendFirebaseMessage(
tokens: string[],
tokens: {token: string, language: string}[],
notification: Notification,
): Promise<void> {
// Re-add once stats are implemented
Expand All @@ -24,11 +24,15 @@ export async function sendFirebaseMessage(
return;
}

const { title, body } = deriveLocalizedNotificationMessage(notification);
const language = tokens[0].language;
const { title, body } = deriveLocalizedNotificationMessage(
notification,
language as LanguageCode,
);
const link = notification.deeplink;

const message: MulticastMessage = {
tokens,
tokens: tokens.map((token) => token.token),
notification: {
title,
body,
Expand Down
2 changes: 1 addition & 1 deletion indexer/packages/notifications/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,4 @@ export function createNotification<T extends NotificationType>(
default:
throw new Error('Unknown notification type');
}
}
}
1 change: 1 addition & 0 deletions indexer/packages/postgres/__tests__/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,5 +902,6 @@ export const defaultLeaderboardPnlOneDayToUpsert: LeaderboardPnlCreateObject = {
export const defaultToken = {
token: 'DEFAULT_TOKEN',
address: defaultAddress,
language: 'en',
updatedAt: createdDateTime.toISO(),
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('Token store', () => {
expect(token).toEqual(expect.objectContaining(defaultToken));

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

Expand All @@ -50,6 +50,7 @@ describe('Token store', () => {
const additionalToken = {
token: 'fake_token',
address: defaultAddress2,
language: 'en',
updatedAt: new Date().toISOString(),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export async function up(knex: Knex): Promise<void> {
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());
});
}
Expand Down
1 change: 1 addition & 0 deletions indexer/packages/postgres/src/models/token-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class TokenModel extends Model {
token!: string;
address!: string;
updatedAt!: IsoString;
language!: string;

static relationMappings = {
wallet: {
Expand Down
2 changes: 2 additions & 0 deletions indexer/packages/postgres/src/stores/token-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,15 @@ export async function findByToken(
export async function registerToken(
token: string,
address: string,
language: string,
options: Options = { txId: undefined },
): Promise<TokenFromDatabase> {
return upsert(
{
token,
address,
updatedAt: DateTime.now().toISO(),
language,
},
options,
);
Expand Down
1 change: 1 addition & 0 deletions indexer/packages/postgres/src/types/db-model-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ export interface TokenFromDatabase {
address: WalletFromDatabase['address'],
token: string,
updatedAt: IsoString,
language: string,
}

export type SubaccountAssetNetTransferMap = { [subaccountId: string]:
Expand Down
3 changes: 3 additions & 0 deletions indexer/packages/postgres/src/types/token-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ type IsoString = string;
export interface TokenCreateObject {
token: string,
address: string,
language: string,
updatedAt: IsoString,
}

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

export enum TokenColumns {
token = 'token',
address = 'address',
language = 'language',
updatedAt = 'updatedAt',
}
Original file line number Diff line number Diff line change
Expand Up @@ -579,10 +579,11 @@ describe('addresses-controller#V4', () => {
describe('/:address/registerToken', () => {
it('Post /:address/registerToken with valid params returns 200', async () => {
const token = 'validToken';
const language = 'en';
const response: request.Response = await sendRequest({
type: RequestMethod.POST,
path: `/v4/addresses/${testConstants.defaultAddress}/registerToken`,
body: { token },
body: { token, language },
expectedStatus: 200,
});

Expand All @@ -596,14 +597,15 @@ describe('addresses-controller#V4', () => {
it('Post /:address/registerToken with valid params calls TokenTable registerToken', async () => {
jest.spyOn(TokenTable, 'registerToken');
const token = 'validToken';
const language = 'en';
await sendRequest({
type: RequestMethod.POST,
path: `/v4/addresses/${testConstants.defaultAddress}/registerToken`,
body: { token },
body: { token, language },
expectedStatus: 200,
});
expect(TokenTable.registerToken).toHaveBeenCalledWith(
token, testConstants.defaultAddress,
token, testConstants.defaultAddress, language,
);
expect(stats.increment).toHaveBeenCalledWith('comlink.addresses-controller.response_status_code.200', 1, {
path: '/:address/registerToken',
Expand Down Expand Up @@ -633,22 +635,46 @@ describe('addresses-controller#V4', () => {
});
});

it('Post /:address/registerToken with no token in body returns 400', async () => {
const token = '';
it.each([
['validToken', '', 'Invalid language code', 'language'],
['validToken', 'qq', 'Invalid language code', 'language'],
])('Post /:address/registerToken with bad language params returns 400', async (token, language, errorMsg, errorParam) => {
const response: request.Response = await sendRequest({
type: RequestMethod.POST,
path: `/v4/addresses/${testConstants.defaultAddress}/registerToken`,
body: { token },
body: { token, language },
expectedStatus: 400,
});

expect(response.body).toEqual({
errors: [
{
location: 'body',
msg: errorMsg,
param: errorParam,
value: language,
},
],
});
});

it.each([
['', 'en', 'Token cannot be empty', 'token'],
])('Post /:address/registerToken with bad token params returns 400', async (token, language, errorMsg, errorParam) => {
const response: request.Response = await sendRequest({
type: RequestMethod.POST,
path: `/v4/addresses/${testConstants.defaultAddress}/registerToken`,
body: { token, language },
expectedStatus: 400,
});

expect(response.body).toEqual({
errors: [
{
location: 'body',
msg: 'Token cannot be empty',
param: 'token',
value: '',
msg: errorMsg,
param: errorParam,
value: token,
},
],
});
Expand Down
3 changes: 3 additions & 0 deletions indexer/services/comlink/public/api-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ print(r.json())

```javascript
const inputBody = '{
"language": "string",
"token": "string"
}';
const headers = {
Expand Down Expand Up @@ -485,6 +486,7 @@ fetch(`${baseURL}/addresses/{address}/registerToken`,
```json
{
"language": "string",
"token": "string"
}
```
Expand All @@ -495,6 +497,7 @@ fetch(`${baseURL}/addresses/{address}/registerToken`,
|---|---|---|---|---|
|address|path|string|true|none|
|body|body|object|true|none|
|» language|body|string|true|none|
|» token|body|string|true|none|

### Responses
Expand Down
4 changes: 4 additions & 0 deletions indexer/services/comlink/public/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1488,11 +1488,15 @@
"application/json": {
"schema": {
"properties": {
"language": {
"type": "string"
},
"token": {
"type": "string"
}
},
"required": [
"language",
"token"
],
"type": "object"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
import { getReqRateLimiter } from '../../../caches/rate-limiters';
import config from '../../../config';
import { complianceAndGeoCheck } from '../../../lib/compliance-and-geo-check';
import { BadRequestError, DatabaseError, NotFoundError } from '../../../lib/errors';
import { DatabaseError, NotFoundError } from '../../../lib/errors';
import {
adjustUSDCAssetPosition,
calculateEquityAndFreeCollateral,
Expand Down Expand Up @@ -362,13 +362,9 @@ class AddressesController extends Controller {
@Post('/:address/registerToken')
public async registerToken(
@Path() address: string,
@Body() body: { token: string },
@Body() body: { token: string, language: string },
): Promise<void> {
const { token } = body;
if (!token) {
throw new BadRequestError('Invalid Token in request');
}

const { token, language } = body;
const foundAddress = await WalletTable.findById(address);
if (!foundAddress) {
throw new NotFoundError(`No address found with address: ${address}`);
Expand All @@ -378,6 +374,7 @@ class AddressesController extends Controller {
await TokenTable.registerToken(
token,
address,
language,
);
} catch (error) {
throw new DatabaseError(`Error registering token: ${error}`);
Expand All @@ -393,8 +390,7 @@ class AddressesController extends Controller {
if (!wallet) {
throw new NotFoundError(`No wallet found for address: ${address}`);
}
const allTokens = await TokenTable.findAll({ address: wallet.address }, [])
.then((tokens) => tokens.map((token) => token.token));
const allTokens = await TokenTable.findAll({ address: wallet.address }, []);
if (allTokens.length === 0) {
throw new NotFoundError(`No tokens found for address: ${address}`);
}
Expand Down Expand Up @@ -548,11 +544,12 @@ router.post(
handleValidationErrors,
ExportResponseCodeStats({ controllerName }),
async (req: express.Request, res: express.Response) => {
const { address, token } = matchedData(req) as RegisterTokenRequest;
const start: number = Date.now();
const { address, token, language = 'en' } = matchedData(req) as RegisterTokenRequest;

try {
const controller: AddressesController = new AddressesController();
await controller.registerToken(address, { token });
await controller.registerToken(address, { token, language });
return res.status(200).send({});
} catch (error) {
return handleControllerError(
Expand All @@ -562,6 +559,11 @@ router.post(
req,
res,
);
} finally {
stats.timing(
`${config.SERVICE_NAME}.${controllerName}.post_registerToken.timing`,
Date.now() - start,
);
}
},
);
Expand Down
7 changes: 7 additions & 0 deletions indexer/services/comlink/src/lib/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,10 @@ export class DatabaseError extends Error {
this.name = 'DatabaseError';
}
}

export class InvalidParamError extends Error {
constructor(message: string) {
super(message);
this.name = 'NotFoundError';
}
}
16 changes: 15 additions & 1 deletion indexer/services/comlink/src/lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
Risk,
} from '../types';
import { ZERO, ZERO_USDC_POSITION } from './constants';
import { NotFoundError } from './errors';
import { InvalidParamError, NotFoundError } from './errors';

/* ------- GENERIC HELPERS ------- */

Expand All @@ -57,6 +57,9 @@ export function handleControllerError(
if (error instanceof NotFoundError) {
return handleNotFoundError(error.message, res);
}
if (error instanceof InvalidParamError) {
return handleInvalidParamError(error.message, res);
}
return handleInternalServerError(
at,
message,
Expand Down Expand Up @@ -89,6 +92,17 @@ function handleInternalServerError(
return createInternalServerErrorResponse(res);
}

function handleInvalidParamError(
message: string,
res: express.Response,
): express.Response {
return res.status(400).json({
errors: [{
msg: message,
}],
});
}

function handleNotFoundError(
message: string,
res: express.Response,
Expand Down
Loading

0 comments on commit 48b1be9

Please sign in to comment.