Skip to content

Commit

Permalink
chore: add test implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
brunotot committed May 10, 2024
1 parent 8fe59ab commit 4eda5f2
Show file tree
Hide file tree
Showing 36 changed files with 400 additions and 132 deletions.
2 changes: 0 additions & 2 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
"@ts-rest/core": "^3.45.0",
"@ts-rest/express": "^3.45.0",
"@ts-rest/open-api": "^3.45.0",
"@tsvdec/core": "^2.0.11",
"@tsvdec/decorators": "^1.0.7",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"bottlejs": "^2.0.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/scripts/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ start "" " $(color $CYAN)3.$(color) Converting path aliases to relative paths"
npx tsc-alias -p "${PWD_BACKEND}/tsconfig.json"
stop "$(color $GREEN)$(color)"

echo -e "\n$(color $GREEN)$(color $CYAN)3.$(color) Starting...\n"
echo -e "\n$(color $GREEN)$(color $CYAN)4.$(color) Starting...\n"
node --no-warnings --loader ts-node/esm --experimental-specifier-resolution=node "${PWD_BACKEND}/dist/main.js"

#
Expand Down
61 changes: 40 additions & 21 deletions packages/backend/src/App.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import express from "express";
import * as swaggerUi from "swagger-ui-express";
import { type MongoClient as MongoClientNative } from "mongodb";
import swaggerUi from "swagger-ui-express";
import { MongoClient, type MongoClientOptions } from "mongodb";
import { generateOpenApi } from "@ts-rest/open-api";
import { initServer, createExpressEndpoints } from "@ts-rest/express";

import { RouterCollection, Environment, Logger } from "@org/backend/config";
import { GLOBAL_MIDDLEWARES } from "@org/backend/infrastructure";
import { CONTRACTS, operationMapper, suppressConsole } from "@org/shared";
import { generateOpenApi } from "@ts-rest/open-api";
import { initServer, createExpressEndpoints } from "@ts-rest/express";

export type MongoConnectParams = {
uri: string;
options?: MongoClientOptions;
};

export type AppOptions = {
mongoConnection: MongoConnectParamsFactory;
};

export type MongoConnectParamsFactory = () => Promise<MongoConnectParams>;

