Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP refactor(billing): rename files and improve logging
Browse files Browse the repository at this point in the history
refs #247
ygrishajev committed Jul 5, 2024
1 parent a18a906 commit dcf0644
Showing 43 changed files with 402 additions and 182 deletions.
2 changes: 2 additions & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
@@ -62,10 +62,12 @@
"pg": "^8.12.0",
"pg-hstore": "^2.3.4",
"pino": "^9.2.0",
"pino-pretty": "^11.2.1",
"protobufjs": "^6.11.2",
"semver": "^7.3.8",
"sequelize": "^6.21.3",
"sequelize-typescript": "^2.1.5",
"sql-formatter": "^15.3.2",
"stripe": "^10.14.0",
"tsyringe": "^4.8.0",
"uuid": "^9.0.1"
11 changes: 5 additions & 6 deletions apps/api/src/app.ts
Original file line number Diff line number Diff line change
@@ -8,10 +8,9 @@ import { sentry } from "@hono/sentry";
import * as Sentry from "@sentry/node";
import { Hono } from "hono";
import { cors } from "hono/cors";
import { container } from "tsyringe";

import { logHttpRequests } from "@src/core/services/log-http-requests/logHttpRequests";
import { Logger } from "@src/core/services/logger/Logger";
import { RequestStorage } from "@src/core/services/request-storage/RequestStorage";
import { HttpLoggerService, LoggerService, RequestStorageService } from "@src/core";
import packageJson from "../package.json";
import { chainDb, syncUserSchema, userDb } from "./db/dbConnection";
import { apiRouter } from "./routers/apiRouter";
@@ -61,8 +60,8 @@ const scheduler = new Scheduler({
}
});

