stripe / openapi

An OpenAPI specification for the Stripe API.
MIT License
390 stars 122 forks source link

Uncertainty on when invoice.subscription is to be expanded. #61

Open rdarder opened 4 years ago

rdarder commented 4 years ago

Hi there! I'm wrapping my head around x-expandableFields and x-expansionResources. I thought I got this right but there's a case that confuses me. Let's take invoice.subscription. it's not an expandableFIeld and it's still anyOf(str, ref(subscription)). So far, every time I get an invoice, I see the subscription automatically expanded. So my questions are:

My intention is to reduce the type ambiguity of the return types, so consumers don't need to check whether -- in this case -- the subscription is an id or the actual object. I can deal with expandableFields most of the times, but this one seems off.

Thank you!!

rdarder commented 4 years ago

Sorry, after re-reading the spec, I got this wrong, but I'm still confused. invoice.subscription is expandable, but I get it expanded even when I set no headers for expanding that field. Do some endpoints choose to expand some fields by default? Is that annotated in the openapi spec somehow and/or is there a way of controlling it?

remi-stripe commented 4 years ago

@rdarder Would you be able to provide a bit more details about how you are testing this exactly? What do you use to fetch the Invoice that leads to you seeing the subscription property be an expanded Subscription object? For example when I try on my end with the latest stripe-mock I get subscription: null today. I only get a full object if I explicitly pass expand[]=subscription as a query param.

rdarder commented 4 years ago

Hi Remi, I think I reported the wrong example, I'm sorry for that. I've gone deeper with the Customer object and have similar scenarios that I'd like to clarify. I'm getting customers via GET /v1/customers, no headers other than the api key. The spec for customer says these fields are expandible:

Here's a sample anonymized customer I get back as part of the response. Below are some questions regarding some specific patterns:

address: anyOf([address]), nullable: true, marked as expandable comes as null, which makes sense. Should I expect that address is always null unless I expand it?

default_source: anyOf(str, ... polymorphic entities), is nullable, is expandable. comes back as a string, which again makes sense. I should expect this to always come back as a string or null as long as I don't expand it, right?

invoice_settings. ref(invoice_settings_customer_setting), not nullable, marked as expandable. comes as an object with its keys set, all to null (which matches the invoice_settings schema). what's the semantic of expandable here?

sources: this is a "page-like" object of a polymorphic entity (payment sources). non nullable and expandable. Also within the object, the "data" attribute is also marked as expandable. it comes back with all fields present (including data, which has entries in it.)

subscriptions: equivalent case to sources: I get back the whole thing without asking for expansion.

Could you help me figure out how expansion works in these scenarios? Thank you!

remi-stripe commented 4 years ago

@rdarder I usually recommend referring more to our API Reference than the openapi for questions like this honestly but here's a detailed answer. In all the examples you gave, there are 4 separate and different situations:

rdarder commented 4 years ago

Thanks for the quick response! I opted to ask here since I'm trying to make sense of your openapi spec extensions rather than individual cases. In other words, I'm pursuing statically typing the resources I get back from stripe's API. I just updated the API version to 2020-08-27, and still get the same exact customer json that I posted earlier. That response includes sources and subscriptions (with their data array populated) in it, and I didn't ask for expanding them explicitly. This is probably my main source of confusion.

remi-stripe commented 4 years ago

@rdarder How are you getting that raw JSON exactly? I just tried quickly on the default test account for our docs and as long as I force the latest API version explicitly in the call then sources is absent (and so are subscriptions and tax_ids).

Here's what I get:

curl https://api.stripe.com/v1/customers/cus_HwfwuUZoxYBjJp   -u sk_test_4eC39HqLyjWDarjtT1zdp7dc:   -H "Stripe-Version: 2020-08-27"
{
  "id": "cus_HwfwuUZoxYBjJp",
  "object": "customer",
  "address": null,
  "balance": 0,
  "created": 1599017108,
  "currency": "usd",
  "default_source": null,
  "delinquent": false,
  "description": null,
  "discount": null,
  "email": null,
  "invoice_prefix": "B530716",
  "invoice_settings": {
    "custom_fields": null,
    "default_payment_method": null,
    "footer": null
  },
  "livemode": false,
  "metadata": {
  },
  "name": null,
  "next_invoice_sequence": 1,
  "phone": null,
  "preferred_locales": [

  ],
  "shipping": null,
  "tax_exempt": "none"
}
rdarder commented 4 years ago

I'm listing all customers through GET /v1/customers

rdarder commented 4 years ago

Nevermind, I wasn't setting the right api version. If I use the 2020-08-27, I get exactly what you said. Thank you!

rdarder commented 4 years ago
  • discount: This is an auto-expanded API resource, this means that it's either null or the whole Discount object when there's one.

Could you help me figure out if there's anything on your openapi spec that would help me determine that this is "auto-expanded"? Cause it's marked as an expandable field.

remi-stripe commented 4 years ago

Whether it is auto-expanded or not depends on whether it's optional and what is the shape described.

For discount for example the spec says that this is either null or an instance of Discount

discount:
  anyOf:
  - "$ref": "#/components/schemas/discount"
  description: Describes the current discount active on the customer, if there
    is one.
  nullable: true

and later discount appears in the list of required which confirms it's always returned.

On the other hand for sources and tax_ids for example you can see it's a list of objects, it's optional since it's not in required and it's includable because the properties appear in the x-expandableFields.

rdarder commented 4 years ago

Thank you. Unfortunately, either I'm misunderstanding or have the wrong spec file. I don't see discount as a required field for the customer schema. I find it, however, in the x-expandableFields. image

remi-stripe commented 4 years ago

@rdarder Really sorry for the delays. I checked in internally on this ask and unfortunately there is no reliable way right now to know for sure if a property is automatically expanded or not in openapi. It's definitely something we'd like to improve upon in the future but it won't happen immediately.

cc @richardm-stripe who is looking into this.