-
Notifications
You must be signed in to change notification settings - Fork 13
Add support for multiple batch keys #261
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
Conversation
Looking good! Couple questions: 1. Merging StrategiesWill this support use cases as described in the original issue? #75 ? Looks like there's different shapes for how secondary stuff (arguments/response) could be exposed. It could be an array that needs merging (your use case), or object properties (the use case described in the original issue description). Maybe even another strategy like boolean merging (e.g. if even if we don't support the original use case for now, given we've seen different "behaviours", I wonder if we want an option to control that (even if this this is the only behaviour we support right now) - something like 2. Data MaskingAssuming we make the following set of network requests:
What is the response to the callsite that made this initial loader request?
They only requested the name property - but would they get back 'rating' too? (can't tell from the tests yet) I think we'd want to make sure we don't leak extra info that wasn't requested - to avoid screwing up the types and introducing buggy responses that people rely on that we may want to fix one day. this is awesome stuff!!! |
src/runtimeHelpers.ts
Outdated
export function getBatchKeyForPartitionItems( | ||
batchKey: string, | ||
ignoreKeys: Array<string>, | ||
items: ReadonlyArray<object>, | ||
): ReadonlyArray<ReadonlyArray<number>> { |
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.
Could partitionItems
be re-used? What is this doing differently?
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.
instead of returning the order, here we return the batchKey value of that item, the main difference is line 135. https://github.com/Yelp/dataloader-codegen/pull/261/files#diff-92b6f52e2c1b302cd9d15133831c595f151215aa1dfda274ff29fbd33d89a9b2R135
For example, if we have (the batch key here is bar_id
and secondary batch key is property
) :
{ bar_id: 100, property: 'name', include_extra_info: true },
{ bar_id: 101, property: 'rating', include_extra_info: false },
{ bar_id: 100, property: 'rating', include_extra_info: true },
function partitionItems
will return [ [ 0, 2 ], [ 1 ] ]
while, function getBatchKeyForPartitionItems
with return [ [ 100, 100 ], [ 101 ] ]
We use this information to help us match results object to requests. see https://github.com/Yelp/dataloader-codegen/pull/261/files#diff-92b6f52e2c1b302cd9d15133831c595f151215aa1dfda274ff29fbd33d89a9b2R349-R350
@magicmark I believe I did cover the use cases as described in the original issue #75. See this unit test the response is like |
result = Object.assign( | ||
{}, | ||
...[newKey, propertyBatchKeyPartion[i][j]].map((key) => ({ [key]: element[key] })), | ||
); |
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.
this syntax is really confusing to me
we usually don't need to explicitly call Object.assign for merging - we can use normal spread syntax:
const foo = {
...myObj1,
...myObj2,
};
I'm a bit more confused about what's going on here though.
> Object.assign({}, ...['hello', 'world'] )
{ '0': 'w', '1': 'o', '2': 'r', '3': 'l', '4': 'd' }
:thonk:
Could we separate out these lines or something? this is making my brain hurt a bit
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 searched how to only keep selected key-value pair in an object, https://stackoverflow.com/questions/54907549/keep-only-selected-keys-in-every-object-from-array and stackoverflow gave me this.
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 still not following :/
Could you give a sample demo input/output of what's happening here?
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.
To just keep newKey ('id') and the property ('name') from the response. So that we only return what we asked for.
For example:
var data = {id: '1', name: 'John', age: 42};
var keys_to_keep = ['id', 'name']
var data_new=Object.assign({}, ...keys_to_keep.map(key => ({[key]: data[key]})));
console.log(data_new)
output:
{ id: '1', name: 'John' }
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.
Gotcha - thanks. (The simple example helped a lot!)
does https://lodash.com/docs/4.17.15#pick work?
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 Object.assign({}, ...
logic feels sufficiently tricky to warrant using the lodash method instead (I think we're already using lodash in here anyway)
examples/swapi/swapi.js
Outdated
getFilmsV2: ({ film_ids, properties }) => { | ||
return [ |
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.
might be worth a comment to explain what's going on here / why v2 exists
const response = await swapiLoaders.getFilmsV2.load({ film_id: this.id, property: 'title' }); | ||
|
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.
can you show the output of $ flow check
when you try to read response.director
?
hopefully it errors, which would verify the data masking is correct
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 added console.log(response)
there, and it only gives me film_id
and title
. I could also do the flow check.
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 added console.log(response)
and console.log(response.director)
.
got this
{ film_id: 4, title: 'The Phantom Menace' }
undefined
data masking is correct, but flow doesn't complain, which is something I should fix.
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 checked the response type just matches the SWAPI_Film_v2 type
I defined here
which is
export type SWAPI_Film_v2 = $ReadOnly<{|
film_id: number,
title: string,
episode_id: number,
director: string,
producer: string,
|}>;
so it won't blow up when I do response.director
.
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!
I think we want flow to blow up here though don’t we, otherwise there’s a mismatch between what actually exists vs what flow thinks exists?
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.
@magicmark I have trouble with generate correct flow type. Here's a try flow reproduce
Could you help me with that? I don't know how to make id
not nullable while change other fields nullable.
On the other hand,
Hmm, here, we only get nullable because the swagger endpoint defines each as property so. That's just what the example endpoint does - there's no guarantee other endpoints will do that.
I think we can document supported resource must return nullable properties? If some endpoint require all properties be non-nullable, and we remove lots of properties in the dataloader-codegen, that seems wrong to me and we shouldn't support that case. So we require properties being nullable make sense 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.
there's a (subtle) difference between getting the superset of all possible null properties and the subset of known guaranteed non-null properties
So if I did:
const properties1 = loaders.fetchStuff({ properties: ['foo', 'bar'] });
const properties2 = loaders.fetchStuff({ properties: ['baz'] });
If I understand what you're saying correctly, you'd expect both properties1
and properties2
to have the following identical type:
{ foo?: string, bar?: string, baz?: string }
If we're being super nitpicky, this is wrong:
- there's no way that
properties1
should ever havebaz
present since we didn't request it. Flow is kinda lying to us. - In
properties2
,baz
should always be present. Again, flow is kinda lying to us.
These aren't catastrophic problems. The stock price of the company won't go up or down as a result either way here :)
But again, as this is foundational infrastructure that hundreds of our resolvers use, I think we probably want to get the flow typings as "correct" as 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.
You commented this before
At a very minumum, we should mark all properties as nullable - the more correct thing to do would be to omit all non-requested properties from the response entirely.
So I thought just having all properties nullable is fine.
I have trouble with getting it as "correct" as 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.
we should mark all properties as nullable
Is that what the current PR does? Or does it rely on the endpoint already doing that for us? That's a key difference.
I think we can document supported resource must return nullable properties?
Is a reasonable alternative.
I'm happy to set up some time to pair on this to see if we can get flow to do the right thing? We can timebox it, and if not, fall back to your 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.
Is that what the current PR does? Or does it rely on the endpoint already doing that for us? That's a key difference.
We rely on the endpoint returns nullable properties. And that's the reasonable alternative thing you commented on.
I'm happy to set up some time to pair on this to see if we can get flow to do the right thing? We can timebox it, and if not, fall back to your suggestion :)
Thanks!
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.
looking good! couple thoughts:
- the examples only show one style of the property merging interface - given the complexity here, might be worth e2e testing all styles
- can we verify the generated flow types are correct? (by showing it blowing up when you try to read a value we didn't request - a screenshot in the PR description would be good)
- can we verify the runtime is correct, by doing the above and console.log() the bad value, and verify it's empty
- do we need an extra config param to control what style of endpoint it is?
Thanks!
I think we want flow to blow up here though don’t we, otherwise there’s a
mismatch between what actually exists vs what flow thinks exists
…On Wed, Jun 2, 2021 at 4:48 PM Yue Guo ***@***.***> wrote:
***@***.***DBD* commented on this pull request.
------------------------------
In examples/swapi/swapi-server.js
<#261 (comment)>
:
> + const response = await swapiLoaders.getFilmsV2.load({ film_id: this.id, property: 'title' });
+
I checked the response type just matches the SWAPI_Film_v2 type I defined
here
<https://github.com/Yelp/dataloader-codegen/pull/261/files#diff-e461ae547272d09d3b85a44a7ab8de37f10cc4a08ae23a9ffb78b247b4425b7eR66-R73>
which is
export type SWAPI_Film_v2 = $ReadOnly<{|
film_id: number,
title: string,
episode_id: number,
director: string,
producer: string,
|}>;
so it won't blow up when I do response.director.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#261 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAMELZHHPDHBWCJ4CG5A67DTQ27NRANCNFSM44WLEJMA>
.
|
issue: #75
The endpoint that would benefit from this would be an endpoint that takes multiple batch ids and returns a nested object. See more explanation in the issue linked above.
Solution:
I add two new configurable key:
propertyBatchKey
andpropertyNewKey
to config the property key we want to batch up.Here are the 3 cases that we support for now:
They all expect to have same input parameters format:
But they could have different response contract:
Testing
update: I added more unit tests so that we know the new propertyBatchKey works fine with existing config.
e2e test with star war example: (data masking is correct, and when we query three fields, they only call the function once)
output: https://fluffy.yelpcorp.com/i/MpJQFFdv18k4jnc6tHCRV2Br5qVpVmjd.html