Skip to content

Commit 43cabb5

Browse files
committed
Merge pull request #334 from Microsoft/retry-report-status
Retry Report Status if failed
2 parents 3ba181a + 73e4fa5 commit 43cabb5

File tree

7 files changed

+220
-77
lines changed

7 files changed

+220
-77
lines changed

CodePush.js

+31-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { AcquisitionManager as Sdk } from "code-push/script/acquisition-sdk";
22
import { Alert } from "./AlertAdapter";
33
import requestFetchAdapter from "./request-fetch-adapter";
4-
import { Platform } from "react-native";
4+
import { AppState, Platform } from "react-native";
55

66
let NativeCodePush = require("react-native").NativeModules.CodePush;
77
const PackageMixins = require("./package-mixins")(NativeCodePush);
@@ -174,19 +174,42 @@ const notifyApplicationReady = (() => {
174174

175175
async function notifyApplicationReadyInternal() {
176176
await NativeCodePush.notifyApplicationReady();
177+
tryReportStatus();
178+
}
179+
180+
async function tryReportStatus(resumeListener) {
177181
const statusReport = await NativeCodePush.getNewStatusReport();
178182
if (statusReport) {
179183
const config = await getConfiguration();
180184
const previousLabelOrAppVersion = statusReport.previousLabelOrAppVersion;
181185
const previousDeploymentKey = statusReport.previousDeploymentKey || config.deploymentKey;
182-
if (statusReport.appVersion) {
183-
const sdk = getPromisifiedSdk(requestFetchAdapter, config);
184-
sdk.reportStatusDeploy(/* deployedPackage */ null, /* status */ null, previousLabelOrAppVersion, previousDeploymentKey);
185-
} else {
186-
config.deploymentKey = statusReport.package.deploymentKey;
187-
const sdk = getPromisifiedSdk(requestFetchAdapter, config);
188-
sdk.reportStatusDeploy(statusReport.package, statusReport.status, previousLabelOrAppVersion, previousDeploymentKey);
186+
try {
187+
if (statusReport.appVersion) {
188+
const sdk = getPromisifiedSdk(requestFetchAdapter, config);
189+
await sdk.reportStatusDeploy(/* deployedPackage */ null, /* status */ null, previousLabelOrAppVersion, previousDeploymentKey);
190+
} else {
191+
config.deploymentKey = statusReport.package.deploymentKey;
192+
const sdk = getPromisifiedSdk(requestFetchAdapter, config);
193+
await sdk.reportStatusDeploy(statusReport.package, statusReport.status, previousLabelOrAppVersion, previousDeploymentKey);
194+
}
195+
196+
log(`Reported status: ${JSON.stringify(statusReport)}`);
197+
NativeCodePush.recordStatusReported(statusReport);
198+
resumeListener && AppState.removeEventListener("change", resumeListener);
199+
} catch (e) {
200+
log(`Report status failed: ${JSON.stringify(statusReport)}`);
201+
NativeCodePush.saveStatusReportForRetry(statusReport);
202+
// Try again when the app resumes
203+
if (!resumeListener) {
204+
resumeListener = (newState) => {
205+
newState === "active" && tryReportStatus(resumeListener);
206+
};
207+
208+
AppState.addEventListener("change", resumeListener);
209+
}
189210
}
211+
} else {
212+
resumeListener && AppState.removeEventListener("change", resumeListener);
190213
}
191214
}
192215

android/app/src/main/java/com/microsoft/codepush/react/CodePush.java

+16
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,12 @@ protected Void doInBackground(Void... params) {
626626
promise.resolve(newAppVersionStatusReport);
627627
return null;
628628
}
629+
} else {
630+
WritableMap retryStatusReport = codePushTelemetryManager.getRetryStatusReport();
631+
if (retryStatusReport != null) {
632+
promise.resolve(retryStatusReport);
633+
return null;
634+
}
629635
}
630636

631637
promise.resolve("");
@@ -720,6 +726,11 @@ public void notifyApplicationReady(Promise promise) {
720726
promise.resolve("");
721727
}
722728

729+
@ReactMethod
730+
public void recordStatusReported(ReadableMap statusReport) {
731+
codePushTelemetryManager.recordStatusReported(statusReport);
732+
}
733+
723734
@ReactMethod
724735
public void restartApp(boolean onlyIfUpdateIsPending) {
725736
// If this is an unconditional restart request, or there
@@ -729,6 +740,11 @@ public void restartApp(boolean onlyIfUpdateIsPending) {
729740
}
730741
}
731742

743+
@ReactMethod
744+
public void saveStatusReportForRetry(ReadableMap statusReport) {
745+
codePushTelemetryManager.saveStatusReportForRetry(statusReport);
746+
}
747+
732748
@ReactMethod
733749
// Replaces the current bundle with the one downloaded from removeBundleUrl.
734750
// It is only to be used during tests. No-ops if the test configuration flag is not set.

android/app/src/main/java/com/microsoft/codepush/react/CodePushTelemetryManager.java

+76-34
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,28 @@
33
import android.content.Context;
44
import android.content.SharedPreferences;
55

6+
import com.facebook.react.bridge.ReadableMap;
67
import com.facebook.react.bridge.WritableMap;
78
import com.facebook.react.bridge.WritableNativeMap;
89

10+
import org.json.JSONException;
11+
import org.json.JSONObject;
12+
913
public class CodePushTelemetryManager {
1014

1115
private Context applicationContext;
16+
private final String APP_VERSION_KEY = "appVersion";
1217
private final String CODE_PUSH_PREFERENCES;
1318
private final String DEPLOYMENT_FAILED_STATUS = "DeploymentFailed";
1419
private final String DEPLOYMENT_KEY_KEY = "deploymentKey";
1520
private final String DEPLOYMENT_SUCCEEDED_STATUS = "DeploymentSucceeded";
1621
private final String LABEL_KEY = "label";
1722
private final String LAST_DEPLOYMENT_REPORT_KEY = "CODE_PUSH_LAST_DEPLOYMENT_REPORT";
23+
private final String PACKAGE_KEY = "package";
24+
private final String PREVIOUS_DEPLOYMENT_KEY_KEY = "previousDeploymentKey";
25+
private final String PREVIOUS_LABEL_OR_APP_VERSION_KEY = "previousLabelOrAppVersion";
26+
private final String RETRY_DEPLOYMENT_REPORT_KEY = "CODE_PUSH_RETRY_DEPLOYMENT_REPORT";
27+
private final String STATUS_KEY = "status";
1828

1929
public CodePushTelemetryManager(Context applicationContext, String codePushPreferencesKey) {
2030
this.applicationContext = applicationContext;
@@ -23,71 +33,103 @@ public CodePushTelemetryManager(Context applicationContext, String codePushPrefe
2333

2434
public WritableMap getBinaryUpdateReport(String appVersion) {
2535
String previousStatusReportIdentifier = this.getPreviousStatusReportIdentifier();
36+
WritableNativeMap reportMap = null;
2637
if (previousStatusReportIdentifier == null) {
27-
this.recordDeploymentStatusReported(appVersion);
28-
WritableNativeMap reportMap = new WritableNativeMap();
29-
reportMap.putString("appVersion", appVersion);
30-
return reportMap;
38+
this.clearRetryStatusReport();
39+
reportMap = new WritableNativeMap();
40+
reportMap.putString(APP_VERSION_KEY, appVersion);
3141
} else if (!previousStatusReportIdentifier.equals(appVersion)) {
32-
this.recordDeploymentStatusReported(appVersion);
33-
WritableNativeMap reportMap = new WritableNativeMap();
42+
this.clearRetryStatusReport();
43+
reportMap = new WritableNativeMap();
3444
if (this.isStatusReportIdentifierCodePushLabel(previousStatusReportIdentifier)) {
3545
String previousDeploymentKey = this.getDeploymentKeyFromStatusReportIdentifier(previousStatusReportIdentifier);
3646
String previousLabel = this.getVersionLabelFromStatusReportIdentifier(previousStatusReportIdentifier);
37-
reportMap.putString("appVersion", appVersion);
38-
reportMap.putString("previousDeploymentKey", previousDeploymentKey);
39-
reportMap.putString("previousLabelOrAppVersion", previousLabel);
47+
reportMap.putString(APP_VERSION_KEY, appVersion);
48+
reportMap.putString(PREVIOUS_DEPLOYMENT_KEY_KEY, previousDeploymentKey);
49+
reportMap.putString(PREVIOUS_LABEL_OR_APP_VERSION_KEY, previousLabel);
4050
} else {
4151
// Previous status report was with a binary app version.
42-
reportMap.putString("appVersion", appVersion);
43-
reportMap.putString("previousLabelOrAppVersion", previousStatusReportIdentifier);
52+
reportMap.putString(APP_VERSION_KEY, appVersion);
53+
reportMap.putString(PREVIOUS_LABEL_OR_APP_VERSION_KEY, previousStatusReportIdentifier);
54+
}
55+
}
56+
57+
return reportMap;
58+
}
59+
60+
public WritableMap getRetryStatusReport() {
61+
SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0);
62+
String retryStatusReportString = settings.getString(RETRY_DEPLOYMENT_REPORT_KEY, null);
63+
if (retryStatusReportString != null) {
64+
clearRetryStatusReport();
65+
try {
66+
JSONObject retryStatusReport = new JSONObject(retryStatusReportString);
67+
return CodePushUtils.convertJsonObjectToWritable(retryStatusReport);
68+
} catch (JSONException e) {
69+
e.printStackTrace();
4470
}
45-
return reportMap;
4671
}
4772

4873
return null;
4974
}
5075

5176
public WritableMap getRollbackReport(WritableMap lastFailedPackage) {
5277
WritableNativeMap reportMap = new WritableNativeMap();
53-
reportMap.putMap("package", lastFailedPackage);
54-
reportMap.putString("status", DEPLOYMENT_FAILED_STATUS);
78+
reportMap.putMap(PACKAGE_KEY, lastFailedPackage);
79+
reportMap.putString(STATUS_KEY, DEPLOYMENT_FAILED_STATUS);
5580
return reportMap;
5681
}
5782

5883
public WritableMap getUpdateReport(WritableMap currentPackage) {
5984
String currentPackageIdentifier = this.getPackageStatusReportIdentifier(currentPackage);
6085
String previousStatusReportIdentifier = this.getPreviousStatusReportIdentifier();
86+
WritableNativeMap reportMap = null;
6187
if (currentPackageIdentifier != null) {
6288
if (previousStatusReportIdentifier == null) {
63-
this.recordDeploymentStatusReported(currentPackageIdentifier);
64-
WritableNativeMap reportMap = new WritableNativeMap();
65-
reportMap.putMap("package", currentPackage);
66-
reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS);
67-
return reportMap;
89+
this.clearRetryStatusReport();
90+
reportMap = new WritableNativeMap();
91+
reportMap.putMap(PACKAGE_KEY, currentPackage);
92+
reportMap.putString(STATUS_KEY, DEPLOYMENT_SUCCEEDED_STATUS);
6893
} else if (!previousStatusReportIdentifier.equals(currentPackageIdentifier)) {
69-
this.recordDeploymentStatusReported(currentPackageIdentifier);
94+
this.clearRetryStatusReport();
95+
reportMap = new WritableNativeMap();
7096
if (this.isStatusReportIdentifierCodePushLabel(previousStatusReportIdentifier)) {
7197
String previousDeploymentKey = this.getDeploymentKeyFromStatusReportIdentifier(previousStatusReportIdentifier);
7298
String previousLabel = this.getVersionLabelFromStatusReportIdentifier(previousStatusReportIdentifier);
73-
WritableNativeMap reportMap = new WritableNativeMap();
74-
reportMap.putMap("package", currentPackage);
75-
reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS);
76-
reportMap.putString("previousDeploymentKey", previousDeploymentKey);
77-
reportMap.putString("previousLabelOrAppVersion", previousLabel);
78-
return reportMap;
99+
reportMap.putMap(PACKAGE_KEY, currentPackage);
100+
reportMap.putString(STATUS_KEY, DEPLOYMENT_SUCCEEDED_STATUS);
101+
reportMap.putString(PREVIOUS_DEPLOYMENT_KEY_KEY, previousDeploymentKey);
102+
reportMap.putString(PREVIOUS_LABEL_OR_APP_VERSION_KEY, previousLabel);
79103
} else {
80104
// Previous status report was with a binary app version.
81-
WritableNativeMap reportMap = new WritableNativeMap();
82-
reportMap.putMap("package", currentPackage);
83-
reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS);
84-
reportMap.putString("previousLabelOrAppVersion", previousStatusReportIdentifier);
85-
return reportMap;
105+
reportMap.putMap(PACKAGE_KEY, currentPackage);
106+
reportMap.putString(STATUS_KEY, DEPLOYMENT_SUCCEEDED_STATUS);
107+
reportMap.putString(PREVIOUS_LABEL_OR_APP_VERSION_KEY, previousStatusReportIdentifier);
86108
}
87109
}
88110
}
89111

90-
return null;
112+
return reportMap;
113+
}
114+
115+
public void recordStatusReported(ReadableMap statusReport) {
116+
if (statusReport.hasKey(APP_VERSION_KEY)) {
117+
saveStatusReportedForIdentifier(statusReport.getString(APP_VERSION_KEY));
118+
} else if (statusReport.hasKey(PACKAGE_KEY)) {
119+
String packageIdentifier = getPackageStatusReportIdentifier(statusReport.getMap(PACKAGE_KEY));
120+
saveStatusReportedForIdentifier(packageIdentifier);
121+
}
122+
}
123+
124+
public void saveStatusReportForRetry(ReadableMap statusReport) {
125+
SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0);
126+
JSONObject statusReportJSON = CodePushUtils.convertReadableToJsonObject(statusReport);
127+
settings.edit().putString(RETRY_DEPLOYMENT_REPORT_KEY, statusReportJSON.toString()).commit();
128+
}
129+
130+
private void clearRetryStatusReport() {
131+
SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0);
132+
settings.edit().remove(RETRY_DEPLOYMENT_REPORT_KEY).commit();
91133
}
92134

