Skip to content

Commit

Permalink
Merge pull request #82 from Rocketseat/chore/change-db-provider
Browse files Browse the repository at this point in the history
chore: change db provider
  • Loading branch information
jaksonxavier authored Jun 13, 2023
2 parents 3da8c8a + 9c91aa1 commit fbaa9f0
Show file tree
Hide file tree
Showing 41 changed files with 610 additions and 183 deletions.
12 changes: 6 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Database
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=docker
DATABASE_PASS=docker
DATABASE_PORT=3306
DATABASE_USER=root
DATABASE_PASS=toor
DATABASE_NAME=app

DATABASE_URL="postgresql://${DATABASE_USER}:${DATABASE_PASS}@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME}"
DATABASE_URL="mysql://${DATABASE_USER}:${DATABASE_PASS}@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME}"

JWT_SECRET_KEY=
JWT_PUBLIC_KEY=
JWT_SECRET_KEY='JWT_SECRET_KEY'
JWT_PUBLIC_KEY='JWT_PUBLIC_KEY'
6 changes: 3 additions & 3 deletions .env.testing.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Database
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=docker
DATABASE_PASS=docker
DATABASE_PORT=3306
DATABASE_USER=root
DATABASE_PASS=toor
DATABASE_NAME=app
35 changes: 19 additions & 16 deletions .github/workflows/run-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@ jobs:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:13
ports:
- 5432:5432
mysql:
image: bitnami/mysql
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: docker
POSTGRES_DB: app
MYSQL_DATABASE: app
MYSQL_USER: docker
MYSQL_PASSWORD: docker
MYSQL_ROOT_PASSWORD: toor
ports:
- 3306:3306
volumes:
- mysql:/var/lib/mysql
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=10
zookeeper:
image: bitnami/zookeeper:3
ports:
Expand Down Expand Up @@ -57,7 +61,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18

- name: Install dependencies (with cache)
uses: bahmutov/npm-install@v1
Expand All @@ -66,9 +70,8 @@ jobs:
run: yarn test:e2e
env:
DATABASE_HOST: localhost
DATABASE_PORT: 5432
DATABASE_USER: postgres
DATABASE_PASS: docker
DATABASE_NAME: app
DATABASE_PORT: ${{ job.services.mysql.ports[3306] }}
DATABASE_USER: root
DATABASE_PASS: toor
JWT_PUBLIC_KEY: ${{secrets.JWT_DUMMY_PUBLIC}}
JWT_SECRET_KEY: ${{secrets.JWT_DUMMY_PRIVATE}}
21 changes: 13 additions & 8 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@ services:
# Application

database:
image: 'bitnami/postgresql'
image: 'bitnami/mysql'
container_name: 'mysql'
ports:
- '5432:5432'
- '3306:3306'
environment:
- POSTGRESQL_USERNAME=docker
- POSTGRESQL_PASSWORD=docker
- POSTGRESQL_DATABASE=app
- MYSQL_DATABASE=app
- MYSQL_USER=docker
- MYSQL_PASSWORD=docker
- MYSQL_ROOT_PASSWORD=toor
volumes:
- 'postgresql_data:/bitnami/postgresql'
- 'mysql_data:/bitnami/mysql'
networks:
- app-net

redis:
image: 'bitnami/redis'
container_name: 'redis'
ports:
- '6379:6379'
environment:
Expand All @@ -29,6 +32,7 @@ services:

zookeeper:
image: 'bitnami/zookeeper:3'
container_name: 'zookeeper'
ports:
- '2181:2181'
volumes:
Expand All @@ -40,6 +44,7 @@ services:

