@fto-consult/expo-ui 6.37.3 → 6.37.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/App.js +0 -1
- package/node_modules/.package-lock.json +26218 -26188
- package/node_modules/@fto-consult/common/babel.config.alias.js +18 -1
- package/node_modules/@fto-consult/common/package.json +1 -1
- package/node_modules/@react-native-async-storage/async-storage/LICENSE +21 -0
- package/node_modules/@react-native-async-storage/async-storage/README.md +27 -0
- package/node_modules/@react-native-async-storage/async-storage/RNCAsyncStorage.podspec +19 -0
- package/node_modules/@react-native-async-storage/async-storage/android/build.gradle +142 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/AndroidManifest.xml +6 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncLocalStorageUtil.java +178 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageErrorUtil.java +45 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageExpoMigration.java +154 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStorageModule.java +424 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/AsyncStoragePackage.java +58 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/ReactDatabaseSupplier.java +163 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/SerialExecutor.java +40 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/ArgumentHelpers.kt +86 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/ErrorHelpers.kt +39 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageModule.kt +90 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/main/java/com/reactnativecommunity/asyncstorage/next/StorageSupplier.kt +161 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/test/java/com/reactnativecommunity/asyncstorage/next/ArgumentHelpersTest.kt +93 -0
- package/node_modules/@react-native-async-storage/async-storage/android/src/test/java/com/reactnativecommunity/asyncstorage/next/StorageTest.kt +141 -0
- package/node_modules/@react-native-async-storage/async-storage/android/testresults.gradle +38 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.h +51 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.m +898 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorage.xcodeproj/project.pbxproj +283 -0
- package/node_modules/@react-native-async-storage/async-storage/ios/RNCAsyncStorageDelegate.h +73 -0
- package/node_modules/@react-native-async-storage/async-storage/jest/async-storage-mock.d.ts +9 -0
- package/node_modules/@react-native-async-storage/async-storage/jest/async-storage-mock.js +109 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js +164 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.native.js +366 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/AsyncStorage.native.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/RCTAsyncStorage.js +30 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/RCTAsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/helpers.js +69 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/helpers.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/hooks.js +44 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/hooks.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/index.js +22 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/index.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/shouldFallbackToLegacyNativeModule.js +39 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/shouldFallbackToLegacyNativeModule.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/types.js +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/commonjs/types.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.js +153 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.native.js +348 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/AsyncStorage.native.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/RCTAsyncStorage.js +20 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/RCTAsyncStorage.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/helpers.js +56 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/helpers.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/hooks.js +34 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/hooks.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/index.js +4 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/index.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/shouldFallbackToLegacyNativeModule.js +31 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/shouldFallbackToLegacyNativeModule.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/types.js +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/module/types.js.map +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/AsyncStorage.d.ts +10 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/AsyncStorage.native.d.ts +16 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/RCTAsyncStorage.d.ts +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/helpers.d.ts +5 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/hooks.d.ts +2 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/index.d.ts +4 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/shouldFallbackToLegacyNativeModule.d.ts +1 -0
- package/node_modules/@react-native-async-storage/async-storage/lib/typescript/types.d.ts +113 -0
- package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/project.pbxproj +385 -0
- package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/xcshareddata/xcschemes/RNCAsyncStorage-macOS.xcscheme +67 -0
- package/node_modules/@react-native-async-storage/async-storage/macos/RNCAsyncStorage.xcodeproj/xcshareddata/xcschemes/RNCAsyncStorage.xcscheme +67 -0
- package/node_modules/@react-native-async-storage/async-storage/package.json +197 -0
- package/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.native.ts +356 -0
- package/node_modules/@react-native-async-storage/async-storage/src/AsyncStorage.ts +173 -0
- package/node_modules/@react-native-async-storage/async-storage/src/RCTAsyncStorage.ts +28 -0
- package/node_modules/@react-native-async-storage/async-storage/src/helpers.ts +74 -0
- package/node_modules/@react-native-async-storage/async-storage/src/hooks.ts +11 -0
- package/node_modules/@react-native-async-storage/async-storage/src/index.ts +7 -0
- package/node_modules/@react-native-async-storage/async-storage/src/shouldFallbackToLegacyNativeModule.ts +34 -0
- package/node_modules/@react-native-async-storage/async-storage/src/types.ts +155 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/PropertySheet.props +16 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/ReactNativeAsyncStorage.vcxproj +172 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/ReactNativeAsyncStorage.vcxproj.filters +34 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage/packages.config +4 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage.sln +172 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/PropertySheet.props +16 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/ReactNativeAsyncStorage61.vcxproj +157 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/ReactNativeAsyncStorage61.vcxproj.filters +34 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61/packages.config +4 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage61.sln +195 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/ReactNativeAsyncStorage62.sln +192 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/DBStorage.cpp +599 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/DBStorage.h +162 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/RNCAsyncStorage.h +118 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactNativeAsyncStorage.def +3 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.cpp +20 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.h +23 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/ReactPackageProvider.idl +7 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/pch.cpp +3 -0
- package/node_modules/@react-native-async-storage/async-storage/windows/code/pch.h +15 -0
- package/node_modules/merge-options/index.d.ts +2 -0
- package/node_modules/merge-options/index.js +171 -0
- package/node_modules/merge-options/index.mjs +8 -0
- package/node_modules/merge-options/license +21 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/index.d.ts +29 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/index.js +10 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/license +9 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/package.json +38 -0
- package/node_modules/merge-options/node_modules/is-plain-obj/readme.md +54 -0
- package/node_modules/merge-options/package.json +59 -0
- package/node_modules/merge-options/readme.md +130 -0
- package/package.json +131 -130
- package/src/components/Chart/appexChart/appexChart.html +23 -23
@@ -0,0 +1,86 @@
|
|
1
|
+
package com.reactnativecommunity.asyncstorage.next
|
2
|
+
|
3
|
+
import com.facebook.react.bridge.Arguments
|
4
|
+
import com.facebook.react.bridge.ReadableArray
|
5
|
+
import org.json.JSONException
|
6
|
+
import org.json.JSONObject
|
7
|
+
|
8
|
+
fun ReadableArray.toEntryList(): List<Entry> {
|
9
|
+
val list = mutableListOf<Entry>()
|
10
|
+
for (keyValue in this.toArrayList()) {
|
11
|
+
if (keyValue !is ArrayList<*> || keyValue.size != 2) {
|
12
|
+
throw AsyncStorageError.invalidKeyValueFormat()
|
13
|
+
}
|
14
|
+
val key = keyValue[0]
|
15
|
+
val value = keyValue[1]
|
16
|
+
|
17
|
+
if (key !is String) {
|
18
|
+
when (key) {
|
19
|
+
null -> throw AsyncStorageError.keyIsNull()
|
20
|
+
else -> throw AsyncStorageError.keyNotString()
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
if (value !is String) {
|
25
|
+
throw AsyncStorageError.valueNotString(key)
|
26
|
+
}
|
27
|
+
|
28
|
+
list.add(Entry(key, value))
|
29
|
+
}
|
30
|
+
return list
|
31
|
+
}
|
32
|
+
|
33
|
+
fun ReadableArray.toKeyList(): List<String> {
|
34
|
+
val list = this.toArrayList()
|
35
|
+
|
36
|
+
for (item in list) {
|
37
|
+
if (item !is String) {
|
38
|
+
throw AsyncStorageError.keyNotString()
|
39
|
+
}
|
40
|
+
}
|
41
|
+
return list as List<String>
|
42
|
+
}
|
43
|
+
|
44
|
+
fun List<Entry>.toKeyValueArgument(): ReadableArray {
|
45
|
+
val args = Arguments.createArray()
|
46
|
+
|
47
|
+
for (entry in this) {
|
48
|
+
val keyValue = Arguments.createArray()
|
49
|
+
keyValue.pushString(entry.key)
|
50
|
+
keyValue.pushString(entry.value)
|
51
|
+
args.pushArray(keyValue)
|
52
|
+
}
|
53
|
+
|
54
|
+
return args
|
55
|
+
}
|
56
|
+
|
57
|
+
fun String?.isValidJson(): Boolean {
|
58
|
+
if (this == null) return false
|
59
|
+
|
60
|
+
return try {
|
61
|
+
JSONObject(this)
|
62
|
+
true
|
63
|
+
} catch (e: JSONException) {
|
64
|
+
false
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
fun JSONObject.mergeWith(newObject: JSONObject): JSONObject {
|
69
|
+
|
70
|
+
val keys = newObject.keys()
|
71
|
+
val mergedObject = JSONObject(this.toString())
|
72
|
+
|
73
|
+
while (keys.hasNext()) {
|
74
|
+
val key = keys.next()
|
75
|
+
val curValue = this.optJSONObject(key)
|
76
|
+
val newValue = newObject.optJSONObject(key)
|
77
|
+
|
78
|
+
if (curValue != null && newValue != null) {
|
79
|
+
val merged = curValue.mergeWith(newValue)
|
80
|
+
mergedObject.put(key, merged)
|
81
|
+
} else {
|
82
|
+
mergedObject.put(key, newObject.get(key))
|
83
|
+
}
|
84
|
+
}
|
85
|
+
return mergedObject
|
86
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
package com.reactnativecommunity.asyncstorage.next
|
2
|
+
|
3
|
+
import com.facebook.react.bridge.Arguments
|
4
|
+
import com.facebook.react.bridge.Callback
|
5
|
+
import kotlinx.coroutines.CoroutineExceptionHandler
|
6
|
+
|
7
|
+
internal fun createExceptionHandler(cb: Callback): CoroutineExceptionHandler {
|
8
|
+
return CoroutineExceptionHandler { _, throwable ->
|
9
|
+
val error = Arguments.createMap()
|
10
|
+
if (throwable !is AsyncStorageError) {
|
11
|
+
error.putString(
|
12
|
+
"message", "Unexpected AsyncStorage error: ${throwable.localizedMessage}"
|
13
|
+
)
|
14
|
+
} else {
|
15
|
+
error.putString("message", throwable.errorMessage)
|
16
|
+
}
|
17
|
+
|
18
|
+
cb(error)
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
internal class AsyncStorageError private constructor(val errorMessage: String) :
|
23
|
+
Throwable(errorMessage) {
|
24
|
+
|
25
|
+
companion object {
|
26
|
+
fun keyIsNull() = AsyncStorageError("Key cannot be null.")
|
27
|
+
|
28
|
+
fun keyNotString() = AsyncStorageError("Provided key is not string. Only strings are supported as storage key.")
|
29
|
+
|
30
|
+
fun valueNotString(key: String?): AsyncStorageError {
|
31
|
+
val detail = if (key == null) "Provided value" else "Value for key \"$key\""
|
32
|
+
return AsyncStorageError("$detail is not a string. Only strings are supported as a value.")
|
33
|
+
}
|
34
|
+
|
35
|
+
fun invalidKeyValueFormat() =
|
36
|
+
AsyncStorageError("Invalid key-value format. Expected a list of [key, value] list.")
|
37
|
+
|
38
|
+
}
|
39
|
+
}
|
@@ -0,0 +1,90 @@
|
|
1
|
+
package com.reactnativecommunity.asyncstorage.next
|
2
|
+
|
3
|
+
import android.content.Context
|
4
|
+
import androidx.annotation.VisibleForTesting
|
5
|
+
import com.facebook.react.bridge.Arguments
|
6
|
+
import com.facebook.react.bridge.Callback
|
7
|
+
import com.facebook.react.bridge.ReactContext
|
8
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
9
|
+
import com.facebook.react.bridge.ReactMethod
|
10
|
+
import com.facebook.react.bridge.ReadableArray
|
11
|
+
import com.reactnativecommunity.asyncstorage.SerialExecutor
|
12
|
+
import kotlinx.coroutines.CoroutineName
|
13
|
+
import kotlinx.coroutines.CoroutineScope
|
14
|
+
import kotlinx.coroutines.Dispatchers
|
15
|
+
import kotlinx.coroutines.SupervisorJob
|
16
|
+
import kotlinx.coroutines.asExecutor
|
17
|
+
import kotlinx.coroutines.launch
|
18
|
+
|
19
|
+
class StorageModule(reactContext: ReactContext) : ReactContextBaseJavaModule(), CoroutineScope {
|
20
|
+
override fun getName() = "RNC_AsyncSQLiteDBStorage"
|
21
|
+
|
22
|
+
// this executor is not used by the module, but it must exists here due to
|
23
|
+
// Detox relying on this implementation detail to run
|
24
|
+
@VisibleForTesting
|
25
|
+
private val executor = SerialExecutor(Dispatchers.Main.asExecutor())
|
26
|
+
|
27
|
+
override val coroutineContext =
|
28
|
+
Dispatchers.IO + CoroutineName("AsyncStorageScope") + SupervisorJob()
|
29
|
+
|
30
|
+
private val storage = StorageSupplier.getInstance(reactContext)
|
31
|
+
|
32
|
+
companion object {
|
33
|
+
@JvmStatic
|
34
|
+
fun getStorageInstance(ctx: Context): AsyncStorageAccess {
|
35
|
+
return StorageSupplier.getInstance(ctx)
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
@ReactMethod
|
40
|
+
fun multiGet(keys: ReadableArray, cb: Callback) {
|
41
|
+
launch(createExceptionHandler(cb)) {
|
42
|
+
val entries = storage.getValues(keys.toKeyList())
|
43
|
+
cb(null, entries.toKeyValueArgument())
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
@ReactMethod
|
48
|
+
fun multiSet(keyValueArray: ReadableArray, cb: Callback) {
|
49
|
+
launch(createExceptionHandler(cb)) {
|
50
|
+
val entries = keyValueArray.toEntryList()
|
51
|
+
storage.setValues(entries)
|
52
|
+
cb(null)
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
@ReactMethod
|
57
|
+
fun multiRemove(keys: ReadableArray, cb: Callback) {
|
58
|
+
launch(createExceptionHandler(cb)) {
|
59
|
+
storage.removeValues(keys.toKeyList())
|
60
|
+
cb(null)
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
@ReactMethod
|
65
|
+
fun multiMerge(keyValueArray: ReadableArray, cb: Callback) {
|
66
|
+
launch(createExceptionHandler(cb)) {
|
67
|
+
val entries = keyValueArray.toEntryList()
|
68
|
+
storage.mergeValues(entries)
|
69
|
+
cb(null)
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
@ReactMethod
|
74
|
+
fun getAllKeys(cb: Callback) {
|
75
|
+
launch(createExceptionHandler(cb)) {
|
76
|
+
val keys = storage.getKeys()
|
77
|
+
val result = Arguments.createArray()
|
78
|
+
keys.forEach { result.pushString(it) }
|
79
|
+
cb.invoke(null, result)
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
@ReactMethod
|
84
|
+
fun clear(cb: Callback) {
|
85
|
+
launch(createExceptionHandler(cb)) {
|
86
|
+
storage.clear()
|
87
|
+
cb(null)
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
@@ -0,0 +1,161 @@
|
|
1
|
+
package com.reactnativecommunity.asyncstorage.next
|
2
|
+
|
3
|
+
import android.content.Context
|
4
|
+
import android.util.Log
|
5
|
+
import androidx.room.ColumnInfo
|
6
|
+
import androidx.room.Dao
|
7
|
+
import androidx.room.Database
|
8
|
+
import androidx.room.Entity
|
9
|
+
import androidx.room.Insert
|
10
|
+
import androidx.room.OnConflictStrategy
|
11
|
+
import androidx.room.PrimaryKey
|
12
|
+
import androidx.room.Query
|
13
|
+
import androidx.room.Room
|
14
|
+
import androidx.room.RoomDatabase
|
15
|
+
import androidx.room.Transaction
|
16
|
+
import androidx.room.migration.Migration
|
17
|
+
import androidx.sqlite.db.SupportSQLiteDatabase
|
18
|
+
import org.json.JSONObject
|
19
|
+
|
20
|
+
private const val DATABASE_VERSION = 2
|
21
|
+
private const val DATABASE_NAME = "AsyncStorage"
|
22
|
+
private const val TABLE_NAME = "Storage"
|
23
|
+
private const val COLUMN_KEY = "key"
|
24
|
+
private const val COLUMN_VALUE = "value"
|
25
|
+
|
26
|
+
|
27
|
+
@Entity(tableName = TABLE_NAME)
|
28
|
+
data class Entry(
|
29
|
+
@PrimaryKey @ColumnInfo(name = COLUMN_KEY) val key: String,
|
30
|
+
@ColumnInfo(name = COLUMN_VALUE) val value: String?
|
31
|
+
)
|
32
|
+
|
33
|
+
@Dao
|
34
|
+
internal interface StorageDao {
|
35
|
+
|
36
|
+
@Transaction
|
37
|
+
@Query("SELECT * FROM $TABLE_NAME WHERE `$COLUMN_KEY` IN (:keys)")
|
38
|
+
suspend fun getValues(keys: List<String>): List<Entry>
|
39
|
+
|
40
|
+
@Transaction
|
41
|
+
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
42
|
+
suspend fun setValues(entries: List<Entry>)
|
43
|
+
|
44
|
+
@Transaction
|
45
|
+
@Query("DELETE FROM $TABLE_NAME WHERE `$COLUMN_KEY` in (:keys)")
|
46
|
+
suspend fun removeValues(keys: List<String>)
|
47
|
+
|
48
|
+
@Transaction
|
49
|
+
suspend fun mergeValues(entries: List<Entry>) {
|
50
|
+
val currentDbEntries = getValues(entries.map { it.key })
|
51
|
+
val newEntries = mutableListOf<Entry>()
|
52
|
+
|
53
|
+
entries.forEach { newEntry ->
|
54
|
+
val oldEntry = currentDbEntries.find { it.key == newEntry.key }
|
55
|
+
if (oldEntry?.value == null) {
|
56
|
+
newEntries.add(newEntry)
|
57
|
+
} else if (!oldEntry.value.isValidJson() || !newEntry.value.isValidJson()) {
|
58
|
+
newEntries.add(newEntry)
|
59
|
+
} else {
|
60
|
+
val newValue =
|
61
|
+
JSONObject(oldEntry.value).mergeWith(JSONObject(newEntry.value)).toString()
|
62
|
+
newEntries.add(newEntry.copy(value = newValue))
|
63
|
+
}
|
64
|
+
}
|
65
|
+
setValues(newEntries)
|
66
|
+
}
|
67
|
+
|
68
|
+
@Transaction
|
69
|
+
@Query("SELECT `$COLUMN_KEY` FROM $TABLE_NAME")
|
70
|
+
suspend fun getKeys(): List<String>
|
71
|
+
|
72
|
+
@Transaction
|
73
|
+
@Query("DELETE FROM $TABLE_NAME")
|
74
|
+
suspend fun clear()
|
75
|
+
}
|
76
|
+
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Previous version of AsyncStorage is violating the SQL standard (based on bug in SQLite),
|
80
|
+
* where PrimaryKey ('key' column) should never be null (https://www.sqlite.org/lang_createtable.html#the_primary_key).
|
81
|
+
* Because of that, we cannot reuse the old DB, because ROOM is guarded against that case (won't compile).
|
82
|
+
*
|
83
|
+
* In order to work around this, two steps are necessary:
|
84
|
+
* - Room DB pre-population from the old database file (https://developer.android.com/training/data-storage/room/prepopulate#from-asset)
|
85
|
+
* - Version migration, so that we can mark 'key' column as NOT-NULL
|
86
|
+
*
|
87
|
+
* This migration will happens only once, when developer enable this feature (when DB is still not created).
|
88
|
+
*/
|
89
|
+
@Suppress("ClassName")
|
90
|
+
private object MIGRATION_TO_NEXT : Migration(1, 2) {
|
91
|
+
override fun migrate(database: SupportSQLiteDatabase) {
|
92
|
+
val oldTableName = "catalystLocalStorage" // from ReactDatabaseSupplier
|
93
|
+
database.execSQL("CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`${COLUMN_KEY}` TEXT NOT NULL, `${COLUMN_VALUE}` TEXT, PRIMARY KEY(`${COLUMN_KEY}`));")
|
94
|
+
// even if the old AsyncStorage has checks for not nullable keys
|
95
|
+
// make sure we don't copy any, to not fail migration
|
96
|
+
database.execSQL("DELETE FROM $oldTableName WHERE `${COLUMN_KEY}` IS NULL")
|
97
|
+
database.execSQL(
|
98
|
+
"""
|
99
|
+
INSERT INTO $TABLE_NAME (`${COLUMN_KEY}`, `${COLUMN_VALUE}`)
|
100
|
+
SELECT `${COLUMN_KEY}`, `${COLUMN_VALUE}`
|
101
|
+
FROM $oldTableName;
|
102
|
+
""".trimIndent()
|
103
|
+
)
|
104
|
+
Log.e("AsyncStorage_Next", "Migration to Next storage completed.")
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
@Database(entities = [Entry::class], version = DATABASE_VERSION, exportSchema = false)
|
109
|
+
internal abstract class StorageDb : RoomDatabase() {
|
110
|
+
abstract fun storage(): StorageDao
|
111
|
+
|
112
|
+
companion object {
|
113
|
+
private var instance: StorageDb? = null
|
114
|
+
|
115
|
+
fun getDatabase(context: Context): StorageDb {
|
116
|
+
var inst = instance
|
117
|
+
if (inst != null) {
|
118
|
+
return inst
|
119
|
+
}
|
120
|
+
synchronized(this) {
|
121
|
+
val oldDbFile = context.getDatabasePath("RKStorage")
|
122
|
+
val db = Room.databaseBuilder(
|
123
|
+
context, StorageDb::class.java, DATABASE_NAME
|
124
|
+
)
|
125
|
+
if (oldDbFile.exists()) {
|
126
|
+
// migrate data from old database, if it exists
|
127
|
+
db.createFromFile(oldDbFile).addMigrations(MIGRATION_TO_NEXT)
|
128
|
+
}
|
129
|
+
inst = db.build()
|
130
|
+
instance = inst
|
131
|
+
return instance!!
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
interface AsyncStorageAccess {
|
138
|
+
suspend fun getValues(keys: List<String>): List<Entry>
|
139
|
+
suspend fun setValues(entries: List<Entry>)
|
140
|
+
suspend fun removeValues(keys: List<String>)
|
141
|
+
suspend fun getKeys(): List<String>
|
142
|
+
suspend fun clear()
|
143
|
+
suspend fun mergeValues(entries: List<Entry>)
|
144
|
+
}
|
145
|
+
|
146
|
+
class StorageSupplier internal constructor(db: StorageDb) : AsyncStorageAccess {
|
147
|
+
companion object {
|
148
|
+
fun getInstance(ctx: Context): AsyncStorageAccess {
|
149
|
+
return StorageSupplier(StorageDb.getDatabase(ctx))
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
private val access = db.storage()
|
154
|
+
|
155
|
+
override suspend fun getValues(keys: List<String>) = access.getValues(keys)
|
156
|
+
override suspend fun setValues(entries: List<Entry>) = access.setValues(entries)
|
157
|
+
override suspend fun removeValues(keys: List<String>) = access.removeValues(keys)
|
158
|
+
override suspend fun mergeValues(entries: List<Entry>) = access.mergeValues(entries)
|
159
|
+
override suspend fun getKeys() = access.getKeys()
|
160
|
+
override suspend fun clear() = access.clear()
|
161
|
+
}
|
@@ -0,0 +1,93 @@
|
|
1
|
+
package com.reactnativecommunity.asyncstorage.next
|
2
|
+
|
3
|
+
import com.facebook.react.bridge.JavaOnlyArray
|
4
|
+
import com.facebook.react.bridge.ReadableArray
|
5
|
+
import com.google.common.truth.Truth.assertThat
|
6
|
+
import org.junit.Assert.assertThrows
|
7
|
+
import org.junit.Test
|
8
|
+
import org.junit.runner.RunWith
|
9
|
+
import org.junit.runners.BlockJUnit4ClassRunner
|
10
|
+
|
11
|
+
@RunWith(BlockJUnit4ClassRunner::class)
|
12
|
+
class ArgumentHelpersTest {
|
13
|
+
|
14
|
+
@Test
|
15
|
+
fun transformsArgumentsToEntryList() {
|
16
|
+
val args = JavaOnlyArray.of(
|
17
|
+
arrayListOf("key1", "value1"),
|
18
|
+
arrayListOf("key2", "value2"),
|
19
|
+
arrayListOf("key3", "value3")
|
20
|
+
)
|
21
|
+
assertThat(args.toEntryList()).isEqualTo(
|
22
|
+
listOf(
|
23
|
+
Entry("key1", "value1"),
|
24
|
+
Entry("key2", "value2"),
|
25
|
+
Entry("key3", "value3"),
|
26
|
+
)
|
27
|
+
)
|
28
|
+
}
|
29
|
+
|
30
|
+
@Test
|
31
|
+
fun transfersArgumentsToKeyList() {
|
32
|
+
val keyList = listOf("key1", "key2", "key3")
|
33
|
+
val args = keyList.toReadableArray()
|
34
|
+
assertThat(args.toKeyList()).isEqualTo(keyList)
|
35
|
+
}
|
36
|
+
|
37
|
+
@Test
|
38
|
+
fun throwsIfArgumentsNotValidFormat() {
|
39
|
+
val invalid = arrayListOf("invalid")
|
40
|
+
val args = JavaOnlyArray.of(invalid)
|
41
|
+
val error = assertThrows(AsyncStorageError::class.java) {
|
42
|
+
args.toEntryList()
|
43
|
+
}
|
44
|
+
|
45
|
+
assertThat(error is AsyncStorageError).isTrue()
|
46
|
+
assertThat(error).hasMessageThat()
|
47
|
+
.isEqualTo("Invalid key-value format. Expected a list of [key, value] list.")
|
48
|
+
}
|
49
|
+
|
50
|
+
@Test
|
51
|
+
fun throwsIfArgumentKeyIsNullOrNotString() {
|
52
|
+
val argsInvalidNull = JavaOnlyArray.of(arrayListOf(null, "invalid"))
|
53
|
+
val errorArgsInvalidNull = assertThrows(AsyncStorageError::class.java) {
|
54
|
+
argsInvalidNull.toEntryList()
|
55
|
+
}
|
56
|
+
assertThat(errorArgsInvalidNull is AsyncStorageError).isTrue()
|
57
|
+
assertThat(errorArgsInvalidNull).hasMessageThat().isEqualTo("Key cannot be null.")
|
58
|
+
|
59
|
+
val notStringArgs = JavaOnlyArray.of(arrayListOf(123, "invalid"))
|
60
|
+
val errorNotString = assertThrows(AsyncStorageError::class.java) {
|
61
|
+
notStringArgs.toEntryList()
|
62
|
+
}
|
63
|
+
assertThat(errorNotString is AsyncStorageError).isTrue()
|
64
|
+
assertThat(errorNotString).hasMessageThat()
|
65
|
+
.isEqualTo("Provided key is not string. Only strings are supported as storage key.")
|
66
|
+
}
|
67
|
+
|
68
|
+
@Test
|
69
|
+
fun throwsIfArgumentValueNotString() {
|
70
|
+
val invalidArgs = JavaOnlyArray.of(arrayListOf("my_key", 666))
|
71
|
+
val error = assertThrows(AsyncStorageError::class.java) {
|
72
|
+
invalidArgs.toEntryList()
|
73
|
+
}
|
74
|
+
assertThat(error is AsyncStorageError).isTrue()
|
75
|
+
assertThat(error).hasMessageThat()
|
76
|
+
.isEqualTo("Value for key \"my_key\" is not a string. Only strings are supported as a value.")
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
fun List<Any?>.toReadableArray(): ReadableArray {
|
81
|
+
val arr = JavaOnlyArray()
|
82
|
+
forEach {
|
83
|
+
when (it) {
|
84
|
+
null -> arr.pushNull()
|
85
|
+
is Boolean -> arr.pushBoolean(it)
|
86
|
+
is Double -> arr.pushDouble(it)
|
87
|
+
is Int -> arr.pushInt(it)
|
88
|
+
is String -> arr.pushString(it)
|
89
|
+
else -> throw NotImplementedError()
|
90
|
+
}
|
91
|
+
}
|
92
|
+
return arr
|
93
|
+
}
|
@@ -0,0 +1,141 @@
|
|
1
|
+
package com.reactnativecommunity.asyncstorage.next
|
2
|
+
|
3
|
+
import androidx.room.Room
|
4
|
+
import androidx.test.ext.junit.runners.AndroidJUnit4
|
5
|
+
import androidx.test.platform.app.InstrumentationRegistry
|
6
|
+
import com.google.common.truth.Truth.assertThat
|
7
|
+
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
8
|
+
import kotlinx.coroutines.runBlocking
|
9
|
+
import org.json.JSONObject
|
10
|
+
import org.junit.After
|
11
|
+
import org.junit.Before
|
12
|
+
import org.junit.Test
|
13
|
+
import org.junit.runner.RunWith
|
14
|
+
import kotlin.random.Random
|
15
|
+
|
16
|
+
@ExperimentalCoroutinesApi
|
17
|
+
@RunWith(AndroidJUnit4::class)
|
18
|
+
class AsyncStorageAccessTest {
|
19
|
+
private lateinit var asyncStorage: AsyncStorageAccess
|
20
|
+
private lateinit var database: StorageDb
|
21
|
+
|
22
|
+
@Before
|
23
|
+
fun setup() {
|
24
|
+
database = Room.inMemoryDatabaseBuilder(
|
25
|
+
InstrumentationRegistry.getInstrumentation().context, StorageDb::class.java
|
26
|
+
).allowMainThreadQueries().build()
|
27
|
+
asyncStorage = StorageSupplier(database)
|
28
|
+
}
|
29
|
+
|
30
|
+
@After
|
31
|
+
fun tearDown() {
|
32
|
+
database.close()
|
33
|
+
}
|
34
|
+
|
35
|
+
@Test
|
36
|
+
fun performsBasicGetSetRemoveOperations() = runBlocking {
|
37
|
+
val entriesCount = 10
|
38
|
+
val entries = createRandomEntries(entriesCount)
|
39
|
+
val keys = entries.map { it.key }
|
40
|
+
assertThat(asyncStorage.getValues(keys)).hasSize(0)
|
41
|
+
asyncStorage.setValues(entries)
|
42
|
+
assertThat(asyncStorage.getValues(keys)).hasSize(entriesCount)
|
43
|
+
val indicesToRemove = (1..4).map { Random.nextInt(0, entriesCount) }.distinct()
|
44
|
+
val toRemove = entries.filterIndexed { index, _ -> indicesToRemove.contains(index) }
|
45
|
+
asyncStorage.removeValues(toRemove.map { it.key })
|
46
|
+
val currentEntries = asyncStorage.getValues(keys)
|
47
|
+
assertThat(currentEntries).hasSize(entriesCount - toRemove.size)
|
48
|
+
}
|
49
|
+
|
50
|
+
@Test
|
51
|
+
fun readsAllKeysAndClearsDb() = runBlocking {
|
52
|
+
val entries = createRandomEntries(8)
|
53
|
+
val keys = entries.map { it.key }
|
54
|
+
asyncStorage.setValues(entries)
|
55
|
+
val dbKeys = asyncStorage.getKeys()
|
56
|
+
assertThat(dbKeys).isEqualTo(keys)
|
57
|
+
asyncStorage.clear()
|
58
|
+
assertThat(asyncStorage.getValues(keys)).hasSize(0)
|
59
|
+
}
|
60
|
+
|
61
|
+
@Test
|
62
|
+
fun mergesDeeplyTwoValues() = runBlocking {
|
63
|
+
val initialEntry = Entry("key", VALUE_INITIAL)
|
64
|
+
val overrideEntry = Entry("key", VALUE_OVERRIDES)
|
65
|
+
asyncStorage.setValues(listOf(initialEntry))
|
66
|
+
asyncStorage.mergeValues(listOf(overrideEntry))
|
67
|
+
val current = asyncStorage.getValues(listOf("key"))[0]
|
68
|
+
assertThat(current.value).isEqualTo(VALUE_MERGED)
|
69
|
+
}
|
70
|
+
|
71
|
+
@Test
|
72
|
+
fun updatesExistingValues() = runBlocking {
|
73
|
+
val key = "test_key"
|
74
|
+
val value = "test_value"
|
75
|
+
val entries = listOf(Entry(key, value))
|
76
|
+
assertThat(asyncStorage.getValues(listOf(key))).hasSize(0)
|
77
|
+
asyncStorage.setValues(entries)
|
78
|
+
assertThat(asyncStorage.getValues(listOf(key))).isEqualTo(entries)
|
79
|
+
val modifiedEntries = listOf(Entry(key, "updatedValue"))
|
80
|
+
asyncStorage.setValues(modifiedEntries)
|
81
|
+
assertThat(asyncStorage.getValues(listOf(key))).isEqualTo(modifiedEntries)
|
82
|
+
}
|
83
|
+
|
84
|
+
|
85
|
+
// Test Helpers
|
86
|
+
private fun createRandomEntries(count: Int = Random.nextInt(10)): List<Entry> {
|
87
|
+
val entries = mutableListOf<Entry>()
|
88
|
+
for (i in 0 until count) {
|
89
|
+
entries.add(Entry("key$i", "value$i"))
|
90
|
+
}
|
91
|
+
return entries
|
92
|
+
}
|
93
|
+
|
94
|
+
private val VALUE_INITIAL = JSONObject(
|
95
|
+
"""
|
96
|
+
{
|
97
|
+
"key":"value",
|
98
|
+
"key2":"override",
|
99
|
+
"key3":{
|
100
|
+
"key4":"value4",
|
101
|
+
"key6":{
|
102
|
+
"key7":"value7",
|
103
|
+
"key8":"override"
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
""".trimMargin()
|
108
|
+
).toString()
|
109
|
+
|
110
|
+
private val VALUE_OVERRIDES = JSONObject(
|
111
|
+
"""
|
112
|
+
{
|
113
|
+
"key2":"value2",
|
114
|
+
"key3":{
|
115
|
+
"key5":"value5",
|
116
|
+
"key6":{
|
117
|
+
"key8":"value8"
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
"""
|
122
|
+
).toString()
|
123
|
+
|
124
|
+
|
125
|
+
private val VALUE_MERGED = JSONObject(
|
126
|
+
"""
|
127
|
+
{
|
128
|
+
"key":"value",
|
129
|
+
"key2":"value2",
|
130
|
+
"key3":{
|
131
|
+
"key4":"value4",
|
132
|
+
"key6":{
|
133
|
+
"key7":"value7",
|
134
|
+
"key8":"value8"
|
135
|
+
},
|
136
|
+
"key5":"value5"
|
137
|
+
}
|
138
|
+
}
|
139
|
+
""".trimMargin()
|
140
|
+
).toString()
|
141
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
// pretty print test results
|
3
|
+
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
|
4
|
+
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
5
|
+
tasks.withType(Test) {
|
6
|
+
testLogging {
|
7
|
+
|
8
|
+
events TestLogEvent.FAILED,
|
9
|
+
TestLogEvent.PASSED,
|
10
|
+
TestLogEvent.SKIPPED,
|
11
|
+
TestLogEvent.STANDARD_OUT
|
12
|
+
exceptionFormat TestExceptionFormat.FULL
|
13
|
+
showExceptions true
|
14
|
+
showCauses true
|
15
|
+
showStackTraces true
|
16
|
+
|
17
|
+
debug {
|
18
|
+
events TestLogEvent.STARTED,
|
19
|
+
TestLogEvent.FAILED,
|
20
|
+
TestLogEvent.PASSED,
|
21
|
+
TestLogEvent.SKIPPED,
|
22
|
+
TestLogEvent.STANDARD_ERROR,
|
23
|
+
TestLogEvent.STANDARD_OUT
|
24
|
+
exceptionFormat TestExceptionFormat.FULL
|
25
|
+
}
|
26
|
+
info.events = debug.events
|
27
|
+
info.exceptionFormat = debug.exceptionFormat
|
28
|
+
|
29
|
+
afterSuite { desc, result ->
|
30
|
+
if (!desc.parent) { // will match the outermost suite
|
31
|
+
def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"
|
32
|
+
def startItem = '| ', endItem = ' |'
|
33
|
+
def repeatLength = startItem.length() + output.length() + endItem.length()
|
34
|
+
println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|