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

Commit

Permalink
feat: Provide Base EntityController (#96)
Browse files Browse the repository at this point in the history
* feat: add base entity controllers
  • Loading branch information
itayod authored and wesleygrimes committed Oct 7, 2019
1 parent 49bb725 commit b3470ef
Show file tree
Hide file tree
Showing 9 changed files with 519 additions and 50 deletions.
46 changes: 45 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@

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

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Feature Modules](#feature-modules---registering-multiple-instances-using-forfeature)
- [Entity Controller](#entity-controller)


## Installation

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

> While we are importing to the AppModule in this example, InMemoryDBModule could be imported in Feature modules just as well.
#### Registering a forRoot InMemoryDBService
#### Registering a forRoot InMemoryDBModule

```typescript
// app.module.ts
Expand Down Expand Up @@ -160,6 +168,42 @@ export class FeatureOneController {

Using this decorator ensures that the correct instance is injected.


## Entity Controller

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.


To use the controllers, simply create a new controller and extend it with one of the provided base controllers.


```typescript
@Controller('api/users')
class UsersController extends InMemoryDbController<UserEntity> {

constructor(protected dbService: InMemoryDBService<UserEntity>) {
super(dbService);
}

}

```

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:

```typescript
@Controller('api/users')
class UsersController extends InMemoryDbController<UserEntity> {

constructor(@InjectInMemoryDBService('customer') protected readonly inMemoryDBService: InMemoryDBService<UserEntity>) {
super(inMemoryDBService);
}

}

```


## Docs

[Click here for more detailed API Documentation](API.md)
Expand Down
45 changes: 23 additions & 22 deletions e2e/test-app/src/customer/customer.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,68 +19,69 @@ describe('Customer Controller', () => {
expect(controller).toBeTruthy();
});

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

test('createCustomer', () => {
test('create', () => {
// arrange
const expectedResult = customer;

// act
jest.spyOn(controller, 'createCustomer')
jest.spyOn(controller, 'create')
.mockImplementation(() => expectedResult);

// assert
expect(controller.createCustomer(customer)).toBe(expectedResult);
expect(controller.create(customer)).toBe(expectedResult);
});

test('getCustomers', () => {
test('getAll', () => {
// arrange
const expectedResult = [customer];

// act
jest.spyOn(controller, 'getCustomers')
jest.spyOn(controller, 'getMany')
.mockImplementation(() => expectedResult);

// assert
expect(controller.getCustomers()).toBe(expectedResult);
expect(controller.getMany()).toBe(expectedResult);
});

test('updateCustomer', () => {
test('get', () => {
// arrange
customer.company = 'NestJS';
const expectedResult = customer;
const expectResult = customer;

// act
jest.spyOn(controller, 'updateCustomer')
.mockImplementation(() => expectedResult);
jest.spyOn(controller, 'get')
.mockImplementation(() => expectResult);

// assert
expect(controller.updateCustomer(customer)).toBe(expectedResult);
expect(controller.get(1)).toBe(expectResult);
});

test('deleteCustomer', () => {
test('update', () => {
// arrange
const expectedResult = null;
customer.company = 'NestJS';
const expectedResult = customer;

// act
jest.spyOn(controller, 'deleteCustomer')
jest.spyOn(controller, 'update')
.mockImplementation(() => expectedResult);

// assert
expect(controller.deleteCustomer(1)).toBe(expectedResult);
expect(controller.update(customer.id, {...customer})).toBe(expectedResult);
});

test('getCustomers', () => {
test('delete', () => {
// arrange
const expectResult = [];
const expectedResult = null;

// act
jest.spyOn(controller, 'getCustomers')
.mockImplementation(() => expectResult);
jest.spyOn(controller, 'delete')
.mockImplementation(() => expectedResult);

// assert
expect(controller.getCustomers()).toBe(expectResult);
expect(controller.delete(1)).toBe(expectedResult);
});

});
});
35 changes: 8 additions & 27 deletions e2e/test-app/src/customer/customer.controller.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,14 @@
import { Controller, Get, Param, Post, Body, Put, Delete } from '@nestjs/common';
import { InMemoryDBService, InjectInMemoryDBService } from '../../../../lib';
import { Controller } from '@nestjs/common';
import { InMemoryDBService, InjectInMemoryDBService, InMemoryDbEntityController } from '../../../../lib';
import { Customer } from './customer';

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

@Get('customers')
getCustomers(): Customer[] {
return this.inMemoryDBService.getAll();
constructor(
@InjectInMemoryDBService('customer') protected readonly inMemoryDBService: InMemoryDBService<Customer>
) {
super(inMemoryDBService);
}

@Get('customers/:id')
getCustomer(@Param('id') id: number): Customer {
return this.inMemoryDBService.get(id);
}

@Post('customers')
createCustomer(@Body() customer: Customer): Customer {
return this.inMemoryDBService.create(customer);
}

@Put('customers/:id')
updateCustomer(@Body() customer: Customer): void {
return this.inMemoryDBService.update(customer);
}

@Delete('customers/:id')
deleteCustomer(@Param('id') id: number): void {
return this.inMemoryDBService.delete(id);
}
}
153 changes: 153 additions & 0 deletions lib/controllers/in-memory-db-async.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { InMemoryDBEntity } from '../interfaces';
import { inMemoryDBServiceFactory } from '../factories';
import { InMemoryDbEntityAsyncController } from './in-memory-db-async.controller';
import { InMemoryDBService } from '../services';

describe('In Memory DB Async Controller', () => {
interface TestEntity extends InMemoryDBEntity {
someField: string;
}

let controller: InMemoryDbEntityAsyncController<TestEntity>;
let service: InMemoryDBService<TestEntity>;

const sampleRecords: TestEntity[] = [
{ id: 1, someField: 'AAA' },
{ id: 2, someField: 'BBB' },
{ id: 3, someField: 'CCC' },
];

class MockController extends InMemoryDbEntityAsyncController<TestEntity> {
constructor(protected dbService: InMemoryDBService<TestEntity>) {
super(dbService);
}
}

beforeEach(() => {
service = inMemoryDBServiceFactory<TestEntity>()();
controller = new MockController(service);
});

describe('get', () => {
let spy;

beforeEach(() => {
spy = jest.spyOn(service, 'getAsync');
});

test('should call service getAsync spy when given valid id', () => {
// act
controller.get(1);
// assert
expect(spy).toHaveBeenCalledWith(1);
});
});

describe('getMany', () => {
test('should call service getManyAsync spy when given list of ids', () => {
// arrange
const spy = spyOn(service, 'getManyAsync');
const testEntityMock = [1, 2, 3];
// act
controller.getMany(testEntityMock);
// assert
expect(spy).toHaveBeenCalledWith(testEntityMock);
});

test('should call service getAllAsync spy when no ids have been given', () => {
// arrange
const spy = spyOn(service, 'getAllAsync');
// act
controller.getMany();
// assert
expect(spy).toHaveBeenCalledWith();
});
});

describe('create', () => {
test('should call createAsync when given a valid record', () => {
// arrange
const spy = jest.spyOn(service, 'createAsync');
const testEntityMock = sampleRecords[0];
// act
controller.create(testEntityMock);
// assert
expect(spy).toHaveBeenCalledWith(testEntityMock);
});

test('should call createManyAsync when given valid records list', () => {
// arrange
const spy = jest.spyOn(service, 'createManyAsync');
// act
controller.create(sampleRecords);
// assert
expect(spy).toHaveBeenCalledWith(sampleRecords);
});
});

describe('update', () => {
let spy;

beforeEach(() => {
spy = jest.spyOn(service, 'updateAsync');
});

test('should call updateAsync when given a valid record and id', () => {
// arrange
const testEntityMock = { someField: 'DDD' };
// act
controller.update(1, testEntityMock);
// assert
expect(spy).toHaveBeenCalledWith({ id: 1, ...testEntityMock });
});
});

describe('updateMany', () => {
let spy;

beforeEach(() => {
spy = jest.spyOn(service, 'updateManyAsync');
});

test('should call updateManyAsync when given valid records list', () => {
// arrange
const testEntityMock = sampleRecords;
// act
controller.updateMany(testEntityMock);
// assert
expect(spy).toHaveBeenCalledWith(testEntityMock);
});
});

describe('delete', () => {
let spy;

beforeEach(() => {
spy = jest.spyOn(service, 'deleteAsync');
});

test('should call deleteAsync when give a valid id', () => {
// act
controller.delete(1);
// assert
expect(spy).toHaveBeenCalledWith(1);
});
});

describe('deleteMany', () => {
let spy;

beforeEach(() => {
spy = jest.spyOn(service, 'deleteManyAsync');
});

test('should call deleteManyAsync when given valid ids list', () => {
// arrange
const testEntityMock = [1, 2, 3];
// act
controller.deleteMany(testEntityMock);
// assert
expect(spy).toHaveBeenCalledWith(testEntityMock);
});
});
});
Loading

0 comments on commit b3470ef

Please sign in to comment.