93135
private String getDeploymentKeyFromStatusReportIdentifier(String statusReportIdentifier) {
@@ -99,7 +141,7 @@ private String getDeploymentKeyFromStatusReportIdentifier(String statusReportIde
99141
}
100142
}
101143

102-
private String getPackageStatusReportIdentifier(WritableMap updatePackage) {
144+
private String getPackageStatusReportIdentifier(ReadableMap updatePackage) {
103145
// Because deploymentKeys can be dynamically switched, we use a
104146
// combination of the deploymentKey and label as the packageIdentifier.
105147
String deploymentKey = CodePushUtils.tryGetString(updatePackage, DEPLOYMENT_KEY_KEY);
@@ -129,7 +171,7 @@ private boolean isStatusReportIdentifierCodePushLabel(String statusReportIdentif
129171
return statusReportIdentifier != null && statusReportIdentifier.contains(":");
130172
}
131173

132-
private void recordDeploymentStatusReported(String appVersionOrPackageIdentifier) {
174+
private void saveStatusReportedForIdentifier(String appVersionOrPackageIdentifier) {
133175
SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0);
134176
settings.edit().putString(LAST_DEPLOYMENT_REPORT_KEY, appVersionOrPackageIdentifier).commit();
135177
}

ios/CodePush/CodePush.h

+3
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,11 @@ failCallback:(void (^)(NSError *err))failCallback;
112112
@interface CodePushTelemetryManager : NSObject
113113

114114
+ (NSDictionary *)getBinaryUpdateReport:(NSString *)appVersion;
115+
+ (NSDictionary *)getRetryStatusReport;
115116
+ (NSDictionary *)getRollbackReport:(NSDictionary *)lastFailedPackage;
116117
+ (NSDictionary *)getUpdateReport:(NSDictionary *)currentPackage;
118+
+ (void)recordStatusReported:(NSDictionary *)statusReport;
119+
+ (void)saveStatusReportForRetry:(NSDictionary *)statusReport;
117120

118121
@end
119122

ios/CodePush/CodePush.m

+22-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ @implementation CodePush {
1515
BOOL _isFirstRunAfterUpdate;
1616
int _minimumBackgroundDuration;
1717
NSDate *_lastResignedDate;
18-
18+
1919
// Used to coordinate the dispatching of download progress events to JS.
2020
long long _latestExpectedContentLength;
2121
long long _latestReceivedConentLength;
@@ -507,14 +507,14 @@ - (void)applicationWillResignActive
507507
[mutableUpdatePackage setValue:[CodePushUpdateUtils modifiedDateStringOfFileAtURL:binaryBundleURL]
508508
forKey:BinaryBundleDateKey];
509509
}
510-
510+
511511
if (notifyProgress) {
512512
// Set up and unpause the frame observer so that it can emit
513513
// progress events every frame if the progress is updated.
514514
_didUpdateProgress = NO;
515515
_paused = NO;
516516
}
517-
517+
518518
[CodePushPackage
519519
downloadPackage:mutableUpdatePackage
520520
expectedBundleFileName:[bundleResourceName stringByAppendingPathExtension:bundleResourceExtension]
@@ -525,7 +525,7 @@ - (void)applicationWillResignActive
525525
_latestExpectedContentLength = expectedContentLength;
526526
_latestReceivedConentLength = receivedContentLength;
527527
_didUpdateProgress = YES;
528-
528+
529529
// If the download is completed, stop observing frame
530530
// updates and synchronously send the last event.
531531
if (expectedContentLength == receivedContentLength) {
@@ -549,7 +549,7 @@ - (void)applicationWillResignActive
549549
if ([CodePushErrorUtils isCodePushError:err]) {
550550
[self saveFailedUpdate:mutableUpdatePackage];
551551
}
552-
552+
553553
// Stop observing frame updates if the download fails.
554554
_didUpdateProgress = NO;
555555
_paused = YES;
@@ -783,19 +783,35 @@ - (void)applicationWillResignActive
783783
NSString *appVersion = [[CodePushConfig current] appVersion];
784784
resolve([CodePushTelemetryManager getBinaryUpdateReport:appVersion]);
785785
return;
786+
} else {
787+
NSDictionary *retryStatusReport = [CodePushTelemetryManager getRetryStatusReport];
788+
if (retryStatusReport) {
789+
resolve(retryStatusReport);
790+
return;
791+
}
786792
}
787793

788794
resolve(nil);
789795
}
790796

797+
RCT_EXPORT_METHOD(recordStatusReported:(NSDictionary *)statusReport)
798+
{
799+
[CodePushTelemetryManager recordStatusReported:statusReport];
800+
}
801+
802+
RCT_EXPORT_METHOD(saveStatusReportForRetry:(NSDictionary *)statusReport)
803+
{
804+
[CodePushTelemetryManager saveStatusReportForRetry:statusReport];
805+
}
806+
791807
#pragma mark - RCTFrameUpdateObserver Methods
792808

793809
- (void)didUpdateFrame:(RCTFrameUpdate *)update
794810
{
795811
if (!_didUpdateProgress) {
796812
return;
797813
}
798-
814+
799815
[self dispatchDownloadProgressEvent];
800816
_didUpdateProgress = NO;
801817
}

0 commit comments

Comments
 (0)