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

Add ctx. isNewInstance to "before save" and "after save" hooks #838

Closed
pungoyal opened this issue Nov 19, 2014 · 19 comments
Closed

Add ctx. isNewInstance to "before save" and "after save" hooks #838

pungoyal opened this issue Nov 19, 2014 · 19 comments
Assignees

Comments

@pungoyal
Copy link

Extend before save and after save hooks and add a new property ctx.isNewInstance (or similar).

This new property will have one of the following values for Model.updateOrCreate:

  • before save:
    • true when the data has not id and the method falls back to a regular create
    • true/false when a non-atomic implementation is used
    • undefined when an atomic implementation is used
  • after save:
    • true when the connector reported that a record was created
    • false when the connector reported that a record was updated
    • undefined when the connector did not report anythin

A thing to figure out: how to handle atomic findOrCreate, as it may fire the before save hook even if the model already exists?


The original issue description

Does it make sense to have a created event on model? Currently we have changed, but that fires for any changes to the model.

@clark0x
Copy link
Contributor

clark0x commented Dec 12, 2014

I have same question. Currently, I fire a created event in afterCreate hook and an updated event in afterUpdate hook in all my models.

I lookup the PR history, this loopbackio/loopback-datasource-juggler#65 introduces changed, deleted, deletedAll by @ritch .

To @ritch so, what do you think?

@ritch
Copy link
Member

ritch commented Dec 15, 2014

+1 for created event

@bajtos
Copy link
Member

bajtos commented Dec 18, 2014

This should be a reasonably easy change, would you (@pungoyal or @clarkorz) mind to contribute it?

@glesage
Copy link

glesage commented Dec 23, 2014

A state other than beforeCreate and afterCreate ? (which already exists)

@bajtos
Copy link
Member

bajtos commented Jan 16, 2015

It is probably better to defer this enhancement until loopbackio/loopback-datasource-juggler#367 is implemented.

@bajtos
Copy link
Member

bajtos commented Feb 3, 2015

The new Operation hooks implemented by loopbackio/loopback-datasource-juggler#367 provide after save hook which does not distinguish between created and updated. One of the reasons is that this information may not be available from updateOrCreate (a.k.a upsert and HTTP PUT) implemented by connectors as an atomic operation.

However, if you are able to come up with a way how to distinguish between CREATE and UPDATE that works in all connectors, then I am ok with adding a new property to the ctx object passed to after save hook and use this property to decide which event to fire.

Another alternative is to let updateOrCreate() trigger always updated event, even in the case where a new model instance was created.

Thoughts?

@clark0x
Copy link
Contributor

clark0x commented Feb 4, 2015

@bajtos loopback-connector-mongodb implements updateOrCreate by using findAndModify. The result that findAndModify command responses could be use to distinguish FIND, CREATE, and UPDATE.

For loopback-connector-mysql, I am not familiar with mysql, but I do find a hint in mysql document:

With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row, 2 if an existing row is updated, and 0 if an existing row is set to its current values.

I don't study all others connectors, but can we tolerate that some connectors could distinguish between CREATE and UPDATE while others couldn't?

@bajtos
Copy link
Member

bajtos commented Feb 4, 2015

@clarkorz thank you for making the research.

Based on what you have described above, I am proposing the following implementation: extend after save hook and add a new property ctx.wasCreated (or ctx.isNewInstance or similar).

This new property will have one of the following values for Model.updateOrCreate:

  1. true when the connector reported that a record was created
  2. false when the connector reported that a record was updated
  3. undefined when the connector did not report anything

The benefit of such solution is that a simple check if (!ctx.isNewInstance) will catch both 2 & 3, while it is still possible to distinguish between 2 and 3 if needed.

Of course, methods that already know what kind of write is being performed (Model.create, Model.updateAll, etc.) will set this flag independently on the connector implementation.

Thoughts?

/cc @raymondfeng @ritch

