-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
core: Onchain Price observation (#4)
* onchain price state initialisation * onchain price update listener added * added ticker routes to api * linter fixes * socket support added * added price listeners on app state init
- Loading branch information
Showing
19 changed files
with
469 additions
and
17 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Route definition declarations guide | ||
|
||
In order to better and/or more strictly type the codebase, every new route | ||
will have to follow these rules: | ||
|
||
1. Every route in /routes folder should have corresponding file inside /definitions | ||
named the same only with .d (definitions) before the .ts file type ending | ||
|
||
2. Inside the routes definitions file, there should be a namespace named the | ||
same as the class inside the routes folder which is being typed. The same | ||
namespace is the default export of that file. | ||
There should also be an enum in which every route/method is defined | ||
|
||
3. Every namespace should implement next generics: | ||
+ `ResponseBody<T extends ENameOfTheRoute>` - Returns specified routes response body | ||
+ `RequestBody<T extends ENameOfTheRoute>` - Returns specified routes request body | ||
+ `RequestQueries<T extends ENameOfTheRoute>` - Returns specified routes query params | ||
+ `RequestParams<T extends ENameOfTheRoute>` - Returns specified routes request params | ||
+ `Response<T extends ENameOfTheRoute>` - Returns expressjs response object | ||
+ `Request<T extends ENameOfTheRoute>` - Returns expressjs request object | ||
+ `RouteMethod<T extends ENameOfTheRoute>` - Returns typization for next function | ||
|
||
Every generic should return default empty object if route wasn't implemented | ||
|
||
4. The naming convention is PascalCase/UpperCamelCase and it should look something like this: | ||
- For namespace name | ||
+ `{ROUTE_NAME}Definitions` - AuthRouteDefinitions | ||
- For enum name | ||
+ `E{ROUTE_NAME}` - EAuthRoute | ||
- For enum values | ||
+ `{HTTP_METHOD}{ROUTE_NAME}` - GetMe or PostAuthGithub |
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,48 @@ | ||
import { EmptyObject, ParamsDictionary } from "../../../types/util.types"; | ||
import { TickerEntity } from "../../../entities/ticker.entity"; | ||
import { IResponseSuccess } from "../../../utils/response.util"; | ||
import { NextFunction, Request as ExpressRequest, Response as ExpressResponse } from "express"; | ||
|
||
|
||
export enum ETickersRoute { | ||
GetTickerList = "GetTickerList", | ||
GetTicker = "GetTicker", | ||
GetTickerHistory = "GetTickerHistory" | ||
} | ||
|
||
declare namespace TickersRouteDefinitions { | ||
type ResponseBody<T extends ETickersRoute> = | ||
// GET /tickers/[ticker] | ||
T extends ETickersRoute.GetTicker ? TickerEntity : | ||
// GET /tickers | ||
T extends ETickersRoute.GetTickerList ? TickerEntity[] : | ||
// GET /tickers/[ticker]/history | ||
T extends ETickersRoute.GetTickerHistory ? EmptyObject : | ||
EmptyObject | ||
|
||
type RequestBody<T extends ETickersRoute> = // eslint-disable-line @typescript-eslint/no-unused-vars | ||
EmptyObject; | ||
|
||
type RequestQueries<T extends ETickersRoute> = // eslint-disable-line @typescript-eslint/no-unused-vars | ||
EmptyObject | ||
|
||
type RequestParams<T extends ETickersRoute> = | ||
// GET /tickers/[ticker] | ||
T extends ETickersRoute.GetTicker ? TickerParams : | ||
// GET /tickers/[ticker]/history | ||
T extends ETickersRoute.GetTickerHistory ? TickerParams : | ||
EmptyObject; | ||
|
||
type Response<T extends ETickersRoute> = ExpressResponse<IResponseSuccess<ResponseBody<T>>> | ||
|
||
type Request<T extends ETickersRoute> = ExpressRequest<RequestParams<T> & ParamsDictionary, IResponseSuccess<ResponseBody<T>>, RequestBody<T>, RequestQueries<T>> | ||
|
||
type RouteMethod<T extends ETickersRoute> = (request: Request<T>, response: Response<T>, next: NextFunction) => Promise<any>; | ||
|
||
// PARAMS | ||
type TickerParams = { | ||
tickerSymbol: string; | ||
} | ||
} | ||
|
||
export default TickersRouteDefinitions; |
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 |
---|---|---|
@@ -1,8 +1,16 @@ | ||
import { Router } from "express"; | ||
import { error } from "./middlewares/error.middleware"; | ||
import StatusRoute from "./routes/status.route"; | ||
import TickersRoute from "./routes/tickers.route"; | ||
import TickersValidator from "./validators/tickers.validator"; | ||
|
||
const v1 = Router(); | ||
|
||
v1.get("/tickers/", TickersRoute.getTickerList); | ||
v1.get("/tickers/:tickerSymbol", TickersValidator.validateGetTicker, TickersRoute.getTicker); | ||
|
||
v1.get("/status", StatusRoute.getStatus); | ||
|
||
v1.use(error); | ||
|
||
export default v1; |
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,31 @@ | ||
import { NextFunction, Request, Response } from "express"; | ||
import { | ||
CustomValidationError, NotFoundError, InvalidRequestError | ||
} from "../../../utils/errors.util"; | ||
import { APIResponse } from "../../../utils/response.util"; | ||
import logger from "../../../utils/logger.util"; | ||
|
||
export async function error(error: Error, request: Request, response: Response, next: NextFunction) { // eslint-disable-line @typescript-eslint/no-unused-vars | ||
logger.error(error); | ||
|
||
let status: number; | ||
let message: string; | ||
let errors: any | undefined; | ||
|
||
if (error instanceof NotFoundError) { | ||
status = 404; | ||
message = error.message || "Not found"; | ||
} else if (error instanceof InvalidRequestError) { | ||
status = 400; | ||
message = error.message || "Invalid request"; | ||
} else if (error instanceof CustomValidationError) { | ||
status = 422; | ||
message = error.message || "Validation error"; | ||
errors = error.err; | ||
} else { | ||
status = 500; | ||
message = "Server error"; | ||
} | ||
|
||
return response.status(status).json(APIResponse.error(status, message, errors)); | ||
} |
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,19 @@ | ||
import { ValidationChain, validationResult } from "express-validator"; | ||
import { NextFunction, RequestHandler } from "express"; | ||
import { CustomValidationError } from "../../../utils/errors.util"; | ||
|
||
async function validateRequest(request?: Request, response?: Response, next?: NextFunction) { | ||
const result = validationResult(request); | ||
const errors = result.mapped(); | ||
|
||
if (!result.isEmpty()) { | ||
return next(new CustomValidationError(errors)); | ||
} else { | ||
return next(); | ||
} | ||
} | ||
|
||
export function val(validationChain: ValidationChain[]): RequestHandler[] { | ||
// @ts-expect-error Type mismatch for validationChain, but actually is fitting | ||
return [...validationChain, validateRequest]; | ||
} |
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,41 @@ | ||
import TickersRouteDefinitions from "../definitions/tickers.route"; | ||
import { ETickersRoute } from "../definitions/tickers.route"; | ||
import { tickerEntityFromReduxState } from "../../../entities/ticker.entity"; | ||
import store from "../../../redux/store"; | ||
import { NotFoundError } from "../../../utils/errors.util"; | ||
import { APIResponse } from "../../../utils/response.util"; | ||
|
||
class TickersRoute { | ||
public static getTicker: TickersRouteDefinitions.RouteMethod<ETickersRoute.GetTicker> = async (request, response, next) => { | ||
try { | ||
const { | ||
tickerSymbol | ||
} = request.params; | ||
|
||
const tickerData = tickerEntityFromReduxState(tickerSymbol, store.getState()); | ||
|
||
if (!tickerData) { | ||
throw new NotFoundError(); | ||
} | ||
|
||
return response.status(200).json(APIResponse.success(tickerData)); | ||
} catch (error) { | ||
next(error); | ||
} | ||
}; | ||
|
||
public static getTickerList: TickersRouteDefinitions.RouteMethod<ETickersRoute.GetTickerList> = async (request, response, next) => { | ||
try { | ||
const rootState = store.getState(); | ||
const symbols = rootState.tickers.symbols; | ||
|
||
const tickerList = symbols.map(symbol => tickerEntityFromReduxState(symbol, rootState)); | ||
|
||
return response.status(200).json(APIResponse.success(tickerList)); | ||
} catch (error) { | ||
next(error); | ||
} | ||
}; | ||
} | ||
|
||
export default TickersRoute; |
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,19 @@ | ||
import TickersRouteDefinitions from "../definitions/tickers.route"; | ||
import { val } from "../middlewares/validate.middleware"; | ||
import { checkSchema } from "express-validator"; | ||
import { ValidatorFields } from "../../../types/util.types"; | ||
|
||
type GetTickerFields = | ||
(keyof TickersRouteDefinitions.TickerParams) | ||
|
||
const getTickerSchema: ValidatorFields<GetTickerFields> = { | ||
tickerSymbol: { | ||
in: ["params"], | ||
errorMessage: "Ticker must be a string", | ||
isString: true | ||
} | ||
}; | ||
|
||
export default class TickersValidator { | ||
public static validateGetTicker = val(checkSchema(getTickerSchema)); | ||
} |
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,22 @@ | ||
import { RootState } from "../redux/redux.types"; | ||
import { Nullable } from "../types/util.types"; | ||
|
||
export type TickerEntity = { | ||
symbol: string; | ||
onchainPrice?: number; | ||
offchainPrice?: number; | ||
} | ||
|
||
export function tickerEntityFromReduxState(tickerSymbol: string, state: RootState): Nullable<TickerEntity> { | ||
const tickerPrices = state.prices.current[tickerSymbol]; | ||
|
||
if (tickerPrices) { | ||
return { | ||
symbol: tickerSymbol, | ||
onchainPrice: tickerPrices.onchain, | ||
offchainPrice: tickerPrices.offchain | ||
}; | ||
} else { | ||
return 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
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
Oops, something went wrong.