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

Add metadata accessors #808

Merged
merged 2 commits into from
Oct 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 11.x.x 2017-XX-XX

* Restores `[STPCard brandFromString:]` method which was marked as deprecated in a recent version [#801](https://github.com/stripe/stripe-ios/pull/801)
* Adds `[STPBankAccount metadata]` and `[STPCard metadata]` read-only accessors and improves annotation for `[STPSource metadata]` [#808](https://github.com/stripe/stripe-ios/pull/808)

## 11.3.0 2017-09-13
* Adds support for creating `STPSourceParams` for P24 source [#779](https://github.com/stripe/stripe-ios/pull/779)
Expand Down
2 changes: 2 additions & 0 deletions Stripe/NSDictionary+Stripe.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ NS_ASSUME_NONNULL_BEGIN

- (NSDictionary *)stp_dictionaryByRemovingNulls;

- (NSDictionary<NSString *, NSString *> *)stp_dictionaryByRemovingNonStrings;
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 we should do this. We don't currently enforce this type for other classes like STPSource and I don't think we should change that or add the restriction here. It is not clear to me from the API docs that they must in fact be string pairs (as opposed to having number, dictionary, or array values). I think we should just leave as NSDictionary * or at most NSDictionary<NSString *, id> *

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I'm actually for this. It is an API constraint, and though the docs aren't super clear we do say:

Note: You can specify up to 20 keys, with key names up to 40 characters long and values up to 500 characters long.

[0] https://stripe.com/docs/api#metadata

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh interesting. I didn't realize we specified that. In that case I'm ok with changing our API to match this constraint 👍


@end

NS_ASSUME_NONNULL_END
Expand Down
18 changes: 18 additions & 0 deletions Stripe/NSDictionary+Stripe.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#import "NSArray+Stripe.h"

NS_ASSUME_NONNULL_BEGIN

@implementation NSDictionary (Stripe)

- (nullable NSDictionary *)stp_dictionaryByRemovingNullsValidatingRequiredFields:(NSArray *)requiredFields {
Expand Down Expand Up @@ -50,6 +52,22 @@ - (NSDictionary *)stp_dictionaryByRemovingNulls {
return [result copy];
}

- (NSDictionary<NSString *, NSString *> *)stp_dictionaryByRemovingNonStrings {
NSMutableDictionary<NSString *, NSString *> *result = [[NSMutableDictionary alloc] init];

[self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, __unused BOOL *stop) {
if ([key isKindOfClass:[NSString class]] && [obj isKindOfClass:[NSString class]]) {
// Save valid key/value pair
result[key] = obj;
}
}];

// Make immutable copy
return [result copy];
}

@end

void linkNSDictionaryCategory(void){}

NS_ASSUME_NONNULL_END
7 changes: 7 additions & 0 deletions Stripe/PublicHeaders/STPBankAccount.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ typedef NS_ENUM(NSInteger, STPBankAccountStatus) {
*/
@property (nonatomic, nullable, readonly) NSString *fingerprint;

/**
A set of key/value pairs associated with the bank account object.

@see https://stripe.com/docs/api#metadata
*/
@property (nonatomic, copy, nullable, readonly) NSDictionary<NSString *, NSString *> *metadata;

/**
The validation status of the bank account. @see STPBankAccountStatus
*/
Expand Down
7 changes: 7 additions & 0 deletions Stripe/PublicHeaders/STPCard.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ typedef NS_ENUM(NSInteger, STPCardFundingType) {
*/
@property (nonatomic, nullable, readonly) NSString *currency;

/**
A set of key/value pairs associated with the card object.

@see https://stripe.com/docs/api#metadata
*/
@property (nonatomic, copy, nullable, readonly) NSDictionary<NSString *, NSString *> *metadata;

/**
Returns a string representation for the provided card brand;
i.e. `[NSString stringFromBrand:STPCardBrandVisa] == @"Visa"`.
Expand Down
4 changes: 3 additions & 1 deletion Stripe/PublicHeaders/STPSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ NS_ASSUME_NONNULL_BEGIN

/**
A set of key/value pairs associated with the source object.

@see https://stripe.com/docs/api#metadata
*/
@property (nonatomic, nullable, readonly) NSDictionary *metadata;
@property (nonatomic, copy, nullable, readonly) NSDictionary<NSString *, NSString *> *metadata;

/**
Information about the owner of the payment instrument.
Expand Down
3 changes: 3 additions & 0 deletions Stripe/STPBankAccount.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ @interface STPBankAccount ()
@property (nonatomic, copy, readwrite) NSString *bankName;
@property (nonatomic, copy, nullable, readwrite) NSString *accountHolderName;
@property (nonatomic, assign, readwrite) STPBankAccountHolderType accountHolderType;
@property (nonatomic, copy, nullable, readwrite) NSDictionary<NSString *, NSString *> *metadata;
@property (nonatomic, copy, nullable, readwrite) NSString *fingerprint;
@property (nonatomic, assign, readwrite) STPBankAccountStatus status;
@property (nonatomic, copy, readwrite) NSDictionary *allResponseFields;
Expand Down Expand Up @@ -101,6 +102,7 @@ - (NSString *)description {
[NSString stringWithFormat:@"country = %@", self.country],
[NSString stringWithFormat:@"currency = %@", self.currency],
[NSString stringWithFormat:@"fingerprint = %@", self.fingerprint],
[NSString stringWithFormat:@"metadata = %@", (self.metadata) ? @"<redacted>" : nil],
[NSString stringWithFormat:@"status = %@", [self.class stringFromStatus:self.status]],

// Owner details
Expand Down Expand Up @@ -144,6 +146,7 @@ + (nullable instancetype)decodedObjectFromAPIResponse:(nullable NSDictionary *)r
bankAccount.country = dict[@"country"];
bankAccount.currency = dict[@"currency"];
bankAccount.fingerprint = dict[@"fingerprint"];
bankAccount.metadata = [dict[@"metadata"] stp_dictionaryByRemovingNonStrings];
bankAccount.status = [self statusFromString:dict[@"status"]];

// Owner details
Expand Down
3 changes: 3 additions & 0 deletions Stripe/STPCard.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ @interface STPCard ()
@property (nonatomic, assign, readwrite) NSUInteger expMonth;
@property (nonatomic, assign, readwrite) NSUInteger expYear;
@property (nonatomic, strong, readwrite) STPAddress *address;
@property (nonatomic, copy, nullable, readwrite) NSDictionary<NSString *, NSString *> *metadata;
@property (nonatomic, copy, readwrite) NSDictionary *allResponseFields;

// See STPCard+Private.h
Expand Down Expand Up @@ -152,6 +153,7 @@ - (NSString *)description {
[NSString stringWithFormat:@"currency = %@", self.currency],
[NSString stringWithFormat:@"dynamicLast4 = %@", self.dynamicLast4],
[NSString stringWithFormat:@"isApplePayCard = %@", (self.isApplePayCard) ? @"YES" : @"NO"],
[NSString stringWithFormat:@"metadata = %@", (self.metadata) ? @"<redacted>" : nil],

// Cardholder details
[NSString stringWithFormat:@"name = %@", (self.name) ? @"<redacted>" : nil],
Expand Down Expand Up @@ -196,6 +198,7 @@ + (nullable instancetype)decodedObjectFromAPIResponse:(nullable NSDictionary *)r
card.currency = dict[@"currency"];
card.expMonth = [dict[@"exp_month"] intValue];
card.expYear = [dict[@"exp_year"] intValue];
card.metadata = [dict[@"metadata"] stp_dictionaryByRemovingNonStrings];

card.address.name = card.name;
card.address.line1 = dict[@"address_line1"];
Expand Down
4 changes: 2 additions & 2 deletions Stripe/STPSource.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ @interface STPSource ()
@property (nonatomic, nullable) NSString *currency;
@property (nonatomic) STPSourceFlow flow;
@property (nonatomic) BOOL livemode;
@property (nonatomic, nullable) NSDictionary *metadata;
@property (nonatomic, copy, nullable, readwrite) NSDictionary<NSString *, NSString *> *metadata;
@property (nonatomic, nullable) STPSourceOwner *owner;
@property (nonatomic, nullable) STPSourceReceiver *receiver;
@property (nonatomic, nullable) STPSourceRedirect *redirect;
Expand Down Expand Up @@ -230,7 +230,7 @@ + (instancetype)decodedObjectFromAPIResponse:(NSDictionary *)response {
source.currency = dict[@"currency"];
source.flow = [[self class] flowFromString:dict[@"flow"]];
source.livemode = [dict[@"livemode"] boolValue];
source.metadata = dict[@"metadata"];
source.metadata = [dict[@"metadata"] stp_dictionaryByRemovingNonStrings];
source.owner = [STPSourceOwner decodedObjectFromAPIResponse:dict[@"owner"]];
source.receiver = [STPSourceReceiver decodedObjectFromAPIResponse:dict[@"receiver"]];
source.redirect = [STPSourceRedirect decodedObjectFromAPIResponse:dict[@"redirect"]];
Expand Down
4 changes: 3 additions & 1 deletion Tests/Tests/BankAccount.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"default_for_currency": false,
"fingerprint": "1JWtPxqbdX5Gamtc",
"last4": "6789",
"metadata": {},
"metadata": {
"order_id": "6735"
},
"routing_number": "110000000",
"status": "new"
}
4 changes: 3 additions & 1 deletion Tests/Tests/BitcoinSource.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"currency": "usd",
"flow": "receiver",
"livemode": false,
"metadata": {},
"metadata": {
"order_id": "6735"
},
"owner": {
"address": {
"city": "Pittsburgh",
Expand Down
4 changes: 3 additions & 1 deletion Tests/Tests/Card.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
"fingerprint": "Xt5EWLLDS7FJjR1c",
"funding": "credit",
"last4": "4242",
"metadata": {},
"metadata": {
"order_id": "6735"
},
"name": "Jane Austen",
"tokenization_method": null
}
61 changes: 61 additions & 0 deletions Tests/Tests/NSDictionary+StripeTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ @interface NSDictionary_StripeTest : XCTestCase

@implementation NSDictionary_StripeTest

#pragma mark - dictionaryByRemovingNullsValidatingRequiredFields

- (void)test_dictionaryByRemovingNullsValidatingRequiredFields_removesNullsDeeply {
NSDictionary *dictionary = @{
@"id": @"card_123",
Expand Down Expand Up @@ -102,5 +104,64 @@ - (void)test_dictionaryByRemovingNullsValidatingRequiredFields_missingRequiredFi
XCTAssertNil([dictionary stp_dictionaryByRemovingNullsValidatingRequiredFields:requiredFields]);
}

#pragma mark - dictionaryByRemovingNonStrings

- (void)test_dictionaryByRemovingNonStrings_basicCases {
NSDictionary *dictionary;
NSDictionary *expected;
NSDictionary *result;

// Empty dictionary
dictionary = @{};
expected = @{};
result = [dictionary stp_dictionaryByRemovingNonStrings];
XCTAssertEqualObjects(result, expected);

// Regular case
dictionary = @{
@"user": @"user_123",
@"nicknames": @"John, Johnny",
};
expected = @{
@"user": @"user_123",
@"nicknames": @"John, Johnny",
};
result = [dictionary stp_dictionaryByRemovingNonStrings];
XCTAssertEqualObjects(result, expected);

// Strips non-NSString keys and values
dictionary = @{
@"user": @"user_123",
@"nicknames": @"John, Johnny",
@"profiles": [NSNull null],
[NSNull null]: @"San Francisco, CA",
[NSNull null]: [NSNull null],
@"age": @(21),
@(21): @"age",
@(21): @(21),
@"fees": @{
@"plan": @"monthly",
},
@"visits": @[
@"january",
@"february",
],
};
expected = @{
@"user": @"user_123",
@"nicknames": @"John, Johnny",
};
result = [dictionary stp_dictionaryByRemovingNonStrings];
XCTAssertEqualObjects(result, expected);
}

- (void)test_dictionaryByRemovingNonStrings_returnsImmutableCopy {
NSDictionary *dictionary = @{@"user": @"user_123"};
NSDictionary *result = [dictionary stp_dictionaryByRemovingNonStrings];

XCTAssert(result);
XCTAssertNotEqual(result, dictionary);
XCTAssertFalse([result isKindOfClass:[NSMutableDictionary class]]);
}

@end
1 change: 1 addition & 0 deletions Tests/Tests/STPBankAccountTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ - (void)testDecodedObjectFromAPIResponseMapping {
XCTAssertEqualObjects(bankAccount.currency, @"usd");
XCTAssertEqualObjects(bankAccount.fingerprint, @"1JWtPxqbdX5Gamtc");
XCTAssertEqualObjects(bankAccount.last4, @"6789");
XCTAssertEqualObjects(bankAccount.metadata, @{@"order_id": @"6735"});
XCTAssertEqualObjects(bankAccount.routingNumber, @"110000000");
XCTAssertEqual(bankAccount.status, STPBankAccountStatusNew);

Expand Down
1 change: 1 addition & 0 deletions Tests/Tests/STPCardTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ - (void)testDecodedObjectFromAPIResponseMapping {
XCTAssertEqual(card.expYear, (NSUInteger)2017);
XCTAssertEqual(card.funding, STPCardFundingTypeCredit);
XCTAssertEqualObjects(card.last4, @"4242");
XCTAssertEqualObjects(card.metadata, @{@"order_id": @"6735"});
XCTAssertEqualObjects(card.name, @"Jane Austen");

XCTAssertNotEqual(card.allResponseFields, response);
Expand Down
1 change: 1 addition & 0 deletions Tests/Tests/STPSourceTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ - (void)testDecodedObjectFromAPIResponseMapping {
XCTAssertEqualObjects(source.currency, @"usd");
XCTAssertEqual(source.flow, STPSourceFlowReceiver);
XCTAssertFalse(source.livemode);
XCTAssertEqualObjects(source.metadata, @{@"order_id": @"6735"});
XCTAssert(source.owner); // STPSourceOwnerTest
XCTAssert(source.receiver); // STPSourceReceiverTest
XCTAssertEqual(source.status, STPSourceStatusPending);
Expand Down