@clark0x
Copy link
Contributor

clark0x commented Feb 5, 2015

LGTM

Besides the new hooks, should we complete the event system in advance? To specific, should we replace changed event with created and updated events, or pass a similar flag (as you describe above) when emitting changed event?

@bajtos
Copy link
Member

bajtos commented Feb 6, 2015

We need to preserve the changed event for backwards compatibility.

The choice is then changed with flag, or emit both changed and created/updated. I slightly prefer the flag as it is consistent with after save, but I don't really mind either option.

@bajtos bajtos changed the title Model 'created' event Add ctx.created to "before save" and "after save" hook Feb 25, 2015
@bajtos bajtos changed the title Add ctx.created to "before save" and "after save" hook Add ctx. isNewInstance to "before save" and "after save" hook Feb 25, 2015
@bajtos bajtos changed the title Add ctx. isNewInstance to "before save" and "after save" hook Add ctx. isNewInstance to "before save" and "after save" hooks Feb 25, 2015
@bajtos bajtos added the #tob label Feb 26, 2015
@kidwm
Copy link

kidwm commented Feb 28, 2015

Sorry, but I have a newbie question, since then how do old model hook afterCreate work?

@bajtos
Copy link
Member

bajtos commented Mar 4, 2015

how do old model hook afterCreate work?

It is called only when you invoke Model.create or when you invoke Model.updateOrCreate without model id. When you call updateOrCreate with an id that does not exist, a new model instance is created but the hook afterCreate is not called at all. This is one of the bugs in the old hooks that is fixed by the new Operation hooks.

@bajtos
Copy link
Member

bajtos commented Mar 4, 2015

See #1148 for an example of multiple different approaches for converting the old "beforeCreate" hook into the new "before save" hook.

@fabien
Copy link
Contributor

fabien commented Mar 6, 2015

@bajtos I would go with ctx.isNewInstance and the three values you proposed: true, false, undefined

@fabien
Copy link
Contributor

fabien commented Mar 6, 2015

@bajtos could you please summarize which context property name and values you expect? I think I'll need this soon as well.

@bajtos
Copy link
Member

bajtos commented Mar 6, 2015

@bajtos could you please summarize which context property name and values you expect? I think I'll need this soon as well.

I am not sure what you are asking me. Your previous comment described exactly what I would like to see implemented:

I would go with ctx.isNewInstance and the three values you proposed: true, false, undefined

I am ok with a partial implementation that does not include necessary changes in all connectors, as long as the behaviour is reasonable in the case when the connector does not provide the flag yet. It's also ok to implement this flag for one of the hooks only, e.g. "before save", and leave the other hook for later.

Last but not least, there is no need to implement this for the "changed" event, since it has been already deprecated in favour of the new Operation hooks.

@notbrain
Copy link

notbrain commented Mar 6, 2015

As part of this I would love to see the operation hooks expanded to include 'after create' and 'after update' that would hide the implementation of isNewInstance and the checking that it entails. If you need specific detail you can use 'after save' and then inspect ctx.isNewInstance for the values but straight up usage for the simple difference between create and update would be +1 in my book. Even if they are just aliases to 'after save' with the requisite checking.

@bajtos
Copy link
Member

bajtos commented Mar 20, 2015

I have landed loopbackio/loopback-datasource-juggler#515 which added ctx.isNewInstance property and implemented support for that in the memory connector. Now we need implement this feature in all connectors, any volunteers?

@bajtos bajtos removed their assignment Mar 20, 2015
@bajtos bajtos added #tob and removed #wip labels Mar 20, 2015
@chandadharap chandadharap added #plan and removed #tob labels Apr 2, 2015
@altsang altsang added #sprint68 and removed #plan labels Apr 7, 2015
@bajtos
Copy link
Member

bajtos commented Apr 8, 2015

Connectors supported:

I am closing this issue are solved, we will keep track of the remaining work in connector-specific issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests