Skip to content
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

Merged
merged 1 commit into from
Mar 5, 2020
Merged

feat: support operation hooks #4730

merged 1 commit into from
Mar 5, 2020

Conversation

hacksparrow
Copy link
Contributor

@hacksparrow hacksparrow commented Feb 24, 2020

Added support for operation hooks. Addresses #3952.

Signed-off-by: Hage Yaapa hage.yaapa@in.ibm.com

Checklist

👉 Read and sign the CLA (Contributor License Agreement) 👈

  • npm test passes on your machine
  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style guide
  • API Documentation in code was updated
  • Documentation in /docs/site was updated
  • Affected artifact templates in packages/cli were updated
  • Affected example projects in examples/* were updated

👉 Check out how to submit a PR 👈

@hacksparrow hacksparrow changed the title WIP: feat: support operation hooks WIP feat: support operation hooks Feb 24, 2020

definePersistedModel(entityClass: typeof Product) {
const model = super.definePersistedModel(entityClass);
model.observe('before save', async ctx => {
Copy link
Contributor

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?

Copy link
Contributor Author

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."

Copy link
Member

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.

Copy link
Contributor

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

Copy link
Contributor Author

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.

@hacksparrow hacksparrow changed the title WIP feat: support operation hooks feat: support operation hooks Feb 25, 2020
@hacksparrow hacksparrow marked this pull request as ready for review February 25, 2020 12:31
@hacksparrow
Copy link
Contributor Author

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
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated as per suggestion.

Copy link
Contributor

@jannyHou jannyHou left a 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:
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Corrected.

Copy link
Contributor

@agnes512 agnes512 left a 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Copyright IBM Corp. 2020

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

@dhmlau dhmlau added this to the Feb 2020 milestone Feb 26, 2020
Copy link
Contributor

@nabdelgadir nabdelgadir left a 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

Copy link
Member

@bajtos bajtos left a 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.

Copy link
Member

@bajtos bajtos left a 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`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';
Copy link
Member

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 in juggler namespace
  • Modify all places below using ObserverMixin to use juggler.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.

Copy link
Contributor Author

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
Copy link
Member

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).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member

@bajtos bajtos left a 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.)

Copy link
Member

@dhmlau dhmlau left a 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'});
Copy link
Member

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'});

Copy link
Contributor Author

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 => {
Copy link
Member

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 => {
Copy link
Member

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.

@hacksparrow
Copy link
Contributor Author

@slnode test please

1 similar comment
@hacksparrow
Copy link
Contributor Author

@slnode test please

@hacksparrow hacksparrow force-pushed the feat/operation-hooks branch 3 times, most recently from 7444da9 to ee077b8 Compare March 5, 2020 13:53
Copy link
Member

@bajtos bajtos left a 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Copy link
Contributor Author

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 <hage.yaapa@in.ibm.com>
@@ -148,6 +148,21 @@ export class DefaultCrudRepository<
return model as typeof juggler.PersistedModel;
}

return this.definePersistedModel(entityClass);
Copy link
Contributor

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.

Copy link
Contributor

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.

@hacksparrow hacksparrow merged commit 8701cce into master Mar 5, 2020
@hacksparrow hacksparrow deleted the feat/operation-hooks branch March 5, 2020 16:19
@agnes512 agnes512 removed this from the Mar 2020 milestone Mar 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants