Skip to content

Commit

Permalink
feat: add mini controllers
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabio Brasileiro authored and Fabio Brasileiro committed Sep 27, 2024
1 parent f3ac4e1 commit 30eea0e
Show file tree
Hide file tree
Showing 25 changed files with 739 additions and 34 deletions.
472 changes: 472 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"test": "vitest run --dir src/user-cases",
"test:watch": "vitest run --dir src/use-cases",
"test:e2e": "vitest run --dir src/http",
"test:e2e:watch": "vitest --dir src/http",
"test:coverage": "vitest run --coverage",
"test:ui": "vitest --ui",
"lint:format": "npx @biomejs/biome format --write ./",
Expand All @@ -26,10 +27,12 @@
"@biomejs/biome": "1.9.2",
"@types/bcryptjs": "2.4.6",
"@types/node": "22.5.5",
"@types/supertest": "6.0.2",
"@vitest/coverage-v8": "2.1.1",
"@vitest/ui": "2.1.1",
"globals": "15.9.0",
"prisma": "5.19.1",
"supertest": "7.0.0",
"tsup": "8.3.0",
"tsx": "4.19.1",
"typescript": "5.6.2",
Expand Down
6 changes: 4 additions & 2 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import fastify from 'fastify'
import { ZodError } from 'zod'
import { env } from './env'
import { appRoutes } from './http/routes'
import { usersRoutes } from './http/controllers/users/routes'
import { gymsRoutes } from './http/controllers/gyms/routes'
import fastifyJwt from '@fastify/jwt'

export const app = fastify()
Expand All @@ -10,7 +11,8 @@ app.register(fastifyJwt, {
secret: env.JWT_SECRET,
})

app.register(appRoutes)
app.register(usersRoutes)
app.register(gymsRoutes)

