-
-
Notifications
You must be signed in to change notification settings - Fork 735
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: users logged out after SDK upgrade due to different cache path; …
…this fixes the bug that was introduced with release 3.0.0 which ignores SDK-internal data that is stored locally on the client side (#1168)
- Loading branch information
1 parent
4842329
commit ec7bd03
Showing
5 changed files
with
344 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
parse/src/main/java/com/parse/ParseCacheDirMigrationUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package com.parse; | ||
|
||
import android.content.Context; | ||
import java.io.File; | ||
import java.util.ArrayList; | ||
|
||
/** | ||
* The {@code ParseMigrationUtils} class perform caching dir migration operation for {@code Parse} | ||
* SDK. | ||
*/ | ||
public class ParseCacheDirMigrationUtils { | ||
private final String TAG = this.getClass().getName(); | ||
private final Object lock = new Object(); | ||
private final Context context; | ||
|
||
protected ParseCacheDirMigrationUtils(Context context) { | ||
this.context = context; | ||
} | ||
|
||
/*Start old data migrations to new respective locations ("/files/com.parse/", "/cache/com.parse/")*/ | ||
protected void runMigrations() { | ||
synchronized (lock) { | ||
runSilentMigration(context); | ||
} | ||
} | ||
|
||
private void runSilentMigration(Context context) { | ||
ArrayList<File> filesToBeMigrated = new ArrayList<>(); | ||
ParseFileUtils.getAllNestedFiles( | ||
getOldParseDir(context).getAbsolutePath(), filesToBeMigrated); | ||
if (filesToBeMigrated.isEmpty()) { | ||
return; | ||
} | ||
boolean useFilesDir = false; | ||
// Hard coded config file names list. | ||
String[] configNamesList = { | ||
"installationId", | ||
"currentUser", | ||
"currentConfig", | ||
"currentInstallation", | ||
"LocalId", | ||
"pushState" | ||
}; | ||
// Start migration for each files in `allFiles`. | ||
for (File itemToMove : filesToBeMigrated) { | ||
try { | ||
for (String configName : configNamesList) { | ||
if (itemToMove.getAbsolutePath().contains(configName)) { | ||
useFilesDir = true; | ||
break; | ||
} else { | ||
useFilesDir = false; | ||
} | ||
} | ||
File fileToSave = | ||
new File( | ||
(useFilesDir ? context.getFilesDir() : context.getCacheDir()) | ||
+ "/com.parse/" | ||
+ getFileOldDir(context, itemToMove), | ||
itemToMove.getName()); | ||
// Perform copy operation if file doesn't exist in the new directory. | ||
if (!fileToSave.exists()) { | ||
ParseFileUtils.copyFile(itemToMove, fileToSave); | ||
logMigrationStatus( | ||
itemToMove.getName(), | ||
itemToMove.getPath(), | ||
fileToSave.getAbsolutePath(), | ||
"Successful."); | ||
} else { | ||
logMigrationStatus( | ||
itemToMove.getName(), | ||
itemToMove.getPath(), | ||
fileToSave.getAbsolutePath(), | ||
"Already exist in new location."); | ||
} | ||
ParseFileUtils.deleteQuietly(itemToMove); | ||
PLog.v(TAG, "File deleted: " + "{" + itemToMove.getName() + "}" + " successfully"); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
// Check again, if all files has been resolved or not. If yes, delete the old dir | ||
// "app_Parse". | ||
filesToBeMigrated.clear(); | ||
ParseFileUtils.getAllNestedFiles( | ||
getOldParseDir(context).getAbsolutePath(), filesToBeMigrated); | ||
if (filesToBeMigrated.isEmpty()) { | ||
try { | ||
ParseFileUtils.deleteDirectory(getOldParseDir(context)); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
PLog.v(TAG, "Migration completed."); | ||
} | ||
|
||
private String getFileOldDir(Context context, File file) { | ||
// Parse the old sub directory name where the file should be moved (new location) by | ||
// following the old sub directory name. | ||
String temp = | ||
file.getAbsolutePath() | ||
.replace(getOldParseDir(context).getAbsolutePath(), "") | ||
.replace("/" + file.getName(), ""); | ||
// Before returning the path, replace file name from the last, eg. dir name & file name | ||
// could be same, as we want to get only dir name. | ||
return replaceLast(temp, file.getName()); | ||
} | ||
|
||
private void logMigrationStatus( | ||
String fileName, String oldPath, String newPath, String status) { | ||
PLog.v( | ||
TAG, | ||
"Migration for file: " | ||
+ "{" | ||
+ fileName | ||
+ "}" | ||
+ " from {" | ||
+ oldPath | ||
+ "} to {" | ||
+ newPath | ||
+ "}, Status: " | ||
+ status); | ||
} | ||
|
||
/*Replace a given string from the last*/ | ||
private String replaceLast(String text, String regex) { | ||
return text.replaceFirst("(?s)" + regex + "(?!.*?" + regex + ")", ""); | ||
} | ||
|
||
private File getOldParseDir(Context context) { | ||
return context.getDir("Parse", Context.MODE_PRIVATE); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
152 changes: 152 additions & 0 deletions
152
parse/src/test/java/com/parse/ParseCacheDirMigrationUtilsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
package com.parse; | ||
|
||
import android.content.Context; | ||
import androidx.test.platform.app.InstrumentationRegistry; | ||
import java.io.File; | ||
import java.util.ArrayList; | ||
import org.junit.After; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.robolectric.RobolectricTestRunner; | ||
|
||
@RunWith(RobolectricTestRunner.class) | ||
public class ParseCacheDirMigrationUtilsTest { | ||
ArrayList<File> writtenFiles = new ArrayList<>(); | ||
private ParseCacheDirMigrationUtils utils; | ||
|
||
@Before | ||
public void setUp() throws Exception { | ||
utils = | ||
new ParseCacheDirMigrationUtils( | ||
InstrumentationRegistry.getInstrumentation().getContext()); | ||
writtenFiles.clear(); | ||
} | ||
|
||
@After | ||
public void tearDown() throws Exception { | ||
writtenFiles.clear(); | ||
} | ||
|
||
@Test | ||
public void testMigrationOnParseSDKInitialization() { | ||
prepareForMockFilesWriting(); | ||
writtenFiles.addAll(writeSomeMockFiles(true)); | ||
Parse.Configuration configuration = | ||
new Parse.Configuration.Builder( | ||
InstrumentationRegistry.getInstrumentation().getContext()) | ||
.applicationId(BuildConfig.LIBRARY_PACKAGE_NAME) | ||
.server("https://api.parse.com/1") | ||
.enableLocalDataStore() | ||
.build(); | ||
Parse.initialize(configuration); | ||
} | ||
|
||
@Test | ||
public void testMockMigration() { | ||
prepareForMockFilesWriting(); | ||
writtenFiles.addAll(writeSomeMockFiles(true)); | ||
|
||
// Run migration. | ||
utils.runMigrations(); | ||
|
||
// Check for cache file after migration. | ||
File cacheDir = InstrumentationRegistry.getInstrumentation().getContext().getCacheDir(); | ||
ArrayList<File> migratedCaches = new ArrayList<>(); | ||
ParseFileUtils.getAllNestedFiles(cacheDir.getAbsolutePath(), migratedCaches); | ||
|
||
// Check for files file after migration. | ||
File filesDir = InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(); | ||
ArrayList<File> migratedFiles = new ArrayList<>(); | ||
ParseFileUtils.getAllNestedFiles(filesDir.getAbsolutePath(), migratedFiles); | ||
|
||
// To check migrations result | ||
int sizeAfterMigration = (migratedCaches.size() + migratedFiles.size()); | ||
int sizeBeforeMigrations = writtenFiles.size(); | ||
|
||
assert (cacheDir.exists() && !migratedCaches.isEmpty()); | ||
assert (filesDir.exists() && !migratedFiles.isEmpty()); | ||
assert sizeBeforeMigrations == sizeAfterMigration; | ||
} | ||
|
||
private void prepareForMockFilesWriting() { | ||
// Delete `"app_Parse"` dir including nested dir and files. | ||
try { | ||
ParseFileUtils.deleteDirectory( | ||
InstrumentationRegistry.getInstrumentation() | ||
.getContext() | ||
.getDir("Parse", Context.MODE_PRIVATE)); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
writtenFiles.clear(); | ||
// Create new `"app_Parse"` dir to write some files. | ||
createFileDir(InstrumentationRegistry.getInstrumentation().getContext().getCacheDir()); | ||
} | ||
|
||
private ArrayList<File> writeSomeMockFiles(Boolean checkForExistingFile) { | ||
ArrayList<File> fileToReturn = new ArrayList<>(); | ||
File oldRef = | ||
InstrumentationRegistry.getInstrumentation() | ||
.getContext() | ||
.getDir("Parse", Context.MODE_PRIVATE); | ||
|
||
// Writing some config & random files for migration process. | ||
File config = new File(oldRef + "/config/", "config"); | ||
fileToReturn.add(config); | ||
File installationId = new File(oldRef + "/CommandCache/", "installationId"); | ||
fileToReturn.add(installationId); | ||
File currentConfig = new File(oldRef + "/", "currentConfig"); | ||
fileToReturn.add(currentConfig); | ||
File currentInstallation = new File(oldRef + "/", "currentInstallation"); | ||
fileToReturn.add(currentInstallation); | ||
File pushState = new File(oldRef + "/push/", "pushState"); | ||
fileToReturn.add(pushState); | ||
File localId = new File(oldRef + "/LocalId/", "LocalId"); | ||
fileToReturn.add(localId); | ||
File cache = new File(oldRef + "/testcache/", "cache"); | ||
fileToReturn.add(cache); | ||
File cache1 = new File(oldRef + "/testcache/", "cache1"); | ||
fileToReturn.add(cache1); | ||
File cache2 = new File(oldRef + "/testcache/another/", "cache4"); | ||
fileToReturn.add(cache2); | ||
File user = new File(oldRef + "/user/", "user_config"); | ||
fileToReturn.add(user); | ||
|
||
// Write all listed files to the app cache ("app_Parse") directory. | ||
for (File item : fileToReturn) { | ||
try { | ||
ParseFileUtils.writeStringToFile(item, "gger", "UTF-8"); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
// To create a file conflict scenario during migration by creating an existing file to the | ||
// new files dir ("*/files/com.parse/*"). | ||
if (checkForExistingFile) { | ||
try { | ||
ParseFileUtils.writeStringToFile( | ||
new File( | ||
InstrumentationRegistry.getInstrumentation() | ||
.getContext() | ||
.getFilesDir() | ||
+ "/com.parse/CommandCache/", | ||
"installationId"), | ||
"gger", | ||
"UTF-8"); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
return fileToReturn; | ||
} | ||
|
||
private File createFileDir(File file) { | ||
if (!file.exists()) { | ||
if (!file.mkdirs()) { | ||
return file; | ||
} | ||
} | ||
return file; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters