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

Fix: @attr defaultValue() results should persist after initialization #9355

Merged

Conversation

christophersansone
Copy link
Contributor

Description

Fixes #9315.

(cc @runspired)

Notes for the release

Before this change, when @attr defines a defaultValue as a function, the default value instantiated on a model instance will not necessarily be the value that is serialized. If the attribute was not set, and therefore the default value is used, then the serialization would call the defaultValue function again instead of returning the value on the model. This is mostly a problem when the attribute value is an array or object, because changes can be made to the object, resulting in those changes being lost upon serialization. With this change, the original defaultValue is persisted at the cache level to avoid this issue.

@christophersansone christophersansone marked this pull request as draft April 12, 2024 23:31
@runspired runspired self-assigned this Apr 12, 2024
@runspired runspired added 🎯 release PR should be backported to release 🎯 canary PR is targeting canary (default) 🎯 lts The PR should be backported to the most recent LTS 🏷️ bug This PR primarily fixes a reported issue 🎯 lts-prev This PR should be back ported to the second-most recent LTS labels Apr 12, 2024
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe we also need to clear defaultAttrs during patchLocalAttributes

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 for the quick feedback!

I reviewed patchLocalAttributes and happy to make the change, but feel like I need a bit more clarification. FWICT, the purpose of that method is to simply remove the key/value pairs from localAttrs that are not in remoteAttrs or inflightAttrs. I'm assuming this would occur when, say, a record is updated from the server while there are still local changes? If so, should we also have similar logic to remove any defaultAttrs that have keys present in localAttrs, remoteAttrs, or inflightAttrs?

The way I see it, yes it might be worthwhile to ensure defaultAttrs is kept clean at every possible chance, but because the order of operations of getAttr would inherently prevent a default value from being exposed if any of the other sets of attributes contains that key, it could be considered superfluous.

In thinking through the scenario, if I understand it correctly:

  • A record is fetched remotely that does not define an attribute with a defaultValue function, so the default value is created.
  • The record is returned from the server again, only this time it does have a value for that, thereby masking the previous default value.
  • The record is returned from the server, yet without the attribute once again. What should happen?

This seems like quite an edge case, but I think my opinion would be that the original default value would take effect again, as opposed to a new one being created.

Happy to do what you think makes the most sense here, just let me know!

Also, I was hoping to find a nice set of unit tests for this cache class, but no luck 😕. Do you have a recommendation regarding where to write some tests for this?

Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like quite an edge case, but I think my opinion would be that the original default value would take effect again, as opposed to a new one being created.

If the server has previously informed us of a state, and that state later disappears, imo the correct thing to do is start over.

I was hoping to find a nice set of unit tests for this cache class, but no luck

The cache requires integration tests. Newest ones are here https://github.com/emberjs/data/tree/main/tests/ember-data__json-api/tests/integration/cache and here https://github.com/emberjs/data/blob/main/tests/main/tests/integration/cache/json-api-cache-test.js

but most of them are scattered through the main test app, with most having been written from the perspective of user observable behaviors (due largely to how cache grew out of Store/Model/InternalModel).

I'd recommend adding new tests at that first link above since this is a behavior specific to JSON:API cache.

Copy link
Contributor

Choose a reason for hiding this comment

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

@christophersansone got any more time for this one? I can poke at it a bit if needed

@christophersansone christophersansone marked this pull request as ready for review April 15, 2024 02:59
@christophersansone
Copy link
Contributor Author

Sorry! The day job got in the way this week. Planning to spend some time on it this weekend.

@christophersansone
Copy link
Contributor Author

christophersansone commented Apr 30, 2024

@runspired Sorry for the delay!

Okay, I added the change to patchLocalAttributes, which I am pretty sure is correct. I also wrote a few tests that I believe cover the basics here, really just focused on when the default values are cached and / or regenerated. Thanks for pointing me in the right direction on these!

I did not figure out how to adequately test the change for patchLocalAttributes. If the server informed us of an attribute, the server can't really clear the entire state of that attribute. The attribute can either be specified in attributes or not. If it's specified, that specified value will be used. If it's not specified, the existing local value will be retained.

Let me know if there's anything else that makes sense to test.

@runspired runspired merged commit b87ebd2 into emberjs:main May 10, 2024
18 checks passed
@daniellubovich
Copy link

@runspired Is this something you think could be easily backported into 4.12?

@runspired
Copy link
Contributor

@daniellubovich the backport commit shouldn't be too hard

daniellubovich pushed a commit to daniellubovich/data that referenced this pull request May 15, 2024
…emberjs#9355)

* Backport of emberjs#9355

* defaultValue function results should persist after initialization

* clear default values on patch changes

* defaultValue caching test

---------

Co-authored-by: christophersansone <[email protected]>
runspired pushed a commit that referenced this pull request May 16, 2024
…9441)

* Fix: @attr defaultValue() results should persist after initialization (#9355)

* Backport of #9355

* defaultValue function results should persist after initialization

* clear default values on patch changes

* defaultValue caching test

---------

Co-authored-by: christophersansone <[email protected]>

* Fix lint errors

---------

Co-authored-by: Christopher Sansone <[email protected]>
Co-authored-by: christophersansone <[email protected]>
Co-authored-by: Daniel Lubovich <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🎯 canary PR is targeting canary (default) 🎯 lts The PR should be backported to the most recent LTS 🎯 lts-prev This PR should be back ported to the second-most recent LTS 🎯 release PR should be backported to release 🏷️ bug This PR primarily fixes a reported issue
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Attributes with defaultValue functions not persisting
3 participants