From a74cb24e9f26d1795dad9f507ce1ecd65b8e61dc Mon Sep 17 00:00:00 2001
From: Supan Adit Pratama <account@supanadit.com>
Date: Wed, 6 Nov 2024 01:00:33 +0700
Subject: [PATCH] wip: typeorm module and service

---
 libs/typeorm/src/config.module-definition.ts  |  7 ++
 libs/typeorm/src/constants.ts                 |  2 +
 libs/typeorm/src/index.ts                     |  4 +
 .../config-module-options.interface.ts        |  5 ++
 libs/typeorm/src/interfaces/index.ts          |  1 +
 libs/typeorm/src/typeorm.module.ts            | 76 +++++++++++++++++++
 libs/typeorm/src/typeorm.service.ts           | 11 +++
 libs/typeorm/tsconfig.lib.json                |  9 +++
 nest-cli.json                                 |  9 +++
 package.json                                  |  5 +-
 src/app.module.ts                             | 11 ++-
 test/jest-e2e.json                            |  4 +-
 tsconfig.json                                 |  6 ++
 13 files changed, 146 insertions(+), 4 deletions(-)
 create mode 100644 libs/typeorm/src/config.module-definition.ts
 create mode 100644 libs/typeorm/src/constants.ts
 create mode 100644 libs/typeorm/src/index.ts
 create mode 100644 libs/typeorm/src/interfaces/config-module-options.interface.ts
 create mode 100644 libs/typeorm/src/interfaces/index.ts
 create mode 100644 libs/typeorm/src/typeorm.module.ts
 create mode 100644 libs/typeorm/src/typeorm.service.ts
 create mode 100644 libs/typeorm/tsconfig.lib.json

diff --git a/libs/typeorm/src/config.module-definition.ts b/libs/typeorm/src/config.module-definition.ts
new file mode 100644
index 0000000..d146d7d
--- /dev/null
+++ b/libs/typeorm/src/config.module-definition.ts
@@ -0,0 +1,7 @@
+import { ConfigurableModuleBuilder } from '@nestjs/common';
+import { TypeORMModuleOptions } from './interfaces';
+
+export const { ConfigurableModuleClass } =
+  new ConfigurableModuleBuilder<TypeORMModuleOptions>()
+    .setClassMethodName('forRoot')
+    .build();
diff --git a/libs/typeorm/src/constants.ts b/libs/typeorm/src/constants.ts
new file mode 100644
index 0000000..f8b73de
--- /dev/null
+++ b/libs/typeorm/src/constants.ts
@@ -0,0 +1,2 @@
+export const TYPEORM_DATASOURCE = 'TYPEORM_DATASOURCE';
+export const TYPEORM_REPOSITORY = 'TYPEORM_REPOSITORY';
diff --git a/libs/typeorm/src/index.ts b/libs/typeorm/src/index.ts
new file mode 100644
index 0000000..5e59fe0
--- /dev/null
+++ b/libs/typeorm/src/index.ts
@@ -0,0 +1,4 @@
+export * from './typeorm.module';
+export * from './typeorm.service';
+export * from './interfaces';
+export * from './constants';
diff --git a/libs/typeorm/src/interfaces/config-module-options.interface.ts b/libs/typeorm/src/interfaces/config-module-options.interface.ts
new file mode 100644
index 0000000..3e6cc26
--- /dev/null
+++ b/libs/typeorm/src/interfaces/config-module-options.interface.ts
@@ -0,0 +1,5 @@
+export interface TypeORMModuleOptions {
+  entity;
+  synchronize?: boolean;
+  entities: string[];
+}
diff --git a/libs/typeorm/src/interfaces/index.ts b/libs/typeorm/src/interfaces/index.ts
new file mode 100644
index 0000000..094edd2
--- /dev/null
+++ b/libs/typeorm/src/interfaces/index.ts
@@ -0,0 +1 @@
+export * from './config-module-options.interface';
diff --git a/libs/typeorm/src/typeorm.module.ts b/libs/typeorm/src/typeorm.module.ts
new file mode 100644
index 0000000..29f7092
--- /dev/null
+++ b/libs/typeorm/src/typeorm.module.ts
@@ -0,0 +1,76 @@
+import { DynamicModule, Module } from '@nestjs/common';
+import { TypeormService } from './typeorm.service';
+import { TypeORMModuleOptions } from './interfaces';
+import { ConfigurableModuleClass } from './config.module-definition';
+import { DataSource } from 'typeorm';
+import { TYPEORM_DATASOURCE, TYPEORM_REPOSITORY } from './constants';
+
+const getDatabaseCredential = (
+  synchronize?: boolean,
+  entities?: string[],
+): any => {
+  const url = new URL(process.env.DATABASE_URL);
+
+  let entityList = [__dirname + '/../**/*.entity{.ts,.js}'];
+  if (entities) {
+    entityList = entities;
+  }
+
+  let protocol;
+  switch (url.protocol) {
+    case 'postgresql':
+      protocol = 'postgres';
+      break;
+    case 'mysql':
+      protocol = 'mysql';
+      break;
+  }
+
+  // Extract the necessary components
+  const username = url.username;
+  const password = url.password;
+  const host = url.hostname;
+  const port = url.port;
+  const databaseName = url.pathname.substring(1); // Remove the leading slash
+  return {
+    type: protocol,
+    host: host,
+    port: port,
+    username: username,
+    password: password,
+    database: databaseName,
+    entities: entityList,
+    synchronize: synchronize ?? false,
+  };
+};
+
+@Module({
+  providers: [TypeormService],
+  exports: [TypeormService],
+})
+export class TypeORMModule<T> extends ConfigurableModuleClass {
+  static forRoot(options: TypeORMModuleOptions): DynamicModule {
+    let providers = [
+      {
+        provide: TYPEORM_DATASOURCE,
+        useFactory: async () => {
+          const dataSource = new DataSource(
+            getDatabaseCredential(options.synchronize, options.entities),
+          );
+          return dataSource.initialize();
+        },
+      },
+      {
+        provide: TYPEORM_REPOSITORY,
+        useFactory: (dataSource: DataSource) =>
+          dataSource.getRepository(options.entity),
+        inject: [TYPEORM_DATASOURCE],
+      },
+    ];
+
+    return {
+      ...super.forRoot(options),
+      providers,
+    };
+  }
+}
diff --git a/libs/typeorm/src/typeorm.service.ts b/libs/typeorm/src/typeorm.service.ts
new file mode 100644
index 0000000..405bcd6
--- /dev/null
+++ b/libs/typeorm/src/typeorm.service.ts
@@ -0,0 +1,11 @@
+import { Inject, Injectable } from '@nestjs/common';
+import { Repository } from 'typeorm';
+import { TYPEORM_REPOSITORY } from './constants';
+
+@Injectable()
+export class TypeormService<T> {
+  constructor(
+    @Inject(TYPEORM_REPOSITORY)
+    private repository: Repository<T>,
+  ) {}
+}
diff --git a/libs/typeorm/tsconfig.lib.json b/libs/typeorm/tsconfig.lib.json
new file mode 100644
index 0000000..b522cee
--- /dev/null
+++ b/libs/typeorm/tsconfig.lib.json
@@ -0,0 +1,9 @@
+{
+  "extends": "../../tsconfig.json",
+  "compilerOptions": {
+    "declaration": true,
+    "outDir": "../../dist/libs/typeorm"
+  },
+  "include": ["src/**/*"],
+  "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
+}
diff --git a/nest-cli.json b/nest-cli.json
index c85f899..630b8ee 100644
--- a/nest-cli.json
+++ b/nest-cli.json
@@ -24,6 +24,15 @@
       "compilerOptions": {
         "tsConfigPath": "libs/prisma/tsconfig.lib.json"
       }
