Skip to content

Commit

Permalink
ref: CurrentDate as dependency for rate limits (#3837)
Browse files Browse the repository at this point in the history
Replace access to the DependencyContainer in the RateLimits code by
passing the current date as a dependency to reduce global state and the
need to call clearTestState.
  • Loading branch information
philipphofmann authored Apr 8, 2024
1 parent b35ccd0 commit 9ce54cc
Show file tree
Hide file tree
Showing 18 changed files with 110 additions and 76 deletions.
6 changes: 4 additions & 2 deletions Sources/Sentry/SentryClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ - (instancetype)initWithOptions:(SentryOptions *)options
fileManager:(SentryFileManager *)fileManager
deleteOldEnvelopeItems:(BOOL)deleteOldEnvelopeItems
{
NSArray<id<SentryTransport>> *transports = [SentryTransportFactory initTransports:options
sentryFileManager:fileManager];
NSArray<id<SentryTransport>> *transports = [SentryTransportFactory
initTransports:options
sentryFileManager:fileManager
currentDateProvider:SentryDependencyContainer.sharedInstance.dateProvider];

SentryTransportAdapter *transportAdapter =
[[SentryTransportAdapter alloc] initWithTransports:transports options:options];
Expand Down
16 changes: 12 additions & 4 deletions Sources/Sentry/SentryDateUtil.m
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
#import "SentryDateUtil.h"
#import "SentryDependencyContainer.h"
#import "SentrySwift.h"

NS_ASSUME_NONNULL_BEGIN

@interface
SentryDateUtil ()

@property (nonatomic, strong) SentryCurrentDateProvider *currentDateProvider;

@end

@implementation SentryDateUtil

+ (BOOL)isInFuture:(NSDate *_Nullable)date
- (instancetype)initWithCurrentDateProvider:(SentryCurrentDateProvider *)currentDateProvider
{
if (self = [super init]) {
self.currentDateProvider = currentDateProvider;
}
return self;
}

- (BOOL)isInFuture:(NSDate *_Nullable)date
{
if (date == nil)
return NO;

NSComparisonResult result =
[[SentryDependencyContainer.sharedInstance.dateProvider date] compare:date];
NSComparisonResult result = [[self.currentDateProvider date] compare:date];
return result == NSOrderedAscending;
}

Expand Down
13 changes: 8 additions & 5 deletions Sources/Sentry/SentryDefaultRateLimits.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#import "SentryConcurrentRateLimitsDictionary.h"
#import "SentryDataCategoryMapper.h"
#import "SentryDateUtil.h"
#import "SentryDependencyContainer.h"
#import "SentryLog.h"
#import "SentryRateLimitParser.h"
#import "SentryRetryAfterHeaderParser.h"
Expand All @@ -17,6 +16,8 @@
@property (nonatomic, strong) SentryConcurrentRateLimitsDictionary *rateLimits;
@property (nonatomic, strong) SentryRetryAfterHeaderParser *retryAfterHeaderParser;
@property (nonatomic, strong) SentryRateLimitParser *rateLimitParser;
@property (nonatomic, strong) SentryCurrentDateProvider *currentDateProvider;
@property (nonatomic, strong) SentryDateUtil *dateUtil;

@end

Expand All @@ -25,11 +26,14 @@ @implementation SentryDefaultRateLimits
- (instancetype)initWithRetryAfterHeaderParser:
(SentryRetryAfterHeaderParser *)retryAfterHeaderParser
andRateLimitParser:(SentryRateLimitParser *)rateLimitParser
currentDateProvider:(SentryCurrentDateProvider *)currentDateProvider
{
if (self = [super init]) {
self.rateLimits = [[SentryConcurrentRateLimitsDictionary alloc] init];
self.retryAfterHeaderParser = retryAfterHeaderParser;
self.rateLimitParser = rateLimitParser;
self.currentDateProvider = currentDateProvider;
self.dateUtil = [[SentryDateUtil alloc] initWithCurrentDateProvider:currentDateProvider];
}
return self;
}
Expand All @@ -39,8 +43,8 @@ - (BOOL)isRateLimitActive:(SentryDataCategory)category
NSDate *categoryDate = [self.rateLimits getRateLimitForCategory:category];
NSDate *allCategoriesDate = [self.rateLimits getRateLimitForCategory:kSentryDataCategoryAll];

BOOL isActiveForCategory = [SentryDateUtil isInFuture:categoryDate];
BOOL isActiveForCategories = [SentryDateUtil isInFuture:allCategoriesDate];
BOOL isActiveForCategory = [self.dateUtil isInFuture:categoryDate];
BOOL isActiveForCategories = [self.dateUtil isInFuture:allCategoriesDate];

if (isActiveForCategory || isActiveForCategories) {
return YES;
Expand All @@ -67,8 +71,7 @@ - (void)update:(NSHTTPURLResponse *)response

if (nil == retryAfterHeaderDate) {
// parsing failed use default value
retryAfterHeaderDate = [[SentryDependencyContainer.sharedInstance.dateProvider date]
dateByAddingTimeInterval:60];
retryAfterHeaderDate = [self.currentDateProvider.date dateByAddingTimeInterval:60];
}

[self updateRateLimit:kSentryDataCategoryAll withDate:retryAfterHeaderDate];
Expand Down
13 changes: 11 additions & 2 deletions Sources/Sentry/SentryRateLimitParser.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#import "SentryRateLimitParser.h"
#import "SentryDataCategoryMapper.h"
#import "SentryDateUtil.h"
#import "SentryDependencyContainer.h"
#import "SentrySwift.h"
#import <Foundation/Foundation.h>

Expand All @@ -10,10 +9,20 @@
@interface
SentryRateLimitParser ()

@property (nonatomic, strong) SentryCurrentDateProvider *currentDateProvider;

@end

@implementation SentryRateLimitParser

- (instancetype)initWithCurrentDateProvider:(SentryCurrentDateProvider *)currentDateProvider
{
if (self = [super init]) {
self.currentDateProvider = currentDateProvider;
}
return self;
}

- (NSDictionary<NSNumber *, NSDate *> *)parse:(NSString *)header
{
if ([header length] == 0) {
Expand Down Expand Up @@ -80,7 +89,7 @@ - (NSNumber *)parseRateLimitSeconds:(NSString *)string
- (NSDate *)getLongerRateLimit:(NSDate *)existingRateLimit
andRateLimitInSeconds:(NSNumber *)newRateLimitInSeconds
{
NSDate *newDate = [SentryDependencyContainer.sharedInstance.dateProvider.date
NSDate *newDate = [self.currentDateProvider.date
dateByAddingTimeInterval:[newRateLimitInSeconds doubleValue]];
return [SentryDateUtil getMaximumDate:newDate andOther:existingRateLimit];
}
Expand Down
7 changes: 4 additions & 3 deletions Sources/Sentry/SentryRetryAfterHeaderParser.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#import "SentryRetryAfterHeaderParser.h"
#import "SentryDependencyContainer.h"
#import "SentryHttpDateParser.h"
#import "SentrySwift.h"
#import <Foundation/Foundation.h>
Expand All @@ -10,15 +9,18 @@
SentryRetryAfterHeaderParser ()

@property (nonatomic, strong) SentryHttpDateParser *httpDateParser;
@property (nonatomic, strong) SentryCurrentDateProvider *currentDateProvider;

@end

@implementation SentryRetryAfterHeaderParser

- (instancetype)initWithHttpDateParser:(SentryHttpDateParser *)httpDateParser
currentDateProvider:(SentryCurrentDateProvider *)currentDateProvider
{
if (self = [super init]) {
self.httpDateParser = httpDateParser;
self.currentDateProvider = currentDateProvider;
}
return self;
}
Expand All @@ -31,8 +33,7 @@ - (NSDate *_Nullable)parse:(NSString *_Nullable)retryAfterHeader

NSInteger retryAfterSeconds = [retryAfterHeader integerValue];
if (0 != retryAfterSeconds) {
return [[SentryDependencyContainer.sharedInstance.dateProvider date]
dateByAddingTimeInterval:retryAfterSeconds];
return [self.currentDateProvider.date dateByAddingTimeInterval:retryAfterSeconds];
}

// parsing as double/seconds failed, try to parse as date
Expand Down
10 changes: 7 additions & 3 deletions Sources/Sentry/SentryTransportFactory.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ @implementation SentryTransportFactory

+ (NSArray<id<SentryTransport>> *)initTransports:(SentryOptions *)options
sentryFileManager:(SentryFileManager *)sentryFileManager
currentDateProvider:(SentryCurrentDateProvider *)currentDateProvider
{
NSURLSessionConfiguration *configuration =
[NSURLSessionConfiguration ephemeralSessionConfiguration];
Expand All @@ -37,11 +38,14 @@ @implementation SentryTransportFactory

SentryHttpDateParser *httpDateParser = [[SentryHttpDateParser alloc] init];
SentryRetryAfterHeaderParser *retryAfterHeaderParser =
[[SentryRetryAfterHeaderParser alloc] initWithHttpDateParser:httpDateParser];
SentryRateLimitParser *rateLimitParser = [[SentryRateLimitParser alloc] init];
[[SentryRetryAfterHeaderParser alloc] initWithHttpDateParser:httpDateParser
currentDateProvider:currentDateProvider];
SentryRateLimitParser *rateLimitParser =
[[SentryRateLimitParser alloc] initWithCurrentDateProvider:currentDateProvider];
id<SentryRateLimits> rateLimits =
[[SentryDefaultRateLimits alloc] initWithRetryAfterHeaderParser:retryAfterHeaderParser
andRateLimitParser:rateLimitParser];
andRateLimitParser:rateLimitParser
currentDateProvider:currentDateProvider];

SentryEnvelopeRateLimit *envelopeRateLimit =
[[SentryEnvelopeRateLimit alloc] initWithRateLimits:rateLimits];
Expand Down
10 changes: 7 additions & 3 deletions Sources/Sentry/include/SentryDateUtil.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#import <Foundation/Foundation.h>
#import "SentryDefines.h"

NS_ASSUME_NONNULL_BEGIN

NS_SWIFT_NAME(DateUtil)
@class SentryCurrentDateProvider;

@interface SentryDateUtil : NSObject
SENTRY_NO_INIT

- (instancetype)initWithCurrentDateProvider:(SentryCurrentDateProvider *)currentDateProvider;

+ (BOOL)isInFuture:(NSDate *_Nullable)date;
- (BOOL)isInFuture:(NSDate *_Nullable)date;

+ (NSDate *_Nullable)getMaximumDate:(NSDate *_Nullable)first andOther:(NSDate *_Nullable)second;

Expand Down
4 changes: 3 additions & 1 deletion Sources/Sentry/include/SentryDefaultRateLimits.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

@class SentryRetryAfterHeaderParser;
@class SentryRateLimitParser;
@class SentryCurrentDateProvider;

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -17,7 +18,8 @@ NS_SWIFT_NAME(DefaultRateLimits)

- (instancetype)initWithRetryAfterHeaderParser:
(SentryRetryAfterHeaderParser *)retryAfterHeaderParser
andRateLimitParser:(SentryRateLimitParser *)rateLimitParser;
andRateLimitParser:(SentryRateLimitParser *)rateLimitParser
currentDateProvider:(SentryCurrentDateProvider *)currentDateProvider;

@end

Expand Down
7 changes: 6 additions & 1 deletion Sources/Sentry/include/SentryRateLimitParser.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#import <Foundation/Foundation.h>
#import "SentryDefines.h"

@class SentryCurrentDateProvider;

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -12,6 +14,9 @@ NS_ASSUME_NONNULL_BEGIN
*/
NS_SWIFT_NAME(RateLimitParser)
@interface SentryRateLimitParser : NSObject
SENTRY_NO_INIT

- (instancetype)initWithCurrentDateProvider:(SentryCurrentDateProvider *)currentDateProvider;

- (NSDictionary<NSNumber *, NSDate *> *)parse:(NSString *)header;

Expand Down
4 changes: 3 additions & 1 deletion Sources/Sentry/include/SentryRetryAfterHeaderParser.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import <Foundation/Foundation.h>

@class SentryHttpDateParser;
@class SentryCurrentDateProvider;

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -11,7 +12,8 @@ NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(RetryAfterHeaderParser)
@interface SentryRetryAfterHeaderParser : NSObject

- (instancetype)initWithHttpDateParser:(SentryHttpDateParser *)httpDateParser;
- (instancetype)initWithHttpDateParser:(SentryHttpDateParser *)httpDateParser
currentDateProvider:(SentryCurrentDateProvider *)currentDateProvider;

/** Parses the HTTP header into a NSDate.
Expand Down
4 changes: 3 additions & 1 deletion Sources/Sentry/include/SentryTransportFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
#import "SentryTransport.h"

@class SentryOptions, SentryFileManager;
@class SentryCurrentDateProvider;

NS_ASSUME_NONNULL_BEGIN

NS_SWIFT_NAME(TransportInitializer)
@interface SentryTransportFactory : NSObject

+ (NSArray<id<SentryTransport>> *)initTransports:(SentryOptions *)options
sentryFileManager:(SentryFileManager *)sentryFileManager;
sentryFileManager:(SentryFileManager *)sentryFileManager
currentDateProvider:(SentryCurrentDateProvider *)currentDateProvider;

@end

Expand Down
33 changes: 17 additions & 16 deletions Tests/SentryTests/Helper/SentryDateUtilTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,51 @@ class SentryDateUtilTests: XCTestCase {
override func setUp() {
super.setUp()
currentDateProvider = TestCurrentDateProvider()
SentryDependencyContainer.sharedInstance().dateProvider = currentDateProvider
}

override func tearDown() {
super.tearDown()
clearTestState()
}

func testIsInFutureWithFutureDte() {
XCTAssertTrue(DateUtil.is(inFuture: currentDateProvider.date().addingTimeInterval(1)))
let sut = SentryDateUtil(currentDateProvider: currentDateProvider)
XCTAssertTrue(sut.is(inFuture: currentDateProvider.date().addingTimeInterval(1)))
}

func testIsInFutureWithPresentDate() {
XCTAssertFalse(DateUtil.is(inFuture: currentDateProvider.date()))
let sut = SentryDateUtil(currentDateProvider: currentDateProvider)

XCTAssertFalse(sut.is(inFuture: currentDateProvider.date()))
}

func testIsInFutureWithPastDate() {
XCTAssertFalse(DateUtil.is(inFuture: currentDateProvider.date().addingTimeInterval(-1)))
}
let sut = SentryDateUtil(currentDateProvider: currentDateProvider)

XCTAssertFalse(sut.is(inFuture: currentDateProvider.date().addingTimeInterval(-1)))
}

func testIsInFutureWithNil() {
XCTAssertFalse(DateUtil.is(inFuture: nil))
let sut = SentryDateUtil(currentDateProvider: currentDateProvider)

XCTAssertFalse(sut.is(inFuture: nil))
}

func testGetMaximumFirstMaximum() {
let maximum = currentDateProvider.date().addingTimeInterval(1)
let actual = DateUtil.getMaximumDate(maximum, andOther: currentDateProvider.date())
let actual = SentryDateUtil.getMaximumDate(maximum, andOther: currentDateProvider.date())

XCTAssertEqual(maximum, actual)
}

func testGetMaximumSecondMaximum() {
let maximum = currentDateProvider.date().addingTimeInterval(1)
let actual = DateUtil.getMaximumDate(currentDateProvider.date(), andOther: maximum)
let actual = SentryDateUtil.getMaximumDate(currentDateProvider.date(), andOther: maximum)

XCTAssertEqual(maximum, actual)
}

func testGetMaximumWithNil() {
let date = currentDateProvider.date()

XCTAssertEqual(date, DateUtil.getMaximumDate(nil, andOther: date))
XCTAssertEqual(date, DateUtil.getMaximumDate(date, andOther: nil))
XCTAssertNil(DateUtil.getMaximumDate(nil, andOther: nil))
XCTAssertEqual(date, SentryDateUtil.getMaximumDate(nil, andOther: date))
XCTAssertEqual(date, SentryDateUtil.getMaximumDate(date, andOther: nil))
XCTAssertNil(SentryDateUtil.getMaximumDate(nil, andOther: nil))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,8 @@ class SentryDefaultRateLimitsTests: XCTestCase {
override func setUp() {
super.setUp()
currentDateProvider = TestCurrentDateProvider()
SentryDependencyContainer.sharedInstance().dateProvider = currentDateProvider

sut = DefaultRateLimits(retryAfterHeaderParser: RetryAfterHeaderParser(httpDateParser: HttpDateParser()), andRateLimitParser: RateLimitParser())
}

override func tearDown() {
super.tearDown()
clearTestState()
sut = DefaultRateLimits(retryAfterHeaderParser: RetryAfterHeaderParser(httpDateParser: HttpDateParser(), currentDateProvider: currentDateProvider), andRateLimitParser: RateLimitParser(currentDateProvider: currentDateProvider), currentDateProvider: currentDateProvider)
}

func testNoUpdateCalled() {
Expand Down Expand Up @@ -98,7 +92,7 @@ class SentryDefaultRateLimitsTests: XCTestCase {
}

func testRetryAfterHeaderHttpDate() {
let headerValue = HttpDateFormatter.string(from: SentryDependencyContainer.sharedInstance().dateProvider.date().addingTimeInterval(1))
let headerValue = HttpDateFormatter.string(from: currentDateProvider.date().addingTimeInterval(1))
assertRetryHeaderWith1Second(value: headerValue)
}

Expand Down
Loading

0 comments on commit 9ce54cc

Please sign in to comment.