Skip to content

Commit

Permalink
feat: create actions controller and documentations
Browse files Browse the repository at this point in the history
  • Loading branch information
shayan-shojaei committed Sep 12, 2022
1 parent 8d3385d commit ee6e0fc
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 14 deletions.
85 changes: 82 additions & 3 deletions src/actions/actions.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,58 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ObjectId } from 'mongodb';
import Ingredient from '../ingredient/ingredient.model';
import { ActionsController } from './actions.controller';
import { ActionsService } from './actions.service';
import AddDTO from './dto/add.dto';
import BatchDTO from './dto/batch.dto';

const ADD_INGREDIENT_1: AddDTO = {
name: 'I1',
amount: 500,
unit: 'G',
};
const ADD_INGREDIENT_2: AddDTO = {
name: 'I1',
amount: 500,
unit: 'G',
};

const BATCH_INGREDIENT: BatchDTO = {
ingredients: [ADD_INGREDIENT_1, ADD_INGREDIENT_2],
};

const STORAGE_1 = '631d9d675237167ab2c1b75e';

describe('ActionsController', () => {
let controller: ActionsController;

const mockActionsService: Partial<ActionsService> = {};
const mockActionsService: Partial<ActionsService> = {
addIngredient: jest.fn().mockImplementation(
async (dto: AddDTO, storage: string) =>
({
...dto,
_id: new ObjectId(),
createdAt: new Date(),
storage: new ObjectId(storage),
} as Ingredient),
),
addIngredientsBatch: jest
.fn()
.mockImplementation(async (dto: BatchDTO, storage: string) =>
dto.ingredients.map(
(ingredient) =>
({
...ingredient,
_id: new ObjectId(),
createdAt: new Date(),
storage: new ObjectId(storage),
} as Ingredient),
),
),
checkStorage: jest
.fn()
.mockImplementation((storageId: ObjectId) => ObjectId.isValid(storageId)),
};

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
Expand All @@ -20,7 +67,39 @@ describe('ActionsController', () => {
expect(controller).toBeDefined();
});

// describe('add', () => {});
// describe('batch', () => {});
describe('add', () => {
it('should add ingredient', async () => {
const response = await controller.add(STORAGE_1, ADD_INGREDIENT_1);
expect(response).toBeDefined();
expect(response.success).toBe(true);
expect(response.data).toEqual({
...ADD_INGREDIENT_1,
_id: expect.any(ObjectId),
storage: new ObjectId(STORAGE_1),
createdAt: expect.any(Date),
} as Ingredient);
});
});
describe('batch', () => {
it('should add a batch of ingredients', async () => {
const response = await controller.addBatch(STORAGE_1, BATCH_INGREDIENT);
expect(response).toBeDefined();
expect(response.success).toBe(true);
expect(response.data).toEqual([
{
...ADD_INGREDIENT_1,
_id: expect.any(ObjectId),
storage: new ObjectId(STORAGE_1),
createdAt: expect.any(Date),
},
{
...ADD_INGREDIENT_2,
_id: expect.any(ObjectId),
storage: new ObjectId(STORAGE_1),
createdAt: expect.any(Date),
},
] as Ingredient[]);
});
});
// describe('schedule', () => {});
});
63 changes: 60 additions & 3 deletions src/actions/actions.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,61 @@
import { Controller } from '@nestjs/common';
import {
Body,
Controller,
HttpCode,
HttpStatus,
Param,
Post,
UseInterceptors,
} from '@nestjs/common';
import {
ApiBody,
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { createSchema } from '../utils/createSchema';
import { ErrorInterceptor } from '../middleware/errorInterceptor.middleware';
import { ActionsService } from './actions.service';
import AddDTO from './dto/add.dto';
import BatchDTO from './dto/batch.dto';
import { ADD_BATCH_EXAMPLE, ADD_EXAMPLE } from './examples';

@Controller('actions')
export class ActionsController {}
@Controller('storage')
@ApiTags('Actions')
export class ActionsController {
constructor(private readonly actions: ActionsService) {}

@Post('/:storageId/add')
@UseInterceptors(ErrorInterceptor)
@ApiBody({ type: AddDTO })
@ApiOkResponse({
description: 'Added ingredient with updated values.',
schema: createSchema(ADD_EXAMPLE),
})
@ApiNotFoundResponse()
@ApiOperation({ summary: 'Add single ingredient to storage' })
@HttpCode(HttpStatus.OK)
async add(@Param('storageId') storageId: string, @Body() body: AddDTO) {
const ingredient = await this.actions.addIngredient(body, storageId);
return { success: true, data: ingredient };
}

@Post('/:storageId/batch')
@UseInterceptors(ErrorInterceptor)
@ApiBody({ type: BatchDTO })
@ApiOkResponse({
description: 'Array of added ingredients with updated values.',
schema: createSchema(ADD_BATCH_EXAMPLE),
})
@ApiNotFoundResponse()
@ApiOperation({ summary: 'Add batch of ingredients to storage' })
@HttpCode(HttpStatus.OK)
async addBatch(
@Param('storageId') storageId: string,
@Body() body: BatchDTO,
) {
const batch = await this.actions.addIngredientsBatch(body, storageId);
return { success: true, data: batch };
}
}
11 changes: 6 additions & 5 deletions src/actions/actions.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@ export class ActionsService {

async addIngredient(add: AddDTO, storage: string) {
const storageId = new ObjectId(storage);
await this.storageExists(storageId);
await this.checkStorage(storageId);
return this.repository.addIngredient(add, storageId);
}

async addIngredientsBatch(batch: BatchDTO, storage: string) {
const storageId = new ObjectId(storage);
await this.storageExists(storageId);
await this.checkStorage(storageId);
return this.repository.addBatchIngredient(batch, storageId);
}

private async storageExists(storage: ObjectId) {
if (!(await this.repository.storageExists(storage))) {
async checkStorage(storage: ObjectId) {
const exists = await this.repository.storageExists(storage);
if (!exists) {
throw new NotFoundException(
`Storage with id ${storage.toString()} was not found.`,
`Storage with id ${storage.toString()}... was not found.`,
);
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/actions/dto/batch.dto.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsArray } from 'class-validator';
import { Type } from 'class-transformer';
import { ArrayMinSize, IsArray, ValidateNested } from 'class-validator';
import AddDTO from './add.dto';

export default class BatchDTO {
@ApiProperty({ description: 'Array of ingredients' })
@IsArray({ each: true })
@ApiProperty({ description: 'Array of ingredients', type: () => [AddDTO] })
@IsArray()
@ValidateNested({ each: true })
@ArrayMinSize(1)
@Type(() => AddDTO)
ingredients: AddDTO[];
}
27 changes: 27 additions & 0 deletions src/actions/examples/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export const ADD_EXAMPLE = {
_id: '631f11ec91ff9645f0690fa3',
name: 'Kiwi',
unit: 'KG',
amount: 50,
storage: '631d9d675237167ab2c1b75e',
createdAt: '2022-09-12T11:03:08.441Z',
};

export const ADD_BATCH_EXAMPLE = [
{
_id: '631f11ec91ff9645f0690fa3',
name: 'Kiwi',
unit: 'KG',
amount: 50,
storage: '631d9d675237167ab2c1b75e',
createdAt: '2022-09-12T11:03:08.441Z',
},
{
_id: '631f1373d5e4af4a1f3fb1aa',
name: 'Cucumber',
unit: 'G',
amount: 500,
storage: '631d9d675237167ab2c1b75e',
createdAt: '2022-09-12T11:09:39.615Z',
},
];
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MongoModule } from 'nest-mongodb';
import ActionsModule from './actions/actions.module';
import IngredientModule from './ingredient/ingredient.module';
import StorageModule from './storage/storage.module';

Expand All @@ -12,6 +13,7 @@ import StorageModule from './storage/storage.module';
MongoModule.forRoot(process.env.MONGO_URI, process.env.MONGO_DB),
StorageModule,
IngredientModule,
ActionsModule,
],
controllers: [],
providers: [],
Expand Down

0 comments on commit ee6e0fc

Please sign in to comment.