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 bc42658
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 15 deletions.
78 changes: 69 additions & 9 deletions packages/rest-crud/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ 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

```sh
npm install --save @loopback/rest-crud
```

## Basic use
## Creating a CRUD Controller

1. Define your model class, e.g. using `lb4 model` tool.
Here is how you would use `defineCrudRestController` for exposing the CRUD
endpoints of an existing model with a respository.

2. Create a Repository class, e.g. using `lb4 repository` tool.

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

```ts
const ProductController = defineCrudRestController<
Expand All @@ -29,18 +28,79 @@ 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. You don't need to run `lb4 model` and `lb4 repository` to generate
the model and respository classes.

```ts
class Product extends Entity {
static definition = new ModelDefinition('Product')
.addProperty('id', {type: 'number', id: true})
.addProperty('name', {type: 'string'});

@property({id: true})
id: number;

@property()
name: string;
}

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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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 bc42658

Please sign in to comment.