export class App {
public readonly app: express.Application;
Expand All @@ -14,47 +26,54 @@ export class App {
public readonly swaggerPath: string;
public readonly url: string;

#mongoConnection: MongoConnectParamsFactory;

private environment = Environment.getInstance();
private logger = Logger.getInstance();
private mongoClient: MongoClientNative;
mongoClient: MongoClient;

constructor(mongoClient: MongoClientNative) {
constructor(options: AppOptions) {
this.app = express();
this.env = this.environment.vars.NODE_ENV;
this.port = this.environment.vars.PORT;
this.swaggerPath = "api-docs";
this.mongoClient = mongoClient;
this.#mongoConnection = options.mongoConnection;
const domain =
this.env === "production"
? `https://${process.env.RAILWAY_PUBLIC_DOMAIN}`
: "http://localhost";
this.url = `${domain}:${this.port}`;

this.#initializeDatabase();
this.#initializeGlobalMiddlewares();
this.#initializeRoutes();
this.#initializeSwagger();
}

public listen() {
this.app.listen(this.port, () => {
this.logger.table({
title: `[Express] MERN Sample App v${this.environment.vars.PACKAGE_JSON_VERSION}`,
data: {
"🟢 NodeJS": process.version,
"🏠 Env": this.env,
"🚀 Port": this.port,
"📝 Swagger": `/${this.swaggerPath}`,
"🆔 PID": `${process.pid}`,
"🧠 Memory": `${Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100} MB`,
"📅 Started": new Date().toLocaleString(),
},
public async listen(): Promise<void> {
await this.#initializeDatabase();
return new Promise(resolve => {
this.app.listen(this.port, () => {
this.logger.table({
title: `[Express] MERN Sample App v${this.environment.vars.PACKAGE_JSON_VERSION}`,
data: {
"🟢 NodeJS": process.version,
"🏠 Env": this.env,
"🚀 Port": this.port,
"📝 Swagger": `/${this.swaggerPath}`,
"🆔 PID": `${process.pid}`,
"🧠 Memory": `${Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100} MB`,
"📅 Started": new Date().toLocaleString(),
},
});
resolve();
});
});
}

async #initializeDatabase() {
try {
const { uri, options } = await this.#mongoConnection();
this.mongoClient = new MongoClient(uri, options);
await this.mongoClient.connect();
} catch (error) {
console.log(error);
Expand Down
33 changes: 0 additions & 33 deletions packages/backend/src/__tests__/product.test.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type ClassMetadataInjectType, ClassMetadataEntry } from "@tsvdec/decorators";
import { type TODO } from "@org/shared";
import type { TODO } from "@org/shared";
import { type MetaClassInjectionData } from "@org/backend/types";
import { ClassMetadataEntry, type ClassMetadataInjectType } from "@org/backend/decorators";

export class InjectorMetadataManager extends ClassMetadataEntry<MetaClassInjectionData> {
static getBy(injection: ClassMetadataInjectType) {
Expand Down
10 changes: 5 additions & 5 deletions packages/backend/src/config/singletons/Environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ export class Environment {
LOG_DIR: z.string().default("../../logs"),
ORIGIN: z.string().default("*"),
CREDENTIALS: z.string().default("true"),
DB_HOST: z.string(),
DB_PORT: z.string(),
DB_DATABASE: z.string(),
ACCESS_TOKEN_SECRET: z.string(),
REFRESH_TOKEN_SECRET: z.string(),
DB_HOST: z.string().optional(),
DB_PORT: z.string().optional(),
DB_DATABASE: z.string().optional(),
ACCESS_TOKEN_SECRET: z.string().default("accessTokenSecret"),
REFRESH_TOKEN_SECRET: z.string().default("refreshTokenSecret"),
});

private constructor() {
Expand Down
6 changes: 6 additions & 0 deletions packages/backend/src/config/singletons/ServiceRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ export class ServiceRegistry {
public iocStartup() {
const injectionClasses = this.injectionClasses;

console.log("Starting with following classes", injectionClasses);

const dependencySchema: Record<string, string[]> = injectionClasses.reduce((acc, Class) => {
const { name, dependencies = [] } = InjectorMetadataManager.getBy(Class).value;
return { ...acc, [name]: dependencies };
}, {});

console.log("Dependency schema", dependencySchema);

function sortInjectionClasses(classes: Class[], dependencySchema: Record<string, string[]>) {
return [...classes].sort((classA, classB) => {
const { name: nameA } = InjectorMetadataManager.getBy(classA).value;
Expand All @@ -56,6 +60,7 @@ export class ServiceRegistry {
const name = decoration.name;
const constructorParams = decoration.constructorParams;
if (constructorParams.length > 0) {
console.log("Setting factory", name);
this.bottle.factory(name, container => {
const instance = new Class(...constructorParams);
const dependencies = dependencySchema[name];
Expand All @@ -66,6 +71,7 @@ export class ServiceRegistry {
return instance;
});
} else {
console.log("Setting service", name);
this.bottle.service(name, Class, ...dependencySchema[name]);
}
});
Expand Down
10 changes: 0 additions & 10 deletions packages/backend/src/configureApp.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/backend/src/decorators/@Autowired.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createFieldDecorator } from "@tsvdec/decorators";
import { createFieldDecorator } from "@org/backend/decorators";
import { ServiceRegistry, InjectorMetadataManager } from "@org/backend/config";

export function Autowired<This, Value>() {
Expand Down
12 changes: 10 additions & 2 deletions packages/backend/src/decorators/@Contract.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createMethodDecorator } from "@tsvdec/decorators";
import { createMethodDecorator } from "@org/backend/decorators";
import { ServiceRegistry, RouterCollection, Logger } from "@org/backend/config";
import { type ContractName, type TODO, ErrorResponse } from "@org/shared";
import { type ErrorLogRepository } from "@org/backend/infrastructure";
Expand All @@ -10,17 +10,25 @@ export function Contract<const Name extends ContractName, This, Fn extends Route
) {
return createMethodDecorator<This, Fn>(({ target, meta }) => {
async function handler(data: TODO): Promise<TODO> {
console.log("HEEEEEEEEEEEEEEY", ServiceRegistry.getInstance().container, target);
const context = meta.context;
try {
return await target.call(ServiceRegistry.getInstance().inject(context), data);
console.log("Calling target route");
console.log(target.name);
const container = ServiceRegistry.getInstance().inject(context);
console.log(container);
return await target.call(container, data);
} catch (error) {
const errorResponse =
error instanceof ErrorResponse
? error
: new ErrorResponse(data.req, 500, (error as TODO).message);
console.log(error);
const errorContent = errorResponse.content;
const errorLogRepository =
ServiceRegistry.getInstance().inject<ErrorLogRepository>("errorLogRepository");

console.log(JSON.stringify(errorResponse.content, null, 2));
try {
await errorLogRepository.insertOne(errorContent);
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/decorators/@Injectable.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { TODO } from "@org/shared";
import { createClassDecorator, type ClassDecoratorSupplier } from "@tsvdec/decorators";
import { createClassDecorator, type ClassDecoratorSupplier } from "@org/backend/decorators";
import { ServiceRegistry, InjectorMetadataManager } from "@org/backend/config";

export function Injectable<This extends new () => TODO>(supplier?: ClassDecoratorSupplier<This>) {
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/decorators/@Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Injectable } from "@org/backend/decorators/@Injectable";
export function Repository<This extends Class>(zodSchema: z.AnyZodObject) {
const modelName = zodSchema.description;
return Injectable<This>(data => {
1;
const context = data.meta.context;
InjectorMetadataManager.getBy(context).setConstructorParams([modelName]);
});
Expand Down
3 changes: 1 addition & 2 deletions packages/backend/src/decorators/@Transactional.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { TODO } from "@org/shared";
import { createMethodDecorator } from "@tsvdec/decorators";

import { createMethodDecorator } from "@org/backend/decorators";
import { ServiceRegistry, InjectorMetadataManager } from "@org/backend/config";

type ClientSession = TODO;
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "@org/backend/decorators/@Injectable";
export * from "@org/backend/decorators/@Repository";
export * from "@org/backend/decorators/@Transactional";
export * from "@org/backend/decorators/@Contract";
export * from "@org/backend/decorators/setup";
22 changes: 22 additions & 0 deletions packages/backend/src/decorators/setup/class-decorators/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { type Class } from "@org/shared";
import { ClassMetadata } from "../shared/ClassMetadata";

export type ClassDecoratorSupplier<This extends Class> = (content: {
meta: ClassMetadata;
clazz: Class;
}) => ReturnType<ClassDecoratorDef<This>>;

export type ClassDecoratorDef<This extends Class> = (
constructor: This,
context: ClassDecoratorContext,
) => void | undefined | This;

export function createClassDecorator<This extends Class>(
supplier: ClassDecoratorSupplier<This>,
): ClassDecoratorDef<This> {
return function (clazz: This, context: ClassDecoratorContext<This>) {
const meta = ClassMetadata.for(context);
meta._setClass(clazz);
return supplier({ clazz, meta });
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./factory";
19 changes: 19 additions & 0 deletions packages/backend/src/decorators/setup/field-decorators/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ClassMetadata } from "../shared/ClassMetadata";

export type FieldDecoratorSupplier<This, Value> = (content: {
meta: ClassMetadata;
}) => ReturnType<FieldDecoratorDef<This, Value>>;

export type FieldDecoratorDef<This, Value> = (
target: undefined,
context: ClassFieldDecoratorContext<This, Value>,
) => void | undefined | ((value: Value) => Value);

export function createFieldDecorator<This, Value>(
supplier: FieldDecoratorSupplier<This, Value>,
): FieldDecoratorDef<This, Value> {
return function (_target: undefined, context: ClassFieldDecoratorContext<This, Value>) {
const meta = ClassMetadata.for(context);
return supplier({ meta });
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./factory";
8 changes: 8 additions & 0 deletions packages/backend/src/decorators/setup/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* @packageDocumentation tsvdec - Decorators module
*/

export * from "./class-decorators";
export * from "./field-decorators";
export * from "./method-decorators";
export * from "./shared";
21 changes: 21 additions & 0 deletions packages/backend/src/decorators/setup/method-decorators/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type TODO } from "@org/shared";
import { ClassMetadata } from "../shared/ClassMetadata";

export type MethodDecoratorSupplier<This, Fn extends (...args: TODO[]) => TODO> = (content: {
meta: ClassMetadata;
target: Fn;
}) => ReturnType<MethodDecoratorDef<This, Fn>>;

export type MethodDecoratorDef<This, Fn extends (...args: TODO[]) => TODO> = (
target: Fn,
context: ClassMethodDecoratorContext<This, Fn>,
) => void | undefined | Fn;

export function createMethodDecorator<This, Fn extends (...args: TODO[]) => TODO>(
supplier: MethodDecoratorSupplier<This, Fn>,
): MethodDecoratorDef<This, Fn> {
return function (target: Fn, context: ClassMethodDecoratorContext<This, Fn>) {
const meta = ClassMetadata.for(context);
return supplier({ target, meta });
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./factory";
Loading

0 comments on commit 4eda5f2

Please sign in to comment.