Skip to content

Commit

Permalink
[M1-TR-210] Backend for M1_TR-4
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonclchua committed Sep 4, 2023
1 parent 942f478 commit 8e4b1c9
Show file tree
Hide file tree
Showing 41 changed files with 1,792 additions and 73 deletions.
22 changes: 19 additions & 3 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
"test:e2e": "jest --config ./test/jest-e2e.json",
"seed": "ts-node src/models/seed.ts"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
Expand All @@ -27,14 +28,18 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"jest-junit": "^16.0.0",
"nestjs-seeder": "^0.3.2",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@faker-js/faker": "^8.0.2",
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/swagger": "^7.1.10",
"@nestjs/testing": "^10.0.0",
"@types/express": "^4.17.17",
"@types/faker": "^6.6.9",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/supertest": "^2.0.12",
Expand Down Expand Up @@ -70,11 +75,22 @@
"**/*.{js,ts}"
],
"coverageDirectory": "../test/coverage",
"coverageReporters": ["clover", "json", "lcov", "text"],
"coverageReporters": [
"clover",
"json",
"lcov",
"text"
],
"testEnvironment": "node",
"reporters": [
"default",
["jest-junit", {"outputDirectory": "./test/result", "outputName": "report.xml"}]
[
"jest-junit",
{
"outputDirectory": "./test/result",
"outputName": "report.xml"
}
]
]
},
"prisma": {
Expand Down
115 changes: 115 additions & 0 deletions server/src/api/customer/customer.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { BadRequestException, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../database/connection.service';

import { CustomerController } from './customer.controller';
import { CustomerService } from './customer.service';

describe('CustomerController', () => {
let customerController: CustomerController;
let customerService: CustomerService;
let prisma: PrismaService

beforeEach(() => {
customerService = new CustomerService(prisma);
customerController = new CustomerController(customerService);
});

describe('create', () => {
it('should return a customer', async () => {
const newCustomer = {
firstName: "John",
lastName: "Doe",
email: "johndoe@gmail.com",
contact: "0123456",
address: "ABC St, DEF Ave, GHI City"
}

jest.spyOn(customerService, 'create').mockResolvedValue(newCustomer)
jest.spyOn(customerController, 'checkIfCustomerAlreadyExists').mockResolvedValue(false)

const createdCustomer = await customerController.create(newCustomer);

expect(createdCustomer).toEqual(newCustomer);
})

it('should return BadRequestException if input is invalid', async () => {
const invalidInput = {
firstName: "",
lastName: "",
email: "invalidEmail",
contact: "012345",
address: "ABC City"
}

jest.spyOn(customerController, 'checkIfCustomerAlreadyExists').mockResolvedValue(false)
jest.spyOn(customerService, 'create').mockRejectedValue(new BadRequestException('error'))

const createCustomerPromise = async () => {
await customerController.create(invalidInput);
}

expect(createCustomerPromise).rejects.toThrow(BadRequestException);
})
})

describe('findOne', () => {
it('should return a single customer', async () => {
const customer = {
id: 1,
firstName: "John",
lastName: "Doe",
email: "johndoe@gmail.com",
contact: "0123456",
address: "ABC St, DEF Ave, GHI City"
}

jest.spyOn(customerService, 'findOne').mockResolvedValue(customer);

const foundCustomer = await customerController.findOne(1);

expect(foundCustomer).toEqual(customer);
})

it('should throw a not found exception when no customer is found', async () => {
jest.spyOn(customerService, 'findOne').mockRejectedValue(new NotFoundException('No customer found.'));

const invalidId = 99;

const findOnePromise = customerController.findOne(invalidId);

await expect(findOnePromise).rejects.toThrow('No customer found.');
})
})

describe('findAll', () => {
it('should return an array of customers', async () => {
const customers = [{
firstName: "John",
lastName: "Doe",
email: "johndoe@gmail.com",
contact: "0123456",
address: "ABC St, DEF Ave, GHI City"
}, {
firstName: "Jane",
lastName: "Doe",
email: "janedoe@gmail.com",
contact: "4567890",
address: "DEF St, GHI Ave, ABC City"
}];

jest.spyOn(customerService, 'findAll').mockResolvedValue(customers)

const foundCustomers = await customerController.findAll();

expect(foundCustomers).toEqual(customers);
})

it('should throw a not found exception when no customer is found', async () => {
jest.spyOn(customerService, 'findAll').mockRejectedValue(new NotFoundException('No customer found.'));

const findAllPromise = customerController.findAll();

await expect(findAllPromise).rejects.toThrow('No customer found.');
})
})
});
61 changes: 61 additions & 0 deletions server/src/api/customer/customer.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Customer } from '@prisma/client';
import { ApiTags } from '@nestjs/swagger';
import { BadRequestException, Body, Controller, Get, NotFoundException, Param, ParseIntPipe, Post, UsePipes, ValidationPipe } from '@nestjs/common';

import { CustomerService } from './customer.service';

import { CreateCustomerDto } from './dtos/create-customer.dto';

@ApiTags('customer')
@Controller('customer')
export class CustomerController {
constructor(
private readonly customerService: CustomerService
) { }

@UsePipes(new ValidationPipe())
@Post()
async create(@Body() options: CreateCustomerDto): Promise<Customer> {
const { email } = options

const existingCustomer = await this.checkIfCustomerAlreadyExists(email)

if (existingCustomer) {
return existingCustomer
}

const newCustomer = await this.customerService.create(options)

if (!newCustomer) {
throw new BadRequestException('Something went wrong. Customer not created.')
}

return newCustomer
}

async checkIfCustomerAlreadyExists(email: string) {
return await this.customerService.findOne({ email })
}

@Get('/:id')
async findOne(@Param('id', ParseIntPipe) id: number): Promise<Customer> {
const customer = await this.customerService.findOne({ id })

if (!customer) {
throw new NotFoundException("No customer found.")
}

return customer
}

@Get()
async findAll(): Promise<Customer[]> {
const customers = await this.customerService.findAll()

if (customers.length == 0) {
throw new NotFoundException('No customer found.')
}

return customers
}
}
13 changes: 13 additions & 0 deletions server/src/api/customer/customer.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';

import { PrismaService } from '../../database/connection.service';

import { CustomerService } from './customer.service';
import { CustomerController } from './customer.controller';

@Module({
providers: [CustomerService, PrismaService],
controllers: [CustomerController],
exports: []
})
export class CustomerModule {}
73 changes: 73 additions & 0 deletions server/src/api/customer/customer.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { PrismaService } from '../../database/connection.service';

import { CustomerService } from './customer.service';

describe('CustomerService', () => {
let customerService: CustomerService;
let prisma: PrismaService

beforeEach(async () => {
customerService = new CustomerService(prisma);
});

describe('create', () => {
it('should return a customer', async () => {
const newCustomer = {
firstName: "John",
lastName: "Doe",
email: "johndoe@gmail.com",
contact: "0123456",
address: "ABC St, DEF Ave, GHI City"
}

jest.spyOn(customerService, 'create').mockResolvedValue(newCustomer)

const createdCustomer = await customerService.create(newCustomer);

expect(createdCustomer).toEqual(newCustomer);
})
})

describe('findOne', () => {
it('should return a single customer', async () => {
const customer = {
id: 1,
firstName: "John",
lastName: "Doe",
email: "johndoe@gmail.com",
contact: "0123456",
address: "ABC St, DEF Ave, GHI City"
}

jest.spyOn(customerService, 'findOne').mockResolvedValue(customer);

const foundCustomer = await customerService.findOne(1);

expect(foundCustomer).toEqual(customer);
})
})

describe('findAll', () => {
it('should return an array of customers', async () => {
const customers = [{
firstName: "John",
lastName: "Doe",
email: "johndoe@gmail.com",
contact: "0123456",
address: "ABC St, DEF Ave, GHI City"
}, {
firstName: "Jane",
lastName: "Doe",
email: "janedoe@gmail.com",
contact: "4567890",
address: "DEF St, GHI Ave, ABC City"
}];

jest.spyOn(customerService, 'findAll').mockResolvedValue(customers)

const foundCustomers = await customerService.findAll();

expect(foundCustomers).toEqual(customers);
})
})
});
12 changes: 12 additions & 0 deletions server/src/api/customer/customer.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Injectable } from '@nestjs/common';

import { PrismaService } from '../../database/connection.service';

import { AbstractService } from '../../shared/abstract-service';

@Injectable()
export class CustomerService extends AbstractService {
constructor(prisma: PrismaService) {
super(prisma, "Customer")
}
}
32 changes: 32 additions & 0 deletions server/src/api/customer/dtos/create-customer.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { IsEmail, IsOptional, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class CreateCustomerDto {
@ApiProperty()
@IsString()
firstName: string;

@ApiProperty()
@IsString()
lastName: string;

@ApiProperty()
@IsEmail()
email: string;

@ApiProperty()
@IsString()
contact: string;

@ApiProperty()
@IsString()
address: string;

@ApiProperty()
@IsOptional()
createdAt?: string;

@ApiProperty()
@IsOptional()
updatedAt?: string;
}
Loading

0 comments on commit 8e4b1c9

Please sign in to comment.