diff --git a/src/app.ts b/src/app.ts index 032ba76..eff3621 100644 --- a/src/app.ts +++ b/src/app.ts @@ -16,6 +16,7 @@ import { getConfigService, ProjectController, SubscriptionController, + TenderlyController, UserController, } from './modules'; @@ -81,6 +82,7 @@ useExpressServer(app, { ProjectController, SubscriptionController, ContractController, + TenderlyController, ], middlewares: [AppErrorHandler, AuthMiddleware], }); diff --git a/src/middleware/errors.ts b/src/middleware/errors.ts index b3c855f..d7287bf 100644 --- a/src/middleware/errors.ts +++ b/src/middleware/errors.ts @@ -6,6 +6,8 @@ import { Service } from 'typedi'; import { getGlobalLogger } from '@bonadocs/di'; +import { ApplicationError } from '../modules/errors'; + @Service() @Middleware({ type: 'after' }) export default class AppErrorHandler implements ExpressErrorMiddlewareInterface { @@ -15,6 +17,14 @@ export default class AppErrorHandler implements ExpressErrorMiddlewareInterface return; } + if (error instanceof ApplicationError) { + response.status(error.statusCode).json({ + status: error.errorCode, + message: error.userFriendlyMessage, + }); + return; + } + if ((error).name === 'PayloadTooLargeError') { response.status(StatusCodes.REQUEST_TOO_LONG).json({ status: 'failed', diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index b56b2f2..c252857 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -53,7 +53,7 @@ export class AuthService { userFriendlyMessage: 'Please check your credentials and try again', }); } - const user = await this.userRepository.findUserByAuth(request.authSource, authUserId, null); + const user = await this.userRepository.findUserByAuth(request.authSource, authUserId); if (!user) { throw new ApplicationError({ logger: this.logger, diff --git a/src/modules/auth/firebase/index.ts b/src/modules/auth/firebase/index.ts index e73c57a..2d9bacc 100644 --- a/src/modules/auth/firebase/index.ts +++ b/src/modules/auth/firebase/index.ts @@ -2,7 +2,7 @@ import crypto, { createHash } from 'crypto'; import { Inject, Service } from 'typedi'; -import { ServiceConfiguration } from '../../configuration/config.interface'; +import { GCP } from '../../configuration/config.interface'; import { ConfigService } from '../../configuration/config.service'; let keysCache: { keys: Record; expiry: number } | null = null; @@ -60,8 +60,7 @@ export class FirebaseJWTProvider { return false; } - const gcpProjectId = - this.configService.getTransformed('gcp').gcp.projectId; + const gcpProjectId = this.configService.getTransformed('gcp').projectId; const nowSeconds = Date.now() / 1000; if ( diff --git a/src/modules/configuration/config.interface.ts b/src/modules/configuration/config.interface.ts index 7519085..fcfe3de 100644 --- a/src/modules/configuration/config.interface.ts +++ b/src/modules/configuration/config.interface.ts @@ -23,3 +23,30 @@ export type ServiceConfiguration = { metaBucket: string; }; }; + +export type GCP = { + projectId: string; + collectionsBucket: string; + abisBucket: string; + automergeBucket: string; + metaBucket: string; +}; + +export type MailGun = { + apiKey: string; + domain: string; + defaultSender: { + name: string; + email: string; + }; +}; + +export type Blockscan = { + etherscan: Record; +}; + +export type Tenderly = { + accessKey: string; + username: string; + project: string; +}; diff --git a/src/modules/connection/dbcontext.ts b/src/modules/connection/dbcontext.ts index 922be77..ac72022 100644 --- a/src/modules/connection/dbcontext.ts +++ b/src/modules/connection/dbcontext.ts @@ -43,12 +43,13 @@ export function withDbContext Promise>( descriptor: TypedPropertyDescriptor, ): TypedPropertyDescriptor { const originalMethod = descriptor.value!; - return async function wrappedFunction(this: object, ...args: any[]) { + + descriptor.value = async function wrappedFunction(this: object, ...args: any[]) { const lastArg = args[args.length - 1] as DbContext | undefined | null; const dbContext = args.length > 0 && lastArg?.isDbContext === true ? (lastArg as DbContext) : undefined; - // if the last argument is a DbContext, we don't need to create a new one + // If the last argument is a DbContext, we don't need to create a new one if (dbContext) { return originalMethod.apply(this, args); } @@ -104,5 +105,7 @@ export function withDbContext Promise>( } poolClient.release(); } - } as TypedPropertyDescriptor; + } as T; + + return descriptor; } diff --git a/src/modules/contract/contract.service.ts b/src/modules/contract/contract.service.ts index 021e02f..06b77df 100644 --- a/src/modules/contract/contract.service.ts +++ b/src/modules/contract/contract.service.ts @@ -5,7 +5,7 @@ import { diConstants } from '@bonadocs/di'; import { BonadocsLogger } from '@bonadocs/logger'; import { getVerifiedContractABI } from '../blockscan/index'; -import { ServiceConfiguration } from '../configuration/config.interface'; +import { GCP } from '../configuration/config.interface'; import { ConfigService } from '../configuration/config.service'; import { ApplicationError, applicationErrorCodes } from '../errors'; import { EvmContractRepository } from '../repositories/evm-contracts/evm-contracts.repository'; @@ -31,7 +31,7 @@ export class ContractService { request.chainId, request.address, ); - const gcpConfig = this.configService.getTransformed('gcp').gcp; + const gcpConfig = this.configService.getTransformed('gcp'); if (abiHash) { const abi = await this.storage.downloadFile(gcpConfig.abisBucket, `${abiHash}.json`); diff --git a/src/modules/errors/index.ts b/src/modules/errors/index.ts index 040e240..ccdd11a 100644 --- a/src/modules/errors/index.ts +++ b/src/modules/errors/index.ts @@ -1 +1 @@ -export { ApplicationError, applicationErrorCodes } from './ApplicationError'; +export * from './ApplicationError'; diff --git a/src/modules/index.ts b/src/modules/index.ts index a9118ae..2a78748 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -5,3 +5,4 @@ export * from './project'; export * from './user'; export * from './contract'; export * from './subscription'; +export * from './tenderly'; diff --git a/src/modules/mailing/mailgun.ts b/src/modules/mailing/mailgun.ts index 51ffe18..9b4d00e 100644 --- a/src/modules/mailing/mailgun.ts +++ b/src/modules/mailing/mailgun.ts @@ -5,7 +5,7 @@ import { Inject, Service } from 'typedi'; import { diConstants } from '@bonadocs/di'; import { BonadocsLogger } from '@bonadocs/logger'; -import { ServiceConfiguration } from '../configuration/config.interface'; +import { MailGun } from '../configuration/config.interface'; import { ConfigService } from '../configuration/config.service'; import { MailError, MailSender, SenderConfig, SendOptions } from './types'; @@ -45,14 +45,11 @@ export class MailgunSender implements MailSender { private loadConfig(): MailgunOptions { return { - domain: this.configService.getTransformed('mail').mailgun.domain, - apiKey: this.configService.getTransformed('mail').mailgun.apiKey, + domain: this.configService.getTransformed('mail').domain, + apiKey: this.configService.getTransformed('mail').apiKey, defaultSender: { - email: - this.configService.getTransformed('mail').mailgun.defaultSender - .email, - name: this.configService.getTransformed('mail').mailgun.defaultSender - .name, + email: this.configService.getTransformed('mail').defaultSender.email, + name: this.configService.getTransformed('mail').defaultSender.name, }, }; } diff --git a/src/modules/project/project.service.ts b/src/modules/project/project.service.ts index 18035b7..5ab89ab 100644 --- a/src/modules/project/project.service.ts +++ b/src/modules/project/project.service.ts @@ -7,7 +7,7 @@ import { BonadocsLogger } from '@bonadocs/logger'; import { AuthService } from '../auth/auth.service'; import { FirebaseJWTProvider } from '../auth/firebase/index'; -import { ServiceConfiguration } from '../configuration/config.interface'; +import { GCP } from '../configuration/config.interface'; import { ConfigService } from '../configuration/config.service'; import { ApplicationError, applicationErrorCodes } from '../errors/ApplicationError'; import { MailgunSender } from '../mailing/mailgun'; @@ -263,8 +263,7 @@ export class ProjectService { } const fileId = randomBytes(16).toString('hex'); - const bucketName = - this.configService.getTransformed('gcp').gcp.collectionsBucket; + const bucketName = this.configService.getTransformed('gcp').collectionsBucket; const fileName = `project-${projectId}/${fileId}.json`; await this.storage.uploadFile( bucketName, @@ -343,8 +342,7 @@ export class ProjectService { logger: this.logger, }); } - const bucketName = - this.configService.getTransformed('gcp').gcp.collectionsBucket; + const bucketName = this.configService.getTransformed('gcp').collectionsBucket; const publicUrl = this.storage.getFilePublicUrl(bucketName, collection.uri); return { @@ -373,8 +371,7 @@ export class ProjectService { }); } - const bucketName = - this.configService.getTransformed('gcp').gcp.collectionsBucket; + const bucketName = this.configService.getTransformed('gcp').collectionsBucket; const fileName = collection?.uri; const savedCollectionData = await this.storage.downloadFile(bucketName, fileName); @@ -410,8 +407,7 @@ export class ProjectService { }); } - const bucketName = - this.configService.getTransformed('gcp').gcp.collectionsBucket; + const bucketName = this.configService.getTransformed('gcp').collectionsBucket; const group = FirebaseJWTProvider.getFcmGroupId(projectId, collectionId); const fileName = `firebase-device-ids/${group}.json`; const existingFile = await this.storage.downloadFile(bucketName, fileName); @@ -471,8 +467,7 @@ export class ProjectService { }); } - const bucketName = - this.configService.getTransformed('gcp').gcp.collectionsBucket; + const bucketName = this.configService.getTransformed('gcp').collectionsBucket; const fileName = collection!.uri; await this.storage.configureFileAccess(bucketName, fileName, request.isPublic); await this.projectRepository.updateProjectCollection( diff --git a/src/modules/repositories/evm-contracts/evm-contracts.repository.test.ts b/src/modules/repositories/evm-contracts/evm-contracts.repository.test.ts index 19eb6f6..930e32c 100644 --- a/src/modules/repositories/evm-contracts/evm-contracts.repository.test.ts +++ b/src/modules/repositories/evm-contracts/evm-contracts.repository.test.ts @@ -8,6 +8,9 @@ import { DbContext } from '../../connection/dbcontext'; import { EvmContractRepository } from './evm-contracts.repository'; import { queries } from './queries'; +jest.mock('../../connection/dbcontext'); +jest.mock('@bonadocs/logger'); + describe('EvmContractRepository', () => { let evmContractsRepository: EvmContractRepository; let context: jest.Mocked; diff --git a/src/modules/repositories/projects/project.repository.test.ts b/src/modules/repositories/projects/project.repository.test.ts index 2e5b2f0..f384cf1 100644 --- a/src/modules/repositories/projects/project.repository.test.ts +++ b/src/modules/repositories/projects/project.repository.test.ts @@ -12,6 +12,8 @@ import { queries } from './queries'; import { CreateProjectDto, GenerateApiKeyDto, InviteUserToProjectDto, PlanDto } from './types'; import { flagsToPermissions, PermissionNames } from './util'; +jest.mock('../../connection/dbcontext'); +jest.mock('@bonadocs/logger'); describe('ProjectRepository', () => { let projectRepository: ProjectRepository; let context: jest.Mocked; diff --git a/src/modules/repositories/projects/project.repository.ts b/src/modules/repositories/projects/project.repository.ts index ed15420..3361d34 100644 --- a/src/modules/repositories/projects/project.repository.ts +++ b/src/modules/repositories/projects/project.repository.ts @@ -37,12 +37,10 @@ export class ProjectRepository { @withDbContext async createProject( data: CreateProjectDto, - context?: DbContext | null, + context: DbContext | null, ): Promise { try { context?.beginTransaction(); - // begin and rollback transaction should happen in the service - const createProjectResult = await context?.query({ text: queries.createProject, values: [data.slug, data.name], diff --git a/src/modules/repositories/users/user.repository.ts b/src/modules/repositories/users/user.repository.ts index 00f5476..39e10df 100644 --- a/src/modules/repositories/users/user.repository.ts +++ b/src/modules/repositories/users/user.repository.ts @@ -119,7 +119,7 @@ export class UserRepository { async findUserByAuth( authSource: string, authId: string, - context: DbContext | null = null, + context?: DbContext, ): Promise { const result = await context?.query({ text: queries.findUserByAuth, diff --git a/src/modules/storage/index.ts b/src/modules/storage/index.ts index abd51d4..3b0f140 100644 --- a/src/modules/storage/index.ts +++ b/src/modules/storage/index.ts @@ -4,7 +4,7 @@ import { Inject, Service } from 'typedi'; import { diConstants } from '@bonadocs/di'; import { BonadocsLogger } from '@bonadocs/logger'; -import { ServiceConfiguration } from '../configuration/config.interface'; +import { GCP } from '../configuration/config.interface'; import { ConfigService } from '../configuration/config.service'; let storageClient: GCloudStorage | undefined; @@ -20,8 +20,7 @@ export class Storage { @Inject(diConstants.logger) private readonly logger: BonadocsLogger, @Inject() private readonly configService: ConfigService, ) { - this.gcpProjectId = - this.configService.getTransformed('gcp').gcp.projectId; + this.gcpProjectId = this.configService.getTransformed('gcp').projectId; } async uploadFile( diff --git a/src/modules/tenderly/index.ts b/src/modules/tenderly/index.ts new file mode 100644 index 0000000..1b8443e --- /dev/null +++ b/src/modules/tenderly/index.ts @@ -0,0 +1 @@ +export { TenderlyController } from './tenderly.controller'; diff --git a/src/modules/tenderly/tenderly.controller.ts b/src/modules/tenderly/tenderly.controller.ts index ffad6db..ecc971a 100644 --- a/src/modules/tenderly/tenderly.controller.ts +++ b/src/modules/tenderly/tenderly.controller.ts @@ -10,7 +10,7 @@ import { TenderlyService } from './tenderly.service'; @Service() @JsonController('/tenderly') @Authorized() -export class ProjectController { +export class TenderlyController { constructor(@Inject() private readonly tenderlyService: TenderlyService) {} @Get('/stimulate')