appHono.use(RequestStorage.middleware());
appHono.use(logHttpRequests());
appHono.use(container.resolve(RequestStorageService).intercept());
appHono.use(container.resolve(HttpLoggerService).intercept());
appHono.use(
"*",
sentry({
@@ -104,7 +103,7 @@ function startScheduler() {
scheduler.start();
}

const appLogger = new Logger({ context: "APP" });
const appLogger = new LoggerService({ context: "APP" });

/**
* Initialize database
Original file line number Diff line number Diff line change
@@ -15,4 +15,4 @@ const envSchema = z.object({
GAS_SAFETY_MULTIPLIER: z.number({ coerce: true }).default(1.5)
});

export const env = envSchema.parse(process.env);
export const envConfig = envSchema.parse(process.env);
6 changes: 3 additions & 3 deletions apps/api/src/billing/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { env } from "./env";
import { USDC_IBC_DENOMS } from "./network";
import { envConfig } from "./env.config";
import { USDC_IBC_DENOMS } from "./network.config";

export const config = {
...env,
...envConfig,
USDC_IBC_DENOMS
};
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -3,15 +3,15 @@ import { singleton } from "tsyringe";

import { UserWalletRepository } from "@src/billing/repositories";
import { CreateWalletInput, CreateWalletOutput } from "@src/billing/routes";
import { WalletInitializer, WalletManager } from "@src/billing/services";
import { WalletInitializerService, WalletService } from "@src/billing/services";
import { WithTransaction } from "@src/core/services";

@singleton()
export class WalletController {
constructor(
private readonly walletManager: WalletManager,
private readonly walletManager: WalletService,
private readonly userWalletRepository: UserWalletRepository,
private readonly walletInitializer: WalletInitializer
private readonly walletInitializer: WalletInitializerService
) {}

@WithTransaction()
2 changes: 1 addition & 1 deletion apps/api/src/billing/model-schemas/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from "./userWalletSchema";
export * from "./user-wallet/user-wallet.schema";
2 changes: 1 addition & 1 deletion apps/api/src/billing/models/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from "./user-wallet/UserWalletModel";
export * from "./user-wallet/user-wallet.model";
8 changes: 4 additions & 4 deletions apps/api/src/billing/providers/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "./userWalletSchemaProvider";
import "./configProvider";
import "./user-wallet-schema.provider";
import "./config.provider";

export * from "./userWalletSchemaProvider";
export * from "./configProvider";
export * from "./user-wallet-schema.provider";
export * from "./config.provider";
2 changes: 1 addition & 1 deletion apps/api/src/billing/repositories/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from "./user-wallet/UserWalletRepository";
export * from "./user-wallet/user-wallet.repository";
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import { singleton } from "tsyringe";

import { InjectUserWalletSchema, UserWalletSchema } from "@src/billing/providers";
import { ApiPgDatabase, InjectPg } from "@src/core/providers";
import { TxManager } from "@src/core/services";
import { TxService } from "@src/core/services";

export type UserInput = Partial<UserWalletSchema["$inferInsert"]>;

@@ -13,7 +13,7 @@ export class UserWalletRepository {
constructor(
@InjectPg() private readonly pg: ApiPgDatabase,
@InjectUserWalletSchema() private readonly userWallet: UserWalletSchema,
private readonly txManager: TxManager
private readonly txManager: TxService
) {}

async create(input: Pick<UserInput, "userId" | "address">) {
2 changes: 1 addition & 1 deletion apps/api/src/billing/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from "./wallet";
export * from "./wallet/wallet.router";
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ import { createRoute, OpenAPIHono } from "@hono/zod-openapi";
import { container } from "tsyringe";
import { z } from "zod";

import { WalletController } from "@src/billing/controllers/wallet/WalletController";
import { WalletController } from "@src/billing/controllers/wallet/wallet.controller";

export const CreateWalletInputSchema = z.object({
userId: z.string().openapi({})
6 changes: 3 additions & 3 deletions apps/api/src/billing/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "./wallet-manager/WalletManager";
export * from "./rpc-message-service/RpcMessageService";
export * from "./wallet-initializer/WalletInitializer";
export * from "@src/billing/services/wallet/wallet.service";
export * from "./rpc-message-service/rpc-message.service";
export * from "./wallet-initializer/wallet-initializer.service";
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { singleton } from "tsyringe";

import { UserInput, UserWalletRepository } from "@src/billing/repositories";
import { WalletManager } from "@src/billing/services";
import { WalletService } from "@src/billing/services";
import { WithTransaction } from "@src/core/services";

@singleton()
export class WalletInitializer {
export class WalletInitializerService {
constructor(
private readonly walletManager: WalletManager,
private readonly walletManager: WalletService,
private readonly userWalletRepository: UserWalletRepository
) {}

Original file line number Diff line number Diff line change
@@ -5,8 +5,8 @@ import add from "date-fns/add";
import { singleton } from "tsyringe";

import { BillingConfig, InjectBillingConfig } from "@src/billing/providers";
import { RpcMessageService } from "@src/billing/services/rpc-message-service/RpcMessageService";
import { ContextualLogger } from "@src/core/services";
import { RpcMessageService } from "@src/billing/services/rpc-message-service/rpc-message.service";
import { ContextualLoggerService } from "@src/core/services";

interface SpendingAuthorizationOptions {
address: string;
@@ -18,7 +18,7 @@ interface SpendingAuthorizationOptions {
}

@singleton()
export class WalletManager {
export class WalletService {
private readonly PREFIX = "akash";

private readonly HD_PATH = "m/44'/118'/0'/0";
@@ -27,12 +27,13 @@ export class WalletManager {

private client: SigningStargateClient;

private readonly logger = new ContextualLogger({ context: WalletManager.name });

constructor(
@InjectBillingConfig() private readonly config: BillingConfig,
private readonly rpcMessageService: RpcMessageService
) {}
private readonly rpcMessageService: RpcMessageService,
private readonly logger: ContextualLoggerService
) {
this.logger.setContext({ context: WalletService.name });
}

async createAndAuthorizeTrialSpending({ addressIndex }: { addressIndex: number }) {
const { address } = await this.createWallet({ addressIndex });
6 changes: 3 additions & 3 deletions apps/api/src/console.ts
Original file line number Diff line number Diff line change
@@ -3,8 +3,8 @@ import "reflect-metadata";
import { Command } from "commander";
import { container } from "tsyringe";

import { WalletController } from "@src/billing/controllers/wallet/WalletController";
import { migrateDb } from "@src/core/services/postgres/postgres";
import { WalletController } from "@src/billing/controllers/wallet/wallet.controller";
import { PostgresMigratorService } from "@src/core";

const program = new Command();

@@ -14,7 +14,7 @@ program
.command("refill-wallets")
.description("Refill draining wallets")
.action(async () => {
await migrateDb();
await container.resolve(PostgresMigratorService).migrate();
await container.resolve(WalletController).refillAll();
});

Original file line number Diff line number Diff line change
@@ -6,7 +6,8 @@ dotenv.config();

const envSchema = z.object({
LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).optional().default("info"),
LOG_FORMAT: z.enum(["json", "pretty"]).optional().default("json"),
POSTGRES_DB_URI: z.string()
});

export const env = envSchema.parse(process.env);
export const envConfig = envSchema.parse(process.env);
5 changes: 2 additions & 3 deletions apps/api/src/core/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { env } from "./env";
import { envConfig } from "./env.config";

export const config = {
...env
...envConfig
};
export type CoreConfig = typeof config;
2 changes: 1 addition & 1 deletion apps/api/src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import "./providers";
export * from "./providers";

export * from "./services";
9 changes: 0 additions & 9 deletions apps/api/src/core/providers/PostgresProvider.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -5,3 +5,5 @@ import { config } from "@src/core/config";
export const CORE_CONFIG = "CORE_CONFIG";

container.register(CORE_CONFIG, { useValue: config });

export type CoreConfig = typeof config;
4 changes: 2 additions & 2 deletions apps/api/src/core/providers/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from "./PostgresProvider";
export * from "./configProvider";
export * from "./postgres.provider";
export * from "./config.provider";
22 changes: 22 additions & 0 deletions apps/api/src/core/providers/postgres.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { DefaultLogger } from "drizzle-orm/logger";
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import { container, inject } from "tsyringe";

import * as billingSchemas from "@src/billing/model-schemas";
import { config } from "@src/core/config";
import { PostgresLoggerService } from "@src/core/services/postgres-logger/postgres-logger.service";

const pool = new Pool({
connectionString: config.POSTGRES_DB_URI
});

export const pgDatabase = drizzle(pool, { logger: new DefaultLogger({ writer: container.resolve(PostgresLoggerService) }), schema: billingSchemas });

export type ApiPgSchema = typeof billingSchemas;

export const POSTGRES_DB = "POSTGRES_DB";
container.register(POSTGRES_DB, { useValue: pgDatabase });

export const InjectPg = () => inject(POSTGRES_DB);
export type ApiPgDatabase = typeof pgDatabase;
11 changes: 0 additions & 11 deletions apps/api/src/core/services/contextual-logger/ContextualLogger.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Bindings } from "pino";
import { injectable } from "tsyringe";

import { LoggerService } from "@src/core/services/logger/logger.service";
import { RequestStorageService } from "@src/core/services/request-storage/request-storage.service";

@injectable()
export class ContextualLoggerService extends LoggerService {
constructor(private readonly requestStorage: RequestStorageService) {
super();
}

setContext(bindings: Bindings) {
this.pino = this.pino.child(bindings);
}

protected toLoggableInput(message: any) {

Check warning on line 17 in apps/api/src/core/services/contextual-logger/contextual-logger.service.ts

GitHub Actions / validate-n-build

Unexpected any. Specify a different type
const { context } = this.requestStorage;
const requestId = context?.get("requestId");
const loggableMessage = super.toLoggableInput(message);

if (!requestId) {
return loggableMessage;
}

if (typeof loggableMessage === "object" && !Array.isArray(loggableMessage)) {
return { requestId, ...loggableMessage };
}

return { requestId, msg: loggableMessage };
}
}
29 changes: 29 additions & 0 deletions apps/api/src/core/services/http-logger/http-logger.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Context, Next } from "hono";
import { singleton } from "tsyringe";

import { ContextualLoggerService } from "@src/core/services";
import type { HonoInterceptor } from "@src/core/types/hono-interceptor.type";

@singleton()
export class HttpLoggerService implements HonoInterceptor {
constructor(private readonly logger: ContextualLoggerService) {
logger.setContext({ context: "HTTP" });
}

intercept() {
return async (c: Context, next: Next) => {
const timer = performance.now();
this.logger.info({
method: c.req.method,
url: c.req.url
});

await next();

this.logger.info({
status: c.res.status,
duration: `${(performance.now() - timer).toFixed(3)}ms`
});
};
}
}
12 changes: 6 additions & 6 deletions apps/api/src/core/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export * from "./tx-manager/TxManager";
export * from "./logger/Logger";
export * from "./contextual-logger/ContextualLogger";
export * from "./postgres/postgres";
export * from "./log-http-requests/logHttpRequests";
export * from "./request-storage/RequestStorage";
export * from "@src/core/services/tx/tx.service";
export * from "./logger/logger.service";
export * from "./contextual-logger/contextual-logger.service";
export * from "@src/core/services/http-logger/http-logger.service";
export * from "./request-storage/request-storage.service";
export * from "./postgres-migrator/postgres-migrator.service";
19 changes: 0 additions & 19 deletions apps/api/src/core/services/log-http-requests/logHttpRequests.ts

This file was deleted.

44 changes: 0 additions & 44 deletions apps/api/src/core/services/logger/Logger.ts

This file was deleted.

50 changes: 50 additions & 0 deletions apps/api/src/core/services/logger/logger.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import pino, { Bindings, LoggerOptions } from "pino";

import { config } from "@src/core/config";

export class LoggerService {
protected pino: pino.Logger;

readonly isPretty = config.LOG_FORMAT === "pretty";

constructor(bindings?: Bindings) {
const options: LoggerOptions = { level: config.LOG_LEVEL };

if (this.isPretty) {
options.transport = {
target: "pino-pretty"
};
}

this.pino = pino(options);

if (bindings) {
this.pino = this.pino.child(bindings);
}
}

info(message: any) {

Check warning on line 26 in apps/api/src/core/services/logger/logger.service.ts

GitHub Actions / validate-n-build

Unexpected any. Specify a different type
message = this.toLoggableInput(message);
return this.pino.info(message);
}

error(message: any) {

Check warning on line 31 in apps/api/src/core/services/logger/logger.service.ts

GitHub Actions / validate-n-build

Unexpected any. Specify a different type
this.pino.error(this.toLoggableInput(message));
}

warn(message: any) {

Check warning on line 35 in apps/api/src/core/services/logger/logger.service.ts

GitHub Actions / validate-n-build

Unexpected any. Specify a different type
return this.pino.warn(this.toLoggableInput(message));
}

debug(message: any) {

Check warning on line 39 in apps/api/src/core/services/logger/logger.service.ts

GitHub Actions / validate-n-build

Unexpected any. Specify a different type
return this.pino.debug(this.toLoggableInput(message));
}

protected toLoggableInput(message: any) {

Check warning on line 43 in apps/api/src/core/services/logger/logger.service.ts

GitHub Actions / validate-n-build

Unexpected any. Specify a different type
if (message instanceof Error) {
return message.stack;
}

return message;
}
}
25 changes: 0 additions & 25 deletions apps/api/src/core/services/postgres/postgres.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { Context, Next } from "hono";
import { AsyncLocalStorage } from "node:async_hooks";
import { singleton } from "tsyringe";
import { v4 as uuid } from "uuid";

export class RequestStorage {
static CONTEXT_KEY = "CONTEXT";
import type { HonoInterceptor } from "@src/core/types/hono-interceptor.type";

static storage = new AsyncLocalStorage<Map<string, Context>>();
@singleton()
export class RequestStorageService implements HonoInterceptor {
private readonly CONTEXT_KEY = "CONTEXT";

static middleware() {
private readonly storage = new AsyncLocalStorage<Map<string, Context>>();

get context() {
return this.storage.getStore()?.get(this.CONTEXT_KEY);
}

intercept() {
return async (c: Context, next: Next) => {
const requestId = c.req.header("X-Request-Id") || uuid();
c.set("requestId", requestId);
@@ -16,16 +24,12 @@ export class RequestStorage {
};
}

static async runWithContext(context: Context, cb: () => Promise<void>) {
private async runWithContext(context: Context, cb: () => Promise<void>) {
return await new Promise((resolve, reject) => {
this.storage.run(new Map(), () => {
this.storage.getStore().set(this.CONTEXT_KEY, context);
cb().then(resolve).catch(reject);
});
});
}

static getContext() {
return this.storage.getStore()?.get(this.CONTEXT_KEY);
}
}
Original file line number Diff line number Diff line change
@@ -4,13 +4,12 @@ import type { PgTransaction } from "drizzle-orm/pg-core";
import { AsyncLocalStorage } from "node:async_hooks";
import { container, singleton } from "tsyringe";

import { ApiPgDatabase, InjectPg } from "@src/core/providers";
import type { ApiPgSchema } from "@src/core/services";
import { ApiPgDatabase, ApiPgSchema, InjectPg } from "@src/core/providers/postgres.provider";

type TxType = "PG_TX";

@singleton()
export class TxManager {
export class TxService {
private readonly storage = new AsyncLocalStorage<Map<TxType, PgTransaction<NodePgQueryResultHKT, ApiPgSchema, ExtractTablesWithRelations<ApiPgSchema>>>>();

constructor(@InjectPg() private readonly pg: ApiPgDatabase) {}
@@ -36,7 +35,7 @@ export function WithTransaction() {
const originalMethod = descriptor.value;

descriptor.value = async function (...args: unknown[]) {
const txManager = container.resolve(TxManager);
const txManager = container.resolve(TxService);
return txManager.transaction(() => originalMethod.apply(this, args));
};

11 changes: 9 additions & 2 deletions apps/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import "reflect-metadata";

import { container } from "tsyringe";

import { initApp } from "@src/app";
import { migrateDb } from "@src/core/services/postgres/postgres";
import { PostgresMigratorService } from "@src/core";

migrateDb().then(() => initApp());
container
.resolve(PostgresMigratorService)
.migrate()
.then(() => initApp());
2 changes: 1 addition & 1 deletion apps/api/test/functional/create-wallet.spec.ts
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import { container } from "tsyringe";

import { app, initDb } from "@src/app";
import { USER_WALLET_SCHEMA, UserWalletSchema } from "@src/billing/providers";
import { ApiPgDatabase, POSTGRES_DB } from "@src/core/providers/PostgresProvider";
import { ApiPgDatabase, POSTGRES_DB } from "@src/core";
import { closeConnections } from "@src/db/dbConnection";

jest.setTimeout(30000);
5 changes: 3 additions & 2 deletions apps/api/test/setup-functional-tests.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import "reflect-metadata";

import dotenv from "dotenv";
import { container } from "tsyringe";

import { migrateDb } from "@src/core/services/postgres/postgres";
import { PostgresMigratorService } from "@src/core";

dotenv.config({ path: ".env.functional.test" });

beforeAll(async () => {
await migrateDb();
await container.resolve(PostgresMigratorService).migrate();
});
186 changes: 183 additions & 3 deletions package-lock.json

0 comments on commit dcf0644

Please sign in to comment.