Skip to content

Commit

Permalink
feat(app-user): userManagement service scaffolding (#1)
Browse files Browse the repository at this point in the history
* feat: create user-management application

* feat: create userModule

* build: set root docker-compose and mysql container

* build: update version nestjs dependency

* feat: set database configuration and add basic entities

* build: configure migrations

* feat: add configModule

* refactor: load dbConfig using configModule

* feat: add healthCheck endpoint
  • Loading branch information
imione authored Dec 21, 2021
1 parent 6cbe126 commit 1e2ade6
Show file tree
Hide file tree
Showing 29 changed files with 17,232 additions and 3,414 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ lerna-debug.log*
*.launch
.settings/
*.sublime-workspace
.vscode

# IDE - VSCode
.vscode/*
Expand Down
14 changes: 14 additions & 0 deletions apps/user-management/__migrations/Migration20211219172816.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Migration } from '@mikro-orm/migrations';

export class Migration20211219172816 extends Migration {

async up(): Promise<void> {
this.addSql('create table `User` (`id` varchar not null, `created_at` datetime not null, `updated_at` datetime not null, `version` int(11) not null default 1, `external_id` varchar(255) not null, `meta` json null) default character set utf8mb4 engine = InnoDB;');
this.addSql('alter table `User` add primary key `User_pkey`(`id`);');
this.addSql('alter table `User` add index `User_created_at_index`(`created_at`);');
this.addSql('alter table `User` add index `User_updated_at_index`(`updated_at`);');
this.addSql('alter table `User` add index `User_external_id_index`(`external_id`);');
this.addSql('alter table `User` add unique `User_external_id_unique`(`external_id`);');
}

}
44 changes: 44 additions & 0 deletions apps/user-management/__migrations/migrate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { MikroORM } from '@mikro-orm/core';

import { Resource } from '@apps/user-management/user/infra/persistence/common/Resource';
import { RootEntity } from '@apps/user-management/user/infra/persistence/common/RootEntity';
import { User } from '@apps/user-management/user/infra/persistence/user/User';

type MigrationMode = 'create' | 'up' | 'down' | 'pending';

(async () => {
const mode = process.argv[2] as MigrationMode;

const orm = await MikroORM.init({
entities: [RootEntity, Resource, User],
dbName: 'UserManagement',
host: '127.0.0.1',
port: 13306,
user: 'root',
password: 'test',
type: 'mysql',
migrations: {
tableName: 'MikroOrmMigrations',
path: './apps/user-management/__migrations',
},
});

const migrator = orm.getMigrator();

switch (mode) {
case 'create':
await migrator.createMigration();
break;
case 'up':
await migrator.up();
break;
case 'down':
break;
case 'pending':
const pendingMigrations = await migrator.getPendingMigrations();
console.log(pendingMigrations.map(migrations => migrations.file));
break;
}

await orm.close(true);
})();
28 changes: 28 additions & 0 deletions apps/user-management/src/AppModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

import { validationSchema } from '@apps/user-management/config/ValidationSchema';
import DatabaseConfig from '@apps/user-management/config/DatabaseConfig';
import { CoreModule } from '@apps/user-management/core/CoreModule';
import { UserModule } from '@apps/user-management/user/UserModule';

const configs = [DatabaseConfig];

@Module({
imports: [
CoreModule,
ConfigModule.forRoot({
envFilePath: [
`${__dirname}/config/envs/.${process.env.NODE_ENV ??
'development'}.env`,
],
load: [...configs],
isGlobal: true,
validationSchema,
}),
UserModule,
],
controllers: [],
providers: [],
})
export class AppModule {}
9 changes: 9 additions & 0 deletions apps/user-management/src/config/DatabaseConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { registerAs } from '@nestjs/config';

export default registerAs('database', () => ({
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT, 10) || 3306,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
dbSchema: process.env.DB_SCHEMA,
}));
16 changes: 16 additions & 0 deletions apps/user-management/src/config/ValidationSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as Joi from 'joi';

export const validationSchema = Joi.object({
NODE_ENV: Joi.string()
.valid('production', 'stage', 'dev')
.required(),
SERVICE_ID: Joi.string().required(),
DB_HOST: Joi.string().required(),
DB_PORT: Joi.number()
.positive()
.integer()
.required(),
DB_USER: Joi.string().required(),
DB_PASSWORD: Joi.string().required(),
DB_SCHEMA: Joi.string().required(),
});
8 changes: 8 additions & 0 deletions apps/user-management/src/config/envs/.development.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
NODE_ENV=dev
SERVICE_ID=UserManagement

DB_HOST=localhost
DB_PORT=13306
DB_USER=root
DB_PASSWORD=test
DB_SCHEMA=UserManagement
37 changes: 37 additions & 0 deletions apps/user-management/src/core/CoreModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService, ConfigType } from '@nestjs/config';

import DatabaseConfig from '@apps/user-management/config/DatabaseConfig';

import { HealthModule } from '@apps/user-management/core/health/HealthModule';

import { Resource } from '@apps/user-management/user/infra/persistence/common/Resource';
import { RootEntity } from '@apps/user-management/user/infra/persistence/common/RootEntity';
import { User } from '@apps/user-management/user/infra/persistence/user/User';

@Module({
imports: [
MikroOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => {
const { host, port, user, password, dbSchema } = configService.get(
'database',
) as ConfigType<typeof DatabaseConfig>;

return {
entities: [RootEntity, Resource, User],
dbName: dbSchema,
host,
port,
user,
password,
type: 'mysql',
};
},
inject: [ConfigService],
}),
HealthModule,
],
})
export class CoreModule {}
19 changes: 19 additions & 0 deletions apps/user-management/src/core/health/HealthController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Controller, Get } from '@nestjs/common';
import { HealthCheckService } from '@nestjs/terminus';

import { DatabaseIndicator } from '@apps/user-management/core/health/indicators/DatabaseIndicator';

@Controller('health')
export class HealthController {
constructor(
private readonly health: HealthCheckService,
private readonly dbIndicator: DatabaseIndicator,
) {}

@Get()
async getHealth() {
return this.health.check([
async () => this.dbIndicator.isHealthy('UserManagement'),
]);
}
}
12 changes: 12 additions & 0 deletions apps/user-management/src/core/health/HealthModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';

import { HealthController } from '@apps/user-management/core/health/HealthController';
import { DatabaseIndicator } from '@apps/user-management/core/health/indicators/DatabaseIndicator';

@Module({
imports: [TerminusModule],
controllers: [HealthController],
providers: [DatabaseIndicator],
})
export class HealthModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { MikroORM } from '@mikro-orm/core';
import { Injectable } from '@nestjs/common';
import {
HealthCheckError,
HealthIndicator,
HealthIndicatorResult,
} from '@nestjs/terminus';

@Injectable()
export class DatabaseIndicator extends HealthIndicator {
constructor(private readonly orm: MikroORM) {
super();
}

async isHealthy(key: string): Promise<HealthIndicatorResult> {
const healthy = await this.orm.isConnected();

const result = this.getStatus(key, healthy);
if (healthy) {
return result;
}

throw new HealthCheckError('Database is unhealthy', result);
}
}
3 changes: 2 additions & 1 deletion src/main.ts → apps/user-management/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

import { AppModule } from '@apps/user-management/AppModule';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
Expand Down
8 changes: 8 additions & 0 deletions apps/user-management/src/user/UserModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Module } from '@nestjs/common';

import { UserController } from '@apps/user-management/user/interface/UserController';

@Module({
controllers: [UserController],
})
export class UserModule {}
12 changes: 12 additions & 0 deletions apps/user-management/src/user/infra/persistence/common/Resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Entity, JsonType, Property } from '@mikro-orm/core';

import { RootEntity } from '@apps/user-management/user/infra/persistence/common/RootEntity';

@Entity({ abstract: true })
export abstract class Resource extends RootEntity {
@Property({ unique: true, index: true })
externalId: string;

@Property({ type: JsonType, nullable: true })
meta: object;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Entity, PrimaryKey, Property } from '@mikro-orm/core';

@Entity({ abstract: true })
export abstract class RootEntity {
@PrimaryKey({ columnType: 'varchar', length: 128 })
id: string;

@Property({ onCreate: () => new Date(), index: true })
createdAt: Date = new Date();

@Property({ onUpdate: () => new Date(), index: true })
updatedAt: Date;

@Property({ version: true })
version: number;
}
6 changes: 6 additions & 0 deletions apps/user-management/src/user/infra/persistence/user/User.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Entity } from '@mikro-orm/core';

import { Resource } from '@apps/user-management/user/infra/persistence/common/Resource';

@Entity({ tableName: 'User' })
export class User extends Resource {}
17 changes: 17 additions & 0 deletions apps/user-management/src/user/interface/UserController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { MikroORM } from '@mikro-orm/core';
import { Controller, Get, Param } from '@nestjs/common';

@Controller('users')
export class UserController {
constructor(private readonly orm: MikroORM) {}

@Get('/:userId')
async getUser(@Param('userId') userId: string): Promise<void> {
console.log(await this.orm.isConnected());

console.log(userId);

return;
}
s;
}
9 changes: 9 additions & 0 deletions apps/user-management/tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"declaration": false,
"outDir": "../../dist/apps/user-management"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
}
14 changes: 14 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: '3.8'

services:
db:
image: mysql:5.7
restart: always
environment:
- MYSQL_ROOT_PASSWORD=test
ports:
- '13306:3306'
volumes:
- ./.mysql:/docker-entrypoint-initdb.d
command:
[mysqld, --character-set-server=utf8, --collation-server=utf8_general_ci]
25 changes: 24 additions & 1 deletion nest-cli.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
{
"collection": "@nestjs/schematics",
"sourceRoot": "src"
"sourceRoot": "apps/msa-iam-sample/src",
"monorepo": true,
"root": "apps/msa-iam-sample",
"compilerOptions": {
"webpack": true,
"tsConfigPath": "apps/msa-iam-sample/tsconfig.app.json"
},
"projects": {
"user-management": {
"type": "application",
"root": "apps/user-management",
"entryFile": "main",
"sourceRoot": "apps/user-management/src",
"compilerOptions": {
"tsConfigPath": "apps/user-management/tsconfig.app.json",
"assets": [
{
"include": "**/*.env",
"watchAssets": true
}
]
}
}
}
}
Loading

0 comments on commit 1e2ade6

Please sign in to comment.