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: remove shallow cloning of object when executing a batch http req… #679

Merged
merged 5 commits into from
Dec 12, 2017

Conversation

bennyhobart
Copy link
Contributor

@bennyhobart bennyhobart commented Nov 28, 2017

Opened this to solve #461, let me know if this change is appropriate and I'll clean up for merge

TODO:

  • Update CHANGELOG.md with your change (include reference to issue & this PR)
  • Make sure all of the significant new logic is covered by tests
  • Rebase your changes on master so that they can be merged easily
  • Make sure all tests and linter rules pass

@apollo-cla
Copy link

@bennyhobart: Thank you for submitting a pull request! Before we can merge it, you'll need to sign the Meteor Contributor Agreement here: https://contribute.meteor.com/

@martijnwalraven
Copy link
Contributor

martijnwalraven commented Dec 4, 2017

Having read the discussion in #461, this seems reasonable to me, because the current behavior is confusing and blocks optimizations we hope to get from batched queries (e.g. when sharing data loaders).

It is likely some people rely on the context being shallowly cloned however, so this change will be breaking. @stubailo, what do you think?

If having a clean context is desirable, we should probably expose a factory-like function for this. Or find another way to customize resetting the context between requests (maybe a prepareForRequest function on the context?).

@stubailo
Copy link
Contributor

stubailo commented Dec 5, 2017

Hmmmmm yeah this would be quite a large breaking change for sure.

What would the factory function approach look like? I think we definitely need to have the ability to have the same context for all requests.

@martijnwalraven
Copy link
Contributor

Maybe we could allow context to be a function? That would give you control over what state should be recreated and what state (like data loaders) should be reused.

For backwards compatibility, we could replace any provided non-function context with a wrapper function that does shallow cloning of the passed in value.

@bennyhobart
Copy link
Contributor Author

I am happy to allow context to be a function and have it default to Object.assign if it is not a function, that solves backwards compat

@martijnwalraven
Copy link
Contributor

@bennyhobart This seems like a reasonable solution, so it would be great if you wanted to work on implementing it!

if (isFunction(context)) {
context = context();
} else if (isBatch) {
context = Object.assign({}, context || {});
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to make this a shallow clone that respects the prototype? Something like Object.assign(Object.create(Object.getPrototypeOf(context)), context)? That would solve the issues people are having with class-based contexts. But maybe that is too much magic and we should steer people towards using a function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we want the default behaviour to be cloning context for batch requests, then your solution will work for more users out of the box!

If anything I'd consider remove cloning because it's not clear it occurs when reading the API (unless i missed something)

@@ -27,6 +27,10 @@ function isQueryOperation(query: DocumentNode, operationName: string) {
return operationAST.operation === 'query';
}

export function isFunction(arg: any): arg is Function {
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems this doesn't have to be exported.

if (isFunction(context)) {
context = context();
} else if (isBatch) {
context = Object.assign({}, Object.getPrototypeOf(context), context);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think what you'd want here is Object.assign(Object.create(Object.getPrototypeOf(context)), context), because Object.assign({}, Object.getPrototypeOf(context), context) doesn't actually set the prototype, it just copies the properties from the prototype.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

and i thought i knew js, sorry about that

@martijnwalraven martijnwalraven merged commit 62c397e into apollographql:master Dec 12, 2017
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants