diff --git a/CodePush.js b/CodePush.js index 993a412..cfe8f3d 100644 --- a/CodePush.js +++ b/CodePush.js @@ -103,9 +103,9 @@ function getPromisifiedSdk(requestFetchAdapter, config) { }); }; - sdk.reportStatusDeploy = (deployedPackage, status) => { + sdk.reportStatusDeploy = (deployedPackage, status, fromLabelOrAppVersion, fromDeploymentKey) => { return new Promise((resolve, reject) => { - module.exports.AcquisitionSdk.prototype.reportStatusDeploy.call(sdk, deployedPackage, status, (err) => { + module.exports.AcquisitionSdk.prototype.reportStatusDeploy.call(sdk, deployedPackage, status, fromLabelOrAppVersion, fromDeploymentKey, (err) => { if (err) { reject(err); } else { @@ -140,13 +140,15 @@ async function notifyApplicationReady() { const statusReport = await NativeCodePush.getNewStatusReport(); if (statusReport) { const config = await getConfiguration(); + const fromLabelOrAppVersion = statusReport.fromLabelOrAppVersion; + const fromDeploymentKey = statusReport.fromDeploymentKey || config.deploymentKey; if (statusReport.appVersion) { const sdk = getPromisifiedSdk(requestFetchAdapter, config); - sdk.reportStatusDeploy(); + sdk.reportStatusDeploy(/* deployedPackage */ null, /* status */ null, fromLabelOrAppVersion, fromDeploymentKey); } else { config.deploymentKey = statusReport.package.deploymentKey; const sdk = getPromisifiedSdk(requestFetchAdapter, config); - sdk.reportStatusDeploy(statusReport.package, statusReport.status); + sdk.reportStatusDeploy(statusReport.package, statusReport.status, fromLabelOrAppVersion, fromDeploymentKey); } } } diff --git a/CodePush.m b/CodePush.m index 3b69041..1dd6d5f 100644 --- a/CodePush.m +++ b/CodePush.m @@ -161,6 +161,11 @@ static NSString *const PackageIsPendingKey = @"isPending"; [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (NSString *)getDeploymentKeyFromStatusReportIdentifier:(NSString *)statusReportIdentifier +{ + return [[statusReportIdentifier componentsSeparatedByString:@":"] firstObject]; +} + - (NSString *)getPackageStatusReportIdentifier:(NSDictionary *)package { // Because deploymentKeys can be dynamically switched, we use a @@ -174,6 +179,18 @@ static NSString *const PackageIsPendingKey = @"isPending"; } } +- (NSString *)getPreviousStatusReportIdentifier +{ + NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; + NSString *sentStatusReportIdentifier = [preferences objectForKey:LastDeploymentReportKey]; + return sentStatusReportIdentifier; +} + +- (NSString *)getVersionLabelFromStatusReportIdentifier:(NSString *)statusReportIdentifier +{ + return [[statusReportIdentifier componentsSeparatedByString:@":"] lastObject]; +} + - (instancetype)init { self = [super init]; @@ -212,13 +229,6 @@ static NSString *const PackageIsPendingKey = @"isPending"; } } -- (BOOL)isDeploymentStatusNotYetReported:(NSString *)appVersionOrPackageIdentifier -{ - NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; - NSString *sentStatusReportIdentifier = [preferences objectForKey:LastDeploymentReportKey]; - return sentStatusReportIdentifier == nil || ![sentStatusReportIdentifier isEqualToString:appVersionOrPackageIdentifier]; -} - /* * This method checks to see whether a specific package hash * has previously failed installation. @@ -267,6 +277,11 @@ static NSString *const PackageIsPendingKey = @"isPending"; return updateIsPending; } +- (BOOL)isStatusReportIdentifierCodePushLabel:(NSString *)statusReportIdentifier +{ + return statusReportIdentifier != nil && [statusReportIdentifier containsString:@":"]; +} + /* * This method updates the React Native bridge's bundle URL * to point at the latest CodePush update, and then restarts @@ -542,9 +557,13 @@ RCT_EXPORT_METHOD(getNewStatusReport:(RCTPromiseResolveBlock)resolve NSDictionary *lastFailedPackage = [failedUpdates lastObject]; if (lastFailedPackage) { NSString *lastFailedPackageIdentifier = [self getPackageStatusReportIdentifier:lastFailedPackage]; - if (lastFailedPackageIdentifier && [self isDeploymentStatusNotYetReported:lastFailedPackageIdentifier]) { + NSString *previousStatusReportIdentifier = [self getPreviousStatusReportIdentifier]; + if (lastFailedPackageIdentifier && (previousStatusReportIdentifier == nil || ![previousStatusReportIdentifier isEqualToString:lastFailedPackageIdentifier])) { [self recordDeploymentStatusReported:lastFailedPackageIdentifier]; - resolve(@{ @"package": lastFailedPackage, @"status": DeploymentFailed }); + resolve(@{ + @"package": lastFailedPackage, + @"status": DeploymentFailed + }); return; } } @@ -555,19 +574,64 @@ RCT_EXPORT_METHOD(getNewStatusReport:(RCTPromiseResolveBlock)resolve NSDictionary *currentPackage = [CodePushPackage getCurrentPackage:&error]; if (!error && currentPackage) { NSString *currentPackageIdentifier = [self getPackageStatusReportIdentifier:currentPackage]; - if (currentPackageIdentifier && [self isDeploymentStatusNotYetReported:currentPackageIdentifier]) { - [self recordDeploymentStatusReported:currentPackageIdentifier]; - resolve(@{ @"package": currentPackage, @"status": DeploymentSucceeded }); - return; + NSString *previousStatusReportIdentifier = [self getPreviousStatusReportIdentifier]; + if (currentPackageIdentifier) { + if (previousStatusReportIdentifier == nil) { + [self recordDeploymentStatusReported:currentPackageIdentifier]; + resolve(@{ + @"package": currentPackage, + @"status": DeploymentSucceeded + }); + return; + } else if (![previousStatusReportIdentifier isEqualToString:currentPackageIdentifier]) { + [self recordDeploymentStatusReported:currentPackageIdentifier]; + if ([self isStatusReportIdentifierCodePushLabel:previousStatusReportIdentifier]) { + NSString *fromDeploymentKey = [self getDeploymentKeyFromStatusReportIdentifier:previousStatusReportIdentifier]; + NSString *fromLabel = [self getVersionLabelFromStatusReportIdentifier:previousStatusReportIdentifier]; + resolve(@{ + @"package": currentPackage, + @"status": DeploymentSucceeded, + @"fromDeploymentKey": fromDeploymentKey, + @"fromLabelOrAppVersion": fromLabel + }); + } else { + // Previous status report was with a binary app version. + resolve(@{ + @"package": currentPackage, + @"status": DeploymentSucceeded, + @"fromLabelOrAppVersion": previousStatusReportIdentifier + }); + } + return; + } } } } else if (isRunningBinaryVersion || [_bridge.bundleURL.scheme hasPrefix:@"http"]) { // Check if the current appVersion has been reported. NSString *appVersion = [[CodePushConfig current] appVersion]; - if ([self isDeploymentStatusNotYetReported:appVersion]) { + NSString *previousStatusReportIdentifier = [self getPreviousStatusReportIdentifier]; + if (previousStatusReportIdentifier == nil) { [self recordDeploymentStatusReported:appVersion]; resolve(@{ @"appVersion": appVersion }); return; + } else if (![previousStatusReportIdentifier isEqualToString:appVersion]) { + [self recordDeploymentStatusReported:appVersion]; + if ([self isStatusReportIdentifierCodePushLabel:previousStatusReportIdentifier]) { + NSString *fromDeploymentKey = [self getDeploymentKeyFromStatusReportIdentifier:previousStatusReportIdentifier]; + NSString *fromLabel = [self getVersionLabelFromStatusReportIdentifier:previousStatusReportIdentifier]; + resolve(@{ + @"appVersion": appVersion, + @"fromDeploymentKey": fromDeploymentKey, + @"fromLabelOrAppVersion": fromLabel + }); + } else { + // Previous status report was with a binary app version. + resolve(@{ + @"appVersion": appVersion, + @"fromLabelOrAppVersion": previousStatusReportIdentifier + }); + } + return; } } diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java index 8d7d64a..a7ee3b0 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java @@ -178,13 +178,10 @@ public class CodePush { } } - private String getPackageStatusReportIdentifier(WritableMap updatePackage) { - // Because deploymentKeys can be dynamically switched, we use a - // combination of the deploymentKey and label as the packageIdentifier. - String deploymentKey = CodePushUtils.tryGetString(updatePackage, DEPLOYMENT_KEY_KEY); - String label = CodePushUtils.tryGetString(updatePackage, LABEL_KEY); - if (deploymentKey != null && label != null) { - return deploymentKey + ":" + label; + private String getDeploymentKeyFromStatusReportIdentifier(String statusReportIdentifier) { + String[] parsedIdentifier = statusReportIdentifier.split(":"); + if (parsedIdentifier.length > 0) { + return parsedIdentifier[0]; } else { return null; } @@ -208,6 +205,18 @@ public class CodePush { } } + private String getPackageStatusReportIdentifier(WritableMap updatePackage) { + // Because deploymentKeys can be dynamically switched, we use a + // combination of the deploymentKey and label as the packageIdentifier. + String deploymentKey = CodePushUtils.tryGetString(updatePackage, DEPLOYMENT_KEY_KEY); + String label = CodePushUtils.tryGetString(updatePackage, LABEL_KEY); + if (deploymentKey != null && label != null) { + return deploymentKey + ":" + label; + } else { + return null; + } + } + private JSONObject getPendingUpdate() { SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0); String pendingUpdateString = settings.getString(PENDING_UPDATE_KEY, null); @@ -226,6 +235,20 @@ public class CodePush { } } + private String getPreviousStatusReportIdentifier() { + SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0); + return settings.getString(LAST_DEPLOYMENT_REPORT_KEY, null); + } + + private String getVersionLabelFromStatusReportIdentifier(String statusReportIdentifier) { + String[] parsedIdentifier = statusReportIdentifier.split(":"); + if (parsedIdentifier.length > 1) { + return parsedIdentifier[1]; + } else { + return null; + } + } + public ReactPackage getReactPackage() { if (codePushReactPackage == null) { codePushReactPackage = new CodePushReactPackage(); @@ -262,16 +285,6 @@ public class CodePush { } } - private boolean isDeploymentStatusNotYetReported(String appVersionOrPackageIdentifier) { - SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0); - String lastDeploymentReportIdentifier = settings.getString(LAST_DEPLOYMENT_REPORT_KEY, null); - if (lastDeploymentReportIdentifier == null) { - return true; - } else { - return !lastDeploymentReportIdentifier.equals(appVersionOrPackageIdentifier); - } - } - private boolean isFailedHash(String packageHash) { JSONArray failedUpdates = getFailedUpdates(); if (packageHash != null) { @@ -306,6 +319,10 @@ public class CodePush { } } + private boolean isStatusReportIdentifierCodePushLabel(String statusReportIdentifier) { + return statusReportIdentifier != null && statusReportIdentifier.contains(":"); + } + private void recordDeploymentStatusReported(String appVersionOrPackageIdentifier) { SharedPreferences settings = applicationContext.getSharedPreferences(CODE_PUSH_PREFERENCES, 0); settings.edit().putString(LAST_DEPLOYMENT_REPORT_KEY, appVersionOrPackageIdentifier).commit(); @@ -465,7 +482,8 @@ public class CodePush { JSONObject lastFailedPackageJSON = failedUpdates.getJSONObject(failedUpdates.length() - 1); WritableMap lastFailedPackage = CodePushUtils.convertJsonObjectToWriteable(lastFailedPackageJSON); String lastFailedPackageIdentifier = getPackageStatusReportIdentifier(lastFailedPackage); - if (lastFailedPackage != null && isDeploymentStatusNotYetReported(lastFailedPackageIdentifier)) { + String previousStatusReportIdentifier = getPreviousStatusReportIdentifier(); + if (lastFailedPackage != null && (previousStatusReportIdentifier == null || !previousStatusReportIdentifier.equals(lastFailedPackageIdentifier))) { recordDeploymentStatusReported(lastFailedPackageIdentifier); WritableNativeMap reportMap = new WritableNativeMap(); reportMap.putMap("package", lastFailedPackage); @@ -482,24 +500,65 @@ public class CodePush { WritableMap currentPackage = codePushPackage.getCurrentPackage(); if (currentPackage != null) { String currentPackageIdentifier = getPackageStatusReportIdentifier(currentPackage); - if (currentPackageIdentifier != null && isDeploymentStatusNotYetReported(currentPackageIdentifier)) { - recordDeploymentStatusReported(currentPackageIdentifier); - WritableNativeMap reportMap = new WritableNativeMap(); - reportMap.putMap("package", currentPackage); - reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS); - promise.resolve(reportMap); - return; + String previousStatusReportIdentifier = getPreviousStatusReportIdentifier(); + if (currentPackageIdentifier != null) { + if (previousStatusReportIdentifier == null) { + recordDeploymentStatusReported(currentPackageIdentifier); + WritableNativeMap reportMap = new WritableNativeMap(); + reportMap.putMap("package", currentPackage); + reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS); + promise.resolve(reportMap); + return; + } else if (!previousStatusReportIdentifier.equals(currentPackageIdentifier)) { + recordDeploymentStatusReported(currentPackageIdentifier); + if (isStatusReportIdentifierCodePushLabel(previousStatusReportIdentifier)) { + String fromDeploymentKey = getDeploymentKeyFromStatusReportIdentifier(previousStatusReportIdentifier); + String fromLabel = getVersionLabelFromStatusReportIdentifier(previousStatusReportIdentifier); + WritableNativeMap reportMap = new WritableNativeMap(); + reportMap.putMap("package", currentPackage); + reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS); + reportMap.putString("fromDeploymentKey", fromDeploymentKey); + reportMap.putString("fromLabelOrAppVersion", fromLabel); + promise.resolve(reportMap); + } else { + // Previous status report was with a binary app version. + WritableNativeMap reportMap = new WritableNativeMap(); + reportMap.putMap("package", currentPackage); + reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS); + reportMap.putString("fromLabelOrAppVersion", previousStatusReportIdentifier); + promise.resolve(reportMap); + } + return; + } } } } else if (isRunningBinaryVersion) { // Check if the current appVersion has been reported. - String binaryIdentifier = "" + getBinaryResourcesModifiedTime(); - if (isDeploymentStatusNotYetReported(binaryIdentifier)) { - recordDeploymentStatusReported(binaryIdentifier); + String previousStatusReportIdentifier = getPreviousStatusReportIdentifier(); + if (previousStatusReportIdentifier == null) { + recordDeploymentStatusReported(appVersion); WritableNativeMap reportMap = new WritableNativeMap(); reportMap.putString("appVersion", appVersion); promise.resolve(reportMap); return; + } else if (!previousStatusReportIdentifier.equals(appVersion)) { + recordDeploymentStatusReported(appVersion); + if (isStatusReportIdentifierCodePushLabel(previousStatusReportIdentifier)) { + String fromDeploymentKey = getDeploymentKeyFromStatusReportIdentifier(previousStatusReportIdentifier); + String fromLabel = getVersionLabelFromStatusReportIdentifier(previousStatusReportIdentifier); + WritableNativeMap reportMap = new WritableNativeMap(); + reportMap.putString("appVersion", appVersion); + reportMap.putString("fromDeploymentKey", fromDeploymentKey); + reportMap.putString("fromLabelOrAppVersion", fromLabel); + promise.resolve(reportMap); + } else { + // Previous status report was with a binary app version. + WritableNativeMap reportMap = new WritableNativeMap(); + reportMap.putString("appVersion", appVersion); + reportMap.putString("fromLabelOrAppVersion", previousStatusReportIdentifier); + promise.resolve(reportMap); + } + return; } } diff --git a/package.json b/package.json index 38417f8..fa523d6 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "url": "https://github.com/Microsoft/react-native-code-push" }, "dependencies": { - "code-push": "1.5.1-beta", + "code-push": "1.5.2-beta", "semver": "^5.1.0" }, "devDependencies": { diff --git a/request-fetch-adapter.js b/request-fetch-adapter.js index eeeac81..abcf541 100644 --- a/request-fetch-adapter.js +++ b/request-fetch-adapter.js @@ -4,10 +4,11 @@ module.exports = { callback = requestBody; requestBody = null; } - - var headers = { + + const headers = { "Accept": "application/json", - "Content-Type": "application/json" + "Content-Type": "application/json", + "X-SDK-Version": getSDKVersion() }; if (requestBody && typeof requestBody === "object") { @@ -30,6 +31,10 @@ module.exports = { } }; +function getSDKVersion() { + return require("./package.json").dependencies["code-push"]; +} + function getHttpMethodName(verb) { // Note: This should stay in sync with the enum definition in // https://github.com/Microsoft/code-push/blob/master/sdk/script/acquisition-sdk.ts#L6