diff --git a/prisma/migrations/20250724210200_mudar_dois_campos_da_tabela_address/migration.sql b/prisma/migrations/20250724210200_mudar_dois_campos_da_tabela_address/migration.sql new file mode 100644 index 0000000..a57fa57 --- /dev/null +++ b/prisma/migrations/20250724210200_mudar_dois_campos_da_tabela_address/migration.sql @@ -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; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7b3b1e6..6849d5f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -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 diff --git a/prisma/seed.ts b/prisma/seed.ts index 71da612..41e2d31 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -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, diff --git a/src/http/controllers/address/getAddress.ts b/src/http/controllers/address/getAddress.ts new file mode 100644 index 0000000..d42c727 --- /dev/null +++ b/src/http/controllers/address/getAddress.ts @@ -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) +} diff --git a/src/http/controllers/address/list.ts b/src/http/controllers/address/list.ts new file mode 100644 index 0000000..704c16d --- /dev/null +++ b/src/http/controllers/address/list.ts @@ -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: + throw err + } + + return reply.status(200).send({ addresses: addresses.address }) +} diff --git a/src/http/controllers/address/register.ts b/src/http/controllers/address/register.ts new file mode 100644 index 0000000..f3d952a --- /dev/null +++ b/src/http/controllers/address/register.ts @@ -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() +} diff --git a/src/http/controllers/address/remove.ts b/src/http/controllers/address/remove.ts new file mode 100644 index 0000000..3413b10 --- /dev/null +++ b/src/http/controllers/address/remove.ts @@ -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() +} diff --git a/src/http/controllers/address/routes.ts b/src/http/controllers/address/routes.ts new file mode 100644 index 0000000..f1fc52c --- /dev/null +++ b/src/http/controllers/address/routes.ts @@ -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) +} diff --git a/src/http/controllers/address/update.ts b/src/http/controllers/address/update.ts new file mode 100644 index 0000000..b1e5bfd --- /dev/null +++ b/src/http/controllers/address/update.ts @@ -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) +} diff --git a/src/http/controllers/routes.ts b/src/http/controllers/routes.ts index 99fc169..c942564 100644 --- a/src/http/controllers/routes.ts +++ b/src/http/controllers/routes.ts @@ -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) } diff --git a/src/http/controllers/users/authenticate.ts b/src/http/controllers/users/authenticate.ts index 38c57e6..8f19a38 100644 --- a/src/http/controllers/users/authenticate.ts +++ b/src/http/controllers/users/authenticate.ts @@ -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, diff --git a/src/http/controllers/users/getUser.ts b/src/http/controllers/users/getUser.ts index e8008ef..56d55ae 100644 --- a/src/http/controllers/users/getUser.ts +++ b/src/http/controllers/users/getUser.ts @@ -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() diff --git a/src/http/controllers/users/list.ts b/src/http/controllers/users/list.ts index 123f860..64935d6 100644 --- a/src/http/controllers/users/list.ts +++ b/src/http/controllers/users/list.ts @@ -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 diff --git a/src/http/controllers/users/register.ts b/src/http/controllers/users/register.ts index 5d78ecc..ed6aa9f 100644 --- a/src/http/controllers/users/register.ts +++ b/src/http/controllers/users/register.ts @@ -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({ diff --git a/src/http/controllers/users/remove.ts b/src/http/controllers/users/remove.ts index b774e3c..b171749 100644 --- a/src/http/controllers/users/remove.ts +++ b/src/http/controllers/users/remove.ts @@ -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' diff --git a/src/http/controllers/users/update.ts b/src/http/controllers/users/update.ts index 1dd9e91..28f44fb 100644 --- a/src/http/controllers/users/update.ts +++ b/src/http/controllers/users/update.ts @@ -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 diff --git a/src/repositories/address-repository.ts b/src/repositories/address-repository.ts new file mode 100644 index 0000000..e212698 --- /dev/null +++ b/src/repositories/address-repository.ts @@ -0,0 +1,12 @@ +import type { Address, Prisma } from '@prisma/client' + +export interface AddressRepository { + create(userId: string, data: Prisma.AddressCreateInput): Promise
+ findByUserId(userId: string): Promise
+ updateByUserId( + userId: string, + data: Prisma.AddressUpdateInput, + ): Promise
+ removeByUserId(userId: string): Promise
+ list(): Promise +} diff --git a/src/repositories/prisma/prisma-address-repository.ts b/src/repositories/prisma/prisma-address-repository.ts new file mode 100644 index 0000000..5a5a2b7 --- /dev/null +++ b/src/repositories/prisma/prisma-address-repository.ts @@ -0,0 +1,58 @@ +import { prisma } from '@/lib/prisma' +import type { Prisma } from '@prisma/client' + +export class PrismaAddressRepository { + async create(userId: string, data: Prisma.AddressCreateInput) { + const address = await prisma.address.create({ + data: { + ...data, + user: { + connect: { id: userId }, + }, + }, + }) + + return address + } + + async findByUserId(userId: string) { + const address = await prisma.address.findFirst({ + where: { + userId, + }, + }) + + return address + } + + async updateByUserId(userId: string, data: Prisma.AddressUpdateInput) { + const address = await prisma.address.update({ + where: { + userId, + }, + data, + }) + + return address + } + + async list() { + const address = await prisma.address.findMany({ + orderBy: { + updatedAt: 'desc', + }, + }) + + return address + } + + async removeByUserId(userId: string) { + const address = await prisma.address.delete({ + where: { + userId, + }, + }) + + return address + } +} diff --git a/src/services/addresses/get-address.ts b/src/services/addresses/get-address.ts new file mode 100644 index 0000000..4ed0d34 --- /dev/null +++ b/src/services/addresses/get-address.ts @@ -0,0 +1,20 @@ +import type { AddressRepository } from '@/repositories/address-repository' +import { AddressNotExistsError } from '../errors/address-not-exists-error' + +interface GetAddressServiceRequest { + userId: string +} + +export class GetAddressService { + constructor(private addressRepository: AddressRepository) {} + + async execute({ userId }: GetAddressServiceRequest) { + const address = await this.addressRepository.findByUserId(userId) + + if (!address) { + throw new AddressNotExistsError() + } + + return { address } + } +} diff --git a/src/services/addresses/list.ts b/src/services/addresses/list.ts new file mode 100644 index 0000000..38bf61d --- /dev/null +++ b/src/services/addresses/list.ts @@ -0,0 +1,11 @@ +import type { AddressRepository } from '@/repositories/address-repository' + +export class ListService { + constructor(private addressRepository: AddressRepository) {} + + async execute() { + const address = await this.addressRepository.list() + + return { address } + } +} diff --git a/src/services/addresses/register.ts b/src/services/addresses/register.ts new file mode 100644 index 0000000..ad44642 --- /dev/null +++ b/src/services/addresses/register.ts @@ -0,0 +1,73 @@ +import type { AddressRepository } from '@/repositories/address-repository' +import type { UsersRepository } from '@/repositories/users-repository' +import { AddressAlreadyExistsError } from '../errors/address-already-exists-error' +import { UserNotExistsError } from '../errors/user-not-exists-error' + +interface RegisterServiceRequest { + number: number + street: string + neighborhood: string + complement?: string + city: string + state?: string + country?: string + zipCode: number + latitude: number + longitude: number + + userId: string +} + +export class RegisterService { + constructor( + private addressRepository: AddressRepository, + private userRepository: UsersRepository, + ) {} + + async execute({ + number, + street, + neighborhood, + complement, + city, + state, + country, + zipCode, + latitude, + longitude, + userId, + }: RegisterServiceRequest) { + const doesUserExists = await this.userRepository.findById(userId) + + if (!doesUserExists) { + throw new UserNotExistsError() + } + + const doesAddressAlreadyExists = + await this.addressRepository.findByUserId(userId) + + if (doesAddressAlreadyExists) { + throw new AddressAlreadyExistsError() + } + + const address = await this.addressRepository.create(userId, { + number, + street, + neighborhood, + complement, + city, + state, + country, + zipCode, + latitude, + longitude, + user: { + connect: { + id: userId, + }, + }, + }) + + return { address } + } +} diff --git a/src/services/addresses/remove.ts b/src/services/addresses/remove.ts new file mode 100644 index 0000000..2c7a354 --- /dev/null +++ b/src/services/addresses/remove.ts @@ -0,0 +1,22 @@ +import type { AddressRepository } from '@/repositories/address-repository' +import { AddressNotExistsError } from '../errors/address-not-exists-error' + +interface RemoveServiceRequest { + userId: string +} + +export class RemoveService { + constructor(private addressRepository: AddressRepository) {} + + async execute({ userId }: RemoveServiceRequest) { + const doesAddressExists = await this.addressRepository.findByUserId(userId) + + if (!doesAddressExists) { + throw new AddressNotExistsError() + } + + const address = await this.addressRepository.removeByUserId(userId) + + return { address } + } +} diff --git a/src/services/addresses/update.ts b/src/services/addresses/update.ts new file mode 100644 index 0000000..5f9b678 --- /dev/null +++ b/src/services/addresses/update.ts @@ -0,0 +1,48 @@ +import type { AddressRepository } from '@/repositories/address-repository' +import type { AddressApiResponse } from '@/utils/get-address-by-zipcode' +import { AddressNotExistsError } from '../errors/address-not-exists-error' + +interface UpdateServiceRequest { + userId: string + data: { + zipCode?: number + number?: number + complement?: string + addressApi: AddressApiResponse | null + } +} + +export class UpdateService { + constructor(private addressRepository: AddressRepository) {} + + async execute({ userId, data }: UpdateServiceRequest) { + const doesAddressExists = await this.addressRepository.findByUserId(userId) + + if (!doesAddressExists) { + throw new AddressNotExistsError() + } + + let address = null + if (data.zipCode !== undefined && data.addressApi !== null) { + address = await this.addressRepository.updateByUserId(userId, { + zipCode: data.zipCode, + number: data.number, + complement: data.complement, + neighborhood: data.addressApi.neighborhoodApi, + street: data.addressApi.streetApi, + city: data.addressApi.cityApi, + latitude: data.addressApi.latitudeApi, + longitude: data.addressApi.longitudeApi, + }) + } + + if (data.zipCode === undefined) { + address = await this.addressRepository.updateByUserId(userId, { + number: data.number, + complement: data.complement, + }) + } + + return { address } + } +} diff --git a/src/services/errors/address-already-exists-error.ts b/src/services/errors/address-already-exists-error.ts new file mode 100644 index 0000000..26dab77 --- /dev/null +++ b/src/services/errors/address-already-exists-error.ts @@ -0,0 +1,5 @@ +export class AddressAlreadyExistsError extends Error { + constructor() { + super('Address already exists.') + } +} diff --git a/src/services/errors/address-not-exists-error.ts b/src/services/errors/address-not-exists-error.ts new file mode 100644 index 0000000..03d9a82 --- /dev/null +++ b/src/services/errors/address-not-exists-error.ts @@ -0,0 +1,5 @@ +export class AddressNotExistsError extends Error { + constructor() { + super('Address not exists.') + } +} diff --git a/src/services/factories/addresses/make-get-address-service.ts b/src/services/factories/addresses/make-get-address-service.ts new file mode 100644 index 0000000..9b09a10 --- /dev/null +++ b/src/services/factories/addresses/make-get-address-service.ts @@ -0,0 +1,9 @@ +import { PrismaAddressRepository } from '@/repositories/prisma/prisma-address-repository' +import { GetAddressService } from '@/services/addresses/get-address' + +export function makeGetAddressService() { + const addressRepository = new PrismaAddressRepository() + const getAddressService = new GetAddressService(addressRepository) + + return getAddressService +} diff --git a/src/services/factories/addresses/make-list-service.ts b/src/services/factories/addresses/make-list-service.ts new file mode 100644 index 0000000..9677010 --- /dev/null +++ b/src/services/factories/addresses/make-list-service.ts @@ -0,0 +1,9 @@ +import { PrismaAddressRepository } from '@/repositories/prisma/prisma-address-repository' +import { ListService } from '@/services/addresses/list' + +export function makeListService() { + const addressRepository = new PrismaAddressRepository() + const listService = new ListService(addressRepository) + + return listService +} diff --git a/src/services/factories/addresses/make-register-service.ts b/src/services/factories/addresses/make-register-service.ts new file mode 100644 index 0000000..edbae20 --- /dev/null +++ b/src/services/factories/addresses/make-register-service.ts @@ -0,0 +1,15 @@ +import { PrismaAddressRepository } from '@/repositories/prisma/prisma-address-repository' +import { PrismaUsersRepository } from '@/repositories/prisma/prisma-users-repository' +import { RegisterService } from '@/services/addresses/register' + +export function makeRegisterService() { + const addressRepository = new PrismaAddressRepository() + const usersRepository = new PrismaUsersRepository() + + const registerService = new RegisterService( + addressRepository, + usersRepository, + ) + + return registerService +} diff --git a/src/services/factories/addresses/make-remove-service.ts b/src/services/factories/addresses/make-remove-service.ts new file mode 100644 index 0000000..36888c7 --- /dev/null +++ b/src/services/factories/addresses/make-remove-service.ts @@ -0,0 +1,9 @@ +import { PrismaAddressRepository } from '@/repositories/prisma/prisma-address-repository' +import { RemoveService } from '@/services/addresses/remove' + +export function makeRemoveService() { + const addressRepository = new PrismaAddressRepository() + const removeService = new RemoveService(addressRepository) + + return removeService +} diff --git a/src/services/factories/addresses/make-update-service.ts b/src/services/factories/addresses/make-update-service.ts new file mode 100644 index 0000000..0d4c8b0 --- /dev/null +++ b/src/services/factories/addresses/make-update-service.ts @@ -0,0 +1,9 @@ +import { PrismaAddressRepository } from '@/repositories/prisma/prisma-address-repository' +import { UpdateService } from '@/services/addresses/update' + +export function makeUpdateService() { + const addressRepository = new PrismaAddressRepository() + const updateService = new UpdateService(addressRepository) + + return updateService +} diff --git a/src/services/factories/make-authenticate-service.ts b/src/services/factories/users/make-authenticate-service.ts similarity index 82% rename from src/services/factories/make-authenticate-service.ts rename to src/services/factories/users/make-authenticate-service.ts index 39e288e..9fe52f1 100644 --- a/src/services/factories/make-authenticate-service.ts +++ b/src/services/factories/users/make-authenticate-service.ts @@ -1,5 +1,5 @@ import { PrismaUsersRepository } from '@/repositories/prisma/prisma-users-repository' -import { AuthenticateService } from '../users/authenticate' +import { AuthenticateService } from '../../users/authenticate' export function makeAuthenticateService() { const usersRepository = new PrismaUsersRepository() diff --git a/src/services/factories/make-get-user-service.ts b/src/services/factories/users/make-get-user-service.ts similarity index 100% rename from src/services/factories/make-get-user-service.ts rename to src/services/factories/users/make-get-user-service.ts diff --git a/src/services/factories/make-list-service.ts b/src/services/factories/users/make-list-service.ts similarity index 100% rename from src/services/factories/make-list-service.ts rename to src/services/factories/users/make-list-service.ts diff --git a/src/services/factories/make-register-service.ts b/src/services/factories/users/make-register-service.ts similarity index 83% rename from src/services/factories/make-register-service.ts rename to src/services/factories/users/make-register-service.ts index 7505741..a3b1395 100644 --- a/src/services/factories/make-register-service.ts +++ b/src/services/factories/users/make-register-service.ts @@ -1,5 +1,5 @@ import { PrismaUsersRepository } from '@/repositories/prisma/prisma-users-repository' -import { RegisterService } from '../users/register' +import { RegisterService } from '../../users/register' export function makeRegisterService() { const usersRepository = new PrismaUsersRepository() diff --git a/src/services/factories/make-remove-service.ts b/src/services/factories/users/make-remove-service.ts similarity index 83% rename from src/services/factories/make-remove-service.ts rename to src/services/factories/users/make-remove-service.ts index 921b66e..d5b33ae 100644 --- a/src/services/factories/make-remove-service.ts +++ b/src/services/factories/users/make-remove-service.ts @@ -1,5 +1,5 @@ import { PrismaUsersRepository } from '@/repositories/prisma/prisma-users-repository' -import { RemoveService } from '../users/remove' +import { RemoveService } from '../../users/remove' export function makeRemoveService() { const usersRepository = new PrismaUsersRepository() diff --git a/src/services/factories/make-update-service.ts b/src/services/factories/users/make-update-service.ts similarity index 100% rename from src/services/factories/make-update-service.ts rename to src/services/factories/users/make-update-service.ts diff --git a/src/utils/get-address-by-zipcode.ts b/src/utils/get-address-by-zipcode.ts new file mode 100644 index 0000000..fd5d65c --- /dev/null +++ b/src/utils/get-address-by-zipcode.ts @@ -0,0 +1,70 @@ +interface BrazilApiResponse { + cep: number + state: string + city: string + neighborhood: string + street: string + service: string + location: { + type: string + coordinates: { + latitude: number + longitude: number + } + } +} + +export interface AddressApiResponse { + latitudeApi: number + longitudeApi: number + streetApi: string + neighborhoodApi: string + cityApi: string + zipCodeApi: number +} + +export async function getAddressByZipCode( + zipCode: number, +): Promise { + let data: BrazilApiResponse + try { + const response = await fetch( + `https://brasilapi.com.br/api/cep/v2/${zipCode}`, + { + signal: AbortSignal.timeout(5000), + }, + ) + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + + data = await response.json() + } catch (err) { + throw new Error( + `Erro ao buscar dados do endereço: ${(err as Error).message}`, + ) + } + + const zipCodeApi = Number(data.cep) + const streetApi = data.street + const neighborhoodApi = data.neighborhood + const cityApi = data.city + const latitudeApi = Number(data.location.coordinates.latitude) + const longitudeApi = Number(data.location.coordinates.longitude) + + if (!latitudeApi || !longitudeApi) { + throw new Error( + 'Não foi possível obter as coordenadas para o CEP informado.', + ) + } + + return { + zipCodeApi, + streetApi, + neighborhoodApi, + cityApi, + latitudeApi, + longitudeApi, + } +} diff --git a/src/utils/validate-address.ts b/src/utils/validate-address.ts new file mode 100644 index 0000000..d4b2443 --- /dev/null +++ b/src/utils/validate-address.ts @@ -0,0 +1,41 @@ +import type { AddressApiResponse } from './get-address-by-zipcode' + +interface AddressUser { + street: string + neighborhood: string + city: string + zipCode: number + latitude: number + longitude: number +} + +const errorMessage = + 'Os dados fornecidos não correspondem ao endereço retornado pela API.' + +export function validateAddressFromApi( + { latitude, longitude, street, neighborhood, city, zipCode }: AddressUser, + { + zipCodeApi, + streetApi, + neighborhoodApi, + cityApi, + latitudeApi, + longitudeApi, + }: AddressApiResponse, +) { + if ( + zipCodeApi !== zipCode || + latitudeApi !== latitude || + longitudeApi !== longitude + ) { + throw new Error(errorMessage) + } + + if ( + streetApi.toLowerCase() !== street.toLowerCase() || + neighborhoodApi.toLowerCase() !== neighborhood.toLowerCase() || + cityApi.toLowerCase() !== city.toLowerCase() + ) { + throw new Error(errorMessage) + } +}