-
Notifications
You must be signed in to change notification settings - Fork 114
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
Implement "advanced" path parameter response reflection #64
Conversation
46dacba
to
6a48baf
Compare
Here's the new code working with a replaced fee ID in an application fee refund response object:
And here's it working with an expanded fee object (it recurses through and tries to replace every fee ID found in either the direct child or any of its descendants):
|
@tmaxwell-stripe Unfortunately I had to write quite a lot of code to get this working property in order to account for all the sharp edges. Would you mind taking a quick pass to see if you can spot anything in here? If it helps at all, although it's far from perfect, I'm fairly confident that the implementation is now quite a bit better than before along pretty much every dimension we care about: (1) ability to reason about code (it's still not easy to reason about per se, but it's gotten better), (2) how exhaustive the testing is, (3) documentation, and (4) handling of edge cases. |
6a48baf
to
4db7595
Compare
generator.go
Outdated
// | ||
// nil if there is no replacement for the ID. | ||
// nil if there were no values extracted from the path. | ||
// | ||
// The value of this field is expected to stay stable across all levels of | ||
// recursion. |
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 comment is no longer correct. Can you replace it with a comment saying that this is actually not passed down recursively?
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.
Ah shoot. Yep, fixing.
} | ||
} | ||
} | ||
|
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 file is getting big. We should consider moving the ID-replacement logic into a separate file.
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.
Yeah, I think this is a good idea. Let me get it on a different pass though. (Aside from a different file, it should probably be in its own package to enforce some modularity too.)
generator.go
Outdated
// Passes through the generated data again to replace the values of any old | ||
// IDs that we replaced. This is a separate step because IDs could have | ||
// been found and replace at any point in the generation process. | ||
distributeReplacedIDs(params.PathParams, data) |
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 two-step structure is counterintuitive; I didn't realize that replaceIDs
was modifying PathParams
. A couple ideas: Could we give replaceIDs
a more descriptive name? Could we make it return a separate object instead of modifying PathParams
, to make the data flow more obvious? Could these comments be clearer?
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.
Yes, that's a great point. I ended up doing three things:
- Renaming:
replaceIDs
is now calledrecordAndReplaceIDs
to hint that it's also storing the IDs it replaces. - Changing the signatures.
params.PathParams
is still mutated in place, but the same struct is now returned as well just to make it clearer that information is flowing out of the first function and being used by the second. - Tightening up the comments.
Here's what the code looks like now:
if params.PathParams != nil {
// Passses through the generated data and replaces IDs that existed in
// the fixtures with IDs that were extracted from the request path, if
// and where appropriate.
//
// Note that the path params are mutated by the function, but we return
// them anyway to make the control flow here more clear.
pathParams := recordAndReplaceIDs(params.PathParams, data)
// Passes through the generated data again to replace the values of any old
// IDs that we replaced. This is a separate step because IDs could have
// been found and replace at any point in the generation process.
distributeReplacedIDs(pathParams, data)
}
I think it'll help clarity a lot. Lemme know if you think of anything else I could improve, and I'll make those changes as well.
Looks generally good, although the implementation is getting unwieldly. I left some suggestions for how to make it easier to understand. |
4db7595
to
b7fdd5d
Compare
Builds on reflecting URL parameters as introduced in #59 to also support reflecting parameters beyond just the object's primary ID. More concretely, if we have a path like this: /application_fees/fee_123/refunds/re_123 Before we'd just reflect the last part in (`re_123`), but here we now reflect the parent object in as well (`fee_123`). The one major caveat to this is that currently our query parameters are not very introspectable in that we don't really know what kind of object `fee_123` is supposed to be. To that end, we use the name of the parameter (in this case `fee`) and compare that it against two places to see if it's likely a referencing this object: * A fixture's `object` value. * The name of the key containing an ID value of subobject. This will work for some objects, but I'm probably going to go through and either (1) do a pass in the backend to correctly name parameters, or (2) implement a lookup table in `stripe-mock` to get this working 100%.
b7fdd5d
to
7da1df2
Compare
Thanks for the review Tim! Going to pull this in for now, but if any other ideas come up, let me know. |
Implement "advanced" path parameter response reflection
Builds on reflecting URL parameters as introduced in #59 to also support
reflecting parameters beyond just the object's primary ID. More
concretely, if we have a path like this:
Before we'd just reflect the last part in (
re_123
), but here we nowreflect the parent object in as well (
fee_123
).The one major caveat to this is that currently our query parameters are
not very introspectable in that we don't really know what kind of object
fee_123
is supposed to be. To that end, we use the name of theparameter (in this case
fee
) and compare that it against two placesto see if it's likely a referencing this object:
This will work for some objects, but I'm probably going to go through
and either (1) do a pass in the backend to correctly name parameters, or
(2) implement a lookup table in
stripe-mock
to get this working 100%.Does most of the work to address #61.