# 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:
--------------------------------------------------------------------------------
```
1 | node_modules
2 | # Keep environment variables out of version control
3 | .env
4 |
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819234017_alter_end_date/migration.sql:
--------------------------------------------------------------------------------
```sql
1 | -- AlterTable
2 | ALTER TABLE "deliveries" ALTER COLUMN "end_at" DROP DEFAULT;
3 |
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819233224_alter_endat_deliveries_table/migration.sql:
--------------------------------------------------------------------------------
```sql
1 | -- AlterTable
2 | ALTER TABLE "deliveries" ALTER COLUMN "end_at" SET DEFAULT CURRENT_TIMESTAMP;
3 |
```
--------------------------------------------------------------------------------
/src/database/prismaClient.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { PrismaClient } from "@prisma/client";
2 |
3 | const prisma = new PrismaClient()
4 |
5 | export { prisma }
```
--------------------------------------------------------------------------------
/src/@types/express/index.d.ts:
--------------------------------------------------------------------------------
```typescript
1 | declare namespace Express{
2 | export interface Request{
3 | id_client: string;
4 | id_deliveryman: string;
5 | }
6 | }
```
--------------------------------------------------------------------------------
/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
```toml
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "postgresql"
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | "sourceMap": true,
4 | "outDir": "dist",
5 | "strict": true,
6 | "lib": ["esnext"],
7 | "esModuleInterop": true
8 | }
9 | }
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819213956_create_clients_table/migration.sql:
--------------------------------------------------------------------------------
```sql
1 | -- CreateTable
2 | CREATE TABLE "clients" (
3 | "id" TEXT NOT NULL,
4 | "username" TEXT NOT NULL,
5 | "password" TEXT NOT NULL,
6 |
7 | CONSTRAINT "clients_pkey" PRIMARY KEY ("id")
8 | );
9 |
10 | -- CreateIndex
11 | CREATE UNIQUE INDEX "clients_username_key" ON "clients"("username");
12 |
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819211355_create_deliveryman/migration.sql:
--------------------------------------------------------------------------------
```sql
1 | -- CreateTable
2 | CREATE TABLE "Deliveryman" (
3 | "id" TEXT NOT NULL,
4 | "username" TEXT NOT NULL,
5 | "password" TEXT NOT NULL,
6 |
7 | CONSTRAINT "Deliveryman_pkey" PRIMARY KEY ("id")
8 | );
9 |
10 | -- CreateIndex
11 | CREATE UNIQUE INDEX "Deliveryman_username_key" ON "Deliveryman"("username");
12 |
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/findAllAvailable/FindAllAvailableUseCase.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { prisma } from "../../../../database/prismaClient";
2 |
3 | export class FindAllAvailableUseCase{
4 | async execute(){
5 | const deliveries = await prisma.deliveries.findMany({
6 | where: {
7 | end_at: null,
8 | id_deliveryman: null,
9 | }
10 | })
11 |
12 | return deliveries
13 | }
14 | }
```
--------------------------------------------------------------------------------
/src/database/repositories/interfaces/IClientRepository.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Clients } from "@prisma/client";
2 |
3 | export interface CreateRequestProps{
4 | username: string;
5 | password: string
6 | }
7 |
8 | export interface FindRequestProps{
9 | id_client: string;
10 | }
11 |
12 | export interface IClientRepository {
13 | create({ username, password }: CreateRequestProps): Promise<Clients>
14 | find({ id_client }: FindRequestProps): Promise<Clients[]>
15 | }
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/createDelivery/CreateDeliveryUseCase.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { prisma } from "../../../../database/prismaClient";
2 |
3 | interface ICreateDelivery{
4 | item_name: string;
5 | id_client: string;
6 | }
7 |
8 | export class CreateDeliveryUseCase{
9 | async execute({ item_name, id_client }: ICreateDelivery){
10 |
11 | const delivery = await prisma.deliveries.create({
12 | data: {
13 | item_name,
14 | id_client,
15 | }
16 | })
17 |
18 | return delivery
19 | }
20 | }
```
--------------------------------------------------------------------------------
/src/modules/deliveries/updateEndDate/UpdateEndDateUseCase.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { prisma } from "../../../database/prismaClient";
2 |
3 | interface IUpdateEndDate{
4 | id_delivery: string;
5 | }
6 |
7 | export class UpdateEndDateUseCase{
8 | async execute({ id_delivery }: IUpdateEndDate){
9 | const delivery = await prisma.deliveries.update({
10 | where: {
11 | id: id_delivery,
12 | },
13 | data: {
14 | end_at: new Date()
15 | }
16 | })
17 |
18 | return delivery
19 | }
20 | }
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/findAllAvailable/FindAllAvailableController.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Request, Response } from "express";
2 | import { FindAllAvailableUseCase } from "./findAllAvailableUseCase";
3 |
4 |
5 |
6 | export class FindAllAvailableController{
7 | async handle(request: Request, response: Response){
8 | const findAllAvailableUseCase = new FindAllAvailableUseCase()
9 |
10 | const deliveries = await findAllAvailableUseCase.execute()
11 |
12 | return response.json(deliveries)
13 | }
14 | }
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819233031_alter_table_deliveries/migration.sql:
--------------------------------------------------------------------------------
```sql
1 | -- DropForeignKey
2 | ALTER TABLE "deliveries" DROP CONSTRAINT "deliveries_id_deliveryman_fkey";
3 |
4 | -- AlterTable
5 | ALTER TABLE "deliveries" ALTER COLUMN "id_deliveryman" DROP NOT NULL,
6 | ALTER COLUMN "end_at" DROP NOT NULL;
7 |
8 | -- AddForeignKey
9 | ALTER TABLE "deliveries" ADD CONSTRAINT "deliveries_id_deliveryman_fkey" FOREIGN KEY ("id_deliveryman") REFERENCES "deliveryman"("id") ON DELETE SET NULL ON UPDATE CASCADE;
10 |
```
--------------------------------------------------------------------------------
/src/modules/deliveries/updateEndDate/UpdateEndDateController.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Request, Response } from "express";
2 | import { UpdateEndDateUseCase } from "./UpdateEndDateUseCase";
3 |
4 |
5 |
6 | export class UpdateEndDateController{
7 | async handle(request: Request, response: Response){
8 | const { id } = request.params;
9 |
10 | const updateEndDateUseCase = new UpdateEndDateUseCase()
11 |
12 | const result = await updateEndDateUseCase.execute({
13 | id_delivery: id
14 | })
15 |
16 | return response.json(result)
17 | }
18 | }
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/updateDeliveryman/UpdateDeliverymanUseCase.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { prisma } from "../../../../database/prismaClient";
2 |
3 | interface IUpdateDeliveryman{
4 | id_delivery: string;
5 | id_deliveryman: string;
6 | }
7 |
8 | export class UpdateDeliverymanUseCase{
9 | async execute({ id_delivery, id_deliveryman }: IUpdateDeliveryman){
10 | const result = await prisma.deliveries.update({
11 | where: {
12 | id: id_delivery
13 | },
14 |
15 | data: {
16 | id_deliveryman
17 | }
18 | })
19 |
20 | return result
21 | }
22 | }
```
--------------------------------------------------------------------------------
/src/modules/deliveryman/useCases/findAllDeliveries/FindAllDeliveriesController.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Request, Response } from "express";
2 | import { FindAllDeliveriesUseCase } from "./FindAllDeliveriesUseCase";
3 |
4 |
5 |
6 | export class FindAllDeliveriesController{
7 | async handle(request: Request, response: Response){
8 | const { id_deliveryman } = request
9 |
10 | const findAllDeliveriesUseCase = new FindAllDeliveriesUseCase()
11 |
12 | const result = await findAllDeliveriesUseCase.execute({
13 | id_deliveryman,
14 | })
15 |
16 | return response.json(result)
17 | }
18 | }
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/createClient/createClientController.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Request, Response } from "express";
2 | import { CreateClientUseCase } from "./createClienteUseCase";
3 |
4 | export class CreateClientController{
5 | constructor (private createClientUseCase: CreateClientUseCase){}
6 |
7 | async handle(request: Request, response: Response){
8 | const { username, password } = request.body;
9 |
10 | const result = await this.createClientUseCase.execute({
11 | username,
12 | password
13 | })
14 |
15 | return response.json(result)
16 | }
17 | }
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819213758_update_name_deliveryman/migration.sql:
--------------------------------------------------------------------------------
```sql
1 | /*
2 | Warnings:
3 |
4 | - You are about to drop the `Deliveryman` table. If the table is not empty, all the data it contains will be lost.
5 |
6 | */
7 | -- DropTable
8 | DROP TABLE "Deliveryman";
9 |
10 | -- CreateTable
11 | CREATE TABLE "deliveryman" (
12 | "id" TEXT NOT NULL,
13 | "username" TEXT NOT NULL,
14 | "password" TEXT NOT NULL,
15 |
16 | CONSTRAINT "deliveryman_pkey" PRIMARY KEY ("id")
17 | );
18 |
19 | -- CreateIndex
20 | CREATE UNIQUE INDEX "deliveryman_username_key" ON "deliveryman"("username");
21 |
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/findClientDeliveries/FindClientDeliveriesUseCase.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { prisma } from "../../../../database/prismaClient";
2 | import { IClientRepository } from "../../../../database/repositories/interfaces/IClientRepository";
3 |
4 | interface IFindClientDeliveries{
5 | id_client: string;
6 | }
7 |
8 | export class FindClientDeliveriesUseCase{
9 | constructor(
10 | private clientRepository: IClientRepository
11 | ){}
12 |
13 | async execute({id_client}: IFindClientDeliveries){
14 | const client = await this.clientRepository.find({id_client})
15 |
16 | return client
17 | }
18 | }
```
--------------------------------------------------------------------------------
/src/modules/account/authenticateClient/AuthenticateClientController.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Request, Response } from "express";
2 | import { AuthenticateClientUseCase } from "./AuthenticateClientUseCase";
3 |
4 |
5 |
6 | export class AuthenticateClientController{
7 | async handle(request: Request, response: Response){
8 | const { username, password } = request.body;
9 |
10 | const authenticateClientUseCase = new AuthenticateClientUseCase()
11 | const result = await authenticateClientUseCase.execute({
12 | username,
13 | password
14 | })
15 |
16 | return response.json(result)
17 | }
18 | }
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/findClientDeliveries/FindClientDeliveriesController.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Request, Response } from "express";
2 | import { FindClientDeliveriesUseCase } from "./FindClientDeliveriesUseCase";
3 |
4 |
5 |
6 | export class FindClientDeliveriesController{
7 | constructor(
8 | private findClientDeliveriesUseCase: FindClientDeliveriesUseCase
9 | ){}
10 |
11 | async handle(request: Request, response: Response){
12 | const { id_client } = request
13 |
14 | const result = await this.findClientDeliveriesUseCase.execute({
15 | id_client
16 | })
17 |
18 | return response.json(result)
19 | }
20 | }
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/createDelivery/CreateDeliveryController.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Request, Response } from "express";
2 | import { CreateDeliveryUseCase } from "./CreateDeliveryUseCase";
3 |
4 | export class CreateDeliveryController{
5 | async handle(request: Request, response: Response){
6 | const { item_name } = request.body;
7 | const { id_client } = request
8 |
9 | const createDeliveryUseCase = new CreateDeliveryUseCase()
10 |
11 | const delivery = await createDeliveryUseCase.execute({
12 | id_client,
13 | item_name
14 | })
15 |
16 | return response.json(delivery)
17 | }
18 | }
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/createClient/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { ClientRepository } from "../../../../database/repositories/ClientRepository";
2 | import { CreateClientController } from "./createClientController";
3 | import { CreateClientUseCase } from "./createClienteUseCase";
4 |
5 | const clientRepository = ClientRepository.getInstance()
6 |
7 | const createClientUseCase = new CreateClientUseCase(clientRepository)
8 |
9 | const createClientController = new CreateClientController(createClientUseCase)
10 |
11 | export { createClientController, clientRepository, createClientUseCase }
12 |
```
--------------------------------------------------------------------------------
/src/modules/account/authenticateDeliveryman/AuthenticateDeliverymanController.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Request, Response } from "express";
2 | import { AuthenticateDeliverymanUseCase } from "./AuthenticateDeliverymanUseCase";
3 |
4 |
5 |
6 | export class AuthenticateDeliverymanController{
7 | async handle(request: Request, response: Response){
8 | const { username, password } = request.body;
9 |
10 | const authenticateDeliverymanUseCase = new AuthenticateDeliverymanUseCase()
11 | const result = await authenticateDeliverymanUseCase.execute({
12 | username,
13 | password
14 | })
15 |
16 | return response.json(result)
17 | }
18 | }
```
--------------------------------------------------------------------------------
/src/modules/deliveries/useCases/updateDeliveryman/UpdateDeliverymanController.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Request, Response } from "express";
2 | import { UpdateDeliverymanUseCase } from "./UpdateDeliverymanUseCase";
3 |
4 | export class UpdateDeliverymanController{
5 | async handle(request: Request, response: Response){
6 | const { id } = request.params
7 | const { id_deliveryman } = request
8 |
9 | const updateDeliverymanUseCase = new UpdateDeliverymanUseCase()
10 |
11 | const delivery = await updateDeliverymanUseCase.execute({
12 | id_delivery: id,
13 | id_deliveryman
14 | })
15 |
16 | return response.json(delivery)
17 | }
18 | }
```
--------------------------------------------------------------------------------
/src/modules/deliveryman/useCases/findAllDeliveries/FindAllDeliveriesUseCase.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { prisma } from "../../../../database/prismaClient";
2 |
3 | interface IFindAllDeliveries{
4 | id_deliveryman: string;
5 | }
6 |
7 | export class FindAllDeliveriesUseCase{
8 | async execute({ id_deliveryman }: IFindAllDeliveries){
9 | const deliveryman = await prisma.deliveryman.findFirst({
10 | where: {
11 | id: id_deliveryman,
12 | },
13 |
14 | select: {
15 | id: true,
16 | username: true,
17 | Deliveries: {
18 | where: {
19 | end_at: null
20 | }
21 | }
22 | }
23 | })
24 |
25 | return deliveryman
26 | }
27 | }
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/findClientDeliveries/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { ClientRepository } from "../../../../database/repositories/ClientRepository";
2 | import { FindClientDeliveriesController } from "./FindClientDeliveriesController";
3 | import { FindClientDeliveriesUseCase } from "./FindClientDeliveriesUseCase";
4 |
5 | const clientRepository = ClientRepository.getInstance()
6 | const findClientDeliveriesUseCase = new FindClientDeliveriesUseCase(clientRepository)
7 | const findClientDeliveriesController = new FindClientDeliveriesController(findClientDeliveriesUseCase)
8 |
9 | export { findClientDeliveriesController }
```
--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------
```typescript
1 | import express, { NextFunction, Request, Response } from "express";
2 | import "express-async-errors"
3 | import { routes } from "./routes";
4 |
5 | const app = express()
6 |
7 | app.use(express.json())
8 |
9 | app.use(routes);
10 |
11 | app.use((err: Error, request: Request, response: Response, next: NextFunction) => {
12 | if(err instanceof Error){
13 | return response.status(400).json({
14 | message: err.message
15 | })
16 | }
17 |
18 | return response.status(500).json({
19 | status: "error",
20 | message: "internal server error"
21 | })
22 | })
23 |
24 | app.listen(3333, () => console.log('server is running'))
25 |
```
--------------------------------------------------------------------------------
/src/middlewares/ensureAuthClient.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { NextFunction, Request, Response } from "express";
2 | import { verify } from "jsonwebtoken";
3 |
4 | interface IPayload{
5 | sub: string;
6 | }
7 |
8 | export async function ensureAuthClient(request: Request, response: Response, next: NextFunction){
9 | const authHeader = request.headers.authorization;
10 |
11 | if(!authHeader){
12 | return response.status(401).json({
13 | message: "token missing"
14 | })
15 | }
16 |
17 | const [, token] = authHeader.split(" ");
18 |
19 | try{
20 | const { sub } = verify(token, 'a905d89574379cec8ba1fc0438bf9597') as IPayload;
21 |
22 | request.id_client = sub;
23 |
24 | return next()
25 | }catch{
26 | return response.status(401).json({
27 | message: "Invalid token"
28 | })
29 | }
30 |
31 | }
```
--------------------------------------------------------------------------------
/src/middlewares/ensureAuthDeliveryman.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { NextFunction, Request, Response } from "express";
2 | import { verify } from "jsonwebtoken";
3 |
4 | interface IPayload{
5 | sub: string;
6 | }
7 |
8 | export async function ensureAuthDeliveryman(request: Request, response: Response, next: NextFunction){
9 | const authHeader = request.headers.authorization;
10 |
11 | if(!authHeader){
12 | return response.status(401).json({
13 | message: "token missing"
14 | })
15 | }
16 |
17 | const [, token] = authHeader.split(" ");
18 |
19 | try{
20 | const { sub } = verify(token, 'a905d89574379cec8ba1fc0438bf9597') as IPayload;
21 |
22 | request.id_deliveryman = sub;
23 |
24 | return next()
25 | }catch{
26 | return response.status(401).json({
27 | message: "Invalid token"
28 | })
29 | }
30 |
31 | }
```
--------------------------------------------------------------------------------
/prisma/migrations/20220819214400_create_deliveries_table/migration.sql:
--------------------------------------------------------------------------------
```sql
1 | -- CreateTable
2 | CREATE TABLE "deliveries" (
3 | "id" TEXT NOT NULL,
4 | "id_client" TEXT NOT NULL,
5 | "id_deliveryman" TEXT NOT NULL,
6 | "item_name" TEXT NOT NULL,
7 | "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
8 | "end_at" TIMESTAMP(3) NOT NULL,
9 |
10 | CONSTRAINT "deliveries_pkey" PRIMARY KEY ("id")
11 | );
12 |
13 | -- AddForeignKey
14 | ALTER TABLE "deliveries" ADD CONSTRAINT "deliveries_id_client_fkey" FOREIGN KEY ("id_client") REFERENCES "clients"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
15 |
16 | -- AddForeignKey
17 | ALTER TABLE "deliveries" ADD CONSTRAINT "deliveries_id_deliveryman_fkey" FOREIGN KEY ("id_deliveryman") REFERENCES "deliveryman"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
18 |
```
--------------------------------------------------------------------------------
/src/modules/deliveryman/useCases/createDeliveryman/CreateDeliverymanController.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Request, Response } from "express";
2 | import { producer } from "../../../../kafka/producer";
3 | import { CreateDeliverymanUseCase } from "./CreateDeliverymanUseCase";
4 | import { randomUUID } from 'node:crypto'
5 |
6 |
7 | export class CreateDeliverymanController {
8 | async handle(request: Request, response: Response){
9 | const { username, password } = request.body;
10 |
11 | const createDeliverymanUseCase = new CreateDeliverymanUseCase()
12 | const result = await createDeliverymanUseCase.execute({
13 | username,
14 | password
15 | })
16 |
17 | await producer({
18 | category: 'deliveryman',
19 | content: `Novo deliveryman criado: ${username}`,
20 | recipientId: randomUUID()
21 | })
22 |
23 | return response.json(result)
24 | }
25 | }
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/createClient/createClienteUseCase.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { prisma } from "../../../../database/prismaClient";
2 | import { IClientRepository } from "../../../../database/repositories/interfaces/IClientRepository";
3 |
4 | interface ICreateClient{
5 | username: string;
6 | password: string;
7 | }
8 |
9 | export class CreateClientUseCase{
10 | constructor(private clientRepository: IClientRepository){}
11 | async execute({ username, password }: ICreateClient){
12 | const clientAlreadyExists = await prisma.clients.findFirst({
13 | where: {
14 | username: {
15 | equals: username,
16 | mode: "insensitive"
17 | }
18 | }
19 | })
20 |
21 | if(clientAlreadyExists){
22 | throw new Error("Client already exists")
23 | }
24 |
25 | const client = this.clientRepository.create({password, username})
26 |
27 | return client
28 | }
29 | }
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "backend-entregas",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "type": "commonjs",
7 | "scripts": {
8 | "dev": "ts-node-dev --exit-child --transpile-only --ignore-watch node_modules src/server.ts",
9 | "test": "jest"
10 | },
11 | "devDependencies": {
12 | "@types/bcrypt": "^5.0.0",
13 | "@types/express": "^4.17.13",
14 | "@types/jest": "^29.4.0",
15 | "@types/jsonwebtoken": "^8.5.8",
16 | "jest": "^29.4.1",
17 | "prisma": "^4.2.1",
18 | "ts-jest": "^29.0.5",
19 | "ts-node-dev": "^2.0.0",
20 | "typescript": "^4.7.4"
21 | },
22 | "dependencies": {
23 | "@prisma/client": "^4.2.1",
24 | "bcrypt": "^5.0.1",
25 | "express": "^4.18.1",
26 | "express-async-errors": "^3.1.1",
27 | "jsonwebtoken": "^8.5.1",
28 | "kafkajs": "^2.2.3"
29 | }
30 | }
31 |
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/findClientDeliveries/FindClientDeliveries.spec.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { InMemoryClientRepository } from "../../../../tests/inMemoryDatabases/InMemoryClientsRepository"
2 | import { FindClientDeliveriesUseCase } from "./FindClientDeliveriesUseCase"
3 |
4 | const clientRepository = new InMemoryClientRepository()
5 | const findClientDeliveries = new FindClientDeliveriesUseCase(clientRepository)
6 |
7 | describe("Find client Delivires", () => {
8 | it("should found a client delivery", async() => {
9 | const client = await findClientDeliveries.execute({
10 | id_client: '455667'
11 | })
12 |
13 | expect(client).toHaveLength(1)
14 | })
15 |
16 | it("should return an error if client not found", async() => {
17 | expect(async () => {
18 | await findClientDeliveries.execute({
19 | id_client: '645456654'
20 | })
21 | }).rejects.toThrow()
22 | })
23 | })
```
--------------------------------------------------------------------------------
/src/modules/account/authenticateClient/AuthenticateClientUseCase.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { prisma } from "../../../database/prismaClient";
2 | import { compare } from "bcrypt"
3 | import { sign } from "jsonwebtoken"
4 |
5 | interface IAuthenticateClient {
6 | username: string;
7 | password: string
8 | }
9 |
10 | export class AuthenticateClientUseCase{
11 | async execute({ username, password }: IAuthenticateClient){
12 | const client = await prisma.clients.findFirst({
13 | where: {
14 | username
15 | }
16 | })
17 |
18 | if(!client){
19 | throw new Error("Username or password is invalid!")
20 | }
21 |
22 | const passwordMatch = await compare(password, client.password)
23 |
24 | if(!passwordMatch){
25 | throw new Error("Username or password is invalid!")
26 | }
27 |
28 | const token = sign( {username}, "a905d89574379cec8ba1fc0438bf9597", {
29 | subject: client.id,
30 | expiresIn: "1d"
31 | })
32 |
33 | return token
34 | }
35 | }
```
--------------------------------------------------------------------------------
/src/modules/deliveryman/useCases/createDeliveryman/CreateDeliverymanUseCase.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { hash } from "bcrypt";
2 | import { prisma } from "../../../../database/prismaClient";
3 |
4 | interface ICreateDeliveryman{
5 | username: string;
6 | password: string;
7 | }
8 |
9 | export class CreateDeliverymanUseCase{
10 | async execute({ username, password }: ICreateDeliveryman){
11 | // validar se username já existe
12 | const deliverymanAlreadyExists = await prisma.deliveryman.findFirst({
13 | where: {
14 | username: {
15 | equals: username,
16 | mode: "insensitive"
17 | }
18 | }
19 | })
20 |
21 | if(deliverymanAlreadyExists){
22 | throw new Error("Deliveryman already exists!")
23 | }
24 |
25 | const hashPassword = await hash(password, 10);
26 |
27 | const deliveryman = await prisma.deliveryman.create({
28 | data: {
29 | username,
30 | password: hashPassword
31 | }
32 | })
33 |
34 | return deliveryman
35 |
36 | }
37 | }
```
--------------------------------------------------------------------------------
/src/modules/account/authenticateDeliveryman/AuthenticateDeliverymanUseCase.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { prisma } from "../../../database/prismaClient";
2 | import { compare } from "bcrypt"
3 | import { sign } from "jsonwebtoken"
4 |
5 | interface IAuthenticateDeliveryman {
6 | username: string;
7 | password: string
8 | }
9 |
10 | export class AuthenticateDeliverymanUseCase{
11 | async execute({ username, password }: IAuthenticateDeliveryman){
12 | const deliveryman = await prisma.deliveryman.findFirst({
13 | where: {
14 | username
15 | }
16 | })
17 |
18 | if(!deliveryman){
19 | throw new Error("Username or password is invalid!")
20 | }
21 |
22 | const passwordMatch = await compare(password, deliveryman.password)
23 |
24 | if(!passwordMatch){
25 | throw new Error("Username or password is invalid!")
26 | }
27 |
28 | const token = sign( {username}, "a905d89574379cec8ba1fc0438bf9597", {
29 | subject: deliveryman.id,
30 | expiresIn: "1d"
31 | })
32 |
33 | return token
34 | }
35 | }
```
--------------------------------------------------------------------------------
/src/kafka/producer.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {Kafka} from 'kafkajs'
2 |
3 | interface MessagesProps{
4 | content: string;
5 | category: string;
6 | recipientId: string;
7 | }
8 |
9 | export async function producer({category, content, recipientId}: MessagesProps){
10 | const kafka = new Kafka({
11 | clientId: 'entregas-node',
12 | brokers: ['stable-spider-6900-us1-kafka.upstash.io:9092'],
13 | sasl: {
14 | mechanism: 'scram-sha-256',
15 | username: 'c3RhYmxlLXNwaWRlci02OTAwJJw1OKOjqd2WIqt7-XXtHqrcZPbhL1aLjGLyXXM',
16 | password: 'b55b6a4a24404e199df02c2f6bc5b806',
17 | },
18 | ssl: true,
19 | })
20 |
21 | const producer = kafka.producer()
22 |
23 | await producer.connect()
24 | await producer.send({
25 | topic: 'notifications.send-notification',
26 | messages: [
27 | {
28 | value: JSON.stringify({
29 | content,
30 | category,
31 | recipientId
32 | })
33 | }
34 | ]
35 | })
36 |
37 | await producer.disconnect()
38 | }
```
--------------------------------------------------------------------------------
/src/modules/clients/useCases/createClient/createClient.spec.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { InMemoryClientRepository } from "../../../../tests/inMemoryDatabases/InMemoryClientsRepository"
2 | import { CreateClientUseCase } from "./createClienteUseCase"
3 |
4 | const clientRepository = new InMemoryClientRepository()
5 | const createClientUseCase = new CreateClientUseCase(clientRepository)
6 |
7 | describe('create client tests', () => {
8 | it('should create a client', async () => {
9 | const client = await createClientUseCase.execute({
10 | password: String(Math.random()),
11 | username: "Jhon Doe " + `${String(Math.random())}`,
12 | })
13 |
14 | expect(client).toHaveProperty('id')
15 | })
16 |
17 | it('should return an error if username already exists', async () => {
18 | expect(async () => {
19 | await createClientUseCase.execute({
20 | password: String(Math.random()),
21 | username: "jhon doe"
22 | })
23 |
24 | await createClientUseCase.execute({
25 | password: String(Math.random()),
26 | username: "jhon doe"
27 | })
28 | }).rejects.toThrow("Client already exists");
29 | })
30 | })
```
--------------------------------------------------------------------------------
/src/tests/inMemoryDatabases/InMemoryClientsRepository.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Clients } from "@prisma/client";
2 | import { CreateRequestProps, FindRequestProps, IClientRepository } from "../../database/repositories/interfaces/IClientRepository";
3 | import { randomUUID } from 'node:crypto'
4 |
5 | export class InMemoryClientRepository implements IClientRepository{
6 | clients: Clients[]
7 |
8 | constructor(){
9 | this.clients = [{
10 | username: 'Jhon Doe',
11 | password: '123',
12 | id: '455667'
13 | }]
14 | }
15 |
16 | async create({ username, password }: CreateRequestProps): Promise<Clients> {
17 | const clientAlreadyExists = this.clients.find((client) => client.username === username)
18 |
19 | if(clientAlreadyExists){
20 | throw new Error("Client already exists")
21 | }
22 |
23 | const client = Object.assign({
24 | username,
25 | password,
26 | id: randomUUID()
27 | })
28 |
29 | this.clients.push(client)
30 |
31 | return client
32 | }
33 | async find({ id_client }: FindRequestProps): Promise<Clients[]> {
34 | const clientFound = this.clients.filter((client) => client.id === id_client)
35 |
36 | if(!clientFound[0]){
37 | throw new Error("Client with this id not found")
38 | }
39 |
40 | return clientFound
41 | }
42 | }
```
--------------------------------------------------------------------------------
/src/database/repositories/ClientRepository.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Clients } from "@prisma/client";
2 | import { hash } from "bcrypt";
3 | import { prisma } from "../prismaClient";
4 | import { CreateRequestProps, FindRequestProps, IClientRepository } from "./interfaces/IClientRepository";
5 |
6 | export class ClientRepository implements IClientRepository {
7 | private static INSTANCE: ClientRepository;
8 |
9 | public static getInstance(): ClientRepository{
10 |
11 | if(!ClientRepository.INSTANCE){
12 | ClientRepository.INSTANCE = new ClientRepository()
13 | }
14 |
15 | return ClientRepository.INSTANCE
16 | }
17 |
18 | async create({ username, password }: CreateRequestProps): Promise<Clients> {
19 | const hashPassword = await hash(password, 10);
20 |
21 | const client = await prisma.clients.create({
22 | data: {
23 | password: hashPassword,
24 | username,
25 | }
26 | })
27 |
28 | return client
29 | }
30 | async find({ id_client }: FindRequestProps): Promise<Clients[]> {
31 | const client = await prisma.clients.findMany({
32 | where: {
33 | id: id_client
34 | },
35 | select: {
36 | id: true,
37 | username: true,
38 | Deliveries: true,
39 | password: true
40 | }
41 | })
42 |
43 | return client
44 | }
45 |
46 | }
```
--------------------------------------------------------------------------------
/src/routes.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Router } from "express";
2 | import { AuthenticateClientController } from "./modules/account/authenticateClient/AuthenticateClientController";
3 | import { AuthenticateDeliverymanController } from "./modules/account/authenticateDeliveryman/AuthenticateDeliverymanController";
4 | import { CreateDeliveryController } from "./modules/deliveries/useCases/createDelivery/CreateDeliveryController";
5 | import { CreateDeliverymanController } from "./modules/deliveryman/useCases/createDeliveryman/CreateDeliverymanController";
6 |
7 | import { ensureAuthClient } from "./middlewares/ensureAuthClient";
8 | import { ensureAuthDeliveryman } from "./middlewares/ensureAuthDeliveryman";
9 | import { FindAllAvailableController } from "./modules/deliveries/useCases/findAllAvailable/FindAllAvailableController";
10 | import { UpdateDeliverymanController } from "./modules/deliveries/useCases/updateDeliveryman/UpdateDeliverymanController";
11 | import { FindAllDeliveriesController } from "./modules/deliveryman/useCases/findAllDeliveries/FindAllDeliveriesController";
12 | import { UpdateEndDateController } from "./modules/deliveries/updateEndDate/UpdateEndDateController";
13 | import { createClientController } from "./modules/clients/useCases/createClient";
14 | import { findClientDeliveriesController } from "./modules/clients/useCases/findClientDeliveries";
15 |
16 | const routes = Router()
17 |
18 | const authenticateClientController = new AuthenticateClientController()
19 |
20 | const createDeliverymanController = new CreateDeliverymanController()
21 | const authenticateDeliverymanController = new AuthenticateDeliverymanController()
22 | const findAllAvailableController = new FindAllAvailableController()
23 |
24 | const createDeliveryController = new CreateDeliveryController()
25 | const updateDeliverymanController = new UpdateDeliverymanController()
26 | const findAllDeliveriesController = new FindAllDeliveriesController()
27 | const updateEndDateController = new UpdateEndDateController()
28 |
29 | const authenticateClient = new AuthenticateClientController()
30 |
31 |
32 | routes.post("/client", (req, res) => {
33 | return createClientController.handle(req, res)
34 | })
35 | routes.get("/client/myDeliveries", ensureAuthClient, (req, res) => {
36 | return findClientDeliveriesController.handle(req, res)
37 | })
38 | routes.post("/client/authenticate", authenticateClientController.handle)
39 |
40 | routes.post("/deliveryman", createDeliverymanController.handle)
41 | routes.get("/deliveryman/myDeliveries", ensureAuthDeliveryman, findAllDeliveriesController.handle)
42 | routes.post("/deliveryman/authenticate", authenticateDeliverymanController.handle)
43 | routes.put("/deliveryman/authenticate", authenticateDeliverymanController.handle)
44 |
45 | routes.get("/delivery/available", ensureAuthDeliveryman, findAllAvailableController.handle)
46 | routes.post("/delivery", ensureAuthClient, createDeliveryController.handle)
47 | routes.put("/delivery/updateDeliveryman/:id", ensureAuthDeliveryman, updateDeliverymanController.handle)
48 | routes.put("/delivery/updateEndDate/:id", ensureAuthDeliveryman, updateEndDateController.handle)
49 |
50 | routes.post('/auth/user', authenticateClient.handle)
51 |
52 | export { routes }
```
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
```typescript
1 | /*
2 | * For a detailed explanation regarding each configuration property and type check, visit:
3 | * https://jestjs.io/docs/configuration
4 | */
5 |
6 | export default {
7 | // All imported modules in your tests should be mocked automatically
8 | // automock: false,
9 |
10 | // Stop running tests after `n` failures
11 | // bail: 0,
12 |
13 | // The directory where Jest should store its cached dependency information
14 | // cacheDirectory: "C:\\Users\\rafae\\AppData\\Local\\Temp\\jest",
15 |
16 | // Automatically clear mock calls, instances, contexts and results before every test
17 | clearMocks: true,
18 |
19 | // Indicates whether the coverage information should be collected while executing the test
20 | // collectCoverage: false,
21 |
22 | // An array of glob patterns indicating a set of files for which coverage information should be collected
23 | // collectCoverageFrom: undefined,
24 |
25 | // The directory where Jest should output its coverage files
26 | // coverageDirectory: undefined,
27 |
28 | // An array of regexp pattern strings used to skip coverage collection
29 | // coveragePathIgnorePatterns: [
30 | // "\\\\node_modules\\\\"
31 | // ],
32 |
33 | // Indicates which provider should be used to instrument code for coverage
34 | coverageProvider: "v8",
35 |
36 | // A list of reporter names that Jest uses when writing coverage reports
37 | // coverageReporters: [
38 | // "json",
39 | // "text",
40 | // "lcov",
41 | // "clover"
42 | // ],
43 |
44 | // An object that configures minimum threshold enforcement for coverage results
45 | // coverageThreshold: undefined,
46 |
47 | // A path to a custom dependency extractor
48 | // dependencyExtractor: undefined,
49 |
50 | // Make calling deprecated APIs throw helpful error messages
51 | // errorOnDeprecated: false,
52 |
53 | // The default configuration for fake timers
54 | // fakeTimers: {
55 | // "enableGlobally": false
56 | // },
57 |
58 | // Force coverage collection from ignored files using an array of glob patterns
59 | // forceCoverageMatch: [],
60 |
61 | // A path to a module which exports an async function that is triggered once before all test suites
62 | // globalSetup: undefined,
63 |
64 | // A path to a module which exports an async function that is triggered once after all test suites
65 | // globalTeardown: undefined,
66 |
67 | // A set of global variables that need to be available in all test environments
68 | // globals: {},
69 |
70 | // 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.
71 | // maxWorkers: "50%",
72 |
73 | // An array of directory names to be searched recursively up from the requiring module's location
74 | // moduleDirectories: [
75 | // "node_modules"
76 | // ],
77 |
78 | // An array of file extensions your modules use
79 | // moduleFileExtensions: [
80 | // "js",
81 | // "mjs",
82 | // "cjs",
83 | // "jsx",
84 | // "ts",
85 | // "tsx",
86 | // "json",
87 | // "node"
88 | // ],
89 |
90 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
91 | // moduleNameMapper: {},
92 |
93 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
94 | // modulePathIgnorePatterns: [],
95 |
96 | // Activates notifications for test results
97 | // notify: false,
98 |
99 | // An enum that specifies notification mode. Requires { notify: true }
100 | // notifyMode: "failure-change",
101 |
102 | // A preset that is used as a base for Jest's configuration
103 | preset: 'ts-jest',
104 |
105 | // Run tests from one or more projects
106 | // projects: undefined,
107 |
108 | // Use this configuration option to add custom reporters to Jest
109 | // reporters: undefined,
110 |
111 | // Automatically reset mock state before every test
112 | // resetMocks: false,
113 |
114 | // Reset the module registry before running each individual test
115 | // resetModules: false,
116 |
117 | // A path to a custom resolver
118 | // resolver: undefined,
119 |
120 | // Automatically restore mock state and implementation before every test
121 | // restoreMocks: false,
122 |
123 | // The root directory that Jest should scan for tests and modules within
124 | // rootDir: undefined,
125 |
126 | // A list of paths to directories that Jest should use to search for files in
127 | // roots: [
128 | // "<rootDir>"
129 | // ],
130 |
131 | // Allows you to use a custom runner instead of Jest's default test runner
132 | // runner: "jest-runner",
133 |
134 | // The paths to modules that run some code to configure or set up the testing environment before each test
135 | // setupFiles: [],
136 |
137 | // A list of paths to modules that run some code to configure or set up the testing framework before each test
138 | // setupFilesAfterEnv: [],
139 |
140 | // The number of seconds after which a test is considered as slow and reported as such in the results.
141 | // slowTestThreshold: 5,
142 |
143 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing
144 | // snapshotSerializers: [],
145 |
146 | // The test environment that will be used for testing
147 | // testEnvironment: "jest-environment-node",
148 |
149 | // Options that will be passed to the testEnvironment
150 | // testEnvironmentOptions: {},
151 |
152 | // Adds a location field to test results
153 | // testLocationInResults: false,
154 |
155 | // The glob patterns Jest uses to detect test files
156 | testMatch: [
157 | "**/**/*.spec.ts"
158 | ],
159 |
160 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
161 | // testPathIgnorePatterns: [
162 | // "\\\\node_modules\\\\"
163 | // ],
164 |
165 | // The regexp pattern or array of patterns that Jest uses to detect test files
166 | // testRegex: [],
167 |
168 | // This option allows the use of a custom results processor
169 | // testResultsProcessor: undefined,
170 |
171 | // This option allows use of a custom test runner
172 | // testRunner: "jest-circus/runner",
173 |
174 | // A map from regular expressions to paths to transformers
175 | // transform: undefined,
176 |
177 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
178 | // transformIgnorePatterns: [
179 | // "\\\\node_modules\\\\",
180 | // "\\.pnp\\.[^\\\\]+$"
181 | // ],
182 |
183 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
184 | // unmockedModulePathPatterns: undefined,
185 |
186 | // Indicates whether each individual test should be reported during the run
187 | // verbose: undefined,
188 |
189 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
190 | // watchPathIgnorePatterns: [],
191 |
192 | // Whether to use watchman for file crawling
193 | // watchman: true,
194 | };
195 |
```