Skip to content
This repository was archived by the owner on Feb 7, 2021. It is now read-only.

Commit b3470ef

Browse files
itayodwesleygrimes
authored andcommitted
feat: Provide Base EntityController (#96)
* feat: add base entity controllers
1 parent 49bb725 commit b3470ef

9 files changed

+519
-50
lines changed

README.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@
1313

1414
This provides a great way to quickly get up and running with prototypes and mock backends.
1515

16+
## Table of Contents
17+
18+
- [Installation](#installation)
19+
- [Quick Start](#quick-start)
20+
- [Feature Modules](#feature-modules---registering-multiple-instances-using-forfeature)
21+
- [Entity Controller](#entity-controller)
22+
23+
1624
## Installation
1725

1826
### Option 1
@@ -48,7 +56,7 @@ To get started, let's first update our `app.module.ts` to include the necessary
4856

4957
> While we are importing to the AppModule in this example, InMemoryDBModule could be imported in Feature modules just as well.
5058
51-
#### Registering a forRoot InMemoryDBService
59+
#### Registering a forRoot InMemoryDBModule
5260

5361
```typescript
5462
// app.module.ts
@@ -160,6 +168,42 @@ export class FeatureOneController {
160168

161169
Using this decorator ensures that the correct instance is injected.
162170

171+
172+
## Entity Controller
173+
174+
In order to prevent code duplication and boilerplate for each controller, we have created two base entity controllers `InMemoryDbEntityController` and `InMemoryDbEntityAsyncController`. This allows you to quickly provide endpoints to make requests without having to manually implement each action.
175+
176+
177+
To use the controllers, simply create a new controller and extend it with one of the provided base controllers.
178+
179+
180+
```typescript
181+
@Controller('api/users')
182+
class UsersController extends InMemoryDbController<UserEntity> {
183+
184+
constructor(protected dbService: InMemoryDBService<UserEntity>) {
185+
super(dbService);
186+
}
187+
188+
}
189+
190+
```
191+
192+
In order to have an Entity Controller use a feature-specific instance of the service, use the decorator `InjectInMemoryDBService` in the controller's provided by this library as shown below:
193+
194+
```typescript
195+
@Controller('api/users')
196+
class UsersController extends InMemoryDbController<UserEntity> {
197+
198+
constructor(@InjectInMemoryDBService('customer') protected readonly inMemoryDBService: InMemoryDBService<UserEntity>) {
199+
super(inMemoryDBService);
200+
}
201+
202+
}
203+
204+
```
205+
206+
163207
## Docs
164208

165209
[Click here for more detailed API Documentation](API.md)

e2e/test-app/src/customer/customer.controller.spec.ts

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,68 +19,69 @@ describe('Customer Controller', () => {
1919
expect(controller).toBeTruthy();
2020
});
2121

22-
describe('CRUD Operations for Customer', () => {
22+
describe('CRUD Operations for ', () => {
2323
const customer: Customer = { id: 1, firstName: 'Kamil', lastName: 'Myśliwiec', company: 'NestJs', title: 'Owner' };
2424

25-
test('createCustomer', () => {
25+
test('create', () => {
2626
// arrange
2727
const expectedResult = customer;
2828

2929
// act
30-
jest.spyOn(controller, 'createCustomer')
30+
jest.spyOn(controller, 'create')
3131
.mockImplementation(() => expectedResult);
3232

3333
// assert
34-
expect(controller.createCustomer(customer)).toBe(expectedResult);
34+
expect(controller.create(customer)).toBe(expectedResult);
3535
});
3636

37-
test('getCustomers', () => {
37+
test('getAll', () => {
3838
// arrange
3939
const expectedResult = [customer];
4040

4141
// act
42-
jest.spyOn(controller, 'getCustomers')
42+
jest.spyOn(controller, 'getMany')
4343
.mockImplementation(() => expectedResult);
4444

4545
// assert
46-
expect(controller.getCustomers()).toBe(expectedResult);
46+
expect(controller.getMany()).toBe(expectedResult);
4747
});
4848

49-
test('updateCustomer', () => {
49+
test('get', () => {
5050
// arrange
51-
customer.company = 'NestJS';
52-
const expectedResult = customer;
51+
const expectResult = customer;
5352

5453
// act
55-
jest.spyOn(controller, 'updateCustomer')
56-
.mockImplementation(() => expectedResult);
54+
jest.spyOn(controller, 'get')
55+
.mockImplementation(() => expectResult);
5756

5857
// assert
59-
expect(controller.updateCustomer(customer)).toBe(expectedResult);
58+
expect(controller.get(1)).toBe(expectResult);
6059
});
6160

62-
test('deleteCustomer', () => {
61+
test('update', () => {
6362
// arrange
64-
const expectedResult = null;
63+
customer.company = 'NestJS';
64+
const expectedResult = customer;
6565

6666
// act
67-
jest.spyOn(controller, 'deleteCustomer')
67+
jest.spyOn(controller, 'update')
6868
.mockImplementation(() => expectedResult);
6969

7070
// assert
71-
expect(controller.deleteCustomer(1)).toBe(expectedResult);
71+
expect(controller.update(customer.id, {...customer})).toBe(expectedResult);
7272
});
7373

74-
test('getCustomers', () => {
74+
test('delete', () => {
7575
// arrange
76-
const expectResult = [];
76+
const expectedResult = null;
7777

7878
// act
79-
jest.spyOn(controller, 'getCustomers')
80-
.mockImplementation(() => expectResult);
79+
jest.spyOn(controller, 'delete')
80+
.mockImplementation(() => expectedResult);
8181

8282
// assert
83-
expect(controller.getCustomers()).toBe(expectResult);
83+
expect(controller.delete(1)).toBe(expectedResult);
8484
});
85+
8586
});
8687
});
Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,14 @@
1-
import { Controller, Get, Param, Post, Body, Put, Delete } from '@nestjs/common';
2-
import { InMemoryDBService, InjectInMemoryDBService } from '../../../../lib';
1+
import { Controller } from '@nestjs/common';
2+
import { InMemoryDBService, InjectInMemoryDBService, InMemoryDbEntityController } from '../../../../lib';
33
import { Customer } from './customer';
44

5-
@Controller('api/')
6-
export class CustomerController {
7-
constructor(@InjectInMemoryDBService('customer') private readonly inMemoryDBService: InMemoryDBService<Customer>) { }
5+
@Controller('api/customers')
6+
export class CustomerController extends InMemoryDbEntityController<Customer> {
87

9-
@Get('customers')
10-
getCustomers(): Customer[] {
11-
return this.inMemoryDBService.getAll();
8+
constructor(
9+
@InjectInMemoryDBService('customer') protected readonly inMemoryDBService: InMemoryDBService<Customer>
10+
) {
11+
super(inMemoryDBService);
1212
}
1313

14-
@Get('customers/:id')
15-
getCustomer(@Param('id') id: number): Customer {
16-
return this.inMemoryDBService.get(id);
17-
}
18-
19-
@Post('customers')
20-
createCustomer(@Body() customer: Customer): Customer {
21-
return this.inMemoryDBService.create(customer);
22-
}
23-
24-
@Put('customers/:id')
25-
updateCustomer(@Body() customer: Customer): void {
26-
return this.inMemoryDBService.update(customer);
27-
}
28-
29-
@Delete('customers/:id')
30-
deleteCustomer(@Param('id') id: number): void {
31-
return this.inMemoryDBService.delete(id);
32-
}
3314
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import { InMemoryDBEntity } from '../interfaces';
2+
import { inMemoryDBServiceFactory } from '../factories';
3+
import { InMemoryDbEntityAsyncController } from './in-memory-db-async.controller';
4+
import { InMemoryDBService } from '../services';
5+
6+
describe('In Memory DB Async Controller', () => {
7+
interface TestEntity extends InMemoryDBEntity {
8+
someField: string;
9+
}
10+
11+
let controller: InMemoryDbEntityAsyncController<TestEntity>;
12+
let service: InMemoryDBService<TestEntity>;
13+
14+
const sampleRecords: TestEntity[] = [
15+
{ id: 1, someField: 'AAA' },
16+
{ id: 2, someField: 'BBB' },
17+
{ id: 3, someField: 'CCC' },
18+
];
19+
20+
class MockController extends InMemoryDbEntityAsyncController<TestEntity> {
21+
constructor(protected dbService: InMemoryDBService<TestEntity>) {
22+
super(dbService);
23+
}
24+
}
25+
26+
beforeEach(() => {
27+
service = inMemoryDBServiceFactory<TestEntity>()();
28+
controller = new MockController(service);
29+
});
30+
31+
describe('get', () => {
32+
let spy;
33+
34+
beforeEach(() => {
35+
spy = jest.spyOn(service, 'getAsync');
36+
});
37+
38+
test('should call service getAsync spy when given valid id', () => {
39+
// act
40+
controller.get(1);
41+
// assert
42+
expect(spy).toHaveBeenCalledWith(1);
43+
});
44+
});
45+
46+
describe('getMany', () => {
47+
test('should call service getManyAsync spy when given list of ids', () => {
48+
// arrange
49+
const spy = spyOn(service, 'getManyAsync');
50+
const testEntityMock = [1, 2, 3];
51+
// act
52+
controller.getMany(testEntityMock);
53+
// assert
54+
expect(spy).toHaveBeenCalledWith(testEntityMock);
55+
});
56+
57+
test('should call service getAllAsync spy when no ids have been given', () => {
58+
// arrange
59+
const spy = spyOn(service, 'getAllAsync');
60+
// act
61+
controller.getMany();
62+
// assert
63+
expect(spy).toHaveBeenCalledWith();
64+
});
65+
});
66+
67+
describe('create', () => {
68+
test('should call createAsync when given a valid record', () => {
69+
// arrange
70+
const spy = jest.spyOn(service, 'createAsync');
71+
const testEntityMock = sampleRecords[0];
72+
// act
73+
controller.create(testEntityMock);
74+
// assert
75+
expect(spy).toHaveBeenCalledWith(testEntityMock);
76+
});
77+
78+
test('should call createManyAsync when given valid records list', () => {
79+
// arrange
80+
const spy = jest.spyOn(service, 'createManyAsync');
81+
// act
82+
controller.create(sampleRecords);
83+
// assert
84+
expect(spy).toHaveBeenCalledWith(sampleRecords);
85+
});
86+
});
87+
88+
describe('update', () => {
89+
let spy;
90+
91+
beforeEach(() => {
92+
spy = jest.spyOn(service, 'updateAsync');
93+
});
94+
95+
test('should call updateAsync when given a valid record and id', () => {
96+
// arrange
97+
const testEntityMock = { someField: 'DDD' };
98+
// act
99+
controller.update(1, testEntityMock);
100+
// assert
101+
expect(spy).toHaveBeenCalledWith({ id: 1, ...testEntityMock });
102+
});
103+
});
104+
105+
describe('updateMany', () => {
106+
let spy;
107+
108+
beforeEach(() => {
109+
spy = jest.spyOn(service, 'updateManyAsync');
110+
});
111+
112+
test('should call updateManyAsync when given valid records list', () => {
113+
// arrange
114+
const testEntityMock = sampleRecords;
115+
// act
116+
controller.updateMany(testEntityMock);
117+
// assert
118+
expect(spy).toHaveBeenCalledWith(testEntityMock);
119+
});
120+
});
121+
122+
describe('delete', () => {
123+
let spy;
124+
125+
beforeEach(() => {
126+
spy = jest.spyOn(service, 'deleteAsync');
127+
});
128+
129+
test('should call deleteAsync when give a valid id', () => {
130+
// act
131+
controller.delete(1);
132+
// assert
133+
expect(spy).toHaveBeenCalledWith(1);
134+
});
135+
});
136+
137+
describe('deleteMany', () => {
138+
let spy;
139+
140+
beforeEach(() => {
141+
spy = jest.spyOn(service, 'deleteManyAsync');
142+
});
143+
144+
test('should call deleteManyAsync when given valid ids list', () => {
145+
// arrange
146+
const testEntityMock = [1, 2, 3];
147+
// act
148+
controller.deleteMany(testEntityMock);
149+
// assert
150+
expect(spy).toHaveBeenCalledWith(testEntityMock);
151+
});
152+
});
153+
});

0 commit comments

Comments
 (0)