Skip to content

Commit

Permalink
Add Create Address and Test
Browse files Browse the repository at this point in the history
  • Loading branch information
kevariable committed May 5, 2024
1 parent 0d9f829 commit 6ceb7ab
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/action/address/create-address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Address, User } from '@prisma/client'
import { CreateAddressRequest } from '../../model/address-model'
import { Validation } from '../../validation/validation'
import { AddressValidation } from '../../validation/address-validation'
import { prismaClient } from '../../application/database'
import GetContact from '../contact/get-contact'

export default class CreateAddress {
static async execute(
user: User,
request: CreateAddressRequest
): Promise<Address> {
const validated = Validation.validate(AddressValidation.CREATE, request)

await GetContact.execute(user, request.contact_id)

const createdAddress = await prismaClient.address.create({
data: validated
})

return createdAddress
}
}
20 changes: 20 additions & 0 deletions src/controller/address-controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NextFunction, Response } from 'express'
import CreateAddress from '../action/address/create-address'
import UserRequest from '../request/user-request'
import { CreateAddressRequest, toAddressResponse } from '../model/address-model'

export default class AddressController {
static async create(req: UserRequest, res: Response, next: NextFunction) {
try {
const data = req.body as CreateAddressRequest

data.contact_id = BigInt(req.params.contactId)

const response = await CreateAddress.execute(req.user!, data)

res.json(toAddressResponse(response)).status(200)
} catch (e: unknown) {
next(e)
}
}
}
46 changes: 46 additions & 0 deletions src/model/address-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Address } from '@prisma/client'
import { Response } from './model'
import { Pagination, isPagination } from './page-model'

export type CreateAddressRequest = {
contact_id: bigint
street?: string | null
city?: string | null
province?: string | null
country: string
postal_code: string
}

export type AddressResponse = {
id: bigint
street: string | null
city: string | null
province: string | null
country: string
postal_code: string
}

export function toAddressResponse(
address: Address | Pagination<Address[]>
): Response<AddressResponse> | Pagination<AddressResponse[]> {
const callback = (address: Address) => ({
id: address.id,

street: address.street,
city: address.city,
province: address.province,
country: address.country,
postal_code: address.postal_code
})

if (isPagination<Address | Address[]>(address)) {
return {
data: address.data.map(callback),
page: address.page
}
}

return {
data: callback(address)
}
}
3 changes: 3 additions & 0 deletions src/route/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import express from 'express'
import { UserController } from '../controller/user-controller'
import { user } from '../middleware/user'
import ContactController from '../controller/contact-controller'
import AddressController from '../controller/address-controller'

export const apiRouter = express.Router()
apiRouter.use(user)
Expand All @@ -15,3 +16,5 @@ apiRouter.get('/api/contacts/:contactId', ContactController.get)
apiRouter.put('/api/contacts/:contactId', ContactController.update)
apiRouter.delete('/api/contacts/:contactId', ContactController.delete)
apiRouter.post('/api/contacts/search', ContactController.search)

apiRouter.post('/api/contacts/:contactId/addresses', AddressController.create)
12 changes: 12 additions & 0 deletions src/validation/address-validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ZodType, z } from 'zod'

export class AddressValidation {
static readonly CREATE: ZodType = z.object({
contact_id: z.bigint(),
street: z.string().min(0).max(255).optional(),
city: z.string().min(0).max(255).optional(),
province: z.string().min(0).max(255).optional(),
country: z.string().min(0).max(255),
postal_code: z.string().min(0).max(255)
})
}
94 changes: 94 additions & 0 deletions tests/address.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import supertest from 'supertest'
import { web } from '../src/application/web'
import { createContact } from './fixtures/contact'
import { createAddressRequest } from './fixtures/address'
import { createUser } from './fixtures/user'
import { AddressResponse } from '../src/model/address-model'
import { Response } from '../src/model/model'
import { faker } from '@faker-js/faker'

describe('POST /api/contacts/{:contactId}/addresses', () => {
it('can create address by contact', async () => {
const user = await createUser()

const contact = await createContact(user)

const request = createAddressRequest(contact)

const response = await supertest(web)
.post(`/api/contacts/${contact.id}/addresses`)
.set('X-API-TOKEN', user.token!)
.send(request)

expect(response.status).toBe(200)

const body: Response<AddressResponse> = response.body

expect(body.data.id).not.toBeNull()
expect(body.data.city).toBe(request.city)
expect(body.data.province).toBe(request.province)
expect(body.data.country).toBe(request.country)
expect(body.data.postal_code).toBe(request.postal_code)
expect(body.data.street).toBe(request.street)
})

it('can create address partial by contact', async () => {
const user = await createUser()

const contact = await createContact(user)

const request = createAddressRequest(contact)

const response = await supertest(web)
.post(`/api/contacts/${contact.id}/addresses`)
.set('X-API-TOKEN', user.token!)
.send({
street: request.street,
country: request.country,
postal_code: request.postal_code
})

expect(response.status).toBe(200)

const body: Response<AddressResponse> = response.body

expect(body.data.id).not.toBeNull()
expect(body.data.city).toBeNull()
expect(body.data.province).toBeNull()
expect(body.data.street).toBe(request.street)
expect(body.data.country).toBe(request.country)
expect(body.data.postal_code).toBe(request.postal_code)
})

it('cant create address if payload is not desirable', async () => {
const user = await createUser()

const contact = await createContact(user)

const response = await supertest(web)
.post(`/api/contacts/${contact.id}/addresses`)
.set('X-API-TOKEN', user.token!)
.send({
street: faker.word.words(50),
country: faker.word.words(50),
postal_code: faker.word.words(50),
province: faker.word.words(50),
city: faker.word.words(50)
})

expect(response.status).toBe(422)
})

it('can create address if contact not available', async () => {
const user = await createUser()

const contact = await createContact(user)

const response = await supertest(web)
.post(`/api/contacts/0/addresses`)
.set('X-API-TOKEN', user.token!)
.send(createAddressRequest(contact))

expect(response.status).toBe(404)
})
})
23 changes: 23 additions & 0 deletions tests/fixtures/address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { faker } from '@faker-js/faker'
import { CreateAddressRequest } from '../../src/model/address-model'
import { Contact, User } from '@prisma/client'
import CreateAddress from '../../src/action/address/create-address'

export const createAddressRequest = (
contact: Contact,
state?: Partial<CreateAddressRequest>
): CreateAddressRequest => ({
contact_id: contact.id,
street: faker.location.streetAddress(),
city: faker.location.city(),
province: faker.location.state(),
country: faker.location.country(),
postal_code: faker.location.zipCode(),
...state
})

export const createAddress = async (user: User, contact: Contact) => {
const request = createAddressRequest(contact)

return await CreateAddress.execute(user, request)
}

0 comments on commit 6ceb7ab

Please sign in to comment.