Skip to content

Commit

Permalink
feat(refresh-token): add retry mechanism in refreshTokenCompletionProxy
Browse files Browse the repository at this point in the history
SUITEDEV-34796

Co-authored-by: LasOri <24588073+LasOri@users.noreply.github.com>
Co-authored-by: matusekma <36794575+matusekma@users.noreply.github.com>
  • Loading branch information
3 people committed Dec 6, 2023
1 parent d01fd3b commit 9b4d83f
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 17 deletions.
6 changes: 5 additions & 1 deletion Sources/EmarsysSDK/Endpoints/EMSEndpoint.m
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,12 @@ - (BOOL)isInlineInAppUrl:(NSString *)url {
return [url hasPrefix:[self eventServiceUrl]] && [url hasSuffix:@"/inline-messages"];
}

- (BOOL)isRefreshContactTokenUrl:(NSURL *)url {
return [url.absoluteString hasSuffix:@"contact-token"];
}

- (NSString *)predictUrl {
return [self.predictUrlProvider provideValue];
}

@end
@end
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@
#import "EMSContactTokenResponseHandler.h"
#import "EMSEndpoint.h"
#import "EMSStorage.h"
#import "EMSStorageProtocol.h"

#define kEMSPushTokenKey @"EMSPushTokenKey"

@interface EMSMobileEngageRefreshTokenCompletionProxy()

@property(nonatomic, assign) NSInteger retryCount;
@property(nonatomic, strong) EMSResponseModel *originalResponseModel;

- (void)reset;

@end

@implementation EMSMobileEngageRefreshTokenCompletionProxy