kafka:
image: 'bitnami/kafka:3'
container_name: 'kafka'
ports:
- '9092:9092'
volumes:
Expand All @@ -63,7 +68,7 @@ volumes:
driver: local
kafka_data:
driver: local
postgresql_data:
mysql_data:
driver: local
redis_data:
driver: local
driver: local
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,14 @@
"dotenv-expand": "8.0.3",
"graphql": "16.3.0",
"graphql-query-complexity": "0.11.0",
"mysql2": "3.2.0",
"nestjs-pino": "2.5.2",
"passport": "0.6.0",
"passport-jwt": "4.0.1",
"pg": "8.7.3",
"pino-http": "6.6.0",
"reflect-metadata": "0.1.13",
"rimraf": "3.0.2",
"rxjs": "7.5.5",
"uuid": "8.3.2"
"rxjs": "7.5.5"
},
"devDependencies": {
"@commitlint/cli": "16.2.3",
Expand Down
10 changes: 10 additions & 0 deletions prisma/migrations/20230411210350_users_table/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- CreateTable
CREATE TABLE `users` (
`id` VARCHAR(191) NOT NULL,
`email` VARCHAR(191) NOT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),

UNIQUE INDEX `users_email_key`(`email`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
3 changes: 3 additions & 0 deletions prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "mysql"
22 changes: 11 additions & 11 deletions prisma/prisma-test-environment.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { Config } from '@jest/types';
import { exec } from 'child_process';
import dotenv from 'dotenv';
import NodeEnvironment from 'jest-environment-node';
import { Client } from 'pg';
import util from 'util';
import { v4 as uuid } from 'uuid';
import mysql from 'mysql2/promise';
import { exec } from 'node:child_process';
import crypto from 'node:crypto';
import util from 'node:util';

import { HOST, NAME, PASSWORD, PORT, USER } from '../src/config/database';
import { HOST, PASSWORD, PORT, USER } from '../src/config/database';

dotenv.config({ path: '.env.testing' });

Expand All @@ -21,8 +21,8 @@ export default class PrismaTestEnvironment extends NodeEnvironment {
constructor(config: Config.ProjectConfig) {
super(config);

this.schema = `test_${uuid()}`;
this.connectionString = `postgresql://${USER}:${PASSWORD}@${HOST}:${PORT}/${NAME}?schema=${this.schema}`;
this.schema = `test_${crypto.randomUUID()}`;
this.connectionString = `mysql://${USER}:${PASSWORD}@${HOST}:${PORT}/${this.schema}`;
}

async setup() {
Expand All @@ -35,12 +35,12 @@ export default class PrismaTestEnvironment extends NodeEnvironment {
}

async teardown() {
const client = new Client({
connectionString: this.connectionString,
});
const client = await mysql.createConnection(this.connectionString);

await client.connect();
await client.query(`DROP SCHEMA IF EXISTS "${this.schema}" CASCADE`);
await client.query(
`DROP DATABASE IF EXISTS \`${client.config.database}\`;`,
);
await client.end();
}
}
9 changes: 6 additions & 3 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
datasource db {
provider = "postgresql"
provider = "mysql"
url = env("DATABASE_URL")
}

Expand All @@ -8,9 +8,12 @@ generator client {
binaryTargets = ["native", "linux-musl", "rhel-openssl-1.0.x"]
}

model Example {
id String @id
model User {
id String @id
email String @unique
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
@@map("users")
}
18 changes: 0 additions & 18 deletions src/application/use-cases/example-use-case.spec.ts

This file was deleted.

16 changes: 0 additions & 16 deletions src/application/use-cases/example-use-case.ts

This file was deleted.

12 changes: 12 additions & 0 deletions src/application/use-cases/user-case.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';

import { DatabaseModule } from '@infra/database/database.module';

import { GetUserByEmailUseCase } from './user/get-user-by-email.use-case';

@Module({
imports: [DatabaseModule],
providers: [GetUserByEmailUseCase],
exports: [GetUserByEmailUseCase],
})
export class UseCasesModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { DomainError } from '@core/domain/errors/DomainError';

export class UserByEmailNotFoundError extends Error implements DomainError {
constructor(email: string) {
super(`User with email '${email}' was not found.`);
this.name = 'UserNotFound';
}
}
50 changes: 50 additions & 0 deletions src/application/use-cases/user/get-user-by-email.use-case.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { EmailBadFormattedError } from '@domain/value-objects/errors/email-bad-formatted-error';

import { UsersRepository } from '@infra/database/repositories/users.repository';

import { makeFakeUser } from '@test/factories/users.factory';
import { InMemoryUsersRepository } from '@test/repositories/in-memory-users.repository';

import { UserByEmailNotFoundError } from './errors/user-by-email-not-found.error';
import { GetUserByEmailUseCase } from './get-user-by-email.use-case';

describe('GetUserByEmailUseCase', () => {
let usersRepository: UsersRepository;

let getUserByEmailUseCase: GetUserByEmailUseCase;

beforeEach(() => {
usersRepository = new InMemoryUsersRepository();

getUserByEmailUseCase = new GetUserByEmailUseCase(usersRepository);
});

it('should be able to get user by email', async () => {
const user = makeFakeUser();

await usersRepository.create(user);

const output = await getUserByEmailUseCase.handle(user.email);

expect(output.isRight()).toBeTruthy();
expect(output.value).toEqual(user);
});

it('should be able an error is returned in case an invalid email address is provided', async () => {
const invalidEmail = 'invalid_email';

const output = await getUserByEmailUseCase.handle(invalidEmail);

expect(output.isLeft()).toBeTruthy();
expect(output.value).toBeInstanceOf(EmailBadFormattedError);
});

it('should be able to return user not found error', async () => {
const email = 'oi@rocketseat.com.br';

const output = await getUserByEmailUseCase.handle(email);

expect(output.isLeft()).toBeTruthy();
expect(output.value).toBeInstanceOf(UserByEmailNotFoundError);
});
});
37 changes: 37 additions & 0 deletions src/application/use-cases/user/get-user-by-email.use-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Injectable } from '@nestjs/common';

import { Either, left, right } from '@core/logic/Either';

import { User } from '@domain/entities/user.entity';
import { Email } from '@domain/value-objects/email';
import { EmailBadFormattedError } from '@domain/value-objects/errors/email-bad-formatted-error';

import { UsersRepository } from '@infra/database/repositories/users.repository';

import { UserByEmailNotFoundError } from './errors/user-by-email-not-found.error';

type GetUserByEmailResponse = Either<
EmailBadFormattedError | UserByEmailNotFoundError,
User
>;

@Injectable()
export class GetUserByEmailUseCase {
constructor(private readonly usersRepository: UsersRepository) {}

async handle(email: string): Promise<GetUserByEmailResponse> {
const isInvalidEmail = !Email.validate(email);

if (isInvalidEmail) {
return left(new EmailBadFormattedError(email));
}

const user = await this.usersRepository.findByEmail(email);

if (!user) {
return left(new UserByEmailNotFoundError(email));
}

return right(user);
}
}
Loading

0 comments on commit fbaa9f0

Please sign in to comment.