Skip to content

Commit 37747db

Browse files
FIS: stored registration error timeout (#4032)
* Registration error date introduced. Timeout test added. * Registration error timeout implemented. * Test the registration error date is set and stored.
1 parent 2c0e629 commit 37747db

11 files changed

+139
-12
lines changed

FirebaseInstallations/Source/Library/FIRInstallationsItem.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,11 @@ NS_ASSUME_NONNULL_BEGIN
8484
/**
8585
* Updates `registrationStatus` and `registrationError` accordingly.
8686
* @param error The error for the Installation Registration API request.
87+
* @param date The date when the error occurred.
8788
* @param registrationParameters The parameters used for the Installation Registration API request.
8889
*/
8990
- (void)updateWithRegistrationError:(NSError *)error
91+
date:(NSDate *)date
9092
registrationParameters:
9193
(FIRInstallationsStoredRegistrationParameters *)registrationParameters;
9294

FirebaseInstallations/Source/Library/FIRInstallationsItem.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,13 @@ + (NSString *)base64URLEncodedStringWithData:(NSData *)data {
103103
}
104104

105105
- (void)updateWithRegistrationError:(NSError *)error
106+
date:(NSDate *)date
106107
registrationParameters:
107108
(FIRInstallationsStoredRegistrationParameters *)registrationParameters {
108109
self.registrationStatus = FIRInstallationStatusRegistrationFailed;
109110
self.registrationError = [[FIRInstallationsStoredRegistrationError alloc]
110111
initWithRegistrationParameters:registrationParameters
112+
date:date
111113
APIError:error];
112114
self.authToken = nil;
113115
self.refreshToken = nil;

FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545

4646
NSTimeInterval const kFIRInstallationsTokenExpirationThreshold = 60 * 60; // 1 hour.
4747

48+
NSTimeInterval const kFIRInstallationsRegistrationErrorTimeout = 24 * 60 * 60; // 1 day.
49+
4850
@interface FIRInstallationsIDController ()
4951
@property(nonatomic, readonly) NSString *appID;
5052
@property(nonatomic, readonly) NSString *appName;
@@ -147,6 +149,7 @@ - (instancetype)initWithGoogleAppID:(NSString *)appID
147149
// Validate if a previous registration attempt failed with an error requiring Firebase
148150
// configuration changes.
149151
if (installation.registrationStatus == FIRInstallationStatusRegistrationFailed &&
152+
[self isRegistrationErrorWithDateUpToDate:installation.registrationError.date] &&
150153
[self areInstallationRegistrationParametersEqualToCurrent:
151154
installation.registrationError.registrationParameters]) {
152155
return installation.registrationError.APIError;
@@ -226,6 +229,11 @@ - (BOOL)areInstallationRegistrationParametersEqualToCurrent:
226229
(parameters.projectID == projectID || [parameters.projectID isEqual:projectID]);
227230
}
228231

232+
- (BOOL)isRegistrationErrorWithDateUpToDate:(NSDate *)errorDate {
233+
return errorDate != nil &&
234+
-[errorDate timeIntervalSinceNow] <= kFIRInstallationsRegistrationErrorTimeout;
235+
}
236+
229237
#pragma mark - FID registration
230238

231239
- (FBLPromise<FIRInstallationsItem *> *)registerInstallationIfNeeded:
@@ -274,6 +282,7 @@ - (BOOL)areInstallationRegistrationParametersEqualToCurrent:
274282

275283
FIRInstallationsItem *failedInstallation = [installation copy];
276284
[failedInstallation updateWithRegistrationError:error
285+
date:[NSDate date]
277286
registrationParameters:[self currentRegistrationParameters]];
278287

279288
// Save the error and then fail with the API error.

FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredRegistrationError.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ NS_ASSUME_NONNULL_BEGIN
3333
@interface FIRInstallationsStoredRegistrationError : NSObject <NSSecureCoding>
3434

3535
@property(nonatomic, readonly) FIRInstallationsStoredRegistrationParameters *registrationParameters;
36+
@property(nonatomic, readonly) NSDate *date;
3637
@property(nonatomic, readonly) NSError *APIError;
3738

3839
/// The version of local storage.
3940
@property(nonatomic, readonly) NSInteger storageVersion;
4041

4142
- (instancetype)initWithRegistrationParameters:
4243
(FIRInstallationsStoredRegistrationParameters *)registrationParameters
44+
date:(NSDate *)date
4345
APIError:(NSError *)error;
4446

4547
@end

FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredRegistrationError.m

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,20 @@
2525
@"registrationParameters";
2626
NSString *const kFIRInstallationsStoredRegistrationErrorAPIErrorKey = @"APIError";
2727
NSString *const kFIRInstallationsStoredRegistrationErrorStorageVersionKey = @"storageVersion";
28+
NSString *const kFIRInstallationsStoredRegistrationErrorDateKey = @"date";
2829

2930
NSInteger const kFIRInstallationsStoredRegistrationErrorStorageVersion = 1;
3031

3132
@implementation FIRInstallationsStoredRegistrationError
3233

3334
- (instancetype)initWithRegistrationParameters:
3435
(FIRInstallationsStoredRegistrationParameters *)registrationParameters
36+
date:(NSDate *)date
3537
APIError:(NSError *)error {
3638
self = [super init];
3739
if (self) {
3840
_registrationParameters = registrationParameters;
41+
_date = date;
3942
_APIError = error;
4043
}
4144
return self;
@@ -54,6 +57,7 @@ + (BOOL)supportsSecureCoding {
5457
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
5558
[coder encodeObject:self.registrationParameters
5659
forKey:kFIRInstallationsStoredRegistrationErrorRegistrationParametersKey];
60+
[coder encodeObject:self.date forKey:kFIRInstallationsStoredRegistrationErrorDateKey];
5761
[coder encodeObject:self.APIError forKey:kFIRInstallationsStoredRegistrationErrorAPIErrorKey];
5862
[coder encodeInteger:kFIRInstallationsStoredRegistrationErrorStorageVersion
5963
forKey:kFIRInstallationsStoredRegistrationErrorStorageVersionKey];
@@ -73,6 +77,8 @@ - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
7377
FIRInstallationsStoredRegistrationParameters *registrationParameters =
7478
[coder decodeObjectOfClass:[FIRInstallationsStoredRegistrationParameters class]
7579
forKey:kFIRInstallationsStoredRegistrationErrorRegistrationParametersKey];
80+
NSDate *date = [coder decodeObjectOfClass:[NSDate class]
81+
forKey:kFIRInstallationsStoredRegistrationErrorDateKey];
7682

7783
NSSet<Class> *allowedErrorClasses =
7884
[NSSet setWithArray:@ [[FIRInstallationsHTTPError class], [NSError class]]];
@@ -87,7 +93,7 @@ - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
8793
return nil;
8894
}
8995

90-
return [self initWithRegistrationParameters:registrationParameters APIError:APIError];
96+
return [self initWithRegistrationParameters:registrationParameters date:date APIError:APIError];
9197
}
9298

9399
@end

FirebaseInstallations/Source/Tests/Unit/FIRInstallationsAPIServiceTests.m

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#import <OCMock/OCMock.h>
2020
#import "FBLPromise+Testing.h"
2121
#import "FIRInstallationsItem+Tests.h"
22+
#import "XCTestCase+DateAsserts.h"
2223

2324
#import "FIRInstallationsAPIService.h"
2425
#import "FIRInstallationsErrorUtil.h"
@@ -405,16 +406,6 @@ - (NSURLResponse *)responseWithStatusCode:(NSUInteger)statusCode {
405406
return response;
406407
}
407408

408-
- (void)assertDate:(NSDate *)date
409-
isApproximatelyEqualCurrentPlusTimeInterval:(NSTimeInterval)timeInterval {
410-
NSDate *expectedDate = [NSDate dateWithTimeIntervalSinceNow:timeInterval];
411-
412-
NSTimeInterval precision = 10;
413-
XCTAssert(ABS([date timeIntervalSinceDate:expectedDate]) <= precision,
414-
@"date: %@ is not equal to expected %@ with precision %f - %@", date, expectedDate,
415-
precision, self.name);
416-
}
417-
418409
- (id)refreshTokenRequestValidationArgWithInstallation:(FIRInstallationsItem *)installation {
419410
return [OCMArg checkWithBlock:^BOOL(NSURLRequest *request) {
420411
XCTAssertEqualObjects(request.HTTPMethod, @"POST");

FirebaseInstallations/Source/Tests/Unit/FIRInstallationsIDControllerTests.m

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#import "FBLPromise+Testing.h"
2424
#import "FIRInstallationsErrorUtil+Tests.h"
2525
#import "FIRInstallationsItem+Tests.h"
26+
#import "XCTestCase+DateAsserts.h"
2627

2728
#import "FIRInstallations.h"
2829
#import "FIRInstallationsAPIService.h"
@@ -1023,6 +1024,9 @@ - (void)testRegisterInstallation_WhenServerRespondsWith400_ThenErrorStoredAndRet
10231024
XCTAssertEqualObjects(
10241025
installation.registrationError.registrationParameters.projectID,
10251026
self.projectID);
1027+
XCTAssertNotNil(installation.registrationError.date);
1028+
[self assertDate:installation.registrationError.date
1029+
isApproximatelyEqualCurrentPlusTimeInterval:0];
10261030
return YES;
10271031
}]])
10281032
.andReturn([FBLPromise resolvedWith:[NSNull null]]);
@@ -1104,6 +1108,47 @@ - (void)testGetInstallation_WhenRegistrationErrorStoredAndConfigurationIsTheSame
11041108
OCMVerifyAll(self.mockAPIService);
11051109
}
11061110

