# Directory Structure
```
├── .gitignore
├── jest.config.ts
├── package.json
├── prisma
│ ├── migrations
│ │ ├── 20220819211355_create_deliveryman
│ │ │ └── migration.sql
│ │ ├── 20220819213758_update_name_deliveryman
│ │ │ └── migration.sql
│ │ ├── 20220819213956_create_clients_table
│ │ │ └── migration.sql
│ │ ├── 20220819214400_create_deliveries_table
│ │ │ └── migration.sql
│ │ ├── 20220819233031_alter_table_deliveries
│ │ │ └── migration.sql
│ │ ├── 20220819233224_alter_endat_deliveries_table
│ │ │ └── migration.sql
│ │ ├── 20220819234017_alter_end_date
│ │ │ └── migration.sql
│ │ └── migration_lock.toml
│ └── schema.prisma
├── src
│ ├── @types
│ │ └── express
│ │ └── index.d.ts
│ ├── database
│ │ ├── prismaClient.ts
│ │ └── repositories
│ │ ├── ClientRepository.ts
│ │ └── interfaces
│ │ └── IClientRepository.ts
│ ├── kafka
│ │ └── producer.ts
│ ├── middlewares
│ │ ├── ensureAuthClient.ts
│ │ └── ensureAuthDeliveryman.ts
│ ├── modules
│ │ ├── account
│ │ │ ├── authenticateClient
│ │ │ │ ├── AuthenticateClientController.ts
│ │ │ │ └── AuthenticateClientUseCase.ts
│ │ │ └── authenticateDeliveryman
│ │ │ ├── AuthenticateDeliverymanController.ts
│ │ │ └── AuthenticateDeliverymanUseCase.ts
│ │ ├── clients
│ │ │ └── useCases
│ │ │ ├── createClient
│ │ │ │ ├── createClient.spec.ts
│ │ │ │ ├── createClientController.ts
│ │ │ │ ├── createClienteUseCase.ts
│ │ │ │ └── index.ts
│ │ │ └── findClientDeliveries
│ │ │ ├── FindClientDeliveries.spec.ts
│ │ │ ├── FindClientDeliveriesController.ts
│ │ │ ├── FindClientDeliveriesUseCase.ts
│ │ │ └── index.ts
│ │ ├── deliveries
│ │ │ ├── updateEndDate
│ │ │ │ ├── UpdateEndDateController.ts
│ │ │ │ └── UpdateEndDateUseCase.ts
│ │ │ └── useCases
│ │ │ ├── createDelivery
│ │ │ │ ├── CreateDeliveryController.ts
│ │ │ │ └── CreateDeliveryUseCase.ts
│ │ │ ├── findAllAvailable
│ │ │ │ ├── FindAllAvailableController.ts
│ │ │ │ └── FindAllAvailableUseCase.ts
│ │ │ └── updateDeliveryman
│ │ │ ├── UpdateDeliverymanController.ts
│ │ │ └── UpdateDeliverymanUseCase.ts
│ │ └── deliveryman
│ │ └── useCases
│ │ ├── createDeliveryman
│ │ │ ├── CreateDeliverymanController.ts
│ │ │ └── CreateDeliverymanUseCase.ts
│ │ └── findAllDeliveries
│ │ ├── FindAllDeliveriesController.ts
│ │ └── FindAllDeliveriesUseCase.ts
│ ├── routes.ts
│ ├── server.ts
│ └── tests
│ └── inMemoryDatabases
│ └── InMemoryClientsRepository.ts
├── tsconfig.json
└── yarn.lock
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
node_modules
# Keep environment variables out of version control
.env
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819234017_alter_end_date/migration.sql:
--------------------------------------------------------------------------------
```sql
-- AlterTable
ALTER TABLE "deliveries" ALTER COLUMN "end_at" DROP DEFAULT;
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819233224_alter_endat_deliveries_table/migration.sql:
--------------------------------------------------------------------------------
```sql
-- AlterTable
ALTER TABLE "deliveries" ALTER COLUMN "end_at" SET DEFAULT CURRENT_TIMESTAMP;
```
--------------------------------------------------------------------------------
/src/database/prismaClient.ts:
--------------------------------------------------------------------------------
```typescript
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient()
export { prisma }
```
--------------------------------------------------------------------------------
/src/@types/express/index.d.ts:
--------------------------------------------------------------------------------
```typescript
declare namespace Express{
export interface Request{
id_client: string;
id_deliveryman: string;
}
}
```
--------------------------------------------------------------------------------
/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
```toml
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"sourceMap": true,
"outDir": "dist",
"strict": true,
"lib": ["esnext"],
"esModuleInterop": true
}
}
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819213956_create_clients_table/migration.sql:
--------------------------------------------------------------------------------
```sql
-- CreateTable
CREATE TABLE "clients" (
"id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"password" TEXT NOT NULL,
CONSTRAINT "clients_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "clients_username_key" ON "clients"("username");
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819211355_create_deliveryman/migration.sql:
--------------------------------------------------------------------------------
```sql
-- CreateTable
CREATE TABLE "Deliveryman" (
"id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"password" TEXT NOT NULL,
CONSTRAINT "Deliveryman_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Deliveryman_username_key" ON "Deliveryman"("username");
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/findAllAvailable/FindAllAvailableUseCase.ts:
--------------------------------------------------------------------------------
```typescript
import { prisma } from "../../../../database/prismaClient";
export class FindAllAvailableUseCase{
async execute(){
const deliveries = await prisma.deliveries.findMany({
where: {
end_at: null,
id_deliveryman: null,
}
})
return deliveries
}
}
```
--------------------------------------------------------------------------------
/src/database/repositories/interfaces/IClientRepository.ts:
--------------------------------------------------------------------------------
```typescript
import { Clients } from "@prisma/client";
export interface CreateRequestProps{
username: string;
password: string
}
export interface FindRequestProps{
id_client: string;
}
export interface IClientRepository {
create({ username, password }: CreateRequestProps): Promise<Clients>
find({ id_client }: FindRequestProps): Promise<Clients[]>
}
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/createDelivery/CreateDeliveryUseCase.ts:
--------------------------------------------------------------------------------
```typescript
import { prisma } from "../../../../database/prismaClient";
interface ICreateDelivery{
item_name: string;
id_client: string;
}
export class CreateDeliveryUseCase{
async execute({ item_name, id_client }: ICreateDelivery){
const delivery = await prisma.deliveries.create({
data: {
item_name,
id_client,
}
})
return delivery
}
}
```
--------------------------------------------------------------------------------
/src/modules/deliveries/updateEndDate/UpdateEndDateUseCase.ts:
--------------------------------------------------------------------------------
```typescript
import { prisma } from "../../../database/prismaClient";
interface IUpdateEndDate{
id_delivery: string;
}
export class UpdateEndDateUseCase{
async execute({ id_delivery }: IUpdateEndDate){
const delivery = await prisma.deliveries.update({
where: {
id: id_delivery,
},
data: {
end_at: new Date()
}
})
return delivery
}
}
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/findAllAvailable/FindAllAvailableController.ts:
--------------------------------------------------------------------------------
```typescript
import { Request, Response } from "express";
import { FindAllAvailableUseCase } from "./findAllAvailableUseCase";
export class FindAllAvailableController{
async handle(request: Request, response: Response){
const findAllAvailableUseCase = new FindAllAvailableUseCase()
const deliveries = await findAllAvailableUseCase.execute()
return response.json(deliveries)
}
}
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819233031_alter_table_deliveries/migration.sql:
--------------------------------------------------------------------------------
```sql
-- DropForeignKey
ALTER TABLE "deliveries" DROP CONSTRAINT "deliveries_id_deliveryman_fkey";
-- AlterTable
ALTER TABLE "deliveries" ALTER COLUMN "id_deliveryman" DROP NOT NULL,
ALTER COLUMN "end_at" DROP NOT NULL;
-- AddForeignKey
ALTER TABLE "deliveries" ADD CONSTRAINT "deliveries_id_deliveryman_fkey" FOREIGN KEY ("id_deliveryman") REFERENCES "deliveryman"("id") ON DELETE SET NULL ON UPDATE CASCADE;
```
--------------------------------------------------------------------------------
/src/modules/deliveries/updateEndDate/UpdateEndDateController.ts:
--------------------------------------------------------------------------------
```typescript
import { Request, Response } from "express";
import { UpdateEndDateUseCase } from "./UpdateEndDateUseCase";
export class UpdateEndDateController{
async handle(request: Request, response: Response){
const { id } = request.params;
const updateEndDateUseCase = new UpdateEndDateUseCase()
const result = await updateEndDateUseCase.execute({
id_delivery: id
})
return response.json(result)
}
}
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/updateDeliveryman/UpdateDeliverymanUseCase.ts:
--------------------------------------------------------------------------------
```typescript
import { prisma } from "../../../../database/prismaClient";
interface IUpdateDeliveryman{
id_delivery: string;
id_deliveryman: string;
}
export class UpdateDeliverymanUseCase{
async execute({ id_delivery, id_deliveryman }: IUpdateDeliveryman){
const result = await prisma.deliveries.update({
where: {
id: id_delivery
},
data: {
id_deliveryman
}
})
return result
}
}
```
--------------------------------------------------------------------------------
/src/modules/deliveryman/useCases/findAllDeliveries/FindAllDeliveriesController.ts:
--------------------------------------------------------------------------------
```typescript
import { Request, Response } from "express";
import { FindAllDeliveriesUseCase } from "./FindAllDeliveriesUseCase";
export class FindAllDeliveriesController{
async handle(request: Request, response: Response){
const { id_deliveryman } = request
const findAllDeliveriesUseCase = new FindAllDeliveriesUseCase()
const result = await findAllDeliveriesUseCase.execute({
id_deliveryman,
})
return response.json(result)
}
}
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/createClient/createClientController.ts:
--------------------------------------------------------------------------------
```typescript
import { Request, Response } from "express";
import { CreateClientUseCase } from "./createClienteUseCase";
export class CreateClientController{
constructor (private createClientUseCase: CreateClientUseCase){}
async handle(request: Request, response: Response){
const { username, password } = request.body;
const result = await this.createClientUseCase.execute({
username,
password
})
return response.json(result)
}
}
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819213758_update_name_deliveryman/migration.sql:
--------------------------------------------------------------------------------
```sql
/*
Warnings:
- You are about to drop the `Deliveryman` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropTable
DROP TABLE "Deliveryman";
-- CreateTable
CREATE TABLE "deliveryman" (
"id" TEXT NOT NULL,
"username" TEXT NOT NULL,
"password" TEXT NOT NULL,
CONSTRAINT "deliveryman_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "deliveryman_username_key" ON "deliveryman"("username");
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/findClientDeliveries/FindClientDeliveriesUseCase.ts:
--------------------------------------------------------------------------------
```typescript
import { prisma } from "../../../../database/prismaClient";
import { IClientRepository } from "../../../../database/repositories/interfaces/IClientRepository";
interface IFindClientDeliveries{
id_client: string;
}
export class FindClientDeliveriesUseCase{
constructor(
private clientRepository: IClientRepository
){}
async execute({id_client}: IFindClientDeliveries){
const client = await this.clientRepository.find({id_client})
return client
}
}
```
--------------------------------------------------------------------------------
/src/modules/account/authenticateClient/AuthenticateClientController.ts:
--------------------------------------------------------------------------------
```typescript
import { Request, Response } from "express";
import { AuthenticateClientUseCase } from "./AuthenticateClientUseCase";
export class AuthenticateClientController{
async handle(request: Request, response: Response){
const { username, password } = request.body;
const authenticateClientUseCase = new AuthenticateClientUseCase()
const result = await authenticateClientUseCase.execute({
username,
password
})
return response.json(result)
}
}
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/findClientDeliveries/FindClientDeliveriesController.ts:
--------------------------------------------------------------------------------
```typescript
import { Request, Response } from "express";
import { FindClientDeliveriesUseCase } from "./FindClientDeliveriesUseCase";
export class FindClientDeliveriesController{
constructor(
private findClientDeliveriesUseCase: FindClientDeliveriesUseCase
){}
async handle(request: Request, response: Response){
const { id_client } = request
const result = await this.findClientDeliveriesUseCase.execute({
id_client
})
return response.json(result)
}
}
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/createDelivery/CreateDeliveryController.ts:
--------------------------------------------------------------------------------
```typescript
import { Request, Response } from "express";
import { CreateDeliveryUseCase } from "./CreateDeliveryUseCase";
export class CreateDeliveryController{
async handle(request: Request, response: Response){
const { item_name } = request.body;
const { id_client } = request
const createDeliveryUseCase = new CreateDeliveryUseCase()
const delivery = await createDeliveryUseCase.execute({
id_client,
item_name
})
return response.json(delivery)
}
}
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/createClient/index.ts:
--------------------------------------------------------------------------------
```typescript
import { ClientRepository } from "../../../../database/repositories/ClientRepository";
import { CreateClientController } from "./createClientController";
import { CreateClientUseCase } from "./createClienteUseCase";
const clientRepository = ClientRepository.getInstance()
const createClientUseCase = new CreateClientUseCase(clientRepository)
const createClientController = new CreateClientController(createClientUseCase)
export { createClientController, clientRepository, createClientUseCase }
```
--------------------------------------------------------------------------------
/src/modules/account/authenticateDeliveryman/AuthenticateDeliverymanController.ts:
--------------------------------------------------------------------------------
```typescript
import { Request, Response } from "express";
import { AuthenticateDeliverymanUseCase } from "./AuthenticateDeliverymanUseCase";
export class AuthenticateDeliverymanController{
async handle(request: Request, response: Response){
const { username, password } = request.body;
const authenticateDeliverymanUseCase = new AuthenticateDeliverymanUseCase()
const result = await authenticateDeliverymanUseCase.execute({
username,
password
})
return response.json(result)
}
}
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/updateDeliveryman/UpdateDeliverymanController.ts:
--------------------------------------------------------------------------------
```typescript
import { Request, Response } from "express";
import { UpdateDeliverymanUseCase } from "./UpdateDeliverymanUseCase";
export class UpdateDeliverymanController{
async handle(request: Request, response: Response){
const { id } = request.params
const { id_deliveryman } = request
const updateDeliverymanUseCase = new UpdateDeliverymanUseCase()
const delivery = await updateDeliverymanUseCase.execute({
id_delivery: id,
id_deliveryman
})
return response.json(delivery)
}
}
```
--------------------------------------------------------------------------------
/src/modules/deliveryman/useCases/findAllDeliveries/FindAllDeliveriesUseCase.ts:
--------------------------------------------------------------------------------
```typescript
import { prisma } from "../../../../database/prismaClient";
interface IFindAllDeliveries{
id_deliveryman: string;
}
export class FindAllDeliveriesUseCase{
async execute({ id_deliveryman }: IFindAllDeliveries){
const deliveryman = await prisma.deliveryman.findFirst({
where: {
id: id_deliveryman,
},
select: {
id: true,
username: true,
Deliveries: {
where: {
end_at: null
}
}
}
})
return deliveryman
}
}
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/findClientDeliveries/index.ts:
--------------------------------------------------------------------------------
```typescript
import { ClientRepository } from "../../../../database/repositories/ClientRepository";
import { FindClientDeliveriesController } from "./FindClientDeliveriesController";
import { FindClientDeliveriesUseCase } from "./FindClientDeliveriesUseCase";
const clientRepository = ClientRepository.getInstance()
const findClientDeliveriesUseCase = new FindClientDeliveriesUseCase(clientRepository)
const findClientDeliveriesController = new FindClientDeliveriesController(findClientDeliveriesUseCase)
export { findClientDeliveriesController }
```
--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------
```typescript
import express, { NextFunction, Request, Response } from "express";
import "express-async-errors"
import { routes } from "./routes";
const app = express()
app.use(express.json())
app.use(routes);
app.use((err: Error, request: Request, response: Response, next: NextFunction) => {
if(err instanceof Error){
return response.status(400).json({
message: err.message
})
}
return response.status(500).json({
status: "error",
message: "internal server error"
})
})
app.listen(3333, () => console.log('server is running'))
```
--------------------------------------------------------------------------------
/src/middlewares/ensureAuthClient.ts:
--------------------------------------------------------------------------------
```typescript
import { NextFunction, Request, Response } from "express";
import { verify } from "jsonwebtoken";
interface IPayload{
sub: string;
}
export async function ensureAuthClient(request: Request, response: Response, next: NextFunction){
const authHeader = request.headers.authorization;
if(!authHeader){
return response.status(401).json({
message: "token missing"
})
}
const [, token] = authHeader.split(" ");
try{
const { sub } = verify(token, 'a905d89574379cec8ba1fc0438bf9597') as IPayload;
request.id_client = sub;
return next()
}catch{
return response.status(401).json({
message: "Invalid token"
})
}
}
```
--------------------------------------------------------------------------------
/src/middlewares/ensureAuthDeliveryman.ts:
--------------------------------------------------------------------------------
```typescript
import { NextFunction, Request, Response } from "express";
import { verify } from "jsonwebtoken";
interface IPayload{
sub: string;
}
export async function ensureAuthDeliveryman(request: Request, response: Response, next: NextFunction){
const authHeader = request.headers.authorization;
if(!authHeader){
return response.status(401).json({
message: "token missing"
})
}
const [, token] = authHeader.split(" ");
try{
const { sub } = verify(token, 'a905d89574379cec8ba1fc0438bf9597') as IPayload;
request.id_deliveryman = sub;
return next()
}catch{
return response.status(401).json({
message: "Invalid token"
})
}
}
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819214400_create_deliveries_table/migration.sql:
--------------------------------------------------------------------------------
```sql
-- CreateTable
CREATE TABLE "deliveries" (
"id" TEXT NOT NULL,
"id_client" TEXT NOT NULL,
"id_deliveryman" TEXT NOT NULL,
"item_name" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"end_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "deliveries_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "deliveries" ADD CONSTRAINT "deliveries_id_client_fkey" FOREIGN KEY ("id_client") REFERENCES "clients"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "deliveries" ADD CONSTRAINT "deliveries_id_deliveryman_fkey" FOREIGN KEY ("id_deliveryman") REFERENCES "deliveryman"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
```
--------------------------------------------------------------------------------
/src/modules/deliveryman/useCases/createDeliveryman/CreateDeliverymanController.ts:
--------------------------------------------------------------------------------
```typescript
import { Request, Response } from "express";
import { producer } from "../../../../kafka/producer";
import { CreateDeliverymanUseCase } from "./CreateDeliverymanUseCase";
import { randomUUID } from 'node:crypto'
export class CreateDeliverymanController {
async handle(request: Request, response: Response){
const { username, password } = request.body;
const createDeliverymanUseCase = new CreateDeliverymanUseCase()
const result = await createDeliverymanUseCase.execute({
username,
password
})
await producer({
category: 'deliveryman',
content: `Novo deliveryman criado: ${username}`,
recipientId: randomUUID()
})
return response.json(result)
}
}
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/createClient/createClienteUseCase.ts:
--------------------------------------------------------------------------------
```typescript
import { prisma } from "../../../../database/prismaClient";
import { IClientRepository } from "../../../../database/repositories/interfaces/IClientRepository";
interface ICreateClient{
username: string;
password: string;
}
export class CreateClientUseCase{
constructor(private clientRepository: IClientRepository){}
async execute({ username, password }: ICreateClient){
const clientAlreadyExists = await prisma.clients.findFirst({
where: {
username: {
equals: username,
mode: "insensitive"
}
}
})
if(clientAlreadyExists){
throw new Error("Client already exists")
}
const client = this.clientRepository.create({password, username})
return client
}
}
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "backend-entregas",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"type": "commonjs",
"scripts": {
"dev": "ts-node-dev --exit-child --transpile-only --ignore-watch node_modules src/server.ts",
"test": "jest"
},
"devDependencies": {
"@types/bcrypt": "^5.0.0",
"@types/express": "^4.17.13",
"@types/jest": "^29.4.0",
"@types/jsonwebtoken": "^8.5.8",
"jest": "^29.4.1",
"prisma": "^4.2.1",
"ts-jest": "^29.0.5",
"ts-node-dev": "^2.0.0",
"typescript": "^4.7.4"
},
"dependencies": {
"@prisma/client": "^4.2.1",
"bcrypt": "^5.0.1",
"express": "^4.18.1",
"express-async-errors": "^3.1.1",
"jsonwebtoken": "^8.5.1",
"kafkajs": "^2.2.3"
}
}
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/findClientDeliveries/FindClientDeliveries.spec.ts:
--------------------------------------------------------------------------------
```typescript
import { InMemoryClientRepository } from "../../../../tests/inMemoryDatabases/InMemoryClientsRepository"
import { FindClientDeliveriesUseCase } from "./FindClientDeliveriesUseCase"
const clientRepository = new InMemoryClientRepository()
const findClientDeliveries = new FindClientDeliveriesUseCase(clientRepository)
describe("Find client Delivires", () => {
it("should found a client delivery", async() => {
const client = await findClientDeliveries.execute({
id_client: '455667'
})
expect(client).toHaveLength(1)
})
it("should return an error if client not found", async() => {
expect(async () => {
await findClientDeliveries.execute({
id_client: '645456654'
})
}).rejects.toThrow()
})
})
```
--------------------------------------------------------------------------------
/src/modules/account/authenticateClient/AuthenticateClientUseCase.ts:
--------------------------------------------------------------------------------
```typescript
import { prisma } from "../../../database/prismaClient";
import { compare } from "bcrypt"
import { sign } from "jsonwebtoken"
interface IAuthenticateClient {
username: string;
password: string
}
export class AuthenticateClientUseCase{
async execute({ username, password }: IAuthenticateClient){
const client = await prisma.clients.findFirst({
where: {
username
}
})
if(!client){
throw new Error("Username or password is invalid!")
}
const passwordMatch = await compare(password, client.password)
if(!passwordMatch){
throw new Error("Username or password is invalid!")
}
const token = sign( {username}, "a905d89574379cec8ba1fc0438bf9597", {
subject: client.id,
expiresIn: "1d"
})
return token
}
}
```
--------------------------------------------------------------------------------
/src/modules/deliveryman/useCases/createDeliveryman/CreateDeliverymanUseCase.ts:
--------------------------------------------------------------------------------
```typescript
import { hash } from "bcrypt";
import { prisma } from "../../../../database/prismaClient";
interface ICreateDeliveryman{
username: string;
password: string;
}
export class CreateDeliverymanUseCase{
async execute({ username, password }: ICreateDeliveryman){
// validar se username já existe
const deliverymanAlreadyExists = await prisma.deliveryman.findFirst({
where: {
username: {
equals: username,
mode: "insensitive"
}
}
})
if(deliverymanAlreadyExists){
throw new Error("Deliveryman already exists!")
}
const hashPassword = await hash(password, 10);
const deliveryman = await prisma.deliveryman.create({
data: {
username,
password: hashPassword
}
})
return deliveryman
}
}
```
--------------------------------------------------------------------------------
/src/modules/account/authenticateDeliveryman/AuthenticateDeliverymanUseCase.ts:
--------------------------------------------------------------------------------
```typescript
import { prisma } from "../../../database/prismaClient";
import { compare } from "bcrypt"
import { sign } from "jsonwebtoken"
interface IAuthenticateDeliveryman {
username: string;
password: string
}
export class AuthenticateDeliverymanUseCase{
async execute({ username, password }: IAuthenticateDeliveryman){
const deliveryman = await prisma.deliveryman.findFirst({
where: {
username
}
})
if(!deliveryman){
throw new Error("Username or password is invalid!")
}
const passwordMatch = await compare(password, deliveryman.password)
if(!passwordMatch){
throw new Error("Username or password is invalid!")
}
const token = sign( {username}, "a905d89574379cec8ba1fc0438bf9597", {
subject: deliveryman.id,
expiresIn: "1d"
})
return token
}
}
```
--------------------------------------------------------------------------------
/src/kafka/producer.ts:
--------------------------------------------------------------------------------
```typescript
import {Kafka} from 'kafkajs'
interface MessagesProps{
content: string;
category: string;
recipientId: string;
}
export async function producer({category, content, recipientId}: MessagesProps){
const kafka = new Kafka({
clientId: 'entregas-node',
brokers: ['stable-spider-6900-us1-kafka.upstash.io:9092'],
sasl: {
mechanism: 'scram-sha-256',
username: 'c3RhYmxlLXNwaWRlci02OTAwJJw1OKOjqd2WIqt7-XXtHqrcZPbhL1aLjGLyXXM',
password: 'b55b6a4a24404e199df02c2f6bc5b806',
},
ssl: true,
})
const producer = kafka.producer()
await producer.connect()
await producer.send({
topic: 'notifications.send-notification',
messages: [
{
value: JSON.stringify({
content,
category,
recipientId
})
}
]
})
await producer.disconnect()
}
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/createClient/createClient.spec.ts:
--------------------------------------------------------------------------------
```typescript
import { InMemoryClientRepository } from "../../../../tests/inMemoryDatabases/InMemoryClientsRepository"
import { CreateClientUseCase } from "./createClienteUseCase"
const clientRepository = new InMemoryClientRepository()
const createClientUseCase = new CreateClientUseCase(clientRepository)
describe('create client tests', () => {
it('should create a client', async () => {
const client = await createClientUseCase.execute({
password: String(Math.random()),
username: "Jhon Doe " + `${String(Math.random())}`,
})
expect(client).toHaveProperty('id')
})
it('should return an error if username already exists', async () => {
expect(async () => {
await createClientUseCase.execute({
password: String(Math.random()),
username: "jhon doe"
})
await createClientUseCase.execute({
password: String(Math.random()),
username: "jhon doe"
})
}).rejects.toThrow("Client already exists");
})
})
```
--------------------------------------------------------------------------------
/src/tests/inMemoryDatabases/InMemoryClientsRepository.ts:
--------------------------------------------------------------------------------
```typescript
import { Clients } from "@prisma/client";
import { CreateRequestProps, FindRequestProps, IClientRepository } from "../../database/repositories/interfaces/IClientRepository";
import { randomUUID } from 'node:crypto'
export class InMemoryClientRepository implements IClientRepository{
clients: Clients[]
constructor(){
this.clients = [{
username: 'Jhon Doe',
password: '123',
id: '455667'
}]
}
async create({ username, password }: CreateRequestProps): Promise<Clients> {
const clientAlreadyExists = this.clients.find((client) => client.username === username)
if(clientAlreadyExists){
throw new Error("Client already exists")
}
const client = Object.assign({
username,
password,
id: randomUUID()
})
this.clients.push(client)
return client
}
async find({ id_client }: FindRequestProps): Promise<Clients[]> {
const clientFound = this.clients.filter((client) => client.id === id_client)
if(!clientFound[0]){
throw new Error("Client with this id not found")
}
return clientFound
}
}
```
--------------------------------------------------------------------------------
/src/database/repositories/ClientRepository.ts:
--------------------------------------------------------------------------------
```typescript
import { Clients } from "@prisma/client";
import { hash } from "bcrypt";
import { prisma } from "../prismaClient";
import { CreateRequestProps, FindRequestProps, IClientRepository } from "./interfaces/IClientRepository";
export class ClientRepository implements IClientRepository {
private static INSTANCE: ClientRepository;
public static getInstance(): ClientRepository{
if(!ClientRepository.INSTANCE){
ClientRepository.INSTANCE = new ClientRepository()
}
return ClientRepository.INSTANCE
}
async create({ username, password }: CreateRequestProps): Promise<Clients> {
const hashPassword = await hash(password, 10);
const client = await prisma.clients.create({
data: {
password: hashPassword,
username,
}
})
return client
}
async find({ id_client }: FindRequestProps): Promise<Clients[]> {
const client = await prisma.clients.findMany({
where: {
id: id_client
},
select: {
id: true,
username: true,
Deliveries: true,
password: true
}
})
return client
}
}
```
--------------------------------------------------------------------------------
/src/routes.ts:
--------------------------------------------------------------------------------
```typescript
import { Router } from "express";
import { AuthenticateClientController } from "./modules/account/authenticateClient/AuthenticateClientController";
import { AuthenticateDeliverymanController } from "./modules/account/authenticateDeliveryman/AuthenticateDeliverymanController";
import { CreateDeliveryController } from "./modules/deliveries/useCases/createDelivery/CreateDeliveryController";
import { CreateDeliverymanController } from "./modules/deliveryman/useCases/createDeliveryman/CreateDeliverymanController";
import { ensureAuthClient } from "./middlewares/ensureAuthClient";
import { ensureAuthDeliveryman } from "./middlewares/ensureAuthDeliveryman";
import { FindAllAvailableController } from "./modules/deliveries/useCases/findAllAvailable/FindAllAvailableController";
import { UpdateDeliverymanController } from "./modules/deliveries/useCases/updateDeliveryman/UpdateDeliverymanController";
import { FindAllDeliveriesController } from "./modules/deliveryman/useCases/findAllDeliveries/FindAllDeliveriesController";
import { UpdateEndDateController } from "./modules/deliveries/updateEndDate/UpdateEndDateController";
import { createClientController } from "./modules/clients/useCases/createClient";
import { findClientDeliveriesController } from "./modules/clients/useCases/findClientDeliveries";
const routes = Router()
const authenticateClientController = new AuthenticateClientController()
const createDeliverymanController = new CreateDeliverymanController()
const authenticateDeliverymanController = new AuthenticateDeliverymanController()
const findAllAvailableController = new FindAllAvailableController()
const createDeliveryController = new CreateDeliveryController()
const updateDeliverymanController = new UpdateDeliverymanController()
const findAllDeliveriesController = new FindAllDeliveriesController()
const updateEndDateController = new UpdateEndDateController()
const authenticateClient = new AuthenticateClientController()
routes.post("/client", (req, res) => {
return createClientController.handle(req, res)
})
routes.get("/client/myDeliveries", ensureAuthClient, (req, res) => {
return findClientDeliveriesController.handle(req, res)
})
routes.post("/client/authenticate", authenticateClientController.handle)
routes.post("/deliveryman", createDeliverymanController.handle)
routes.get("/deliveryman/myDeliveries", ensureAuthDeliveryman, findAllDeliveriesController.handle)
routes.post("/deliveryman/authenticate", authenticateDeliverymanController.handle)
routes.put("/deliveryman/authenticate", authenticateDeliverymanController.handle)
routes.get("/delivery/available", ensureAuthDeliveryman, findAllAvailableController.handle)
routes.post("/delivery", ensureAuthClient, createDeliveryController.handle)
routes.put("/delivery/updateDeliveryman/:id", ensureAuthDeliveryman, updateDeliverymanController.handle)
routes.put("/delivery/updateEndDate/:id", ensureAuthDeliveryman, updateEndDateController.handle)
routes.post('/auth/user', authenticateClient.handle)
export { routes }
```
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
```typescript
/*
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/configuration
*/
export default {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "C:\\Users\\rafae\\AppData\\Local\\Temp\\jest",
// Automatically clear mock calls, instances, contexts and results before every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
// The directory where Jest should output its coverage files
// coverageDirectory: undefined,
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// Indicates which provider should be used to instrument code for coverage
coverageProvider: "v8",
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// The default configuration for fake timers
// fakeTimers: {
// "enableGlobally": false
// },
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "mjs",
// "cjs",
// "jsx",
// "ts",
// "tsx",
// "json",
// "node"
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
preset: 'ts-jest',
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state before every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: undefined,
// Automatically restore mock state and implementation before every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
// roots: [
// "<rootDir>"
// ],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
// testEnvironment: "jest-environment-node",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
testMatch: [
"**/**/*.spec.ts"
],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jest-circus/runner",
// A map from regular expressions to paths to transformers
// transform: undefined,
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "\\\\node_modules\\\\",
// "\\.pnp\\.[^\\\\]+$"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};
```