forked from yaperos/app-nodejs-codechallenge
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
centralized database config and add validation and tests for create a…
…nd transaction update
- Loading branch information
1 parent
3db9ffe
commit 35948c5
Showing
13 changed files
with
6,175 additions
and
1,014 deletions.
There are no files selected for viewing
81 changes: 50 additions & 31 deletions
81
apps/transactions/src/adapters/controllers/transaction.controller.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 |
---|---|---|
@@ -1,47 +1,66 @@ | ||
import { Request, Response } from 'express'; | ||
import { UpdateTransactionUseCase } from '../../application/updateTransaction.usecase'; | ||
import { TransactionRepositoryImpl } from '../../domain/repositories/transaction.repository'; | ||
import { TransactionRepository } from '../../domain/repositories/transaction.repository'; | ||
import { CreateTransactionUseCase } from '../../application/createTransaction.usecase'; | ||
import { CreateTransactionDTO } from '../../domain/dtos/createTransaction.dto'; | ||
import { validateCreateTransactionDto, validateUpdateTransactionBody } from '../validation/transaction.validator'; | ||
|
||
const transactionRepository = new TransactionRepositoryImpl(); | ||
const updateTransactionUseCase = new UpdateTransactionUseCase(transactionRepository); | ||
const createTransactionUseCase = new CreateTransactionUseCase(transactionRepository); | ||
|
||
export class TransactionController { | ||
private transactionRepository: TransactionRepository; | ||
|
||
export const createTransaction = async (req: Request, res: Response) => { | ||
try { | ||
const { accountExternalIdDebit, accountExternalIdCredit, transferTypeId, value } = req.body; | ||
constructor(transactionRepository: TransactionRepository) { | ||
this.transactionRepository = transactionRepository | ||
} | ||
|
||
const createTransactionDto: CreateTransactionDTO = { | ||
accountExternalIdDebit, | ||
accountExternalIdCredit, | ||
transferTypeId, | ||
value | ||
}; | ||
|
||
public createTransaction = async (req: Request, res: Response) => { | ||
try { | ||
|
||
const savedTransaction = await createTransactionUseCase.execute(createTransactionDto); | ||
if (!validateCreateTransactionDto(req.body)) { | ||
return res.status(400).json({ message: 'incorrect body' }); | ||
} | ||
|
||
return res.status(200).json(savedTransaction); | ||
} catch (error) { | ||
console.error('Error creating transaction:', error); | ||
return res.status(500).json({ message: 'Error creating transaction', error }); | ||
} | ||
}; | ||
const { accountExternalIdDebit, accountExternalIdCredit, transferTypeId, value } = req.body; | ||
|
||
const createTransactionDto: CreateTransactionDTO = { | ||
accountExternalIdDebit, | ||
accountExternalIdCredit, | ||
transferTypeId, | ||
value | ||
}; | ||
|
||
export const updateTransaction = async (req: Request, res: Response) => { | ||
const createTransactionUseCase = new CreateTransactionUseCase(this.transactionRepository) | ||
const savedTransaction = await createTransactionUseCase.execute(createTransactionDto); | ||
|
||
try { | ||
const { id } = req.params; | ||
const updatedData = req.body; | ||
return res.status(200).json(savedTransaction); | ||
} catch (error) { | ||
console.error('Error creating transaction:', error); | ||
return res.status(500).json({ message: 'Error creating transaction', error }); | ||
} | ||
}; | ||
|
||
const updateTransaction = await updateTransactionUseCase.execute(id, updatedData) | ||
public updateTransaction = async (req: Request, res: Response): Promise<Response | void> => { | ||
try { | ||
|
||
return res.status(200).json(updateTransaction) | ||
} catch (error) { | ||
console.error('Error updating transaction:', error); | ||
return res.status(500).json({ message: 'Error updating transaction', error }) | ||
} | ||
} | ||
if (!validateUpdateTransactionBody(req.body)) { | ||
return res.status(400).json({ message: 'incorrect body' }); | ||
} | ||
|
||
const { id } = req.params; | ||
const updatedData = req.body; | ||
|
||
const updateTransactionUseCase = new UpdateTransactionUseCase(this.transactionRepository); | ||
const updatedTransaction = await updateTransactionUseCase.execute(id, updatedData); | ||
|
||
return res.status(200).json(updatedTransaction); | ||
} catch (error: any) { | ||
if (error?.message === 'Transaction not found') { | ||
return res.status(404).json({ message: 'Transaction not found' }); | ||
} | ||
console.error('Error updating transaction:', error); | ||
return res.status(500).json({ message: 'Error updating transaction', error }); | ||
} | ||
}; | ||
|
||
} |
14 changes: 7 additions & 7 deletions
14
apps/transactions/src/adapters/routes/transaction.routes.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 |
---|---|---|
@@ -1,18 +1,18 @@ | ||
import { Router, Request, Response } from 'express'; | ||
import { createTransaction, updateTransaction } from '../controllers/transaction.controller'; | ||
import { TransactionController } from '../controllers/transaction.controller'; | ||
import { TransactionRepositoryImpl } from '../../domain/repositories/transaction.repository'; | ||
|
||
const router = Router(); | ||
const transactionRepository = new TransactionRepositoryImpl(); | ||
const transactionController = new TransactionController(transactionRepository) | ||
|
||
router.get('/', (req: Request, res: Response) => { | ||
res.json({ message: "Hello, this is the transactions API!" }); | ||
}); | ||
|
||
router.post('/create', async (req: Request, res: Response) => { | ||
await createTransaction(req, res) | ||
await transactionController.createTransaction(req, res) | ||
}); | ||
|
||
router.put('/:id', async (req: Request, res: Response) => { | ||
await updateTransaction(req, res) | ||
}) | ||
await transactionController.updateTransaction(req, res); | ||
}); | ||
|
||
export default router; |
37 changes: 37 additions & 0 deletions
37
apps/transactions/src/adapters/validation/transaction.validator.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,37 @@ | ||
import { CreateTransactionDTO } from '../../domain/dtos/createTransaction.dto'; | ||
import { UpdatedData } from '../../domain/dtos/updatedTransaction.dto'; | ||
|
||
export const validateCreateTransactionDto = (data: any): data is CreateTransactionDTO => { | ||
const { accountExternalIdDebit, accountExternalIdCredit, transferTypeId, value } = data; | ||
|
||
if ( | ||
typeof accountExternalIdDebit !== 'string' || | ||
typeof accountExternalIdCredit !== 'string' || | ||
typeof transferTypeId !== 'number' || | ||
![1, 2, 3].includes(transferTypeId) || | ||
typeof value !== 'number' || | ||
value <= 0 | ||
) { | ||
return false; | ||
} | ||
|
||
return true; | ||
}; | ||
|
||
export const validateUpdateTransactionBody = (data: any): data is UpdatedData => { | ||
const { transactionType, transactionStatus, value, createdAt } = data; | ||
|
||
if ( | ||
typeof transactionType !== 'object' || | ||
typeof transactionType.name !== 'number' || | ||
typeof transactionStatus !== 'object' || | ||
typeof transactionStatus.name !== 'string' || | ||
typeof value !== 'number' || | ||
value <= 0 || | ||
!(createdAt instanceof Date) | ||
) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} |
9 changes: 7 additions & 2 deletions
9
apps/transactions/src/application/updateTransaction.usecase.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 |
---|---|---|
@@ -1,14 +1,19 @@ | ||
import { UpdatedData } from "../domain/dtos/updatedTransaction.dto"; | ||
import { Transaction } from "../domain/entities/transaction.entity"; | ||
import { TransactionRepository } from '../domain/repositories/transaction.repository'; | ||
|
||
|
||
export class UpdateTransactionUseCase { | ||
constructor(private transactionRepository: TransactionRepository) { } | ||
|
||
async execute(id: string, updatedData: Partial<Transaction>): Promise<Transaction> { | ||
async execute(id: string, updatedData: UpdatedData): Promise<Transaction> { | ||
const transaction = await this.transactionRepository.findById(id) | ||
if (!transaction) { | ||
throw new Error('Transaction not found') | ||
} | ||
return await this.transactionRepository.update(id, updatedData) | ||
transaction.status = updatedData.transactionStatus.name | ||
transaction.transferTypeId = updatedData.transactionType.name | ||
await this.transactionRepository.update(id, { status: transaction.status, transferTypeId: transaction.transferTypeId }) | ||
return transaction | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
apps/transactions/src/domain/dtos/updatedTransaction.dto.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,11 @@ | ||
export interface UpdatedData { | ||
transactionExternalId: string; | ||
transactionType: { | ||
name: number; | ||
}; | ||
transactionStatus: { | ||
name: string; | ||
}; | ||
value: number; | ||
createdAt: 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
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
36 changes: 25 additions & 11 deletions
36
apps/transactions/src/infrastructure/database/transaction.ormconfig.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 |
---|---|---|
@@ -1,16 +1,30 @@ | ||
import { DataSource } from 'typeorm'; | ||
import { Transaction } from '../../domain/entities/transaction.entity'; | ||
import dotenv from 'dotenv'; | ||
|
||
const AppDataSource = new DataSource({ | ||
type: 'postgres', | ||
host: 'localhost', | ||
port: 5432, | ||
username: 'postgres', | ||
password: '123456', | ||
database: 'postgres', | ||
synchronize: true, | ||
logging: true, | ||
entities: [Transaction], | ||
}); | ||
let AppDataSource: DataSource | ||
if (process.env.NODE_ENV === 'test') { | ||
dotenv.config({ path: '.env.test' }); | ||
AppDataSource = new DataSource({ | ||
type: 'sqlite', | ||
database: './db.sqlite', | ||
synchronize: true, | ||
logging: ['query', 'error'], | ||
entities: [Transaction], | ||
}); | ||
} else { | ||
dotenv.config(); | ||
AppDataSource = new DataSource({ | ||
type: process.env.DB_TYPE as 'postgres', | ||
host: process.env.DB_HOST, | ||
port: parseInt(process.env.DB_PORT || '5432', 10), | ||
username: process.env.DB_USERNAME, | ||
password: process.env.DB_PASSWORD, | ||
database: process.env.DB_DATABASE, | ||
synchronize: Boolean(process.env.DB_SYNCHRONIZE), | ||
logging: Boolean(process.env.DB_LOGGING), | ||
entities: [Transaction], | ||
}); | ||
} | ||
|
||
export default AppDataSource; |
Oops, something went wrong.