Skip to content

Commit

Permalink
feat(repository): have @repository take in constructor as arg
Browse files Browse the repository at this point in the history
  • Loading branch information
shimks committed Apr 10, 2018
1 parent 59b9e9f commit 3db07eb
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 87 deletions.
5 changes: 2 additions & 3 deletions docs/site/Controller-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Here's an example of what the template will produce given a `Todo` model and a
`TodoRepository`:

```ts
import {Filter, Where} from '@loopback/repository';
import {Filter, Where, repository} from '@loopback/repository';
import {
post,
param,
Expand All @@ -86,13 +86,12 @@ import {
del,
requestBody
} from '@loopback/rest';
import {inject} from '@loopback/context';
import {Todo} from '../models';
import {TodoRepository} from '../repositories';

export class TodoController {
constructor(
@inject('repositories.TodoRepository')
@repository(TodoRepository)
public todoRepository: TodoRepository,
) {}

Expand Down
4 changes: 2 additions & 2 deletions docs/site/Controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ import {repository} from '@loopback/repository';

export class HelloController {
constructor(
@repository(HelloRepository.name) protected repository: HelloRepository,
@repository(HelloRepository) protected repository: HelloRepository,
) {}

// returns a list of our objects
Expand Down Expand Up @@ -273,7 +273,7 @@ import {repository} from '@loopback/repository';

export class HelloController {
constructor(
@repository(HelloRepository.name) protected repo: HelloRepository,
@repository(HelloRepository) protected repo: HelloRepository,
) {}

// returns a list of our objects
Expand Down
3 changes: 2 additions & 1 deletion docs/site/Decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,8 @@ For usage examples, see [Define Models](Repositories.md#define-models)
### Repository Decorator

Syntax:
[`@repository(model: string | typeof Entity, dataSource?: string | juggler.DataSource)`](http://apidocs.loopback.io/@loopback%2frepository/#1503)

[@repository(modelOrRepo: string | Class<Repository<Model>> | typeof Entity, dataSource?: string | juggler.DataSource)](http://apidocs.loopback.io/@loopback%2frepository/#1503)

This decorator either injects an existing repository or creates a repository
from a model and a datasource.
Expand Down
4 changes: 2 additions & 2 deletions docs/site/Repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ DataSource for in the constructor of your controller class as follows:
```ts
export class AccountController {
constructor(
@repository(AccountRepository.name) public repository: AccountRepository,
@repository(AccountRepository) public repository: AccountRepository,
) {}
```
Expand Down Expand Up @@ -331,7 +331,7 @@ Injection:
```ts
export class AccountController {
@repository(NewRepository.name) private repository: NewRepository;
@repository(NewRepository) private repository: NewRepository;
}
```
Expand Down
6 changes: 3 additions & 3 deletions docs/site/todo-tutorial-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import {TodoRepository} from '../repositories';

export class TodoController {
constructor(
@repository(TodoRepository.name) protected todoRepo: TodoRepository,
@repository(TodoRepository) protected todoRepo: TodoRepository,
) {}
}
```
Expand Down Expand Up @@ -71,7 +71,7 @@ import {HttpErrors, post, param, requestBody} from '@loopback/rest';

export class TodoController {
constructor(
@repository(TodoRepository.name) protected todoRepo: TodoRepository,
@repository(TodoRepository) protected todoRepo: TodoRepository,
) {}

@post('/todo')
Expand Down Expand Up @@ -121,7 +121,7 @@ import {

export class TodoController {
constructor(
@repository(TodoRepository.name) protected todoRepo: TodoRepository,
@repository(TodoRepository) protected todoRepo: TodoRepository,
) {}

@post('/todo')
Expand Down
7 changes: 1 addition & 6 deletions examples/todo/src/controllers/todo.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@ import {
} from '@loopback/rest';

export class TodoController {
// TODO(bajtos) Fix documentation (and argument names?) of @repository()
// to allow the usage below.
// See https://github.com/strongloop/loopback-next/issues/744
constructor(
@repository(TodoRepository.name) protected todoRepo: TodoRepository,
) {}
constructor(@repository(TodoRepository) protected todoRepo: TodoRepository) {}

@post('/todo')
async createTodo(@requestBody() todo: Todo) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Filter, Where} from '@loopback/repository';
import {Filter, Where, repository} from '@loopback/repository';
import {
post,
param,
Expand All @@ -8,14 +8,13 @@ import {
del,
requestBody
} from '@loopback/openapi-v3';
import {inject} from '@loopback/context';
import {<%= modelName %>} from '../models';
import {<%= repositoryName %>} from '../repositories';

export class <%= name %>Controller {

constructor(
@inject('repositories.<%= repositoryName %>')
@repository(<%= repositoryName %>)
public <%= repositoryNameCamel %> : <%= repositoryName %>,
) {}

Expand Down
5 changes: 1 addition & 4 deletions packages/cli/test/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,7 @@ describe('lb4 controller', () => {
assert.fileContent(tmpDir + withInputName, /class FooBarController/);

// Repository and injection
assert.fileContent(
tmpDir + withInputName,
/\@inject\('repositories.BarRepository'\)/
);
assert.fileContent(tmpDir + withInputName, /\@repository\(BarRepository\)/);
assert.fileContent(
tmpDir + withInputName,
/barRepository \: BarRepository/
Expand Down
2 changes: 1 addition & 1 deletion packages/repository/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ import {post, requestBody, get, param} from '@loopback/openapi-v3';
export class NoteController {
constructor(
// Use constructor dependency injection to set up the repository
@repository(NoteRepository.name) public noteRepo: NoteRepository,
@repository(NoteRepository) public noteRepo: NoteRepository,
) {}

// Create a new note
Expand Down
98 changes: 77 additions & 21 deletions packages/repository/src/decorators/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@
// License text available at https://opensource.org/licenses/MIT

import * as assert from 'assert';
import {Model, Entity} from '../model';
import {Repository} from '../repository';
import {Entity} from '../model';
import {DataSource} from '../datasource';
import {
DefaultCrudRepository,
DataSourceConstructor,
} from '../legacy-juggler-bridge';
import {juggler} from '../loopback-datasource-juggler';
import {inject, Context, Injection} from '@loopback/context';
import {Class} from '../common-types';
import {Repository} from '../repository';
import {Model} from '../model';

/**
* Type definition for decorators returned by `@repository` decorator factory
*/
export type RepositoryDecorator = (
target: Object,
key?: string | symbol,
// tslint:disable-next-line:no-any
descriptorOrIndex?: TypedPropertyDescriptor<any> | number,
) => void;

/**
* Metadata for a repository
Expand Down Expand Up @@ -42,7 +54,7 @@ export class RepositoryMetadata {
/**
* Constructor for RepositoryMetadata
*
* @param model Name or class of the model. If the value is a string and
* @param modelOrRepo Name or class of the model. If the value is a string and
* `dataSource` is not present, it will treated as the name of a predefined
* repository
* @param dataSource Name or instance of the data source
Expand Down Expand Up @@ -76,41 +88,85 @@ export class RepositoryMetadata {
}

/**
* Decorator for model definitions
* @param model Name of the repo or name/class of the model
* @param dataSource Name or instance of the data source
* @returns {(target:AnyType)}
* Decorator for repository injections on properties or method arguments
*
* ```ts
* class CustomerController {
* @repository(CustomerRepository) public custRepo: CustomerRepository;
*
* For example:
* constructor(
* @repository(ProductRepository) public prodRepo: ProductRepository,
* ) {}
* // ...
* }
* ```
*
* - @repository('myCustomerRepo')
* - @repository('Customer', 'mysqlDataSource')
* - @repository(Customer, mysqlDataSource)
* - @repository('Customer', mysqlDataSource)
* - @repository(Customer, 'mysqlDataSource')
* @param repositoryName Name of the repo
*/
export function repository<T extends Model>(
export function repository(
repositoryName: string | Class<Repository<Model>>,
): RepositoryDecorator;

/**
* Decorator for DefaultCrudRepository generation and injection on properties
* or method arguments based on the given model and dataSource (or their names)
*
* ```ts
* class CustomerController {
* @repository('Customer', 'mySqlDataSource')
* public custRepo: DefaultCrudRepository<
* Customer,
* typeof Customer.prototype.id
* >;
*
* constructor(
* @repository(Product, mySqlDataSource)
* public prodRepo: DefaultCrudRepository<
* Product,
* typeof Product.prototype.id
* >,
* ) {}
* // ...
* }
* ```
*
* @param model Name/class of the model
* @param dataSource Name/instance of the dataSource
*/
export function repository(
model: string | typeof Entity,
dataSource: string | juggler.DataSource,
): RepositoryDecorator;

export function repository(
modelOrRepo: string | Class<Repository<Model>> | typeof Entity,
dataSource?: string | juggler.DataSource,
) {
const meta = new RepositoryMetadata(model, dataSource);
const stringOrModel =
typeof modelOrRepo !== 'string' && modelOrRepo.prototype.execute
? modelOrRepo.name
: (modelOrRepo as typeof Entity);
const meta = new RepositoryMetadata(stringOrModel, dataSource);
return function(
target: Object,
key?: symbol | string,
descriptor?: TypedPropertyDescriptor<Repository<T>> | number,
// tslint:disable-next-line:no-any
descriptorOrIndex?: TypedPropertyDescriptor<any> | number,
) {
if (key || typeof descriptor === 'number') {
if (key || typeof descriptorOrIndex === 'number') {
if (meta.name) {
// Make it shortcut to `@inject('repositories.MyRepo')`
// Please note key is undefined for constructor. If strictNullChecks
// is true, the compiler will complain as reflect-metadata won't
// accept undefined or null for key. Use ! to fool the compiler.
inject('repositories.' + meta.name, meta)(target, key!, descriptor);
inject('repositories.' + meta.name, meta)(
target,
key!,
descriptorOrIndex,
);
} else {
// Use repository-factory to create a repository from model + dataSource
// inject('repository-factory', meta)(target, key!, descriptor);
inject('', meta, resolve)(target, key!, descriptor);
// throw new Error('@repository(model, dataSource) is not implemented');
inject('', meta, resolve)(target, key!, descriptorOrIndex);
}
return;
}
Expand Down
Loading

0 comments on commit 3db07eb

Please sign in to comment.