-
Notifications
You must be signed in to change notification settings - Fork 460
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
Handle unmarshaling OrderItem to account for parent
expansion
#681
Conversation
13dfafe
to
920479d
Compare
parent
expansion
parent
expansionparent
expansion
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.
Great work @spastorelli-stripe! Thanks so much for tackling the fix.
Added a few comments below.
order.go
Outdated
// UnmarshalJSON handles deserialization of an OrderItem. | ||
// This custom unmarshaling is needed because the resulting | ||
// Parent property may be an id or a full SKU struct when parent is expanded. | ||
func (oi *OrderItem) UnmarshalJSON(data []byte) error { |
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.
@spastorelli-stripe Would you mind taking a look at the implementation for ExternalAccount
(another fairly close polymorphic resource) and seeing if we can put together an implementation a little more like that one?
Namely, a few differences:
- The custom
UnmarshalJSON
moves fromOrderItem
toOrderItemParent
. Type
onOrderItemParent
picks up a JSON annotation ofjson:"object"
.
I think that other way has a few advantages:
- You move the custom implementation right onto the polymorphic object itself, which is better conceptually.
- You need a one or two fewer
json.Unmarshal
invocations. - You can avoid the boilerplate of assigning
Type
yourself likeoi.Parent.Type = OrderItemParentTypeCoupon
.
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 for the suggestion! Just to make sure I'm not overlooking something, were you suggesting to take an approach similar to this:
https://gist.github.com/spastorelli-stripe/a368768498d0607c3c20c1bddbf0169b#file-order-go-L52-L57
https://gist.github.com/spastorelli-stripe/a368768498d0607c3c20c1bddbf0169b#file-order-go-L218-L245
One issue I would see with moving the custom UnmarshalJSON
method from OrderItem
to OrderItemParent
is that the data
byte array provided to the call would only include the bytes for the "parent": ....
property of the order_item
object [1].
As this parent
value isn't consistently returned as a subhash, it doesn't always contain an object
property and as a result Type
on OrderItemParent
may not get consistently populated [1] and from my understanding we wouldn't be able to backfill it from the OrderItem
since it isn't accessible from OrderItemParent
.
My concern would be that if we provide a Type
on OrderItemParent
there could be expectation at the call site that this value is populated whenever the parent is requested to be expanded.
It may not be an issue but wanted to double check with you just in case.
Let me know what you think.
[1]
For SKU order items, parent
would be expanded nicely:
{
object: "order_item",
amount: 499,
currency: "usd",
description: "test name",
parent: {
id: "TEST-SKU-qSvWbSxdyAHQxEje",
object: "sku",
...
updated: 1535639219
},
quantity: 1,
type: "sku"
}
But for items like shipping
or discount
where parent
doesn't expand (despite requesting to expand it) only the ID string is returned:
{
object: "order_item",
amount: -399,
currency: "usd",
description: "50usdoffonce2: $50.00 off",
parent: "50usdoffonce2",
quantity: null,
type: "discount"
}
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 for the suggestion! Just to make sure I'm not overlooking something, were you suggesting to take an approach similar to this:
Yeah, that's exactly it!
One issue I would see with moving the custom
UnmarshalJSON
method fromOrderItem
toOrderItemParent
is that thedata
byte array provided to the call would only include the bytes for the"parent": ....
property of theorder_item
object [1].
Ah, I see why you went the way you did.
My preference would still be to change this. I think your justification makes sense, but it feels like we're trying to paper over a broken API with this approach rather than fix the source.
I was just looking at the backend code, and although apparently only SKUs are expandable according to your implementation, it looks at least like the intent was that discounts should be expandable as well, and it's bug if they're not expanding correctly. The lack of expansion on other types is hopefully an oversight that we can correct — ideally, if one type is expandable, than they all are.
While waiting for those fixes though, I don't think it's that bad to just have users check the Type
on OrderItem
because they'll be holding a reference to that anyway. e.g.:
if orderItem.Parent == OrderItemParentTypeSKU {
// Do something with orderItem.Parent.SKU
This keeps the implementation more symmetric with how the API actually looks, and we can expand it if more parents become expandable in the future. What do you think?
ptal @spastorelli-stripe
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. Makes sense. Changed as per suggestions. PTAL
order_test.go
Outdated
"type": "sku", | ||
} | ||
bytes, err := json.Marshal(&orderItemData) | ||
assert.NoError(t, err) |
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.
Great tests!
Would you mind wrapping each section in {}
braces? I find this looks a little cleaner and is a bit easier to follow. For example:
func TestOrderItem_UnmarshalJSON(t *testing.T) {
// Try unmarshaling a SKU
{
...
}
// Try unmarshaling a coupon
{
...
}
// Try unmarshaling a shipment
{
...
}
...
}
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 for the suggestion. Done
order.go
Outdated
oi.Parent.Type = OrderItemParentTypeShipping | ||
case OrderItemTypeSKU: | ||
// Currently only SKUs `parent` is returned as an object when expanded. | ||
// For other items only IDs are returned. |
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.
That's actually how the API works?? Horrible :$
order.go
Outdated
OrderItemParentTypeCoupon OrderItemParentType = "coupon" | ||
OrderItemParentTypeDiscount OrderItemParentType = "discount" | ||
OrderItemParentTypeShipping OrderItemParentType = "shipping" | ||
OrderItemParentTypeSKU OrderItemParentType = "sku" |
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.
Nit, but we generally order "computer alphabetically", and uppercase letters sort before lowercase ones. Would you mind putting SKU
here above Shipping
?
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.
Done
920479d
to
da15f07
Compare
Thanks @brandur-stripe for the review and suggestions. |
da15f07
to
983153b
Compare
Thanks for your follow @brandur-stripe. I believe I have addressed all suggestions. PTAL |
Thank you @spastorelli-stripe! This is a pretty complicated case, so thanks for tackling it. LGTM. |
Released as 48.0.0. |
Thanks to you both for your work on this. (And it's nice to see a more effective software development flow than the one I deal with at my company! :-) |
* Extract out nil field removal as a helper * Fixing price equality logic * Fixing typing
Attempts to fix the unmarshalling error that can occur when expanding
parent
of an Order Item object, when callingorder.List
and expanding ondata.items.parent
.r? @brandur-stripe
cc @stripe/api-libraries