Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(backend): introduce User module with basic entity and signup
- Loading branch information
Showing
12 changed files
with
302 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { | ||
CreateDateColumn, | ||
PrimaryGeneratedColumn, | ||
UpdateDateColumn, | ||
} from 'typeorm'; | ||
|
||
export abstract class BaseEditableEntity { | ||
@PrimaryGeneratedColumn('uuid') | ||
id: string; | ||
|
||
@CreateDateColumn() | ||
createdAt: Date; | ||
|
||
@UpdateDateColumn() | ||
updatedAt: Date; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { CreateDateColumn, PrimaryGeneratedColumn } from 'typeorm'; | ||
|
||
export abstract class BaseReadonlyEntity { | ||
@PrimaryGeneratedColumn('uuid') | ||
id: string; | ||
|
||
@CreateDateColumn() | ||
createdAt: Date; | ||
} |
15 changes: 15 additions & 0 deletions
15
apps/backend/src/database/migrations/1627407734939-user.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { MigrationInterface, QueryRunner } from 'typeorm'; | ||
|
||
export class user1627407734939 implements MigrationInterface { | ||
name = 'user1627407734939'; | ||
|
||
public async up(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query( | ||
`CREATE TABLE "user" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "auth_id" character varying(40) NOT NULL, "email" character varying(255) NOT NULL, "first_name" character varying(80), "last_name" character varying(80), CONSTRAINT "UQ_56d00ec31dc3eed1c3f6bff4f58" UNIQUE ("auth_id"), CONSTRAINT "UQ_e12875dfb3b1d92d7d7c5377e22" UNIQUE ("email"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`, | ||
); | ||
} | ||
|
||
public async down(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`DROP TABLE "user"`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { IsEmail, IsNotEmpty, IsString } from 'class-validator'; | ||
|
||
export class CreateUserDto { | ||
@IsString() | ||
@IsNotEmpty() | ||
firstName: string; | ||
|
||
@IsString() | ||
@IsNotEmpty() | ||
lastName: string; | ||
|
||
@IsEmail() | ||
@IsNotEmpty() | ||
email: string; | ||
|
||
@IsString() | ||
@IsNotEmpty() | ||
password: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export class CreateUserError extends Error { | ||
constructor(error: Error) { | ||
super(`Failed to create a new user. ${error}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { UserController } from './user.controller'; | ||
import { UserService } from './user.service'; | ||
|
||
describe('UserController', () => { | ||
let controller: UserController; | ||
|
||
const userServiceMock = { | ||
createUser: jest.fn(), | ||
}; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [UserController], | ||
providers: [ | ||
{ | ||
provide: UserService, | ||
useValue: userServiceMock, | ||
}, | ||
], | ||
}).compile(); | ||
|
||
controller = module.get<UserController>(UserController); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(controller).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { | ||
Controller, | ||
Post, | ||
HttpStatus, | ||
HttpException, | ||
Body, | ||
} from '@nestjs/common'; | ||
import { ApiResponse, ApiTags } from '@nestjs/swagger'; | ||
|
||
import { CognitoCreateUserError } from '../cognito/error/cognito-create-user.error'; | ||
import { CreateUserDto } from './dto/create-user.dto'; | ||
import { User } from './user.entity'; | ||
import { UserService } from './user.service'; | ||
|
||
@Controller('user') | ||
export class UserController { | ||
constructor(private readonly userService: UserService) {} | ||
|
||
@Post() | ||
@ApiTags('user') | ||
@ApiResponse({ | ||
status: HttpStatus.CREATED, | ||
description: 'User has been successfully createds', | ||
}) | ||
@ApiResponse({ | ||
status: HttpStatus.SERVICE_UNAVAILABLE, | ||
description: 'Third party service error', | ||
}) | ||
@ApiResponse({ | ||
status: HttpStatus.INTERNAL_SERVER_ERROR, | ||
description: 'Internal server error', | ||
}) | ||
@ApiResponse({ | ||
status: HttpStatus.BAD_REQUEST, | ||
description: 'Bad request', | ||
}) | ||
public async createUser(@Body() createUserDto: CreateUserDto): Promise<User> { | ||
const { firstName, lastName, email, password } = createUserDto; | ||
try { | ||
return await this.userService.createUser( | ||
firstName, | ||
lastName, | ||
email, | ||
password, | ||
); | ||
} catch (error) { | ||
if (error instanceof CognitoCreateUserError) { | ||
throw new HttpException( | ||
'Service temporary unavailable', | ||
HttpStatus.SERVICE_UNAVAILABLE, | ||
); | ||
} | ||
throw new HttpException( | ||
'Internal server error', | ||
HttpStatus.INTERNAL_SERVER_ERROR, | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { Column, Entity } from 'typeorm'; | ||
import { BaseEditableEntity } from '../common/model/base-editable.entity'; | ||
|
||
@Entity() | ||
export class User extends BaseEditableEntity { | ||
@Column({ | ||
name: 'auth_id', | ||
type: 'varchar', | ||
length: 40, | ||
nullable: false, | ||
unique: true, | ||
}) | ||
public authId: string; | ||
|
||
@Column({ | ||
name: 'email', | ||
type: 'varchar', | ||
length: 255, | ||
nullable: false, | ||
unique: true, | ||
}) | ||
public email: string; | ||
|
||
@Column({ name: 'first_name', type: 'varchar', length: 80, nullable: true }) | ||
public firstName: string; | ||
|
||
@Column({ name: 'last_name', type: 'varchar', length: 80, nullable: true }) | ||
public lastName: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { TypeOrmModule } from '@nestjs/typeorm'; | ||
|
||
import { UserService } from './user.service'; | ||
import { UserController } from './user.controller'; | ||
import { CognitoModule } from '../cognito/cognito.module'; | ||
import { User } from './user.entity'; | ||
import { LoggerModule } from '../logger/logger.module'; | ||
|
||
@Module({ | ||
providers: [UserService], | ||
controllers: [UserController], | ||
imports: [TypeOrmModule.forFeature([User]), CognitoModule, LoggerModule], | ||
}) | ||
export class UserModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { getRepositoryToken } from '@nestjs/typeorm'; | ||
|
||
import { User } from './user.entity'; | ||
import { UserService } from './user.service'; | ||
import { LoggerService } from '../logger/logger.service'; | ||
import { LoggerServiceMock } from '../logger/logger.service.mock'; | ||
import { CognitoService } from '../cognito/cognito.service'; | ||
|
||
describe('UserService', () => { | ||
let service: UserService; | ||
|
||
const userRepositoryMock = { | ||
save: jest.fn((user: User) => Promise.resolve(user)), | ||
}; | ||
|
||
const cognitoServiceMock = { | ||
createUser: jest.fn((user) => Promise.resolve(user)), | ||
}; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ | ||
UserService, | ||
{ | ||
provide: LoggerService, | ||
useValue: LoggerServiceMock, | ||
}, | ||
{ | ||
provide: getRepositoryToken(User), | ||
useValue: userRepositoryMock, | ||
}, | ||
{ | ||
provide: CognitoService, | ||
useValue: cognitoServiceMock, | ||
}, | ||
], | ||
}).compile(); | ||
|
||
service = module.get<UserService>(UserService); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(service).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
import { Repository } from 'typeorm'; | ||
|
||
import { User } from './user.entity'; | ||
import { LoggerService } from '../logger/logger.service'; | ||
import { CognitoService } from '../cognito/cognito.service'; | ||
import { CreateUserError } from './error/create-user.error'; | ||
|
||
@Injectable() | ||
export class UserService { | ||
constructor( | ||
private readonly loggerService: LoggerService, | ||
private readonly cognitoService: CognitoService, | ||
@InjectRepository(User) private readonly usersRepository: Repository<User>, | ||
) { | ||
this.loggerService.setContext(UserService.name); | ||
} | ||
|
||
public async createUser( | ||
firstName: string, | ||
lastName: string, | ||
email: string, | ||
password: string, | ||
): Promise<User> { | ||
const user = new User(); | ||
|
||
user.firstName = firstName; | ||
user.lastName = lastName; | ||
user.email = email; | ||
|
||
const authId = await this.cognitoService.createUser(user.email, password); | ||
|
||
user.authId = authId; | ||
|
||
try { | ||
return await this.usersRepository.save(user); | ||
} catch (error) { | ||
this.loggerService.log(`Failed to create a new user. ${error}`); | ||
throw new CreateUserError(error); | ||
} | ||
} | ||
} |