- (instancetype)initWithCompletionProxy:(id <EMSRESTClientCompletionProxyProtocol>)completionProxy
Expand Down Expand Up @@ -40,21 +48,37 @@ - (instancetype)initWithCompletionProxy:(id <EMSRESTClientCompletionProxyProtoco
- (EMSRESTClientCompletionBlock)completionBlock {
__weak typeof(self) weakSelf = self;
return ^(EMSRequestModel *requestModel, EMSResponseModel *responseModel, NSError *error) {
if (responseModel.statusCode == 401 && [self.endpoint isMobileEngageUrl:requestModel.url.absoluteString]) {
if (weakSelf.retryCount >= 3 || (error && [weakSelf.endpoint isRefreshContactTokenUrl:requestModel.url])) {
EMSRequestModel *request = weakSelf.originalRequestModel;
EMSResponseModel *response = weakSelf.originalResponseModel;

[weakSelf reset];
[response setStatusCode:418];
weakSelf.completionProxy.completionBlock(request, response, error);
} else if (responseModel.statusCode == 401 && [weakSelf.endpoint isMobileEngageUrl:requestModel.url.absoluteString]) {
[weakSelf.storage setData:nil
forKey:kEMSPushTokenKey];
weakSelf.originalRequestModel = requestModel;
weakSelf.originalResponseModel = responseModel;
[weakSelf.restClient executeWithRequestModel:[weakSelf.requestFactory createRefreshTokenRequestModel]
coreCompletionProxy:weakSelf];
} else if (responseModel.isSuccess && weakSelf.originalRequestModel) {
} else if (responseModel.isSuccess && [weakSelf.endpoint isRefreshContactTokenUrl:requestModel.url]) {
[weakSelf.contactResponseHandler processResponse:responseModel];
[NSThread sleepForTimeInterval:0.5f];
weakSelf.retryCount += 1;
[weakSelf.restClient executeWithRequestModel:weakSelf.originalRequestModel
coreCompletionProxy:weakSelf];
weakSelf.originalRequestModel = nil;
} else {
[weakSelf reset];
weakSelf.completionProxy.completionBlock(requestModel, responseModel, error);
}
};
}

- (void)reset {
self.originalRequestModel = nil;
self.originalResponseModel = nil;
self.retryCount = 0;
}

@end
4 changes: 3 additions & 1 deletion Sources/Private/EMSEndpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

- (BOOL)isInlineInAppUrl:(NSString *)url;

- (BOOL)isRefreshContactTokenUrl:(NSURL *)url;

- (NSString *)predictUrl;

- (NSString *)deeplinkUrl;
Expand All @@ -54,4 +56,4 @@

- (void)reset;

@end
@end
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
#import "EMSStorage.h"
#import "EMSStorageProtocol.h"

@interface EMSMobileEngageRefreshTokenCompletionProxy()

@property(nonatomic, assign) NSInteger retryCount;
@property(nonatomic, strong) EMSResponseModel *originalResponseModel;

- (void)reset;

@end

@interface EMSMobileEngageRefreshTokenCompletionProxyTests : XCTestCase

@property(nonatomic, strong) EMSRESTClient *mockRestClient;
Expand Down Expand Up @@ -143,7 +152,7 @@ - (void)testInit_storage_mustNotBeNil {
}

- (void)testCompletionBlock_shouldDelegate_toCompletionProxy_whenOnSuccess {
EMSRequestModel *requestModel = [self generateRequestModel];
EMSRequestModel *requestModel = [self generateRequestModel: @""];
EMSResponseModel *responseModel = [self generateResponseWithStatusCode:200];

__block EMSRequestModel *returnedRequestModel;
Expand All @@ -167,8 +176,68 @@ - (void)testCompletionBlock_shouldDelegate_toCompletionProxy_whenOnSuccess {
XCTAssertEqualObjects(returnedError, self.error);
}

- (void)testCompletionBlock_should_call_completionBlock_and_reset_when_retry_count_overflows {
EMSRequestModel *requestModel = [self generateRequestModel: @""];
EMSResponseModel *responseModel = [self generateResponseWithStatusCode:200];
EMSMobileEngageRefreshTokenCompletionProxy *partialMockProxy = OCMPartialMock(self.refreshCompletionProxy);
partialMockProxy.retryCount = 4;
partialMockProxy.originalRequestModel = requestModel;
partialMockProxy.originalResponseModel = responseModel;

__block EMSRequestModel *returnedRequestModel;
__block EMSResponseModel *returnedResponseModel;
__block NSError *returnedError;
XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"waitForCompletionBlock"];
OCMStub([self.mockCompletionProxy completionBlock]).andReturn(^(EMSRequestModel *requestModel, EMSResponseModel *responseModel, NSError *error) {
returnedRequestModel = requestModel;
returnedResponseModel = responseModel;
returnedError = error;
[expectation fulfill];
});

self.refreshCompletionProxy.completionBlock(requestModel, responseModel, self.error);

XCTWaiterResult waiterResult = [XCTWaiter waitForExpectations:@[expectation]
timeout:10];
XCTAssertEqual(waiterResult, XCTWaiterResultCompleted);
XCTAssertEqualObjects(returnedRequestModel, requestModel);
XCTAssertEqual(returnedResponseModel.statusCode, 418);
XCTAssertEqualObjects(returnedError, self.error);
OCMVerify([partialMockProxy reset]);
}

- (void)testCompletionBlock_should_call_completionBlock_and_reset_when_error_happens_on_refresTokenRequest {
EMSRequestModel *requestModel = [self generateRequestModel: @"contact-token"];
EMSResponseModel *responseModel = [self generateResponseWithStatusCode:200];
EMSMobileEngageRefreshTokenCompletionProxy *partialMockProxy = OCMPartialMock(self.refreshCompletionProxy);

partialMockProxy.originalRequestModel = requestModel;
partialMockProxy.originalResponseModel = responseModel;

__block EMSRequestModel *returnedRequestModel;
__block EMSResponseModel *returnedResponseModel;
__block NSError *returnedError;
XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"waitForCompletionBlock"];
OCMStub([self.mockCompletionProxy completionBlock]).andReturn(^(EMSRequestModel *requestModel, EMSResponseModel *responseModel, NSError *error) {
returnedRequestModel = requestModel;
returnedResponseModel = responseModel;
returnedError = error;
[expectation fulfill];
});

self.refreshCompletionProxy.completionBlock(requestModel, responseModel, self.error);

XCTWaiterResult waiterResult = [XCTWaiter waitForExpectations:@[expectation]
timeout:10];
XCTAssertEqual(waiterResult, XCTWaiterResultCompleted);
XCTAssertEqualObjects(returnedRequestModel, requestModel);
XCTAssertEqual(returnedResponseModel.statusCode, 418);
XCTAssertEqualObjects(returnedError, self.error);
OCMVerify([partialMockProxy reset]);
}

- (void)testCompletionBlock_delegateToCompletionProxy_when_statusCode401_andNotV3 {
EMSRequestModel *requestModel = [self generateRequestModel];
EMSRequestModel *requestModel = [self generateRequestModel: @""];
EMSResponseModel *responseModel = [self generateResponseWithStatusCode:401];

OCMReject([self.mockResponseHandler processResponse:[OCMArg any]]);
Expand All @@ -187,7 +256,7 @@ - (void)testCompletionBlock_delegateToCompletionProxy_when_statusCode401_andNotV

- (void)testCompletionBlock_shouldRefreshToken_when_requestIsV3 {
EMSRequestModel *requestModel = [self generateRequestModelWithUrlString:[self.endpoint eventUrlWithApplicationCode:@"testApplicationCode"]];
EMSRequestModel *requestModelForRefresh = [self generateRequestModel];
EMSRequestModel *requestModelForRefresh = [self generateRequestModel: @""];
EMSResponseModel *responseModel = [self generateResponseWithStatusCode:401];

OCMStub([self.mockRequestFactory createRefreshTokenRequestModel]).andReturn(requestModelForRefresh);
Expand All @@ -203,22 +272,21 @@ - (void)testCompletionBlock_shouldRefreshToken_when_requestIsV3 {
}

- (void)testCompletionBlock_shouldHandleResponse {
EMSRequestModel *requestModel = [self generateRequestModel];
EMSRequestModel *mockOriginalRequestModel = [self generateRequestModel];
EMSRequestModel *requestModel = [self generateRequestModel: @"contact-token"];
EMSRequestModel *mockOriginalRequestModel = [self generateRequestModel: @""];
EMSResponseModel *responseModel = [self generateResponseWithStatusCode:200];

self.refreshCompletionProxy.originalRequestModel = mockOriginalRequestModel;

self.refreshCompletionProxy.completionBlock(requestModel, responseModel, self.error);
self.refreshCompletionProxy.completionBlock(requestModel, responseModel, nil);

OCMVerify([self.mockResponseHandler processResponse:responseModel]);
OCMVerify([self.mockRestClient executeWithRequestModel:mockOriginalRequestModel
coreCompletionProxy:self.refreshCompletionProxy]);
XCTAssertNil(self.refreshCompletionProxy.originalRequestModel);
}

- (void)testCompletionBlock_shouldNotHandleResponse_when_success_and_noOriginalRequestModel {
EMSRequestModel *requestModel = [self generateRequestModel];
EMSRequestModel *requestModel = [self generateRequestModel: @""];
EMSResponseModel *responseModel = [self generateResponseWithStatusCode:200];

OCMReject([self.mockResponseHandler processResponse:[OCMArg any]]);
Expand All @@ -235,8 +303,8 @@ - (void)testCompletionBlock_shouldNotHandleResponse_when_success_and_noOriginalR
XCTAssertEqual(waiterResult, XCTWaiterResultCompleted);
}

- (EMSRequestModel *)generateRequestModel {
return [self generateRequestModelWithUrlString:@"https://www.emarsys.com"];
- (EMSRequestModel *)generateRequestModel:(NSString*) urlPath {
return [self generateRequestModelWithUrlString:[NSString stringWithFormat:@"https://www.emarsys.com/%@", urlPath]];
}

- (EMSRequestModel *)generateRequestModelWithUrlString:(NSString *)url {
Expand All @@ -254,7 +322,7 @@ - (EMSResponseModel *)generateResponseWithStatusCode:(int)statusCode {
headers:@{@"responseHeaderKey": @"responseHeaderValue"}
body:[@"data" dataUsingEncoding:NSUTF8StringEncoding]
parsedBody:nil
requestModel:[self generateRequestModel]
requestModel:[self generateRequestModel: @""]
timestamp:[[EMSTimestampProvider new] provideTimestamp]];
}

Expand Down

0 comments on commit 9b4d83f

Please sign in to comment.