-
Notifications
You must be signed in to change notification settings - Fork 129
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CT-1040] Push Notifications (4 of 4) - Send Notifications From Ender (…
- Loading branch information
1 parent
51e6d2f
commit 5acd685
Showing
6 changed files
with
258 additions
and
1 deletion.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
155 changes: 155 additions & 0 deletions
155
indexer/services/ender/__tests__/helpers/notification-functions.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import { | ||
sendOrderFilledNotification, | ||
sendOrderTriggeredNotification, | ||
} from '../../src/helpers/notifications/notifications-functions'; | ||
import { | ||
dbHelpers, | ||
OrderFromDatabase, | ||
PerpetualMarketFromDatabase, | ||
PerpetualMarketStatus, | ||
PerpetualMarketType, | ||
SubaccountTable, | ||
testMocks, | ||
} from '@dydxprotocol-indexer/postgres'; | ||
|
||
import { | ||
createNotification, | ||
sendFirebaseMessage, | ||
NotificationType, | ||
} from '@dydxprotocol-indexer/notifications'; | ||
import { | ||
defaultSubaccountId, | ||
defaultMarket, | ||
defaultFirebaseNotificationToken, | ||
} from '@dydxprotocol-indexer/postgres/build/__tests__/helpers/constants'; | ||
|
||
// Mock only the sendFirebaseMessage function | ||
jest.mock('@dydxprotocol-indexer/notifications', () => { | ||
const actualModule = jest.requireActual('@dydxprotocol-indexer/notifications'); | ||
return { | ||
...actualModule, // keep all other exports intact | ||
sendFirebaseMessage: jest.fn(), | ||
createNotification: jest.fn(), | ||
}; | ||
}); | ||
|
||
const mockMarket: PerpetualMarketFromDatabase = { | ||
id: '1', | ||
clobPairId: '1', | ||
ticker: 'BTC-USD', | ||
marketId: 1, | ||
status: PerpetualMarketStatus.ACTIVE, | ||
priceChange24H: '0', | ||
volume24H: '0', | ||
trades24H: 0, | ||
nextFundingRate: '0', | ||
openInterest: '0', | ||
quantumConversionExponent: 1, | ||
atomicResolution: 1, | ||
subticksPerTick: 1, | ||
stepBaseQuantums: 1, | ||
liquidityTierId: 1, | ||
marketType: PerpetualMarketType.ISOLATED, | ||
baseOpenInterest: '0', | ||
}; | ||
|
||
describe('notification functions', () => { | ||
beforeEach(async () => { | ||
await testMocks.seedData(); | ||
}); | ||
|
||
afterEach(async () => { | ||
await dbHelpers.clearData(); | ||
}); | ||
describe('sendOrderFilledNotification', () => { | ||
it('should create and send an order filled notification', async () => { | ||
const mockOrder: OrderFromDatabase = { | ||
id: '1', | ||
subaccountId: defaultSubaccountId, | ||
clientId: '1', | ||
clobPairId: String(defaultMarket.id), | ||
side: 'BUY', | ||
size: '10', | ||
totalFilled: '0', | ||
price: '100.50', | ||
type: 'LIMIT', | ||
status: 'OPEN', | ||
timeInForce: 'GTT', | ||
reduceOnly: false, | ||
orderFlags: '0', | ||
goodTilBlock: '1000000', | ||
createdAtHeight: '900000', | ||
clientMetadata: '0', | ||
triggerPrice: undefined, | ||
updatedAt: new Date().toISOString(), | ||
updatedAtHeight: '900001', | ||
} as OrderFromDatabase; | ||
|
||
await sendOrderFilledNotification(mockOrder, mockMarket); | ||
|
||
// Assert that createNotification was called with correct arguments | ||
expect(createNotification).toHaveBeenCalledWith( | ||
NotificationType.ORDER_FILLED, | ||
{ | ||
AMOUNT: '10', | ||
MARKET: 'BTC-USD', | ||
AVERAGE_PRICE: '100.50', | ||
}, | ||
); | ||
|
||
expect(sendFirebaseMessage).toHaveBeenCalledWith( | ||
[ | ||
expect.objectContaining({ | ||
token: defaultFirebaseNotificationToken.token, | ||
language: defaultFirebaseNotificationToken.language, | ||
}), | ||
], | ||
undefined, | ||
); | ||
}); | ||
|
||
describe('sendOrderTriggeredNotification', () => { | ||
it('should create and send an order triggered notification', async () => { | ||
const subaccount = await SubaccountTable.findById(defaultSubaccountId); | ||
const mockOrder: OrderFromDatabase = { | ||
id: '1', | ||
subaccountId: subaccount!.id, | ||
clientId: '1', | ||
clobPairId: '1', | ||
side: 'BUY', | ||
size: '10', | ||
price: '100.50', | ||
type: 'LIMIT', | ||
status: 'OPEN', | ||
timeInForce: 'GTT', | ||
reduceOnly: false, | ||
orderFlags: '0', | ||
goodTilBlock: '1000000', | ||
createdAtHeight: '900000', | ||
clientMetadata: '0', | ||
triggerPrice: '99.00', | ||
updatedAt: new Date().toISOString(), | ||
updatedAtHeight: '900001', | ||
} as OrderFromDatabase; | ||
|
||
await sendOrderTriggeredNotification(mockOrder, mockMarket, subaccount!); | ||
|
||
expect(createNotification).toHaveBeenCalledWith( | ||
NotificationType.ORDER_TRIGGERED, | ||
{ | ||
MARKET: 'BTC-USD', | ||
PRICE: '100.50', | ||
AMOUNT: '10', | ||
}, | ||
); | ||
|
||
expect(sendFirebaseMessage).toHaveBeenCalledWith([expect.objectContaining( | ||
{ | ||
token: defaultFirebaseNotificationToken.token, | ||
language: defaultFirebaseNotificationToken.language, | ||
}, | ||
)], undefined); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
indexer/services/ender/src/helpers/notifications/notifications-functions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { logger, stats } from '@dydxprotocol-indexer/base'; | ||
import { | ||
createNotification, | ||
NotificationDynamicFieldKey, | ||
NotificationType, | ||
sendFirebaseMessage, | ||
} from '@dydxprotocol-indexer/notifications'; | ||
import { | ||
OrderFromDatabase, | ||
PerpetualMarketFromDatabase, | ||
SubaccountFromDatabase, | ||
SubaccountTable, | ||
FirebaseNotificationTokenTable, | ||
} from '@dydxprotocol-indexer/postgres'; | ||
|
||
import config from '../../config'; | ||
|
||
export async function sendOrderFilledNotification( | ||
order: OrderFromDatabase, | ||
market: PerpetualMarketFromDatabase, | ||
) { | ||
const start = Date.now(); | ||
try { | ||
const subaccount = await SubaccountTable.findById(order.subaccountId); | ||
if (!subaccount) { | ||
throw new Error(`Subaccount not found for id ${order.subaccountId}`); | ||
} | ||
|
||
const tokens = (await FirebaseNotificationTokenTable.findAll( | ||
{ address: subaccount.address }, []) | ||
); | ||
if (tokens.length === 0) { | ||
return; | ||
} | ||
|
||
const notification = createNotification( | ||
NotificationType.ORDER_FILLED, | ||
{ | ||
[NotificationDynamicFieldKey.AMOUNT]: order.size.toString(), | ||
[NotificationDynamicFieldKey.MARKET]: market.ticker, | ||
[NotificationDynamicFieldKey.AVERAGE_PRICE]: order.price, | ||
}, | ||
); | ||
|
||
await sendFirebaseMessage(tokens, notification); | ||
} catch (error) { | ||
logger.error({ | ||
at: 'ender#notification-functions', | ||
message: 'Error sending order filled notification', | ||
error, | ||
}); | ||
} finally { | ||
stats.timing(`${config.SERVICE_NAME}.send_order_filled_notification.timing`, Date.now() - start); | ||
} | ||
} | ||
|
||
export async function sendOrderTriggeredNotification( | ||
order: OrderFromDatabase, | ||
market: PerpetualMarketFromDatabase, | ||
subaccount: SubaccountFromDatabase, | ||
) { | ||
const start = Date.now(); | ||
try { | ||
const tokens = (await FirebaseNotificationTokenTable.findAll( | ||
{ address: subaccount.address }, [], | ||
)); | ||
if (tokens.length === 0) { | ||
return; | ||
} | ||
const notification = createNotification( | ||
NotificationType.ORDER_TRIGGERED, | ||
{ | ||
[NotificationDynamicFieldKey.MARKET]: market.ticker, | ||
[NotificationDynamicFieldKey.PRICE]: order.price, | ||
[NotificationDynamicFieldKey.AMOUNT]: order.size.toString(), | ||
}, | ||
); | ||
await sendFirebaseMessage(tokens, notification); | ||
} catch (error) { | ||
logger.error({ | ||
at: 'ender#notification-functions', | ||
message: 'Error sending order triggered notification', | ||
error, | ||
}); | ||
} finally { | ||
stats.timing(`${config.SERVICE_NAME}.send_order_triggered_notification.timing`, Date.now() - start); | ||
} | ||
} |