From 5e1708e505b10362596068dc69a2662c6f4ce430 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 12 Apr 2025 13:02:43 +0200 Subject: [PATCH] generic factory --- package-lock.json | 11 +++++++++++ package.json | 1 + src/controller/GenericController.ts | 19 +++++++++++++------ src/controller/userController.ts | 6 ++++-- src/entities/baseEntity.ts | 6 +++--- src/factories/GenericFactory.ts | 25 +++++++++++++++++++++++++ src/factories/userFactory.ts | 20 ++++++++++++++++++++ src/routes/GenericRouter.ts | 8 ++++++-- src/routes/userRouter.ts | 4 +++- src/services/GenericService.ts | 4 ++-- src/viewmodels/GenericModel.ts | 13 +++++++++++++ src/viewmodels/userModel.ts | 17 +++++++++++++++++ 12 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 src/factories/GenericFactory.ts create mode 100644 src/factories/userFactory.ts create mode 100644 src/viewmodels/GenericModel.ts create mode 100644 src/viewmodels/userModel.ts diff --git a/package-lock.json b/package-lock.json index 352fedf..b0851f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "@types/express": "^4.17.17", "@types/node": "^16.18.41", "ts-node": "10.7.0", + "ts-transformer-keys": "^0.4.4", "typescript": "^4.5.2" } }, @@ -3039,6 +3040,16 @@ } } }, + "node_modules/ts-transformer-keys": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/ts-transformer-keys/-/ts-transformer-keys-0.4.4.tgz", + "integrity": "sha512-LrqgvaFvar01/5mbunRyeLTSIkqoC2xfcpL/90aDY6vR07DGyH+UaYGdIEsUudnlAw2Sr0pxFgdZvE0QIyI4qA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=2.4.1" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", diff --git a/package.json b/package.json index eb2013b..3985fbc 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@types/express": "^4.17.17", "@types/node": "^16.18.41", "ts-node": "10.7.0", + "ts-transformer-keys": "^0.4.4", "typescript": "^4.5.2" } } diff --git a/src/controller/GenericController.ts b/src/controller/GenericController.ts index 0bc7dc7..d7059ec 100644 --- a/src/controller/GenericController.ts +++ b/src/controller/GenericController.ts @@ -1,19 +1,26 @@ import { Request, Response } from "express"; import GenericService from "../services/GenericService"; import BaseEntity from "../entities/baseEntity"; +import GenericFactory from "../factories/GenericFactory"; +import { GenericViewModel } from "../viewmodels/GenericModel"; -export default class GenericController, Repository extends BaseEntity> { - constructor(protected readonly service: Service) {} +export default class GenericController< + Service extends GenericService, + Factory extends GenericFactory, + Repository extends BaseEntity, + ViewModel extends GenericViewModel +> { + constructor(protected readonly service: Service, protected readonly factory: Factory) {} async getAll(req: Request, res: Response): Promise { let data: Array = await this.service.getAll(); - res.json(data); + res.json(this.factory.mapArray(data)); } async getById(req: Request, res: Response): Promise { - let id: number = parseInt(req.params.id); + let id: string = req.params.id; let data: Repository = await this.service.getById(id); - res.json(data); + res.json(this.factory.mapSingle(data)); } async create(req: Request, res: Response): Promise { @@ -27,7 +34,7 @@ export default class GenericController { - let id: number = parseInt(req.params.id); + let id: string = req.params.id; await this.service.delete(id); res.sendStatus(204); } diff --git a/src/controller/userController.ts b/src/controller/userController.ts index 761b2a5..a2b8e62 100644 --- a/src/controller/userController.ts +++ b/src/controller/userController.ts @@ -2,10 +2,12 @@ import { Request, Response } from "express"; import User from "../entities/user"; import UserService from "../services/userService"; import GenericController from "./GenericController"; +import { UserViewModel } from "../viewmodels/userModel"; +import UserFactory from "../factories/userFactory"; -export default class UserController extends GenericController { +export default class UserController extends GenericController { constructor() { - super(new UserService()); + super(new UserService(), new UserFactory()); } // overwrite defined generic controller function diff --git a/src/entities/baseEntity.ts b/src/entities/baseEntity.ts index 367fd8b..b8e29fc 100644 --- a/src/entities/baseEntity.ts +++ b/src/entities/baseEntity.ts @@ -1,6 +1,6 @@ -import { Entity, PrimaryGeneratedColumn } from "typeorm"; +import { PrimaryColumn } from "typeorm"; export default abstract class BaseEntity { - @PrimaryGeneratedColumn("increment") - id: number; + @PrimaryColumn({ generated: "uuid" }) + id: string; } diff --git a/src/factories/GenericFactory.ts b/src/factories/GenericFactory.ts new file mode 100644 index 0000000..6d2aa53 --- /dev/null +++ b/src/factories/GenericFactory.ts @@ -0,0 +1,25 @@ +import { keys } from "ts-transformer-keys"; +import BaseEntity from "../entities/baseEntity"; +import { GenericViewModel } from "../viewmodels/GenericModel"; + +export default interface GenericFactory { + mapSingle(row: Repository): ViewModel; + mapArray(rows: Repository[]): ViewModel[]; +} + +// export default class GenericFactory { +// constructor() {} + +// mapSingle(row: Repository): T { +// let keysOfT = keys(); + +// return keysOfT.reduce((acc, key) => { +// acc[key] = (row as any)?.[key]; +// return acc; +// }, {} as T); +// } + +// mapArray(rows: Repository[]): T[] { +// return rows.map((r) => this.mapSingle(r)); +// } +// } diff --git a/src/factories/userFactory.ts b/src/factories/userFactory.ts new file mode 100644 index 0000000..cf44e53 --- /dev/null +++ b/src/factories/userFactory.ts @@ -0,0 +1,20 @@ +import User from "../entities/user"; +import { UserViewModel } from "../viewmodels/userModel"; +import GenericFactory from "./GenericFactory"; + +export default class UserFactory implements GenericFactory { + mapSingle(row: User): UserViewModel { + return { + id: row.id, + firstname: row.firstname, + }; + } + + mapArray(rows: User[]): UserViewModel[] { + return rows.map((r) => this.mapSingle(r)); + } + + mapWithX(row: User): Array<{ id: string }> { + return []; + } +} diff --git a/src/routes/GenericRouter.ts b/src/routes/GenericRouter.ts index ec7e0e1..bc6883f 100644 --- a/src/routes/GenericRouter.ts +++ b/src/routes/GenericRouter.ts @@ -2,11 +2,15 @@ import express from "express"; import GenericController from "../controller/GenericController"; import GenericService from "../services/GenericService"; import BaseEntity from "../entities/baseEntity"; +import { GenericViewModel } from "../viewmodels/GenericModel"; +import GenericFactory from "../factories/GenericFactory"; export default class GenericRouter< - Controller extends GenericController, + Controller extends GenericController, Service extends GenericService, - Repository extends BaseEntity + Factory extends GenericFactory, + Repository extends BaseEntity, + ViewModel extends GenericViewModel > { public router = express.Router({ mergeParams: true }); constructor(protected controller: Controller) { diff --git a/src/routes/userRouter.ts b/src/routes/userRouter.ts index 5e4a92a..d8b03ab 100644 --- a/src/routes/userRouter.ts +++ b/src/routes/userRouter.ts @@ -1,9 +1,11 @@ import UserController from "../controller/userController"; import User from "../entities/user"; +import UserFactory from "../factories/userFactory"; import UserService from "../services/userService"; +import { UserViewModel } from "../viewmodels/userModel"; import GenericRouter from "./GenericRouter"; -export default class UserRouter extends GenericRouter { +export default class UserRouter extends GenericRouter { constructor() { super(new UserController()); // extend generic routes by specific routes diff --git a/src/services/GenericService.ts b/src/services/GenericService.ts index 0a9efc5..24c76c9 100644 --- a/src/services/GenericService.ts +++ b/src/services/GenericService.ts @@ -10,7 +10,7 @@ export default class GenericService { return await dataSource.getRepository(this.entity).createQueryBuilder().getMany(); } - async getById(rowId: number): Promise { + async getById(rowId: string): Promise { return await dataSource.getRepository(this.entity).createQueryBuilder().where({ id: rowId }).getOneOrFail(); } @@ -24,7 +24,7 @@ export default class GenericService { return await dataSource.createQueryBuilder().update(this.entity).set(value).execute(); } - async delete(rowId: number): Promise { + async delete(rowId: string): Promise { return await dataSource.createQueryBuilder().delete().from(this.entity).where({ id: rowId }).execute(); } } diff --git a/src/viewmodels/GenericModel.ts b/src/viewmodels/GenericModel.ts new file mode 100644 index 0000000..2e84769 --- /dev/null +++ b/src/viewmodels/GenericModel.ts @@ -0,0 +1,13 @@ +import BaseEntity from "../entities/baseEntity"; + +interface GenericViewModel extends BaseEntity { + id: string; +} + +interface GenericCreateModel {} + +interface GenericUpdateModel extends BaseEntity { + id: string; +} + +export { GenericViewModel, GenericCreateModel, GenericUpdateModel }; diff --git a/src/viewmodels/userModel.ts b/src/viewmodels/userModel.ts new file mode 100644 index 0000000..5dcef8e --- /dev/null +++ b/src/viewmodels/userModel.ts @@ -0,0 +1,17 @@ +import { GenericCreateModel, GenericUpdateModel, GenericViewModel } from "./GenericModel"; + +interface UserViewModel extends GenericViewModel { + id: string; + firstname: string; +} + +interface UserCreateModel extends GenericCreateModel { + firstname: string; +} + +interface UserUpdateModel extends GenericUpdateModel { + id: string; + firstname: string; +} + +export { UserViewModel, UserCreateModel, UserUpdateModel };