Skip to content

Commit

Permalink
AnyOf<> generic class to handle polymorphic parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
ob-stripe committed Feb 14, 2019
1 parent 4a03db9 commit ab3a4aa
Show file tree
Hide file tree
Showing 34 changed files with 824 additions and 116 deletions.
4 changes: 4 additions & 0 deletions src/Stripe.net/Infrastructure/FormEncoding/FormEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ private static List<KeyValuePair<string, object>> FlattenParamsValue(object valu
flatParams = SingleParam(keyPrefix, string.Empty);
break;

case IAnyOf anyOf:
flatParams = FlattenParamsValue(anyOf.GetValue(), keyPrefix);
break;

case INestedOptions options:
flatParams = FlattenParamsOptions(options, keyPrefix);
break;
Expand Down
106 changes: 106 additions & 0 deletions src/Stripe.net/Infrastructure/JsonConverters/AnyOfConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
namespace Stripe.Infrastructure
{
using System;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

/// <summary>
/// Converts a <see cref="IAnyOf"/> to and from JSON.
/// </summary>
public class AnyOfConverter : JsonConverter
{
/// <summary>
/// Gets a value indicating whether this <see cref="JsonConverter"/> can write JSON.
/// </summary>
/// <value>
/// <c>true</c> if this <see cref="JsonConverter"/> can write JSON; otherwise, <c>false</c>.
/// </value>
public override bool CanWrite => true;

/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
switch (value)
{
case null:
serializer.Serialize(writer, null);
break;

case IAnyOf anyOf:
serializer.Serialize(writer, anyOf.GetValue());
break;

default:
throw new JsonSerializationException(string.Format(
"Unexpected value when converting AnyOf. Expected IAnyOf, got {0}.",
value.GetType()));
}
}

/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing value of object being read.</param>
/// <param name="serializer">The calling serializer.</param>
/// <returns>The object value.</returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}

object o = null;

// Try to deserialize with each possible type
var jToken = JToken.Load(reader);
foreach (var type in objectType.GenericTypeArguments)
{
try
{
using (var subReader = jToken.CreateReader())
{
o = serializer.Deserialize(subReader, type);
}

// If deserialization succeeds, stop
break;
}
catch (JsonException)
{
// Do nothing, just try the next type
}
}

if (o == null)
{
throw new JsonSerializationException(string.Format(
"Cannot deserialize the current JSON object into any of the expected types ({0}).",
string.Join(", ", objectType.GenericTypeArguments.Select(t => t.FullName))));
}

return Activator.CreateInstance(objectType, o);
}

/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return typeof(IAnyOf).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
}
}
23 changes: 15 additions & 8 deletions src/Stripe.net/Services/Account/AccountSharedOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
namespace Stripe
{
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Stripe.Infrastructure;
Expand Down Expand Up @@ -34,14 +33,22 @@ public abstract class AccountSharedOptions : BaseOptions
[JsonProperty("email")]
public string Email { get; set; }

/// <summary>
/// <para>
/// A card or bank account to attach to the account. You can provide either a token, like
/// the ones returned by <a href="https://stripe.com/docs/stripe.js">Stripe.js</a>, or a
/// <see cref="AccountBankAccountOptions"/> or <see cref="AccountCardOptions"/> instance.
/// </para>
/// <para>
/// By default, providing an external account sets it as the new default external account
/// for its currency, and deletes the old default if one exists. To add additional external
/// accounts without replacing the existing default for the currency, use the bank account
/// or card creation API.
/// </para>
/// </summary>
[JsonProperty("external_account")]
public string ExternalAccountId { get; set; }

[JsonProperty("external_account")]
public AccountCardOptions ExternalCardAccount { get; set; }

[JsonProperty("external_account")]
public AccountBankAccountOptions ExternalBankAccount { get; set; }
[JsonConverter(typeof(AnyOfConverter))]
public AnyOf<string, AccountBankAccountOptions, AccountCardOptions> ExternalAccount { get; set; }

[JsonProperty("legal_entity")]
public AccountLegalEntityOptions LegalEntity { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ namespace Stripe
{
using System;
using Newtonsoft.Json;
using Stripe.Infrastructure;

public class BalanceTransactionListOptions : ListOptionsWithCreated
{
/// <summary>
/// A filter on the list based on the object available_on field. The value can be a
/// <see cref="DateTime"/> or a <see cref="DateRangeOptions"/>.
/// </summary>
[JsonProperty("available_on")]
public DateTime? AvailableOn { get; set; }

[JsonProperty("available_on")]
public DateRangeOptions AvailableOnRange { get; set; }
[JsonConverter(typeof(AnyOfConverter))]
public AnyOf<DateTime?, DateRangeOptions> AvailableOn { get; set; }

[JsonProperty("currency")]
public string Currency { get; set; }
Expand Down
13 changes: 9 additions & 4 deletions src/Stripe.net/Services/BankAccounts/BankAccountCreateOptions.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
namespace Stripe
{
using Newtonsoft.Json;
using Stripe.Infrastructure;

public class BankAccountCreateOptions : BaseOptions
{
/// <summary>
/// REQUIRED. Either a token, like the ones returned by
/// <a href="https://stripe.com/docs/stripe.js">Stripe.js</a>, or a
/// <see cref="SourceBankAccount"/> instance containing a user’s bank account
/// details.
/// </summary>
[JsonProperty("source")]
public string SourceToken { get; set; }

[JsonProperty("source")]
public SourceBankAccount SourceBankAccount { get; set; }
[JsonConverter(typeof(AnyOfConverter))]
public AnyOf<string, SourceBankAccount> Source { get; set; }
}
}
14 changes: 9 additions & 5 deletions src/Stripe.net/Services/Cards/CardCreateOptions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
namespace Stripe
{
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Stripe.Infrastructure;

public class CardCreateOptions : BaseOptions
{
Expand All @@ -12,11 +12,15 @@ public class CardCreateOptions : BaseOptions
[JsonProperty("metadata")]
public Dictionary<string, string> Metadata { get; set; }

/// <summary>
/// REQUIRED. Either a token, like the ones returned by
/// <a href="https://stripe.com/docs/stripe.js">Stripe.js</a>, or a
/// <see cref="CardCreateNestedOptions"/> instance containing a user’s card
/// details.
/// </summary>
[JsonProperty("source")]
public string SourceToken { get; set; }

[JsonProperty("source")]
public CardCreateNestedOptions SourceCard { get; set; }
[JsonConverter(typeof(AnyOfConverter))]
public AnyOf<string, CardCreateNestedOptions> Source { get; set; }

[JsonProperty("validate")]
public bool? Validate { get; set; }
Expand Down
13 changes: 9 additions & 4 deletions src/Stripe.net/Services/Charges/ChargeCreateOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace Stripe
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Stripe.Infrastructure;

public class ChargeCreateOptions : BaseOptions
{
Expand Down Expand Up @@ -92,11 +93,15 @@ public class ChargeCreateOptions : BaseOptions
[JsonProperty("customer")]
public string CustomerId { get; set; }

/// <summary>
/// A payment source to be charged. This can be the ID of a card (i.e., credit or debit
/// card), a bank account, a source, a token, or a connected account. For certain
/// sources—namely, cards, bank accounts, and attached sources—you must also pass the ID of
/// the associated customer.
/// </summary>
[JsonProperty("source")]
public string SourceId { get; set; }

[JsonProperty("source")]
public CardCreateNestedOptions SourceCard { get; set; }
[JsonConverter(typeof(AnyOfConverter))]
public AnyOf<string, CardCreateNestedOptions> Source { get; set; }

/// <summary>
/// An arbitrary string to be displayed on your customer's credit card statement. This may
Expand Down
20 changes: 16 additions & 4 deletions src/Stripe.net/Services/Customers/CustomerCreateOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,23 @@ public class CustomerCreateOptions : BaseOptions
[JsonProperty("shipping")]
public ShippingOptions Shipping { get; set; }

/// <summary>
/// The source can either be a Token or a Source, as returned by
/// <a href="https://stripe.com/docs/elements">Elements</a>, or a
/// <see cref="CardCreateNestedOptions"/> containing a user’s credit card details. You must
/// provide a source if the customer does not already have a valid source attached, and you
/// are subscribing the customer to be charged automatically for a plan that is not free.
/// Passing <c>source</c> will create a new source object, make it the customer default
/// source, and delete the old customer default if one exists. If you want to add an
/// additional source, instead use
/// <see cref="CardService.Create(string, CardCreateOptions, RequestOptions)"/> to add the
/// card and then <see cref="CustomerService.Update(string, CustomerUpdateOptions, RequestOptions)"/>
/// to set it as the default. Whenever you attach a card to a customer, Stripe will
/// automatically validate the card.
/// </summary>
[JsonProperty("source")]
public string SourceToken { get; set; }

[JsonProperty("source")]
public CardCreateNestedOptions SourceCard { get; set; }
[JsonConverter(typeof(AnyOfConverter))]
public AnyOf<string, CardCreateNestedOptions> Source { get; set; }

[JsonProperty("tax_info")]
public CustomerTaxInfoOptions TaxInfo { get; set; }
Expand Down
17 changes: 12 additions & 5 deletions src/Stripe.net/Services/Customers/CustomerUpdateOptions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
namespace Stripe
{
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Stripe.Infrastructure;

public class CustomerUpdateOptions : BaseOptions
{
Expand Down Expand Up @@ -33,11 +33,18 @@ public class CustomerUpdateOptions : BaseOptions
[JsonProperty("shipping")]
public ShippingOptions Shipping { get; set; }

/// <summary>
/// A Token’s or a Source’s ID, as returned by
/// <a href="https://stripe.com/docs/elements">Elements</a>. Passing <c>source</c> will
/// create a new source object, make it the new customer default source, and delete the old\
/// customer default if one exists. If you want to add additional sources instead of
/// replacing the existing default, use
/// <see cref="CardService.Create(string, CardCreateOptions, RequestOptions)"/>. Whenever
/// you attach a card to a customer, Stripe will automatically validate the card.
/// </summary>
[JsonProperty("source")]
public string SourceToken { get; set; }

[JsonProperty("source")]
public CardCreateNestedOptions SourceCard { get; set; }
[JsonConverter(typeof(AnyOfConverter))]
public AnyOf<string, CardCreateNestedOptions> Source { get; set; }

[JsonProperty("tax_info")]
public CustomerTaxInfoOptions TaxInfo { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,33 @@ namespace Stripe
{
using System.Collections.Generic;
using Newtonsoft.Json;
using Stripe.Infrastructure;

public class ExternalAccountCreateOptions : BaseOptions
{
/// <summary>
/// REQUIRED. Either a token, like the ones returned by
/// <a href="https://stripe.com/docs/stripe.js">Stripe.js</a>, or a
/// <see cref="AccountBankAccountOptions"/> instance containing a user’s bank account
/// details.
/// </summary>
[JsonProperty("external_account")]
public string ExternalAccountTokenId { get; set; }

[JsonProperty("external_account")]
public AccountBankAccountOptions ExternalAccountBankAccount { get; set; }

[JsonProperty("metadata")]
public Dictionary<string, string> Metadata { get; set; }
[JsonConverter(typeof(AnyOfConverter))]
public AnyOf<string, AccountBankAccountOptions> ExternalAccount { get; set; }

/// <summary>
/// When set to <c>true</c>, or if this is the first external account added in this
/// currency, this account becomes the default external account for its currency.
/// </summary>
[JsonProperty("default_for_currency")]
public bool? DefaultForCurrency { get; set; }
}

/// <summary>
/// A set of key-value pairs that you can attach to an external account object. It can be
/// useful for storing additional information about the external account in a structured
/// format.
/// </summary>
[JsonProperty("metadata")]
public Dictionary<string, string> Metadata { get; set; }
}
}
Loading

0 comments on commit ab3a4aa

Please sign in to comment.