app.setErrorHandler((error, _request, reply) => {
if (error instanceof ZodError) {
Expand Down
3 changes: 0 additions & 3 deletions src/http/controllers/authenticate.spec.ts

This file was deleted.

32 changes: 32 additions & 0 deletions src/http/controllers/check-ins/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { makeCreateGymUseCase } from '@/use-cases/factories/make-create-gym-use-case'
import type { FastifyReply, FastifyRequest } from 'fastify'
import { z } from 'zod'

export async function create(request: FastifyRequest, reply: FastifyReply) {
const createGymBodySchema = z.object({
title: z.string(),
description: z.string().nullable(),
phone: z.string().nullable(),
latitude: z.number().refine(value => {
return Math.abs(value) <= 90
}),

longitude: z.number().refine(value => {
return Math.abs(value) <= 180
}),
})

const { title, description, phone, latitude, longitude } =
createGymBodySchema.parse(request.body)
const createGymUseCase = makeCreateGymUseCase()

await createGymUseCase.execute({
title,
description,
phone,
latitude,
longitude,
})

return reply.status(201).send()
}
Empty file.
Empty file.
10 changes: 10 additions & 0 deletions src/http/controllers/check-ins/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { FastifyInstance } from "fastify";

import { verifyJWT } from "@/http/middlewares/verify-jwt";
import { create } from "../gyms/create";

export async function checkInsRoutes(app: FastifyInstance) {
app.addHook('onRequest', verifyJWT)

app.post('/gyms/:gymId/check-ins', create)
}
Empty file.
32 changes: 32 additions & 0 deletions src/http/controllers/gyms/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { makeCreateGymUseCase } from '@/use-cases/factories/make-create-gym-use-case'
import type { FastifyReply, FastifyRequest } from 'fastify'
import { z } from 'zod'

export async function create(request: FastifyRequest, reply: FastifyReply) {
const createGymBodySchema = z.object({
title: z.string(),
description: z.string().nullable(),
phone: z.string().nullable(),
latitude: z.number().refine(value => {
return Math.abs(value) <= 90
}),

longitude: z.number().refine(value => {
return Math.abs(value) <= 180
}),
})

const { title, description, phone, latitude, longitude } =
createGymBodySchema.parse(request.body)
const createGymUseCase = makeCreateGymUseCase()

await createGymUseCase.execute({
title,
description,
phone,
latitude,
longitude,
})

return reply.status(201).send()
}
26 changes: 26 additions & 0 deletions src/http/controllers/gyms/nearby.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { makeFetchNearbyGymsUseCase } from '@/use-cases/factories/make-fetch-nearby-gyms-use-case'
import type { FastifyReply, FastifyRequest } from 'fastify'
import { z } from 'zod'

export async function nearby(request: FastifyRequest, reply: FastifyReply) {
const nearbyGymQuerySchema = z.object({
latitude: z.number().refine(value => {
return Math.abs(value) <= 90
}),
longitude: z.number().refine(value => {
return Math.abs(value) <= 180
}),
})

const { latitude, longitude } = nearbyGymQuerySchema.parse(request.body)
const fetchNearbyGymsUseCase = makeFetchNearbyGymsUseCase()

const { gyms } = await fetchNearbyGymsUseCase.execute({
userLatitude: latitude,
userLogngitude: longitude,
})

return reply.status(200).send({
gyms,
})
}
15 changes: 15 additions & 0 deletions src/http/controllers/gyms/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FastifyInstance } from 'fastify'

import { verifyJWT } from '@/http/middlewares/verify-jwt'
import { search } from './search'
import { nearby } from './nearby'
import {create} from './create'

export async function gymsRoutes(app: FastifyInstance) {
app.addHook('onRequest', verifyJWT)

app.get('/gyms/search', search)
app.get('/gyms/nearby',nearby)

app.post('/gyms', create)
}
22 changes: 22 additions & 0 deletions src/http/controllers/gyms/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { makeSearchGymsUseCase } from '@/use-cases/factories/make-search-gyms-use-case'
import type { FastifyReply, FastifyRequest } from 'fastify'
import { z } from 'zod'

export async function search(request: FastifyRequest, reply: FastifyReply) {
const serchGymQuerySchema = z.object({
q: z.string(),
page: z.coerce.number().min(1).default(1),
})

const { q, page } = serchGymQuerySchema.parse(request.body)
const searchGymsUseCase = makeSearchGymsUseCase()

const { gyms } = await searchGymsUseCase.execute({
query: q,
page,
})

return reply.status(200).send({
gyms,
})
}
2 changes: 0 additions & 2 deletions src/http/controllers/register.spec.ts

This file was deleted.

31 changes: 31 additions & 0 deletions src/http/controllers/users/authenticate.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import request from 'supertest'
import { app } from '@/app'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'

describe('Authenticate (e2e)', () => {
beforeAll(async () => {
await app.ready()
})

afterAll(async () => {
await app.close()
})

it('should be able to authenticate', async () => {
await request(app.server).post('/users').send({
name: 'John Doe',
email: '[email protected]',
password: '123456',
})

const response = await request(app.server).post('/sessions').send({
email: '[email protected]',
password: '123456',
})

expect(response.statusCode).toEqual(200)
expect(response.body).toEqual({
token: expect.any(String),
})
})
})
File renamed without changes.
File renamed without changes.
40 changes: 40 additions & 0 deletions src/http/controllers/users/profile.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import request from 'supertest'
import { app } from '@/app'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'

describe('Profile (e2e)', () => {
beforeAll(async () => {
await app.ready()
})

afterAll(async () => {
await app.close()
})

it('should be able to user profile', async () => {
await request(app.server).post('/users').send({
name: 'John Doe',
email: '[email protected]',
password: '123456',
})

const authResponse = await request(app.server).post('/sessions').send({
email: '[email protected]',
password: '123456',
})

const { token } = authResponse.body

const profileResponse = await request(app.server)
.get('/me')
.set('Authorization', `Bearer ${token}`)
.send()

expect(profileResponse.statusCode).toEqual(200)
expect(profileResponse.body.user).toEqual(
expect.objectContaining({
email: '[email protected]',
})
)
})
})
File renamed without changes.
23 changes: 23 additions & 0 deletions src/http/controllers/users/register.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import request from 'supertest'
import { app } from '@/app'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'

describe('Register (e2e)', () => {
beforeAll(async () => {
await app.ready()
})

afterAll(async () => {
await app.close()
})

it('should be able to register', async () => {
const response = await request(app.server).post('/users').send({
name: 'John Doe',
email: '[email protected]',
password: '123456',
})

expect(response.statusCode).toEqual(201)
})
})
File renamed without changes.
16 changes: 16 additions & 0 deletions src/http/controllers/users/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { FastifyInstance } from 'fastify'
import { authenticate } from './authenticate'
import { register } from './register'
import { baseRouter } from './baseRouter'
import { profile } from './profile'
import { verifyJWT } from '@/http/middlewares/verify-jwt'

export async function usersRoutes(app: FastifyInstance) {
app.get('/', baseRouter)

app.post('/users', register)

app.post('/sessions', authenticate)
/** Authentication */
app.get('/me', { onRequest: [verifyJWT] }, profile)
}
16 changes: 0 additions & 16 deletions src/http/routes.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/use-cases/factories/make-create-gym-use-case.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PrismaGymsRepository } from '@/repositories/prisma/prisma-gyms-repository'
import { CreateGymUseCase } from '../create-gym'

export function makeFetchNearbyGymsUseCase() {
export function makeCreateGymUseCase() {
const gymsRepository = new PrismaGymsRepository()
const useCase = new CreateGymUseCase(gymsRepository)

Expand Down
22 changes: 12 additions & 10 deletions src/vitest-environments/prisma.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,44 @@
import { PrismaClient } from '@prisma/client'
import 'dotenv/config'

import { randomUUID } from 'node:crypto'
import { execSync } from 'node:child_process'
import { Environment } from 'vitest'
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

function generateDatabaseURL(schema: string) {
if (!process.env.DATABASE_URL) {
throw new Error('Please provide a DATABASE_URL environment variable.')
}

const url = new URL(process.env.DATABASE_URL)

url.searchParams.set('schema', schema)

return url.toString()
}

const PrismaTestEnvironment: Environment = {
export default (<Environment>{
name: 'prisma',
async setup() {
const schema = randomUUID()
const databaseURL = generateDatabaseURL(schema)
console.log(generateDatabaseURL(schema))

process.env.DATABASE_URL = databaseURL
// Perform any setup you need for the Prisma environment

execSync('npx prisma migrate deploy')

return {
async teardown() {
await prisma.$executeRawUnsafe(
`DROP SCHEMA IF EXISTS "${schema}" CASCADE`
)
// Perform any teardown you need for the Prisma environment
await prisma.$disconnect

await prisma.$disconnect()
},
transformMode: 'ssr', // Use either 'ssr' or 'web', depending on your needs
transformMode: 'ssr',
}
},
transformMode: 'ssr',
}

export default PrismaTestEnvironment
})

0 comments on commit 30eea0e

Please sign in to comment.