1111+
- (void)testGetInstallation_WhenStoredRegistrationErrorIsOutdated_ThenSendsAPIRequest {
1112+
__block FBLPromise<FIRInstallationsItem *> *storedInstallationPromise;
1113+
OCMExpect([self.mockInstallationsStore installationForAppID:self.appID appName:self.appName])
1114+
.andDo(^(NSInvocation *invocation) {
1115+
[invocation setReturnValue:&storedInstallationPromise];
1116+
});
1117+
1118+
// 1.1. Expect installation to be requested from the store.
1119+
NSDate *date25HoursAgo = [NSDate dateWithTimeIntervalSinceNow:-25 * 60 * 60];
1120+
FIRInstallationsItem *storedInstallation =
1121+
[self createFailedToRegisterInstallationWithParameters:[self currentRegistrationParameters]
1122+
date:date25HoursAgo];
1123+
storedInstallationPromise = [FBLPromise resolvedWith:storedInstallation];
1124+
1125+
// 1.2. Expect registration API request to be sent.
1126+
FIRInstallationsItem *registeredInstallation =
1127+
[FIRInstallationsItem createRegisteredInstallationItem];
1128+
OCMExpect([self.mockAPIService registerInstallation:storedInstallation])
1129+
.andReturn([FBLPromise resolvedWith:registeredInstallation]);
1130+
1131+
// 1.3. Expect registered Installation to be stored.
1132+
OCMExpect([self.mockInstallationsStore saveInstallation:[OCMArg checkWithBlock:^BOOL(id obj) {
1133+
XCTAssertEqualObjects(obj, registeredInstallation);
1134+
storedInstallationPromise =
1135+
[FBLPromise resolvedWith:obj];
1136+
return YES;
1137+
}]])
1138+
.andReturn([FBLPromise resolvedWith:[NSNull null]]);
1139+
1140+
// 2. Request Installation.
1141+
FBLPromise<FIRInstallationsItem *> *promise = [self.controller getInstallationItem];
1142+
XCTAssert(FBLWaitForPromisesWithTimeout(0.5));
1143+
1144+
// 3. Check.
1145+
XCTAssertEqualObjects(promise.value.identifier, registeredInstallation.identifier);
1146+
XCTAssertNil(promise.error);
1147+
1148+
OCMVerifyAll(self.mockInstallationsStore);
1149+
OCMVerifyAll(self.mockAPIService);
1150+
}
1151+
11071152
#pragma mark - Helpers
11081153

