Skip to content

Commit f47ce4b

Browse files
authored
Throw an error if the fetch response returns an error (irrespective o… (#4438)
* Throw an error if the fetch response returns an error (irrespective of status code). * Remove duplicate metadata set call. * Add comment.
1 parent 374f1ac commit f47ce4b

File tree

3 files changed

+174
-53
lines changed

3 files changed

+174
-53
lines changed

FirebaseRemoteConfig/Sources/RCNFetch.m

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ - (void)fetchWithUserProperties:(NSDictionary *)userProperties
358358
NSInteger statusCode = [((NSHTTPURLResponse *)response) statusCode];
359359

360360
if (error || (statusCode != kRCNFetchResponseHTTPStatusCodeOK)) {
361-
// Update failure fetch time and database.
361+
// Update metadata about fetch failure.
362362
[strongSelf->_settings updateMetadataWithFetchSuccessStatus:NO];
363363
if (error) {
364364
if (strongSelf->_settings.lastFetchStatus == FIRRemoteConfigFetchStatusSuccess) {
@@ -393,27 +393,25 @@ - (void)fetchWithUserProperties:(NSDictionary *)userProperties
393393
return [strongSelf reportCompletionOnHandler:completionHandler
394394
withStatus:strongSelf->_settings.lastFetchStatus
395395
withError:error];
396-
} else {
397-
// Return back the received error.
398-
// Must set lastFetchStatus before setting Fetch Error.
399-
strongSelf->_settings.lastFetchStatus = FIRRemoteConfigFetchStatusFailure;
400-
strongSelf->_settings.lastFetchError = FIRRemoteConfigErrorInternalError;
401-
NSDictionary<NSErrorUserInfoKey, id> *userInfo = @{
402-
NSLocalizedDescriptionKey :
403-
(error ? [error localizedDescription]
404-
: [NSString stringWithFormat:@"Internal Error. Status code: %ld",
405-
(long)statusCode])
406-
};
407-
return [strongSelf
408-
reportCompletionOnHandler:completionHandler
409-
withStatus:FIRRemoteConfigFetchStatusFailure
410-
withError:[NSError
411-
errorWithDomain:FIRRemoteConfigErrorDomain
412-
code:FIRRemoteConfigErrorInternalError
413-
userInfo:userInfo]];
414396
}
415-
}
416-
}
397+
} // Response error code 429, 500, 503
398+
} // StatusCode != kRCNFetchResponseHTTPStatusCodeOK
399+
// Return back the received error.
400+
// Must set lastFetchStatus before setting Fetch Error.
401+
strongSelf->_settings.lastFetchStatus = FIRRemoteConfigFetchStatusFailure;
402+
strongSelf->_settings.lastFetchError = FIRRemoteConfigErrorInternalError;
403+
NSDictionary<NSErrorUserInfoKey, id> *userInfo = @{
404+
NSLocalizedDescriptionKey :
405+
(error ? [error localizedDescription]
406+
: [NSString
407+
stringWithFormat:@"Internal Error. Status code: %ld", (long)statusCode])
408+
};
409+
return [strongSelf
410+
reportCompletionOnHandler:completionHandler
411+
withStatus:FIRRemoteConfigFetchStatusFailure
412+
withError:[NSError errorWithDomain:FIRRemoteConfigErrorDomain
413+
code:FIRRemoteConfigErrorInternalError
414+
userInfo:userInfo]];
417415
}
418416

419417
// Fetch was successful. Check if we have data.

