-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(backend): introduce Order module
- Loading branch information
Showing
22 changed files
with
530 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
export class ReadActiveCreditError extends Error { | ||
constructor(error) { | ||
super(`No active credit is available. ${error}`); | ||
super(`Failed to read user's active credit. ${error}`); | ||
} | ||
} |
4 changes: 3 additions & 1 deletion
4
apps/backend/src/credit/interface/credit-duration.interface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
export type CreditDuration = 'MONTHLY'; | ||
export enum CreditDuration { | ||
Monthly = 'Monthly', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
export type CreditLimit = 'FREE' | 'STANDARD'; | ||
export enum CreditLimit { | ||
Free = 'Free', | ||
Standard = 'Standard', | ||
} |
39 changes: 39 additions & 0 deletions
39
apps/backend/src/database/migrations/1629996471851-orders.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { MigrationInterface, QueryRunner } from 'typeorm'; | ||
|
||
export class orders1629996471851 implements MigrationInterface { | ||
name = 'orders1629996471851'; | ||
|
||
public async up(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query( | ||
`CREATE TYPE "order_status_enum" AS ENUM('Created', 'PaymentPending', 'PaymentSuccessful', 'PaymentFailed')`, | ||
); | ||
await queryRunner.query( | ||
`CREATE TYPE "order_credit_limit_type_enum" AS ENUM('Free', 'Standard')`, | ||
); | ||
await queryRunner.query( | ||
`CREATE TYPE "order_credit_duration_type_enum" AS ENUM('Monthly')`, | ||
); | ||
await queryRunner.query( | ||
`CREATE TABLE "order" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "price" integer NOT NULL, "status" "order_status_enum" NOT NULL DEFAULT 'Created', "credit_limit_type" "order_credit_limit_type_enum" NOT NULL, "credit_duration_type" "order_credit_duration_type_enum" NOT NULL, "user_id" uuid, "credit_id" uuid, CONSTRAINT "REL_d8625eb6d6e1217afd9ca73cd7" UNIQUE ("credit_id"), CONSTRAINT "PK_1031171c13130102495201e3e20" PRIMARY KEY ("id"))`, | ||
); | ||
await queryRunner.query( | ||
`ALTER TABLE "order" ADD CONSTRAINT "FK_199e32a02ddc0f47cd93181d8fd" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, | ||
); | ||
await queryRunner.query( | ||
`ALTER TABLE "order" ADD CONSTRAINT "FK_d8625eb6d6e1217afd9ca73cd75" FOREIGN KEY ("credit_id") REFERENCES "credit"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, | ||
); | ||
} | ||
|
||
public async down(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query( | ||
`ALTER TABLE "order" DROP CONSTRAINT "FK_d8625eb6d6e1217afd9ca73cd75"`, | ||
); | ||
await queryRunner.query( | ||
`ALTER TABLE "order" DROP CONSTRAINT "FK_199e32a02ddc0f47cd93181d8fd"`, | ||
); | ||
await queryRunner.query(`DROP TABLE "order"`); | ||
await queryRunner.query(`DROP TYPE "order_credit_duration_type_enum"`); | ||
await queryRunner.query(`DROP TYPE "order_credit_limit_type_enum"`); | ||
await queryRunner.query(`DROP TYPE "order_status_enum"`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { IsEnum } from 'class-validator'; | ||
|
||
import { CreditDuration } from '../../credit/interface/credit-duration.interface'; | ||
import { CreditLimit } from '../../credit/interface/credit-limit.interface'; | ||
|
||
export class CreateOrderDto { | ||
@ApiProperty() | ||
@IsEnum(CreditLimit) | ||
creditLimit: CreditLimit; | ||
|
||
@ApiProperty() | ||
@IsEnum(CreditDuration) | ||
creditDuration: CreditDuration; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { IsUUID, IsDate, IsInt, IsEnum } from 'class-validator'; | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
|
||
import { Order } from '../order.entity'; | ||
import { CreditLimit } from '../../credit/interface/credit-limit.interface'; | ||
import { CreditDuration } from '../../credit/interface/credit-duration.interface'; | ||
import { OrderStatus } from '../interface/order-status.interface'; | ||
|
||
export class OrderDto { | ||
@ApiProperty() | ||
@IsUUID() | ||
id: string; | ||
|
||
@ApiProperty() | ||
@IsDate() | ||
createdAt: Date; | ||
|
||
@ApiProperty() | ||
@IsDate() | ||
updatedAt: Date; | ||
|
||
@ApiProperty() | ||
@IsEnum(CreditLimit) | ||
creditLimit: CreditLimit; | ||
|
||
@ApiProperty() | ||
@IsEnum(CreditDuration) | ||
creditDuration: CreditDuration; | ||
|
||
@ApiProperty() | ||
@IsInt() | ||
public price: number; | ||
|
||
@ApiProperty() | ||
@IsEnum(OrderStatus) | ||
public status: OrderStatus; | ||
|
||
public static createDtoFromEntity(order: Order): OrderDto { | ||
const dto = new OrderDto(); | ||
|
||
dto.id = order.id; | ||
dto.price = order.price; | ||
dto.status = order.status; | ||
dto.createdAt = order.createdAt; | ||
dto.updatedAt = order.updatedAt; | ||
dto.creditLimit = order.creditLimit; | ||
dto.creditDuration = order.creditDuration; | ||
|
||
return dto; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export class CreateOrderError extends Error { | ||
constructor(error: Error) { | ||
super(`Failed to create a new order. ${error}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export class InvalidOrderStatusError extends Error { | ||
constructor(message: string) { | ||
super(`Invalid status for order. ${message}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export class ReadOrderError extends Error { | ||
constructor(error: Error) { | ||
super(`Failed to read a order. ${error}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export class UnathorizedOrderError extends Error { | ||
constructor() { | ||
super(`Unathorized access to an Order.`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export class UpdateOrderError extends Error { | ||
constructor(error: Error) { | ||
super(`Failed to update a order. ${error}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export enum OrderStatus { | ||
Created = 'Created', | ||
PaymentPending = 'PaymentPending', | ||
PaymentSuccessful = 'PaymentSuccessful', | ||
PaymentFailed = 'PaymentFailed', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { OrderController } from './order.controller'; | ||
import { OrderService } from './order.service'; | ||
|
||
describe('OrderController', () => { | ||
let controller: OrderController; | ||
|
||
const orderServiceMock = { | ||
createOrder: jest.fn(), | ||
getOrders: jest.fn(), | ||
}; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [OrderController], | ||
providers: [ | ||
{ | ||
provide: OrderService, | ||
useValue: orderServiceMock, | ||
}, | ||
], | ||
}).compile(); | ||
|
||
controller = module.get<OrderController>(OrderController); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(controller).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { | ||
Controller, | ||
Post, | ||
HttpStatus, | ||
HttpException, | ||
Request, | ||
Body, | ||
Get, | ||
} from '@nestjs/common'; | ||
import { | ||
ApiTags, | ||
ApiOkResponse, | ||
ApiUnauthorizedResponse, | ||
ApiCreatedResponse, | ||
ApiInternalServerErrorResponse, | ||
} from '@nestjs/swagger'; | ||
|
||
import { Authorised } from '../auth/auth.decorator'; | ||
import { RequestWithUser } from '../common/interface/request-with-user.interface'; | ||
import { CreateOrderDto } from './dto/create-order.dto'; | ||
import { OrderDto } from './dto/order.dto'; | ||
import { OrderService } from './order.service'; | ||
|
||
@ApiTags('orders') | ||
@Controller('orders') | ||
export class OrderController { | ||
constructor(private readonly orderService: OrderService) {} | ||
|
||
@Post('/') | ||
@Authorised() | ||
@ApiCreatedResponse() | ||
@ApiUnauthorizedResponse() | ||
@ApiInternalServerErrorResponse() | ||
public async createOrder( | ||
@Request() request: RequestWithUser, | ||
@Body() createOrderDto: CreateOrderDto, | ||
): Promise<OrderDto> { | ||
try { | ||
const user = request.user; | ||
const { creditDuration, creditLimit } = createOrderDto; | ||
|
||
const order = await this.orderService.createOrder( | ||
creditLimit, | ||
creditDuration, | ||
user, | ||
); | ||
|
||
return OrderDto.createDtoFromEntity(order); | ||
} catch (error) { | ||
throw new HttpException( | ||
'Internal server error', | ||
HttpStatus.INTERNAL_SERVER_ERROR, | ||
); | ||
} | ||
} | ||
|
||
@Get('/') | ||
@Authorised() | ||
@ApiOkResponse() | ||
@ApiUnauthorizedResponse() | ||
@ApiInternalServerErrorResponse() | ||
public async getOrders( | ||
@Request() request: RequestWithUser, | ||
): Promise<OrderDto[]> { | ||
try { | ||
const user = request.user; | ||
|
||
const orders = await this.orderService.getOrders(user); | ||
|
||
return orders.map(OrderDto.createDtoFromEntity); | ||
} catch (error) { | ||
throw new HttpException( | ||
'Internal server error', | ||
HttpStatus.INTERNAL_SERVER_ERROR, | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { Column, ManyToOne, JoinColumn, Entity, OneToOne } from 'typeorm'; | ||
|
||
import { User } from '../user/user.entity'; | ||
import { Credit } from '../credit/credit.entity'; | ||
import { BaseEditableEntity } from '../common/model/base-editable.entity'; | ||
import { OrderStatus } from './interface/order-status.interface'; | ||
import { CreditLimit } from '../credit/interface/credit-limit.interface'; | ||
import { CreditDuration } from '../credit/interface/credit-duration.interface'; | ||
|
||
@Entity() | ||
export class Order extends BaseEditableEntity { | ||
@Column({ name: 'price', type: 'int' }) | ||
public price: number; | ||
|
||
@Column({ | ||
name: 'status', | ||
type: 'enum', | ||
enum: OrderStatus, | ||
default: OrderStatus.Created, | ||
}) | ||
public status: OrderStatus; | ||
|
||
@Column({ | ||
name: 'credit_limit_type', | ||
type: 'enum', | ||
enum: CreditLimit, | ||
}) | ||
public creditLimit: CreditLimit; | ||
|
||
@Column({ | ||
name: 'credit_duration_type', | ||
type: 'enum', | ||
enum: CreditDuration, | ||
}) | ||
public creditDuration: CreditDuration; | ||
|
||
@ManyToOne(() => User, { eager: true }) | ||
@JoinColumn({ name: 'user_id', referencedColumnName: 'id' }) | ||
public user: User; | ||
|
||
@OneToOne(() => Credit, { eager: true, nullable: true }) | ||
@JoinColumn({ name: 'credit_id', referencedColumnName: 'id' }) | ||
public credit?: Credit; | ||
|
||
public belongsTo(userId: string): boolean { | ||
return this.user.id === userId; | ||
} | ||
} |
Oops, something went wrong.