11091154
- (void)expectInstallationsStoreGetInstallationNotFound {
@@ -1139,14 +1184,22 @@ - (XCTestExpectation *)installationIDDidChangeNotificationExpectation {
11391184
}
11401185

11411186
- (FIRInstallationsItem *)createFailedToRegisterInstallationWithParameters:
1142-
(FIRInstallationsStoredRegistrationParameters *)registrationParameters {
1187+
(FIRInstallationsStoredRegistrationParameters *)registrationParameters
1188+
date:(NSDate *)date {
11431189
FIRInstallationsStoredRegistrationError *error = [[FIRInstallationsStoredRegistrationError alloc]
11441190
initWithRegistrationParameters:registrationParameters
1191+
date:date
11451192
APIError:[FIRInstallationsErrorUtil APIErrorWithHTTPCode:400]];
11461193
FIRInstallationsItem *installation = [FIRInstallationsItem createWithRegistrationFailure:error];
11471194
return installation;
11481195
}
11491196

1197+
- (FIRInstallationsItem *)createFailedToRegisterInstallationWithParameters:
1198+
(FIRInstallationsStoredRegistrationParameters *)registrationParameters {
1199+
return [self createFailedToRegisterInstallationWithParameters:registrationParameters
1200+
date:[NSDate date]];
1201+
}
1202+
11501203
- (FIRInstallationsStoredRegistrationParameters *)currentRegistrationParameters {
11511204
return [[FIRInstallationsStoredRegistrationParameters alloc] initWithAPIKey:self.APIKey
11521205
projectID:self.projectID];

FirebaseInstallations/Source/Tests/Unit/FIRInstallationsStoredItemTests.m

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ - (void)testItemArchivingUnarchiving {
5959
XCTAssertEqual(unarchivedItem.registrationStatus, item.registrationStatus);
6060

6161
XCTAssertEqualObjects(unarchivedItem.registrationError.APIError, item.registrationError.APIError);
62+
XCTAssertEqualObjects(unarchivedItem.registrationError.date, item.registrationError.date);
6263
XCTAssertEqualObjects(unarchivedItem.registrationError.registrationParameters.APIKey,
6364
item.registrationError.registrationParameters.APIKey);
6465
XCTAssertEqualObjects(unarchivedItem.registrationError.registrationParameters.projectID,
@@ -76,6 +77,7 @@ - (FIRInstallationsStoredRegistrationError *)createRegistrationError {
7677
userInfo:@{NSLocalizedFailureReasonErrorKey : @"value"}];
7778
FIRInstallationsStoredRegistrationError *registrationError =
7879
[[FIRInstallationsStoredRegistrationError alloc] initWithRegistrationParameters:params
80+
date:[NSDate date]
7981
APIError:error];
8082
XCTAssertEqualObjects(registrationError.APIError, error);
8183
XCTAssertEqualObjects(registrationError.registrationParameters, params);

FirebaseInstallations/Source/Tests/Utils/FIRTestKeychain.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
#import <Foundation/Foundation.h>
1718
#import <Security/Security.h>
1819

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2019 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <XCTest/XCTest.h>
18+
19+
NS_ASSUME_NONNULL_BEGIN
20+
21+
@interface XCTestCase (FIRTestsDateUtils)
22+
23+
- (void)assertDate:(NSDate *)date
24+
isApproximatelyEqualCurrentPlusTimeInterval:(NSTimeInterval)timeInterval;
25+
26+
@end
27+
28+
NS_ASSUME_NONNULL_END
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2019 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "XCTestCase+DateAsserts.h"
18+
19+
@implementation XCTestCase (FIRTestsDateUtils)
20+
21+
- (void)assertDate:(NSDate *)date
22+
isApproximatelyEqualCurrentPlusTimeInterval:(NSTimeInterval)timeInterval {
23+
NSDate *expectedDate = [NSDate dateWithTimeIntervalSinceNow:timeInterval];
24+
25+
NSTimeInterval precision = 10;
26+
XCTAssert(ABS([date timeIntervalSinceDate:expectedDate]) <= precision,
27+
@"date: %@ is not equal to expected %@ with precision %f - %@", date, expectedDate,
28+
precision, self.name);
29+
}
30+
31+
@end

0 commit comments

Comments
 (0)