FirebaseRemoteConfig/Tests/Sample/RemoteConfigSampleApp/ViewController.m

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -159,39 +159,39 @@ - (IBAction)fetchButtonPressed:(id)sender {
159159
- (IBAction)fetchAndActivateButtonPressed:(id)sender {
160160
// fetchConfig api callback, this is triggered when client receives response from server
161161
ViewController *__weak weakSelf = self;
162-
FIRRemoteConfigFetchAndActivateCompletion fetchAndActivateCompletion =
163-
^(FIRRemoteConfigFetchAndActivateStatus status, NSError *error) {
164-
ViewController *strongSelf = weakSelf;
165-
if (!strongSelf) {
166-
return;
167-
}
168-
NSMutableString *output = @"Fetch and activate status :";
169-
if (status == FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote) {
170-
[output appendString:@"Success from remote fetch."];
171-
} else if (status == FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData) {
172-
[output appendString:@"Success using pre-fetched data."];
173-
} else if (status == FIRRemoteConfigFetchAndActivateStatusError) {
174-
output appendString
175-
: [NSString stringWithFormat:@"Failure: %@", [error localizedDescription]];
176-
}
162+
FIRRemoteConfigFetchAndActivateCompletion fetchAndActivateCompletion = ^(
163+
FIRRemoteConfigFetchAndActivateStatus status, NSError *error) {
164+
ViewController *strongSelf = weakSelf;
165+
if (!strongSelf) {
166+
return;
167+
}
168+
NSMutableString *output = [@"Fetch and activate status :" mutableCopy];
169+
if (status == FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote) {
170+
[output appendString:@"Success from remote fetch."];
171+
} else if (status == FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData) {
172+
[output appendString:@"Success using pre-fetched data."];
173+
} else if (status == FIRRemoteConfigFetchAndActivateStatusError) {
174+
[output
175+
appendString:[NSString stringWithFormat:@"Failure: %@", [error localizedDescription]]];
176+
}
177177

178-
if (error) {
179-
[output appendFormat:@"%@\n", error];
180-
}
181-
if (status == FIRRemoteConfigFetchAndActivateStatusError) {
182-
[output appendString:[NSString stringWithFormat:@"Fetch And Activate Error :%@.\n",
183-
[strongSelf errorString:error.code]]];
184-
if (error.code == FIRRemoteConfigErrorThrottled) {
185-
[output appendString:[NSString stringWithFormat:@"Throttled.\n"]];
186-
}
187-
}
188-
// activate status
189-
[[FRCLog sharedInstance] logToConsole:output];
190-
if (status == FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote ||
191-
status == FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData) {
192-
[strongSelf printResult:[[NSMutableString alloc] init]];
193-
}
194-
};
178+
if (error) {
179+
[output appendFormat:@"%@\n", error];
180+
}
181+
if (status == FIRRemoteConfigFetchAndActivateStatusError) {
182+
[output appendString:[NSString stringWithFormat:@"Fetch And Activate Error :%@.\n",
183+
[strongSelf errorString:error.code]]];
184+
if (error.code == FIRRemoteConfigErrorThrottled) {
185+
[output appendString:[NSString stringWithFormat:@"Throttled.\n"]];
186+
}
187+
}
188+
// activate status
189+
[[FRCLog sharedInstance] logToConsole:output];
190+
if (status == FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote ||
191+
status == FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData) {
192+
[strongSelf printResult:[[NSMutableString alloc] init]];
193+
}
194+
};
195195

196196
// fetchConfig api call
197197
[self.RCInstances[self.currentNamespace][self.FIRAppName]

FirebaseRemoteConfig/Tests/Unit/RCNRemoteConfigTest.m

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,129 @@ - (void)testFetchConfigsFailed {
558558
}];
559559
}
560560

561+
// TODO(mandard): Break up test with helper methods.
562+
- (void)testFetchConfigsFailedErrorNoNetwork {
563+
// Override the setup values to return back an error status.
564+
RCNConfigContent *configContent = [[RCNConfigContent alloc] initWithDBManager:_DBManager];
565+
// Populate the default, second app, second namespace instances.
566+
for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
567+
NSString *currentAppName = nil;
568+
FIROptions *currentOptions = nil;
569+
NSString *currentNamespace = nil;
570+
switch (i) {
571+
case RCNTestRCInstanceSecondNamespace:
572+
currentAppName = RCNTestsDefaultFIRAppName;
573+
currentOptions = [self firstAppOptions];
574+
currentNamespace = RCNTestsPerfNamespace;
575+
break;
576+
case RCNTestRCInstanceSecondApp:
577+
currentAppName = RCNTestsSecondFIRAppName;
578+
currentOptions = [self secondAppOptions];
579+
currentNamespace = FIRNamespaceGoogleMobilePlatform;
580+
break;
581+
case RCNTestRCInstanceDefault:
582+
default:
583+
currentAppName = RCNTestsDefaultFIRAppName;
584+
currentOptions = [self firstAppOptions];
585+
currentNamespace = RCNTestsFIRNamespace;
586+
break;
587+
}
588+
NSString *fullyQualifiedNamespace =
589+
[NSString stringWithFormat:@"%@:%@", currentNamespace, currentAppName];
590+
RCNUserDefaultsManager *userDefaultsManager =
591+
[[RCNUserDefaultsManager alloc] initWithAppName:currentAppName
592+
bundleID:[NSBundle mainBundle].bundleIdentifier
593+
namespace:fullyQualifiedNamespace];
594+
userDefaultsManager.lastFetchTime = 0;
595+
596+
FIRRemoteConfig *config =
597+
OCMPartialMock([[FIRRemoteConfig alloc] initWithAppName:currentAppName
598+
FIROptions:currentOptions
599+
namespace:currentNamespace
600+
DBManager:_DBManager
601+
configContent:configContent
602+
analytics:nil]);
603+
604+
_configInstances[i] = config;
605+
RCNConfigSettings *settings =
606+
[[RCNConfigSettings alloc] initWithDatabaseManager:_DBManager
607+
namespace:fullyQualifiedNamespace
608+
firebaseAppName:currentAppName
609+
googleAppID:currentOptions.googleAppID];
610+
dispatch_queue_t queue = dispatch_queue_create(
611+
[[NSString stringWithFormat:@"testqueue: %d", i] cStringUsingEncoding:NSUTF8StringEncoding],
612+
DISPATCH_QUEUE_SERIAL);
613+
_configFetch[i] = OCMPartialMock([[RCNConfigFetch alloc] initWithContent:configContent
614+
DBManager:_DBManager
615+
settings:settings
616+
analytics:nil
617+
experiment:nil
618+
queue:queue
619+
namespace:fullyQualifiedNamespace
620+
options:currentOptions]);
621+
622+
OCMStub([_configFetch[i] fetchConfigWithExpirationDuration:43200 completionHandler:OCMOCK_ANY])
623+
.andDo(^(NSInvocation *invocation) {
624+
void (^handler)(FIRRemoteConfigFetchStatus status, NSError *_Nullable error) = nil;
625+
// void (^handler)(FIRRemoteConfigFetchCompletion);
626+
[invocation getArgument:&handler atIndex:3];
627+
[_configFetch[i] fetchWithUserProperties:[[NSDictionary alloc] init]
628+
completionHandler:handler];
629+
});
630+
631+
_response[i] = @{};
632+
633+
_responseData[i] = [NSJSONSerialization dataWithJSONObject:_response[i] options:0 error:nil];
634+
635+
// A no network error is accompanied with an HTTP status code of 0.
636+
_URLResponse[i] =
637+
[[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"https://firebase.com"]
638+
statusCode:0
639+
HTTPVersion:nil
640+
headerFields:@{@"etag" : @"etag1"}];
641+
642+
id completionBlock =
643+
[OCMArg invokeBlockWithArgs:_responseData[i], _URLResponse[i], [NSNull null], nil];
644+
645+
OCMExpect([_configFetch[i] URLSessionDataTaskWithContent:[OCMArg any]
646+
completionHandler:completionBlock])
647+
.andReturn(nil);
648+
[_configInstances[i] updateWithNewInstancesForConfigFetch:_configFetch[i]
649+
configContent:configContent
650+
configSettings:settings
651+
configExperiment:nil];
652+
}
653+
#pragma clang diagnostic push
654+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
655+
// Make the fetch calls for all instances.
656+
NSMutableArray<XCTestExpectation *> *expectations =
657+
[[NSMutableArray alloc] initWithCapacity:RCNTestRCNumTotalInstances];
658+
659+
for (int i = 0; i < RCNTestRCNumTotalInstances; i++) {
660+
expectations[i] = [self
661+
expectationWithDescription:
662+
[NSString stringWithFormat:@"Test enumerating configs successfully - instance %d", i]];
663+
XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusNoFetchYet);
664+
FIRRemoteConfigFetchCompletion fetchCompletion =
665+
^void(FIRRemoteConfigFetchStatus status, NSError *error) {
666+
XCTAssertEqual(_configInstances[i].lastFetchStatus, FIRRemoteConfigFetchStatusFailure);
667+
XCTAssertFalse([_configInstances[i] activateFetched]);
668+
XCTAssertNotNil(error);
669+
// No such key, still return a static value.
670+
FIRRemoteConfigValue *value = _configInstances[RCNTestRCInstanceDefault][@"key1"];
671+
XCTAssertEqual((int)value.source, (int)FIRRemoteConfigSourceStatic);
672+
XCTAssertEqualObjects(value.stringValue, @"");
673+
XCTAssertEqual(value.boolValue, NO);
674+
[expectations[i] fulfill];
675+
};
676+
[_configInstances[i] fetchWithExpirationDuration:43200 completionHandler:fetchCompletion];
677+
}
678+
[self waitForExpectationsWithTimeout:_expectationTimeout
679+
handler:^(NSError *error) {
680+
XCTAssertNil(error);
681+
}];
682+
}
683+
561684
// Activate should return false if a fetch response returns 200 with NO_CHANGE as the response body.
562685
- (void)testActivateOnFetchNoChangeStatus {
563686
// Override the setup values to return back an error status.

0 commit comments

Comments
 (0)