-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix(repository): clarify argument type for @repository
#1213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| target: Object, | ||
| key?: symbol | string, | ||
| descriptor?: TypedPropertyDescriptor<Repository<T>> | number, | ||
| descriptor?: TypedPropertyDescriptor<BoundValue> | number, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we changing this to BoundValue since it's type is any.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, descriptor argument is pretty irrelevant considering we're never going to be using it as a propertyDescriptor since @repository has no use cases for decorating methods themselves (and if a user tries it, it errors out). My reasoning for replacing Repository<T> was to get rid of the usage of generics since we don't need it at all (at least from what I understand).
I might change the argument name descriptor to something else so that it's less confusing.
| export type RepositoryDecorator = ( | ||
| target: Object, | ||
| key?: string | symbol, | ||
| descriptorOrIndex?: TypedPropertyDescriptor<BoundValue> | number, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason we can't return the type as TypedPropertyDescriptor<Repository<T>>?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^^^ same as reasoning above: to get rid of generics
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because github hid my comments, I'm copypasting from above comment:
Honestly, descriptor argument is pretty irrelevant considering we're never going to be using it as a propertyDescriptor since @repository has no use cases for decorating methods themselves (and if a user tries it, it errors out). My reasoning for replacing Repository<T> was to get rid of the usage of generics since we don't need it at all (at least from what I understand).
I might change the argument name descriptor to something else so that it's less confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| constructor( | ||
| @inject('repositories.<%= repositoryName %>') | ||
| @repository(<%= repositoryName %>.name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-1
Any reason we can't just do the .name in our implementation of the @repository decorator rather than have the user add it? Poor UX imo and requires people to understand what is happening here.
We should be able to resolve a Dependency given a Class imo. @repository(UserRepository).
Out of scope of this PR but I think this can apply in general to @inject as follows @inject(UserController) should be enough for us to be able to resolve this type. IIRC Angular's DI framework is capable of resolving an instance given a Class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although I agree that the decorator should be able to resolve from just the class, it's outside of the scope for tasks in #744. I'll agree to change the @repository arguments in this PR so that it can resolve the dependencies just from the class, but only if everyone unanimously agrees that this is our approach going forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense to use the class for DI. However, I think we should create an issue for it and address it separately to get this patch landed for its intended purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 I originally intended to get miroslav's feedback to TV's proposal, but since that might not be coming soon I think I'd like to go ahead with the original plan
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for allowing @repository(UserRepository), +1 for leaving it out of scope of #744 and creating a new (follow-up) issue.
|
I agree that it seems like it's making DI a little more brittle if we follow this approach BUT there are ways around it still.
IoC is maintained regardless as the dependency is declared in the constructor signature instead of just instantiating a class directly in the constructor yourself which would render all of the above ways useless. |
| * @repository(Product, mySqlDataSource) | ||
| * public prodRepo: DefaultCrudRepository< | ||
| * Product, | ||
| * typeof Customer.prototype.id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typeof Product.prototype.id
I agree with what @virkt25 said. One idea that comes to mind if you'd like to use a file to swap out test/production repositories is to use a generic key name |
docs/site/Decorators.md
Outdated
| [`@repository(model: string | typeof Entity, dataSource?: string | juggler.DataSource)`](http://apidocs.loopback.io/@loopback%2frepository/#1503) | ||
|
|
||
| ```ts | ||
| @repository(modelOrRepo: string | typeof Entity, dataSource?: string | juggler.DataSource) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you mean to remove the link to apidocs?
|
|
||
| constructor( | ||
| @inject('repositories.<%= repositoryName %>') | ||
| @repository(<%= repositoryName %>.name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense to use the class for DI. However, I think we should create an issue for it and address it separately to get this patch landed for its intended purpose.
184a17b to
6fc8de6
Compare
b-admike
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with this patch, but would definitely like to see @raymondfeng or @bajtos chime in when they're back.
|
I would make an argument for not passing in the It is poor UX to have one approach require a string and another just the Class itself. |
|
Just to make sure that we're on the same page, the implementation of I too think that it is poor UX to force users to pass in |
| target: Object, | ||
| key?: symbol | string, | ||
| descriptor?: TypedPropertyDescriptor<Repository<T>> | number, | ||
| descriptorOrIndex?: TypedPropertyDescriptor<BoundValue> | number, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use any instead of BoundValue. BoundValue represents something that's bound in a Context. Here, you are describing a property of some type, not a value bound via ctx.bind().
d2f7687 to
bc43e0e
Compare
| constructor( | ||
| @inject('repositories.TodoRepository') | ||
| @repository(TodoRepository) | ||
| public todoRepository: TodoRepository, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the type not inferred to be TodoRepository?? -- Is there a way for it to be inferred automatically now?
This applies to all instances below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a very useful feature that I've not thought about. I think this task has been scope creeped enough that I'll create a new issue to implement this feature.
| describe('repository class', () => { | ||
| let ctx: Context; | ||
|
|
||
| before(function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you refactor function to a named function and call that from the before. Something like givenCtx.
See our test guidelines: http://loopback.io/doc/en/contrib/style-guide.html#hooks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good find
bc43e0e to
763a6ab
Compare
|
@raymondfeng Can I get your review on this PR? Thanks |
763a6ab to
d845eaa
Compare
| // inject('repository-factory', meta)(target, key!, descriptor); | ||
| inject('', meta, resolve)(target, key!, descriptor); | ||
| // throw new Error('@repository(model, dataSource) is not implemented'); | ||
| // inject('repository-factory', meta)(target, key!, descriptorOrIndex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can get rid of this line
86837c1 to
c8f27a9
Compare
This PR splits
@repositorydecorator into two overloaded methods for clarity in their two separate use cases: repository injection and repository generation based on a model and a datasource.I had a question come up while I was working on the PR. Is advocating repository injection through
@repository(MyRepository.name)the approach we want to take? I'd think that this is something that would go against the principles of Inversion of Control, which is what our Context system is based on.I thought the point of the context system was to allow different
componentsof an app to be swapped out in a single file. For example, if a user has a test repository and a production repository that he needs to swap out for deployment, our Context system should theoretically provide a single place for the user to swap out the repositories. In the case with@repository(TestRepository.name), the user would have to also go into the controller to physically changeTestRepositorytoProductionRepository.Maybe my understanding of Inversion of Control or our selling point of the framework is wrong, but I'd like to hear what others think about this conflict between what our framework is designed for and how we're designing the framework.
Checklist
npm testpasses on your machinepackages/cliwere updatedpackages/example-*were updated