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

Revamp interfaces on Stripe model classes #1317

Conversation

ob-stripe
Copy link
Contributor

@ob-stripe ob-stripe commented Oct 2, 2018

r? @remi-stripe
cc @stripe/api-libraries

  • Adds an IStripeEntity interface implemented by StripeEntity
  • Adds IHasId and IHasObject interfaces
  • Removes StripeEntityWithId
  • Ensures that all model classes derive from StripeEntity, and add IHasId and IHasObject interfaces where needed
  • Removes ISupportMetadata (ISupportMetadata was only useful to properly encode metadata with the old plugin system, it's useless now)
  • Renames AdditionalOwners to AdditionalOwner (the model represents a single additional owner)
  • Removes support for old format of request attribute on event objects (cf. https://stripe.com/docs/upgrades#2017-05-25)

@ob-stripe
Copy link
Contributor Author

Also, to clarify the motivation behind this: having both IHasId and IHasObject interfaces makes it possible to cleanly define e.g. IExternalAccount that implements both interfaces above (since an external account always has both an id and an object attribute).

IHasId can be used for field expansion (I've updated the StringOrObject generic in this PR) and IHasObject can be used in a generic JSON deserializer that instantiates the correct concrete class based on the value of the object attribute.

@ob-stripe ob-stripe force-pushed the ob-interfaces-interfaces-everywhere branch from b09bd74 to 04415cc Compare October 3, 2018 11:35
@ob-stripe ob-stripe changed the title [wip] More interfaces on Stripe model classes Revamp interfaces on Stripe model classes Oct 3, 2018
Copy link
Contributor

@remi-stripe remi-stripe left a comment

Choose a reason for hiding this comment

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

Some minor comments in the code.

Should we do something similar for Deleted and Metadata to not have to put it on every resource that needs it?
Would be neat but then StripeEntityWithIdAndObjectAndDeletedAndMetdata is not really cool. Maybe metadata should be the default for StripeEntityWithId and you'd have to explicitly use StripeEntityWithIdAndNoMetadata for the ones that explicitly don't support it? But it's easy to miss if so.

@@ -5,11 +5,8 @@ namespace Stripe
using Newtonsoft.Json;
using Stripe.Infrastructure;

public class Dispute : StripeEntityWithId, ISupportMetadata
public class Dispute : StripeEntityWithIdAndObject
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think Discount has an id, at least it's not in the docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 Fixed.

@@ -3,7 +3,7 @@ namespace Stripe
using System.Collections.Generic;
using Newtonsoft.Json;

public class RecipientActiveAccount : StripeEntityWithId, ISupportMetadata
public class RecipientActiveAccount : StripeEntityWithId
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not StripeEntityWithIdAndObject

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 Fixed.

@@ -1,12 +0,0 @@
namespace Stripe
Copy link
Contributor

Choose a reason for hiding this comment

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

Wasn't this used to ensure we added Metadata to the Resource too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure why resource classes used this interface at all, but I don't think this is a good reason: the interface would only force you add the Metadata property if you remembered to add the interface...

@@ -22,7 +22,7 @@ public static void Map(object value, Action<string> updateId, Action<T> updateOb
else if (value is string)
{
updateId((string)value);
updateObject(null);
updateObject(default(T));
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you put a real comment on this line?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

@remi-stripe remi-stripe assigned ob-stripe and unassigned remi-stripe Oct 3, 2018
@ob-stripe ob-stripe force-pushed the ob-interfaces-interfaces-everywhere branch from 8f016e4 to 467b140 Compare October 3, 2018 14:21
@ob-stripe
Copy link
Contributor Author

Would be neat but then StripeEntityWithIdAndObjectAndDeletedAndMetdata is not really cool. Maybe metadata should be the default for StripeEntityWithId and you'd have to explicitly use StripeEntityWithIdAndNoMetadata for the ones that explicitly don't support it? But it's easy to miss if so.

Yeah, tbh I'm already not thrilled about all the StripeEntityWith... superclasses. I'd rather we always derive from StripeEntity and add the interfaces implemented by each class.

I'd also rather avoid adding more interfaces / classes for other common fields (like metadata and deleted) unless we actually need them in the infrastructure part of the code.

@remi-stripe
Copy link
Contributor

Yeah, tbh I'm already not thrilled about all the StripeEntityWith... superclasses. I'd rather we always derive from StripeEntity and add the interfaces implemented by each class.

Should we do StripeEntity and StripeEntityWithId and revert the rest? Do we need an interface for object/metadata/deleted? Or should we just rely on us catching it during PRs? All tests access Object already anyway to verify deserialization so it would be hard to miss

@ob-stripe
Copy link
Contributor Author

Should we do StripeEntity and StripeEntityWithId and revert the rest?

To be clear, I was suggesting going one step further and get rid of StripeEntityWithId as well. All model classes would derive from StripeEntity and those that have an ID would implement IHasId and explicitly declare the Id property.

Do we need an interface for object/metadata/deleted?

Right now we have no need for the latter two. Having an IHasObject will help me with a clean implementation of #1313: a generic converter that needs to look at the object property to choose which class to instantiate could use such an interface.

@remi-stripe
Copy link
Contributor

I do like the distinction between StripeEntity and StripeEntityWithId. I think it's worth having it because it makes it clear what should have an Id so I guess we're mostly back to the old state right?

@ob-stripe
Copy link
Contributor Author

I'd argue that if the goal is to make it clear what resources should have an ID, then using the IHasId interface directly would be a better fit since telling you what should go into a class is exactly what interfaces are for :)

@remi-stripe
Copy link
Contributor

But IHasId requires adding the Id property everywhere and it's just repetitive. But yeah sorry I'm running out of opinion on that one to be honest :(
I don't really feel strongly so I trust you to do the right choice (easy to say I know!)

@ob-stripe
Copy link
Contributor Author

When it comes to model classes, I think explicitness is more valuable than DRY. Having each model class directly define all of its properties makes it easier to compare the model to a reference (e.g. the API ref or the OpenAPI spec). I also think it's cleaner to have both IHasId and IHasObject interfaces (and future IHasFoo interfaces, if we ever need them) be added to model classes in the same way.

ptal @remi-stripe

Copy link
Contributor

@remi-stripe remi-stripe left a comment

Choose a reason for hiding this comment

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

not sure we use IHasId properly

@@ -2,8 +2,11 @@ namespace Stripe
{
using Newtonsoft.Json;

public class EventRequest : StripeEntityWithId
public class EventRequest : StripeEntity, IHasId
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think IHasId makes sense here. It's not a Stripe resource, it has the field id because it's the name of the field for the request. I don't think it makes sense to use IHasId in that case

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 Agreed.

@@ -2,8 +2,11 @@ namespace Stripe
{
using Newtonsoft.Json;

public class EphemeralKeyAssociatedObject : StripeEntityWithId
public class EphemeralKeyAssociatedObject : StripeEntity, IHasId
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think IHasId makes sense here. It's not a Stripe resource, it has the field id because it's the name of the field for the associated object. I don't think it makes sense to use IHasId in that case

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 Agreed.

{
[JsonProperty("id")]
public string Id { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

OOC does this really have an Id field? It's a fake union right? Is it because of the expansion logic where we set the field Id?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm planning on removing all these union classes in #1313, so this is kind of moot.

Copy link
Contributor

Choose a reason for hiding this comment

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

no you're moot

{
[JsonProperty("id")]
public string Id { get; set; }

Copy link
Contributor

Choose a reason for hiding this comment

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

Same as PaymentSource

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same, this will go away in #1313.

{
[JsonProperty("id")]
public string Id { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as the other ones

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 Agreed.

@ob-stripe ob-stripe force-pushed the ob-interfaces-interfaces-everywhere branch from ccdf2f2 to 3762630 Compare October 4, 2018 14:46
@ob-stripe
Copy link
Contributor Author

ptal @remi-stripe

@ob-stripe ob-stripe force-pushed the ob-interfaces-interfaces-everywhere branch from 317bb20 to 6a5df72 Compare October 4, 2018 15:09
@ob-stripe ob-stripe merged commit e191637 into integration-next-major-version Oct 4, 2018
@ob-stripe ob-stripe deleted the ob-interfaces-interfaces-everywhere branch October 4, 2018 15:29
@ob-stripe ob-stripe mentioned this pull request Oct 4, 2018
32 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants