diff --git a/history/src/app/history/index.ts b/history/src/app/history/index.ts index 24398cc..ab52823 100644 --- a/history/src/app/history/index.ts +++ b/history/src/app/history/index.ts @@ -1,2 +1,3 @@ -export * from "./get-price-history" -export * from "./update-price-history" +export * from "./get-price-history"; +export * from "./update-price-history"; +export * from "./notify-price-change"; diff --git a/history/src/app/history/index.types.d.ts b/history/src/app/history/index.types.d.ts index 24def67..e467455 100644 --- a/history/src/app/history/index.types.d.ts +++ b/history/src/app/history/index.types.d.ts @@ -1,4 +1,8 @@ type GetPriceHistoryArgs = { - currency: string - range?: string -} + currency: string; + range?: string; +}; + +type NotifyPriceChangeArgs = { + range?: PriceRange; +}; diff --git a/history/src/app/history/notify-price-change.ts b/history/src/app/history/notify-price-change.ts new file mode 100644 index 0000000..e16b19c --- /dev/null +++ b/history/src/app/history/notify-price-change.ts @@ -0,0 +1,32 @@ +import { defaultBaseCurrency } from "@config"; +import { PriceRange } from "@domain/price"; +import { checkedToCurrency } from "@domain/primitives"; +import { PriceRepository } from "@services/database"; +import { NotificationsService } from "@services/notifications"; + +export const notifyPriceChange = async ({ range }: NotifyPriceChangeArgs) => { + const quote = checkedToCurrency("USD"); + + if (quote instanceof Error) { + return quote; + } + + const rangeToQuery = range || PriceRange.OneDay; + const prices = await PriceRepository().listPrices({ + base: defaultBaseCurrency, + quote, + range: rangeToQuery, + }); + + if (prices instanceof Error) { + return prices; + } + + const notificationsService = NotificationsService(); + + return notificationsService.priceChanged({ + initialPrice: prices[0], + finalPrice: prices[prices.length - 1], + range: rangeToQuery, + }); +}; diff --git a/history/src/app/index.ts b/history/src/app/index.ts index c740feb..c733cde 100644 --- a/history/src/app/index.ts +++ b/history/src/app/index.ts @@ -1,18 +1,18 @@ -import { wrapAsyncToRunInSpan } from "@services/tracing" +import { wrapAsyncToRunInSpan } from "@services/tracing"; -import * as HistoryMod from "./history" +import * as HistoryMod from "./history"; const allFunctions = { History: { ...HistoryMod }, -} +}; for (const subModule in allFunctions) { for (const fn in allFunctions[subModule]) { allFunctions[subModule][fn] = wrapAsyncToRunInSpan({ namespace: `app.${subModule.toLowerCase()}`, fn: allFunctions[subModule][fn], - }) + }); } } -export const { History } = allFunctions +export const { History } = allFunctions; diff --git a/history/src/domain/notifications/errors.ts b/history/src/domain/notifications/errors.ts new file mode 100644 index 0000000..5ebfa23 --- /dev/null +++ b/history/src/domain/notifications/errors.ts @@ -0,0 +1,6 @@ +import { ErrorLevel, ServiceError } from "../errors"; + +export class NotificationsServiceError extends ServiceError {} +export class UnknownNotificationServiceError extends NotificationsServiceError { + level = ErrorLevel.Critical; +} diff --git a/history/src/domain/notifications/index.ts b/history/src/domain/notifications/index.ts new file mode 100644 index 0000000..49bbc16 --- /dev/null +++ b/history/src/domain/notifications/index.ts @@ -0,0 +1 @@ +export * from "./errors"; diff --git a/history/src/domain/notifications/index.types.d.ts b/history/src/domain/notifications/index.types.d.ts new file mode 100644 index 0000000..ea1bf14 --- /dev/null +++ b/history/src/domain/notifications/index.types.d.ts @@ -0,0 +1,13 @@ +type NotificationsServiceError = import("./errors").NotificationsServiceError; + +interface INotificationsService { + priceChanged( + args: PriceChangedArgs + ): Promise; +} + +type PriceChangedArgs = { + range: PriceRange; + initialPrice: Tick; + finalPrice: Tick; +}; diff --git a/history/src/servers/history/cron.ts b/history/src/servers/history/cron.ts index 6f4065b..65d42a9 100644 --- a/history/src/servers/history/cron.ts +++ b/history/src/servers/history/cron.ts @@ -1,16 +1,17 @@ -import dotenv from "dotenv" +import dotenv from "dotenv"; -import { History } from "@app" +import { History } from "@app"; -import { closeDbConnections } from "@services/database" +import { closeDbConnections } from "@services/database"; -dotenv.config() +dotenv.config(); const startServer = async () => { - await History.updatePriceHistory() - await closeDbConnections() -} + await History.updatePriceHistory(); + await History.notifyPriceChange({}); + await closeDbConnections(); +}; if (require.main === module) { - startServer() + startServer(); } diff --git a/history/src/services/notifications/index.ts b/history/src/services/notifications/index.ts new file mode 100644 index 0000000..d84539f --- /dev/null +++ b/history/src/services/notifications/index.ts @@ -0,0 +1,12 @@ +export const NotificationsService = (): INotificationsService => { + const priceChanged = async ( + args: PriceChangedArgs + ): Promise => { + const priceChangedEvent = createPriceChangedEvent(args); + + // Send event to notification service + }; + return { + priceChanged, + }; +}; diff --git a/history/src/services/notifications/price-change-event.ts b/history/src/services/notifications/price-change-event.ts new file mode 100644 index 0000000..c3dfefb --- /dev/null +++ b/history/src/services/notifications/price-change-event.ts @@ -0,0 +1,22 @@ +const PriceDirection = { + Increase: "Increase", + Decrease: "Decrease", +} as const; + +type PriceDirection = (typeof PriceDirection)[keyof typeof PriceDirection]; + +type PriceChangedEvent = { + currentPriceInUsd: string; + priceChangeDirection: PriceDirection; + priceChangeInBips: string; + timeRange: PriceRange; + timestamp: string; +}; + +const createPriceChangedEvent = ({ + range, + initialPrice, + finalPrice, +}: PriceChangedArgs): PriceChangedEvent => { + throw new Error("Method not implemented."); +};