Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
Warnings:

- Changed the type of `zip_code` on the `addresses` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.

*/
-- AlterTable
ALTER TABLE "addresses" ALTER COLUMN "complement" DROP NOT NULL,
DROP COLUMN "zip_code",
ADD COLUMN "zip_code" INTEGER NOT NULL;
10 changes: 5 additions & 5 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ datasource db {
}

model Address {
id String @id @default(cuid())
id String @id @default(cuid())
number Int
street String
neighborhood String
complement String
complement String?
city String
state String @default("SP")
country String @default("BR")
zipCode String @map("zip_code")
state String @default("SP")
country String @default("BR")
zipCode Int @map("zip_code")
latitude Float
longitude Float

Expand Down
2 changes: 1 addition & 1 deletion prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ async function seed() {
street: faker.location.street(),
neighborhood: faker.location.secondaryAddress(),
number: Number.parseInt(faker.location.buildingNumber()),
zipCode: faker.location.zipCode(),
zipCode: Number.parseInt(faker.location.zipCode()),
latitude: faker.location.latitude(),
longitude: faker.location.longitude(),
userId: randomUser.id,
Expand Down
23 changes: 23 additions & 0 deletions src/http/controllers/address/getAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { FastifyReply, FastifyRequest } from 'fastify'

import { AddressNotExistsError } from '@/services/errors/address-not-exists-error'
import { makeGetAddressService } from '@/services/factories/addresses/make-get-address-service'

export async function getAddress(request: FastifyRequest, reply: FastifyReply) {
const userId = request.user.sub

let address = null
try {
const getAddressService = makeGetAddressService()

address = await getAddressService.execute({ userId })
} catch (err) {
if (err instanceof AddressNotExistsError) {
return reply.status(404).send({ message: err.message })
}

throw err
}

return reply.status(200).send(address)
}
17 changes: 17 additions & 0 deletions src/http/controllers/address/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { FastifyReply, FastifyRequest } from 'fastify'

import { makeListService } from '@/services/factories/addresses/make-list-service'

export async function list(_: FastifyRequest, reply: FastifyReply) {
let addresses = null
try {
const listService = makeListService()

addresses = await listService.execute()
} catch (err) {
// biome-ignore lint/complexity/noUselessCatch: <is necessary to set the error>
throw err
}

return reply.status(200).send({ addresses: addresses.address })
}
86 changes: 86 additions & 0 deletions src/http/controllers/address/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { FastifyReply, FastifyRequest } from 'fastify'
import { z } from 'zod'

import { AddressAlreadyExistsError } from '@/services/errors/address-already-exists-error'
import { UserNotExistsError } from '@/services/errors/user-not-exists-error'
import { makeRegisterService } from '@/services/factories/addresses/make-register-service'
import {
type AddressApiResponse,
getAddressByZipCode,
} from '@/utils/get-address-by-zipcode'
import { validateAddressFromApi } from '@/utils/validate-address'

export async function register(request: FastifyRequest, reply: FastifyReply) {
const registerBodySchema = z.object({
number: z.number().int().nonnegative().min(100).max(99999),
street: z.string().nonempty(),
neighborhood: z.string().nonempty(),
complement: z.string().optional(),
city: z.string().nonempty(),
zipCode: z
.number()
.int()
.min(10000000)
.max(99999999)
.refine(
zip => {
const prefix = Number.parseInt(zip.toString().substring(0, 2))
return prefix >= 1 && prefix <= 19
},
{
message: 'CEP não pertence ao estado de São Paulo',
},
),
latitude: z.number().min(-90).max(90).optional(),
longitude: z.number().min(-180).max(180).optional(),
})

const parsedBody = registerBodySchema.parse(request.body)
let { zipCode, latitude, longitude, street, neighborhood, city } = parsedBody

let addressData: AddressApiResponse
try {
addressData = await getAddressByZipCode(zipCode)
} catch (err) {
return reply.status(400).send({
message: (err as Error).message,
})
}

if (!latitude || !longitude) {
latitude = addressData.latitudeApi
longitude = addressData.longitudeApi
}

try {
validateAddressFromApi(
{ zipCode, street, neighborhood, city, latitude, longitude },
addressData,
)
} catch (err) {
return reply.status(400).send({ message: (err as Error).message })
}

try {
const registerService = makeRegisterService()

await registerService.execute({
...parsedBody,
userId: request.user.sub,
latitude,
longitude,
})
} catch (err) {
if (err instanceof UserNotExistsError) {
return reply.status(404).send({ message: err.message })
}

if (err instanceof AddressAlreadyExistsError) {
return reply.status(409).send({ message: err.message })
}

throw err
}

return reply.status(201).send()
}
21 changes: 21 additions & 0 deletions src/http/controllers/address/remove.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { FastifyReply, FastifyRequest } from 'fastify'

import { AddressNotExistsError } from '@/services/errors/address-not-exists-error'
import { makeRemoveService } from '@/services/factories/addresses/make-remove-service'

export async function remove(request: FastifyRequest, reply: FastifyReply) {
const userId = request.user.sub
try {
const removeService = makeRemoveService()

await removeService.execute({ userId })
} catch (err) {
if (err instanceof AddressNotExistsError) {
return reply.status(400).send({ message: err.message })
}

throw err
}

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

import { verifyJWT } from '@/http/middlewares/verify-jwt'
import { verifyUserRole } from '@/http/middlewares/verify-user-role'

import { getAddress } from './getAddress'
import { list } from './list'
import { register } from './register'
import { remove } from './remove'
import { update } from './update'

export async function addressRoutes(app: FastifyInstance) {
app.post('/address', { onRequest: [verifyJWT] }, register)
app.get('/address', { onRequest: [verifyJWT] }, getAddress)
app.patch('/address', { onRequest: [verifyJWT] }, update)
app.delete('/address', { onRequest: [verifyJWT] }, remove)

app.get('/addresses', { onRequest: [verifyUserRole('ADMIN')] }, list)
}
79 changes: 79 additions & 0 deletions src/http/controllers/address/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { FastifyReply, FastifyRequest } from 'fastify'
import { z } from 'zod'

import { AddressNotExistsError } from '@/services/errors/address-not-exists-error'
import { makeUpdateService } from '@/services/factories/addresses/make-update-service'
import { getAddressByZipCode } from '@/utils/get-address-by-zipcode'

export async function update(request: FastifyRequest, reply: FastifyReply) {
const updateBodySchema = z
.object({
zipCode: z
.number()
.int()
.min(10000000)
.max(99999999)
.refine(
zip => {
const prefix = Number.parseInt(zip.toString().substring(0, 2))
return prefix >= 1 && prefix <= 19
},
{
message: 'CEP não pertence ao estado de São Paulo',
},
)
.optional(),
number: z.number().int().nonnegative().min(100).max(99999).optional(),
complement: z.string().optional(),
})
.refine(
data => {
if (data.zipCode !== undefined && data.number === undefined) {
return false
}

return (
data.zipCode !== undefined ||
data.number !== undefined ||
data.complement !== undefined
)
},
{
message:
'Se zipCode for fornecido, number também deve ser fornecido. Pelo menos um campo deve ser fornecido.',
},
)

const userId = request.user.sub
const parsedBody = updateBodySchema.parse(request.body)
const { zipCode } = parsedBody

let addressApi = null
try {
if (zipCode) {
addressApi = await getAddressByZipCode(zipCode)
}
} catch (err) {
return reply.status(400).send({
message: (err as Error).message,
})
}

let address = null
try {
const updateService = makeUpdateService()

address = await updateService.execute({
userId,
data: { ...parsedBody, addressApi },
})
} catch (err) {
if (err instanceof AddressNotExistsError) {
return reply.status(404).send({ message: err.message })
}

throw err
}

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

import { addressRoutes } from './address/routes'
import { usersRoutes } from './users/routes'

export async function appRoutes(app: FastifyInstance) {
app.register(usersRoutes)
app.register(addressRoutes)
}
2 changes: 1 addition & 1 deletion src/http/controllers/users/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { FastifyReply, FastifyRequest } from 'fastify'
import { z } from 'zod'

import { InvalidCredentialsError } from '@/services/errors/invalid-credentials-error'
import { makeAuthenticateService } from '@/services/factories/make-authenticate-service'
import { makeAuthenticateService } from '@/services/factories/users/make-authenticate-service'

export async function authenticate(
request: FastifyRequest,
Expand Down
2 changes: 1 addition & 1 deletion src/http/controllers/users/getUser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FastifyReply, FastifyRequest } from 'fastify'

import { makeGetUserService } from '@/services/factories/make-get-user-service'
import { makeGetUserService } from '@/services/factories/users/make-get-user-service'

export async function getUser(request: FastifyRequest, reply: FastifyReply) {
const getUser = makeGetUserService()
Expand Down
2 changes: 1 addition & 1 deletion src/http/controllers/users/list.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { FastifyReply, FastifyRequest } from 'fastify'

import { UserNotExistsError } from '@/services/errors/user-not-exists-error'
import { makeListService } from '@/services/factories/make-list-service'
import { makeListService } from '@/services/factories/users/make-list-service'

export async function list(_: FastifyRequest, reply: FastifyReply) {
let users = null
Expand Down
2 changes: 1 addition & 1 deletion src/http/controllers/users/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { FastifyReply, FastifyRequest } from 'fastify'
import { z } from 'zod'

import { UserAlreadyExistsError } from '@/services/errors/user-already-exists-error'
import { makeRegisterService } from '@/services/factories/make-register-service'
import { makeRegisterService } from '@/services/factories/users/make-register-service'

export async function register(request: FastifyRequest, reply: FastifyReply) {
const registerBodySchema = z.object({
Expand Down
2 changes: 1 addition & 1 deletion src/http/controllers/users/remove.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { FastifyReply, FastifyRequest } from 'fastify'

import { UserNotExistsError } from '@/services/errors/user-not-exists-error'
import { makeRemoveService } from '@/services/factories/make-remove-service'
import { makeRemoveService } from '@/services/factories/users/make-remove-service'

import { logout } from './logout'

Expand Down
2 changes: 1 addition & 1 deletion src/http/controllers/users/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { FastifyReply, FastifyRequest } from 'fastify'
import { z } from 'zod'

import { UserNotExistsError } from '@/services/errors/user-not-exists-error'
import { makeUpdateService } from '@/services/factories/make-update-service'
import { makeUpdateService } from '@/services/factories/users/make-update-service'

export async function update(request: FastifyRequest, reply: FastifyReply) {
const updateBodySchema = z
Expand Down
12 changes: 12 additions & 0 deletions src/repositories/address-repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Address, Prisma } from '@prisma/client'

export interface AddressRepository {
create(userId: string, data: Prisma.AddressCreateInput): Promise<Address>
findByUserId(userId: string): Promise<Address | null>
updateByUserId(
userId: string,
data: Prisma.AddressUpdateInput,
): Promise<Address>
removeByUserId(userId: string): Promise<Address>
list(): Promise<Address[]>
}
Loading