Skip to content

Commit

Permalink
feat: add defineCrudRepositoryClass
Browse files Browse the repository at this point in the history
Add `defineCrudRepositoryClass` - a helper to create named repository classes
  • Loading branch information
Hage Yaapa committed Oct 31, 2019
1 parent aef7b88 commit fce609b
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 13 deletions.
70 changes: 63 additions & 7 deletions packages/rest-crud/README.md
Expand Up @@ -4,8 +4,8 @@ REST API controller implementing default CRUD semantics.

## Overview

This module allows applications to quickly expose a model via REST API without
having to implement a custom controller class.
This module allows applications to quickly expose models via REST API without
having to implement custom controller or repository classes.

## Installation

Expand All @@ -15,11 +15,19 @@ npm install --save @loopback/rest-crud

## Basic use

1. Define your model class, e.g. using `lb4 model` tool.
`@loopback/rest-crud` exposes two helper methods (`defineCrudRestController` and
`defineCrudRepositoryClass`) for creating controllers and respositories using
code.

2. Create a Repository class, e.g. using `lb4 repository` tool.
For the examples in the following sections, we are assuming a model named
`Product`, and a datasource named `db` have already been created.

3. Create a REST CRUD controller class for your model.
### Creating a CRUD Controller

Here is how you would use `defineCrudRestController` for exposing the CRUD
endpoints of an existing model with a respository.

1. Create a REST CRUD controller class for your model.

```ts
const ProductController = defineCrudRestController<
Expand All @@ -29,18 +37,66 @@ npm install --save @loopback/rest-crud
>(Product, {basePath: '/products'});
```

4. Set up dependency injection for the ProductController.
2. Set up dependency injection for the ProductController.

```ts
inject('repositories.ProductRepository')(ProductController, undefined, 0);
```

5. Register the controller with your application.
3. Register the controller with your application.

```ts
app.controller(ProductController);
```

### Creating a CRUD repository

Use the `defineCrudRepositoryClass` method to create named repositories (based
on the Model) for your app.

Usage example:

```ts
const db = new juggler.DataSource({connector: 'memory'});
const ProductRepository = defineCrudRepositoryClass(Product);
const repo = new ProductRepository(db);
```

### Integrated example

Here is an example of an app which uses `defineCrudRepositoryClass` and
`defineCrudRestController` to fulfill its repository and controller
requirements.

```ts
export class TryApplication extends BootMixin(
ServiceMixin(RepositoryMixin(RestApplication)),
) {
constructor(options: ApplicationConfig = {}) {
...
}

async boot():Promise<void> {
await super.boot();

const ProductRepository = defineCrudRepositoryClass(Product);
const repoBinding = this.repository(ProductRepository);

inject('datasources.db')(ProductRepository, undefined, 0);
this.repository(ProductRepository);

const ProductController = defineCrudRestController<
Product,
typeof Product.prototype.id,
'id'
>(Product, {basePath: '/products'});

inject(repoBinding.key)(ProductController, undefined, 0);
this.controller(ProductController);
}
}
```

## Contributions

- [Guidelines](https://github.com/strongloop/loopback-next/blob/master/docs/CONTRIBUTING.md)
Expand Down
Expand Up @@ -4,7 +4,6 @@
// License text available at https://opensource.org/licenses/MIT

import {
DefaultCrudRepository,
Entity,
EntityCrudRepository,
juggler,
Expand All @@ -19,7 +18,7 @@ import {
givenHttpServerConfig,
toJSON,
} from '@loopback/testlab';
import {defineCrudRestController} from '../..';
import {defineCrudRepositoryClass, defineCrudRestController} from '../..';

// In this test scenario, we create a product with a required & an optional
// property and use the default model settings (strict mode, forceId).
Expand Down Expand Up @@ -295,10 +294,10 @@ describe('CrudRestController for a simple Product model', () => {

async function setupTestScenario() {
const db = new juggler.DataSource({connector: 'memory'});
repo = new DefaultCrudRepository<Product, typeof Product.prototype.id>(
Product,
db,
);

const ProductRepository = defineCrudRepositoryClass(Product);

repo = new ProductRepository(db);

const CrudRestController = defineCrudRestController<
Product,
Expand Down
@@ -0,0 +1,22 @@
// Copyright IBM Corp. 2019. All Rights Reserved.
// Node module: @loopback/rest-crud
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {Entity, model, property} from '@loopback/repository';
import {expect} from '@loopback/testlab';
import {defineCrudRepositoryClass} from '../..';

describe('defineCrudRepositoryClass', () => {
it('should generate repository based on Model name', async () => {
@model()
class Product extends Entity {
@property({id: true})
id: number;
}

const ProductRepository = defineCrudRepositoryClass(Product);

expect(ProductRepository.name).to.equal('ProductRepository');
});
});
1 change: 1 addition & 0 deletions packages/rest-crud/src/index.ts
Expand Up @@ -4,3 +4,4 @@
// License text available at https://opensource.org/licenses/MIT

export * from './crud-rest.controller';
export * from './repository-builder';
55 changes: 55 additions & 0 deletions packages/rest-crud/src/repository-builder.ts
@@ -0,0 +1,55 @@
// Copyright IBM Corp. 2019. All Rights Reserved.
// Node module: @loopback/rest-crud
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {
DefaultCrudRepository,
Entity,
EntityCrudRepository,
juggler,
} from '@loopback/repository';
import * as assert from 'assert';

/**
* Create (define) a repository class for the given model.
*
* Example usage:
*
* ```ts
* const ProductRepository = defineCrudRepositoryClass(Product);
* ```
*
* @param modelCtor A model class, e.g. `Product`.
*/
export function defineCrudRepositoryClass<
T extends Entity,
IdType,
Relations extends object = {}
>(
entityClass: typeof Entity & {prototype: T},
): RepositoryClass<T, IdType, Relations> {
const repoName = entityClass.name + 'Repository';
const defineNamedRepo = new Function(
'EntityCtor',
'BaseRepository',
`return class ${repoName} extends BaseRepository {
constructor(dataSource) {
super(EntityCtor, dataSource);
}
};`,
);

// TODO(bajtos) make DefaultCrudRepository configurable (?)
const repo = defineNamedRepo(entityClass, DefaultCrudRepository);
assert.equal(repo.name, repoName);
return repo;
}

export interface RepositoryClass<
T extends Entity,
IdType,
Relations extends object
> {
new (ds: juggler.DataSource): EntityCrudRepository<T, IdType, Relations>;
}

0 comments on commit fce609b

Please sign in to comment.