+    },
+    "typeorm": {
+      "type": "library",
+      "root": "libs/typeorm",
+      "entryFile": "index",
+      "sourceRoot": "libs/typeorm/src",
+      "compilerOptions": {
+        "tsConfigPath": "libs/typeorm/tsconfig.lib.json"
+      }
     }
   }
 }
\ No newline at end of file
diff --git a/package.json b/package.json
index dae1738..dc45db1 100644
--- a/package.json
+++ b/package.json
@@ -92,7 +92,8 @@
     ],
     "moduleNameMapper": {
       "^@aditama-labs/nest-autocrud/skeleton(|/.*)$": "<rootDir>/libs/skeleton/$1",
-      "^@aditama-labs/nest-autocrud/prisma(|/.*)$": "<rootDir>/libs/prisma/$1"
+      "^@aditama-labs/nest-autocrud/prisma(|/.*)$": "<rootDir>/libs/prisma/$1",
+      "^@aditama-labs/nest-autocrud/typeorm(|/.*)$": "<rootDir>/libs/typeorm/src/$1"
     }
   }
-}
+}
\ No newline at end of file
diff --git a/src/app.module.ts b/src/app.module.ts
index c4f0269..4fd1ccb 100644
--- a/src/app.module.ts
+++ b/src/app.module.ts
@@ -7,13 +7,22 @@ const getDatabaseCredential = (): any => {
   const url = new URL(process.env.DATABASE_URL);
 
   // Extract the necessary components
+  let protocol;
+  switch(url.protocol){
+    case 'postgresql':
+      protocol = 'postgres';
+      break;
+    case 'mysql':
+      protocol = 'mysql';
+      break;
+  }
   const username = url.username;
   const password = url.password;
   const host = url.hostname;
   const port = url.port;
   const databaseName = url.pathname.substring(1); // Remove the leading slash
   return {
-    type: 'postgres',
+    type: protocol,
     host: host,
     port: port,
     username: username,
diff --git a/test/jest-e2e.json b/test/jest-e2e.json
index 7a372e7..5d9a0c0 100644
--- a/test/jest-e2e.json
+++ b/test/jest-e2e.json
@@ -14,6 +14,8 @@
     "@aditama-labs/nest-autocrud/skeleton/(.*)": "<rootDir>/../libs/skeleton/$1",
     "@aditama-labs/nest-autocrud/skeleton": "<rootDir>/../libs/skeleton",
     "@aditama-labs/nest-autocrud/prisma/(.*)": "<rootDir>/../libs/prisma/$1",
-    "@aditama-labs/nest-autocrud/prisma": "<rootDir>/../libs/prisma"
+    "@aditama-labs/nest-autocrud/prisma": "<rootDir>/../libs/prisma",
+    "@aditama-labs/nest-autocrud/typeorm/(.*)": "<rootDir>/../libs/typeorm/src/$1",
+    "@aditama-labs/nest-autocrud/typeorm": "<rootDir>/../libs/typeorm/src"
   }
 }
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index cbc5a3a..955a14b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -29,6 +29,12 @@
       ],
       "@aditama-labs/nest-autocrud/prisma/*": [
         "libs/prisma/*"
+      ],
+      "@aditama-labs/nest-autocrud/typeorm": [
+        "libs/typeorm/src"
+      ],
+      "@aditama-labs/nest-autocrud/typeorm/*": [
+        "libs/typeorm/src/*"
       ]
     }
   }