-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
feat: support operation hooks #4730
Conversation
|
||
definePersistedModel(entityClass: typeof Product) { | ||
const model = super.definePersistedModel(entityClass); | ||
model.observe('before save', async ctx => { |
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.
We can access the model as this.modelClass
. Why not leveraging that to set up observers instead of overriding definedPersistedModel
?
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.
Initially we were considering having an observe
method on DefaultCrudRepository
, which would call this.modelClass.observe
. After some brainstorming, @bajtos suggested the possible problems with observe
is not worth it. Eg: calling observe()
multiple times and how to handle it. He had suggested another approach but it wasn't very DX-friendly, so we settled on the current one.
"You want to directly access the model? Then you will have to override the definePersistedModel
method."
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.
@raymondfeng please check the description of the user story and the discussion in that GH issue for more context.
@hacksparrow please update PR description with a reference to the GH issue this is related to.
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.
@hacksparrow We do support multiple observers for a given model class/operation - see https://github.com/strongloop/loopback-datasource-juggler/blob/master/lib/observer.js#L51
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.
Yes, that's understood. We were wondering how should LB4 respond when an operation hook is installed multiple times: a. Throw an error? b. Accept silently - how does the user know what happened?
Also, it is not impossible to call the observe
method before the modelClass
is defined. What happens in that case?
Since this is a temporary workaround, we didn't want to invest too much time handling various possible scenarios, edge cases etc. We can do that when we investigate the use of interceptor for the "real" implementation.
07f33c7
to
4305959
Compare
Needs loopbackio/loopback-datasource-juggler#1820 to land for tests to pass. |
following GitHub issue: | ||
[loopback-next#3952](https://github.com/strongloop/loopback-next/issues/3952) | ||
" %} | ||
Operation hooks are not supported in LoopBack 4 yet. It will be, eventually; you |
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.
Are we really going to have operation hooks in LB4? Maybe reword it as:
See the [Operation hooks for models/repositories spike](https://github.com/strongloop/loopback-next/issues/1919) for the progress
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.
Updated as per suggestion.
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.
LGTM 👍
Make sure to return the model instance from the derived class. | ||
|
||
Here is an example of a repository implementing `definePersistedModel` and | ||
applying an operation hook on a model instance: |
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.
nitpick: I remember the observe
method is called by a model class, not instance, not sure is if it's still the case in the legacy juggler bridge.
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.
Thanks! Corrected.
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.
Not familiar with the area but it looks reasonable to me 👍
@@ -0,0 +1,56 @@ | |||
// Copyright IBM Corp. 2019. All Rights Reserved. |
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.
// Copyright IBM Corp. 2020
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.
Updated
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 not too familiar with this area but the changes look reasonable to me
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.
The changes look mostly good, kudos for adding a unit test to verify the solution we are offering in the docs 👏
Needs loopbackio/loopback-datasource-juggler#1820 to land for tests to pass.
I think it's not enough to land the PR, we need the change to be published to npm too.
packages/repository/src/__tests__/acceptance/operation-hooks.acceptance.ts
Outdated
Show resolved
Hide resolved
6bff685
to
471ad03
Compare
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.
Almost there 👏
I have few minor comments to address, PTAL.
operation hooks a process that is possible only after the model class is | ||
attached to the repository and accidental registration of the same operation | ||
hook multiple times becomes obvious. With an API which directly exposes the | ||
`observe` method of the model class, this would not have beeb possible. |
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.
`observe` method of the model class, this would not have beeb possible. | |
`observe` method of the model class, this would not have been possible. |
@@ -5,7 +5,7 @@ | |||
|
|||
import {Getter} from '@loopback/context'; | |||
import assert from 'assert'; | |||
import legacy from 'loopback-datasource-juggler'; | |||
import legacy, {ObserverMixin} from 'loopback-datasource-juggler'; |
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 think we are intentionally keeping juggler types very separated from LB4 types. Take a look at lines 41-53, where we are re-exporting a subset of legacy types in juggler
namespace.
Please make the following changes:
- Revert this line back.
- Re-export
legacy.ObserverMixin
injuggler
namespace - Modify all places below using
ObserverMixin
to usejuggler.Mixin
Please add a comment to all those modified places to mention that it's a temporary workaround and add a link to the GitHub issue tracking the follow-up task of improving juggler typings to correctly include ObserverMixin in PersistedModelClass
API.
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.
👍
return this.definePersistedModel(entityClass); | ||
} | ||
|
||
// Create an internal legacy Model attached to the 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.
Please replace this comment with a proper tsdoc-style one, explain LB4 app developers that they can override this method in their custom repository classes to tweak the legacy Model (e.g. register operation 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.
👍
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.
LGTM. Please fix the tsdoc per my comment below and make sure all CI checks are green (I think there are linting problems in the codebase and commit messages.)
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.
@hacksparrow, I was trying to fix the code linting error in this PR, but didn't go further. I'm not sure if it's because we didn't release the change in loopbackio/loopback-datasource-juggler#1820. So, I left some comments.
const expectedArray = [beforeSave, afterSave]; | ||
|
||
it('supports operation hooks', async () => { | ||
const p = await repo.create({slug: 'pencil'}); |
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.
code linter complaining about p
is not being used.
Should change this to:
await repo.create({slug: 'pencil'});
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.
👍
|
||
definePersistedModel(entityClass: typeof Product) { | ||
const modelClass = super.definePersistedModel(entityClass); | ||
modelClass.observe(beforeSave, async ctx => { |
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.
code linter is complaining about Promise returned in function argument where a void return was expected
. It might be because we're waiting for your changes in juggler to be released: loopbackio/loopback-datasource-juggler#1820.
this.hooksCalled.push(beforeSave); | ||
}); | ||
|
||
modelClass.observe(afterSave, async ctx => { |
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.
code linter is complaining about Promise returned in function argument where a void return was expected
. It might be because we're waiting for your changes in juggler to be released: loopbackio/loopback-datasource-juggler#1820.
6ac39b1
to
eaf2579
Compare
@slnode test please |
1 similar comment
@slnode test please |
eaf2579
to
2222b54
Compare
7444da9
to
ee077b8
Compare
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.
Nice! I love how much simpler is the new supporting implementation in @loopback/repository
👏
I have a tiny comment regarding the migration guide, feel free to ignore it.
|
||
Although possible, we are not providing an API which directly exposes the | ||
`observe` method of the model class. The current API makes the registration of | ||
operation hooks a process that is possible only after the model class is |
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.
operation hooks a process that is possible only after the model class is | |
operation hooks a process that is possible only at the time when the model class is |
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.
👍
Add support for operation hooks. Signed-off-by: Hage Yaapa <[email protected]>
ee077b8
to
e30f896
Compare
@@ -148,6 +148,21 @@ export class DefaultCrudRepository< | |||
return model as typeof juggler.PersistedModel; | |||
} | |||
|
|||
return this.definePersistedModel(entityClass); |
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 don’t we emit an event when definePersistedModel is done so that listeners can be registered to set up operation hooks? Overriding a method is always more advanced for a lot of developers.
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.
For the record, we briefly discussed if it's feasible to use events to set up operation hooks and decided to defer that.
Added support for operation hooks. Addresses #3952.
Signed-off-by: Hage Yaapa [email protected]
Checklist
👉 Read and sign the CLA (Contributor License Agreement) 👈
npm test
passes on your machinepackages/cli
were updatedexamples/*
were updated👉 Check out how to submit a PR 👈