@amazon-devices/expo-file-system 2.0.1758683737
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/.eslintrc.js +2 -0
- package/CHANGELOG.md +317 -0
- package/CMakeLists.txt +52 -0
- package/LICENSE +7 -0
- package/README.kepler.md +260 -0
- package/README.md +46 -0
- package/THIRD_PARTY_NOTICES.txt +24 -0
- package/android/build.gradle +118 -0
- package/android/src/main/AndroidManifest.xml +17 -0
- package/android/src/main/java/expo/modules/filesystem/AppDirectoriesModule.kt +26 -0
- package/android/src/main/java/expo/modules/filesystem/CountingRequestBody.kt +52 -0
- package/android/src/main/java/expo/modules/filesystem/FilePermissionModule.kt +48 -0
- package/android/src/main/java/expo/modules/filesystem/FileSystemExceptions.kt +48 -0
- package/android/src/main/java/expo/modules/filesystem/FileSystemFileProvider.kt +5 -0
- package/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt +1110 -0
- package/android/src/main/java/expo/modules/filesystem/FileSystemPackage.kt +10 -0
- package/android/src/main/java/expo/modules/filesystem/FileSystemRecords.kt +92 -0
- package/android/src/main/res/xml/file_system_provider_paths.xml +5 -0
- package/app.plugin.js +1 -0
- package/build/ExponentFileSystem.d.ts +4 -0
- package/build/ExponentFileSystem.d.ts.map +1 -0
- package/build/ExponentFileSystem.js +5 -0
- package/build/ExponentFileSystem.js.map +1 -0
- package/build/ExponentFileSystem.kepler.d.ts +4 -0
- package/build/ExponentFileSystem.kepler.d.ts.map +1 -0
- package/build/ExponentFileSystem.kepler.js +112 -0
- package/build/ExponentFileSystem.kepler.js.map +1 -0
- package/build/ExponentFileSystem.web.d.ts +3 -0
- package/build/ExponentFileSystem.web.d.ts.map +1 -0
- package/build/ExponentFileSystem.web.js +3 -0
- package/build/ExponentFileSystem.web.js.map +1 -0
- package/build/ExponentFileSystemShim.d.ts +4 -0
- package/build/ExponentFileSystemShim.d.ts.map +1 -0
- package/build/ExponentFileSystemShim.js +21 -0
- package/build/ExponentFileSystemShim.js.map +1 -0
- package/build/FileSystem.d.ts +361 -0
- package/build/FileSystem.d.ts.map +1 -0
- package/build/FileSystem.js +616 -0
- package/build/FileSystem.js.map +1 -0
- package/build/FileSystem.types.d.ts +304 -0
- package/build/FileSystem.types.d.ts.map +1 -0
- package/build/FileSystem.types.js +47 -0
- package/build/FileSystem.types.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +3 -0
- package/build/index.js.map +1 -0
- package/build/types.d.ts +35 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/expo-module.config.json +11 -0
- package/ios/EXFilePermissionModule.h +16 -0
- package/ios/EXFilePermissionModule.m +68 -0
- package/ios/EXFileSystem.h +25 -0
- package/ios/EXFileSystem.m +1084 -0
- package/ios/EXFileSystemAssetLibraryHandler.h +6 -0
- package/ios/EXFileSystemAssetLibraryHandler.m +142 -0
- package/ios/EXFileSystemHandler.h +17 -0
- package/ios/EXFileSystemLocalFileHandler.h +7 -0
- package/ios/EXFileSystemLocalFileHandler.m +79 -0
- package/ios/EXSessionTasks/EXSessionCancelableUploadTaskDelegate.h +16 -0
- package/ios/EXSessionTasks/EXSessionCancelableUploadTaskDelegate.m +55 -0
- package/ios/EXSessionTasks/EXSessionDownloadTaskDelegate.h +13 -0
- package/ios/EXSessionTasks/EXSessionDownloadTaskDelegate.m +64 -0
- package/ios/EXSessionTasks/EXSessionHandler.h +19 -0
- package/ios/EXSessionTasks/EXSessionHandler.m +49 -0
- package/ios/EXSessionTasks/EXSessionResumableDownloadTaskDelegate.h +18 -0
- package/ios/EXSessionTasks/EXSessionResumableDownloadTaskDelegate.m +66 -0
- package/ios/EXSessionTasks/EXSessionTaskDelegate.h +32 -0
- package/ios/EXSessionTasks/EXSessionTaskDelegate.m +58 -0
- package/ios/EXSessionTasks/EXSessionTaskDispatcher.h +19 -0
- package/ios/EXSessionTasks/EXSessionTaskDispatcher.m +96 -0
- package/ios/EXSessionTasks/EXSessionUploadTaskDelegate.h +8 -0
- package/ios/EXSessionTasks/EXSessionUploadTaskDelegate.m +52 -0
- package/ios/EXSessionTasks/EXTaskHandlersManager.h +21 -0
- package/ios/EXSessionTasks/EXTaskHandlersManager.m +56 -0
- package/ios/Encoding.swift +88 -0
- package/ios/ExpoFileSystem.h +1 -0
- package/ios/ExpoFileSystem.podspec +32 -0
- package/ios/FileSystemBackgroundSessionHandler.swift +25 -0
- package/ios/FileSystemExceptions.swift +69 -0
- package/ios/FileSystemHelpers.swift +66 -0
- package/ios/FileSystemModule.swift +263 -0
- package/ios/FileSystemRecords.swift +74 -0
- package/ios/NSData+EXFileSystem.h +9 -0
- package/ios/NSData+EXFileSystem.m +19 -0
- package/ios/NetworkingHelpers.swift +84 -0
- package/ios/Tests/EXFileSystemSpec.swift +55 -0
- package/jest.config.js +3 -0
- package/kepler/ExponentFileSystem.cpp +498 -0
- package/kepler/ExponentFileSystem.h +60 -0
- package/kepler/KeplerInit.cpp +28 -0
- package/kepler/NetworkingRequestHandler.cpp +47 -0
- package/kepler/test/FileSystem.cpp +425 -0
- package/package.json +1 -0
- package/plugin/build/withFileSystem.d.ts +3 -0
- package/plugin/build/withFileSystem.js +12 -0
- package/plugin/src/withFileSystem.ts +13 -0
- package/plugin/tsconfig.json +9 -0
- package/plugin/tsconfig.tsbuildinfo +1 -0
- package/react-native.config.js +21 -0
- package/src/ExponentFileSystem.kepler.ts +167 -0
- package/src/ExponentFileSystem.ts +7 -0
- package/src/ExponentFileSystem.web.ts +2 -0
- package/src/ExponentFileSystemShim.ts +23 -0
- package/src/FileSystem.ts +756 -0
- package/src/FileSystem.types.ts +339 -0
- package/src/index.ts +2 -0
- package/src/types.ts +34 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
*
|
|
4
|
+
* PROPRIETARY/CONFIDENTIAL
|
|
5
|
+
*
|
|
6
|
+
* Use is subject to license terms.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include <KeplerScript/utils/KeplerUIManager.h>
|
|
10
|
+
#include <gmock/gmock-actions.h>
|
|
11
|
+
#include <gmock/gmock.h>
|
|
12
|
+
#include <gtest/gtest.h>
|
|
13
|
+
|
|
14
|
+
#include <iostream>
|
|
15
|
+
|
|
16
|
+
#include "../ExponentFileSystem.h"
|
|
17
|
+
#include "JSObjectsConversion.h"
|
|
18
|
+
#include "catch.hpp"
|
|
19
|
+
|
|
20
|
+
namespace {
|
|
21
|
+
void handlePromiseRejection(volta::react::NativeJSObject payload) {
|
|
22
|
+
std::string rejectionMessage = std::get<std::string>(payload["message"]);
|
|
23
|
+
FAIL(rejectionMessage);
|
|
24
|
+
}
|
|
25
|
+
} // namespace
|
|
26
|
+
|
|
27
|
+
SCENARIO("Testing Expo FileSystem module") {
|
|
28
|
+
ExponentFileSystem fileSystemModule;
|
|
29
|
+
|
|
30
|
+
WHEN("documentDirectory method is called") {
|
|
31
|
+
auto documentDirectory = fileSystemModule.documentDirectory();
|
|
32
|
+
|
|
33
|
+
THEN("Proper value is returned") {
|
|
34
|
+
REQUIRE(documentDirectory == "/data");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
WHEN("cacheDirectory method is called") {
|
|
39
|
+
auto cacheDirectory = fileSystemModule.cacheDirectory();
|
|
40
|
+
|
|
41
|
+
THEN("Proper value is returned") {
|
|
42
|
+
REQUIRE(cacheDirectory == "/cache");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
WHEN("bundleDirectory method is called") {
|
|
47
|
+
auto bundleDirectory = fileSystemModule.bundleDirectory();
|
|
48
|
+
|
|
49
|
+
THEN("Proper value is returned") {
|
|
50
|
+
REQUIRE(bundleDirectory == "");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Unable to test due to lack of 'Promise' support
|
|
55
|
+
// WHEN("getInfoAsync is called for a nonexisting file") {
|
|
56
|
+
// auto promise = fileSystemModule.getInfoAsync("/data/nonExistingFile.txt", {});
|
|
57
|
+
|
|
58
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
59
|
+
// volta::react::Callback([](auto args) {
|
|
60
|
+
// THEN("Resolved promise contains appropriate file info for non existing file") {
|
|
61
|
+
// REQUIRE(args.size() == 1);
|
|
62
|
+
// REQUIRE(std::holds_alternative<volta::react::NativeJSObject>(args[0]));
|
|
63
|
+
|
|
64
|
+
// volta::react::NativeJSObject fileInfo = std::get<volta::react::NativeJSObject>(args[0]);
|
|
65
|
+
|
|
66
|
+
// REQUIRE(std::holds_alternative<bool>(fileInfo["exists"]));
|
|
67
|
+
// REQUIRE(std::holds_alternative<bool>(fileInfo["isDirectory"]));
|
|
68
|
+
|
|
69
|
+
// REQUIRE_FALSE(std::get<bool>(fileInfo["exists"]));
|
|
70
|
+
// REQUIRE_FALSE(std::get<bool>(fileInfo["isDirectory"]));
|
|
71
|
+
// }
|
|
72
|
+
// }),
|
|
73
|
+
// volta::react::Callback([](auto args) {
|
|
74
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
75
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
76
|
+
// handlePromiseRejection(payload);
|
|
77
|
+
// })));
|
|
78
|
+
// }
|
|
79
|
+
|
|
80
|
+
// Unable to test due to lack of 'Promise' support
|
|
81
|
+
// WHEN("getInfoAsync is called for an existing file") {
|
|
82
|
+
// auto promise =
|
|
83
|
+
// fileSystemModule.getInfoAsync("/data/expo-unit-tests", volta::react::NativeJSObject{{"size",
|
|
84
|
+
// true}});
|
|
85
|
+
|
|
86
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
87
|
+
// volta::react::Callback([](auto args) {
|
|
88
|
+
// THEN("Resolved promise contains appropriate file info for existing file") {
|
|
89
|
+
// REQUIRE(args.size() == 1);
|
|
90
|
+
// REQUIRE(std::holds_alternative<volta::react::NativeJSObject>(args[0]));
|
|
91
|
+
|
|
92
|
+
// volta::react::NativeJSObject fileInfo = std::get<volta::react::NativeJSObject>(args[0]);
|
|
93
|
+
|
|
94
|
+
// REQUIRE(std::holds_alternative<bool>(fileInfo["exists"]));
|
|
95
|
+
// REQUIRE(std::holds_alternative<bool>(fileInfo["isDirectory"]));
|
|
96
|
+
// REQUIRE(std::holds_alternative<std::string>(fileInfo["size"]));
|
|
97
|
+
// REQUIRE(std::holds_alternative<std::string>(fileInfo["modification"]));
|
|
98
|
+
|
|
99
|
+
// REQUIRE(std::get<bool>(fileInfo["exists"]));
|
|
100
|
+
// REQUIRE_FALSE(std::get<bool>(fileInfo["isDirectory"]));
|
|
101
|
+
// REQUIRE(std::get<std::string>(fileInfo["size"]) > "1024");
|
|
102
|
+
// REQUIRE(std::get<std::string>(fileInfo["modification"]) > "1718788070");
|
|
103
|
+
// }
|
|
104
|
+
// }),
|
|
105
|
+
// volta::react::Callback([](auto args) {
|
|
106
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
107
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
108
|
+
// handlePromiseRejection(payload);
|
|
109
|
+
// })));
|
|
110
|
+
// }
|
|
111
|
+
|
|
112
|
+
// Unable to test due to lack of 'Promise' support
|
|
113
|
+
// WHEN("getContentUriAsync is called") {
|
|
114
|
+
// auto promise = fileSystemModule.getContentUriAsync("expo-unit-tests");
|
|
115
|
+
|
|
116
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
117
|
+
// volta::react::Callback([](auto args) {
|
|
118
|
+
// THEN("Resolved promise contains formatted URI") {
|
|
119
|
+
// REQUIRE(args.size() == 1);
|
|
120
|
+
// REQUIRE(std::holds_alternative<std::string>(args[0]));
|
|
121
|
+
|
|
122
|
+
// REQUIRE(std::get<std::string>(args[0]) == "/data/expo-unit-tests");
|
|
123
|
+
// }
|
|
124
|
+
// }),
|
|
125
|
+
// volta::react::Callback([](auto args) {
|
|
126
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
127
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
128
|
+
// handlePromiseRejection(payload);
|
|
129
|
+
// })));
|
|
130
|
+
// }
|
|
131
|
+
|
|
132
|
+
// Unable to test due to lack of 'Promise' support
|
|
133
|
+
// WHEN("writeAsStringAsync is called") {
|
|
134
|
+
// auto promise = fileSystemModule.writeAsStringAsync("/data/hello-world.txt", "Hello world!", {});
|
|
135
|
+
|
|
136
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
137
|
+
// volta::react::Callback([](auto args) {
|
|
138
|
+
// THEN("Promise is resolved on success") {
|
|
139
|
+
// REQUIRE(args.size() == 0);
|
|
140
|
+
// }
|
|
141
|
+
// }),
|
|
142
|
+
// volta::react::Callback([](auto args) {
|
|
143
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
144
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
145
|
+
// handlePromiseRejection(payload);
|
|
146
|
+
// })));
|
|
147
|
+
// }
|
|
148
|
+
|
|
149
|
+
// Unable to test due to lack of 'Promise' support
|
|
150
|
+
// WHEN("readAsStringAsync is called") {
|
|
151
|
+
// auto promise = fileSystemModule.readAsStringAsync("/data/hello-world.txt", {});
|
|
152
|
+
|
|
153
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
154
|
+
// volta::react::Callback([](auto args) {
|
|
155
|
+
// THEN("Resolved promise contains file content") {
|
|
156
|
+
// REQUIRE(args.size() == 1);
|
|
157
|
+
// REQUIRE(std::holds_alternative<std::string>(args[0]));
|
|
158
|
+
|
|
159
|
+
// REQUIRE(std::get<std::string>(args[0]) == "Hello world!");
|
|
160
|
+
// }
|
|
161
|
+
// }),
|
|
162
|
+
// volta::react::Callback([](auto args) {
|
|
163
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
164
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
165
|
+
// handlePromiseRejection(payload);
|
|
166
|
+
// })));
|
|
167
|
+
// }
|
|
168
|
+
|
|
169
|
+
// Unable to test due to lack of 'Promise' support
|
|
170
|
+
// WHEN("makeDirectoryAsync is called") {
|
|
171
|
+
// auto promise = fileSystemModule.makeDirectoryAsync("/data/hello", {});
|
|
172
|
+
|
|
173
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
174
|
+
// volta::react::Callback([](auto args) {
|
|
175
|
+
// THEN("Promise is resolved on success") {
|
|
176
|
+
// REQUIRE(args.size() == 0);
|
|
177
|
+
// }
|
|
178
|
+
// }),
|
|
179
|
+
// volta::react::Callback([](auto args) {
|
|
180
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
181
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
182
|
+
// std::string rejectionMessage = std::get<std::string>(payload["message"]);
|
|
183
|
+
// if (rejectionMessage.find("already exists") != std::string::npos) {
|
|
184
|
+
// THEN("Promise is rejected with appropriate error message when the test is run more than
|
|
185
|
+
// once") {
|
|
186
|
+
// REQUIRE(rejectionMessage == "Directory already exists!");
|
|
187
|
+
// }
|
|
188
|
+
// } else {
|
|
189
|
+
// handlePromiseRejection(payload);
|
|
190
|
+
// }
|
|
191
|
+
// })));
|
|
192
|
+
// }
|
|
193
|
+
|
|
194
|
+
// Unable to test due to lack of 'Promise' support
|
|
195
|
+
// WHEN("copyAsync is called") {
|
|
196
|
+
// auto promise = fileSystemModule.copyAsync(
|
|
197
|
+
// volta::react::NativeJSObject{{"from", "data/hello-world.txt"}, {"to", "data/hello/world.txt"}});
|
|
198
|
+
|
|
199
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
200
|
+
// volta::react::Callback([](auto args) {
|
|
201
|
+
// THEN("Promise is resolved on success") {
|
|
202
|
+
// REQUIRE(args.size() == 0);
|
|
203
|
+
// }
|
|
204
|
+
// }),
|
|
205
|
+
// volta::react::Callback([](auto args) {
|
|
206
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
207
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
208
|
+
// std::string rejectionMessage = std::get<std::string>(payload["message"]);
|
|
209
|
+
// if (rejectionMessage.find("cannot copy: File exists") != std::string::npos) {
|
|
210
|
+
// THEN("Promise is rejected with appropriate error message when the test is run more than
|
|
211
|
+
// once") {
|
|
212
|
+
// }
|
|
213
|
+
// } else {
|
|
214
|
+
// handlePromiseRejection(payload);
|
|
215
|
+
// }
|
|
216
|
+
// })));
|
|
217
|
+
// }
|
|
218
|
+
|
|
219
|
+
// Unable to test due to lack of 'Promise' support
|
|
220
|
+
// WHEN("moveAsync is called") {
|
|
221
|
+
// auto promise = fileSystemModule.moveAsync(
|
|
222
|
+
// volta::react::NativeJSObject{{"from", "data/hello-world.txt"}, {"to",
|
|
223
|
+
// "data/hello/hello-world.txt"}});
|
|
224
|
+
|
|
225
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
226
|
+
// volta::react::Callback([](auto args) {
|
|
227
|
+
// THEN("Promise is resolved on success") {
|
|
228
|
+
// REQUIRE(args.size() == 0);
|
|
229
|
+
// }
|
|
230
|
+
// }),
|
|
231
|
+
// volta::react::Callback([](auto args) {
|
|
232
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
233
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
234
|
+
// handlePromiseRejection(payload);
|
|
235
|
+
// })));
|
|
236
|
+
// }
|
|
237
|
+
|
|
238
|
+
// Unable to test due to lack of 'Promise' support
|
|
239
|
+
// WHEN("deleteAsync is called") {
|
|
240
|
+
// auto promise = fileSystemModule.deleteAsync("data/hello/hello-world.txt", {});
|
|
241
|
+
|
|
242
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
243
|
+
// volta::react::Callback([](auto args) {
|
|
244
|
+
// THEN("Promise is resolved on success") {
|
|
245
|
+
// REQUIRE(args.size() == 0);
|
|
246
|
+
// }
|
|
247
|
+
// }),
|
|
248
|
+
// volta::react::Callback([](auto args) {
|
|
249
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
250
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
251
|
+
// handlePromiseRejection(payload);
|
|
252
|
+
// })));
|
|
253
|
+
// }
|
|
254
|
+
|
|
255
|
+
// Unable to test due to lack of 'Promise' support
|
|
256
|
+
// WHEN("readDirectoryAsync is called") {
|
|
257
|
+
// auto promise = fileSystemModule.readDirectoryAsync("/data/hello");
|
|
258
|
+
|
|
259
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
260
|
+
// volta::react::Callback([](auto args) {
|
|
261
|
+
// THEN("Resolved promise contains directory content") {
|
|
262
|
+
// REQUIRE(args.size() == 1);
|
|
263
|
+
// REQUIRE(std::holds_alternative<volta::react::NativeJSArray>(args[0]));
|
|
264
|
+
|
|
265
|
+
// volta::react::NativeJSArray directoryContent =
|
|
266
|
+
// std::get<volta::react::NativeJSArray>(args[0]);
|
|
267
|
+
|
|
268
|
+
// REQUIRE(directoryContent.size() > 0);
|
|
269
|
+
|
|
270
|
+
// for (volta::react::NativeJSValue entry : directoryContent)
|
|
271
|
+
// REQUIRE(std::holds_alternative<std::string>(entry));
|
|
272
|
+
// }
|
|
273
|
+
// }),
|
|
274
|
+
// volta::react::Callback([](auto args) {
|
|
275
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
276
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
277
|
+
// handlePromiseRejection(payload);
|
|
278
|
+
// })));
|
|
279
|
+
// }
|
|
280
|
+
|
|
281
|
+
// Unable to test due to lack of 'Promise' support
|
|
282
|
+
// WHEN("getFreeDiskStorageAsync is called") {
|
|
283
|
+
// auto promise = fileSystemModule.getFreeDiskStorageAsync();
|
|
284
|
+
|
|
285
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
286
|
+
// volta::react::Callback([](auto args) {
|
|
287
|
+
// THEN("Resolved promise contains free disk capacity in bytes") {
|
|
288
|
+
// REQUIRE(args.size() == 1);
|
|
289
|
+
// REQUIRE(std::holds_alternative<std::string>(args[0]));
|
|
290
|
+
// }
|
|
291
|
+
// }),
|
|
292
|
+
// volta::react::Callback([](auto args) {
|
|
293
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
294
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
295
|
+
// handlePromiseRejection(payload);
|
|
296
|
+
// })));
|
|
297
|
+
// }
|
|
298
|
+
|
|
299
|
+
// Unable to test due to lack of 'Promise' support
|
|
300
|
+
// WHEN("getTotalDiskCapacityAsync is called") {
|
|
301
|
+
// auto promise = fileSystemModule.getTotalDiskCapacityAsync();
|
|
302
|
+
|
|
303
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
304
|
+
// volta::react::Callback([](auto args) {
|
|
305
|
+
// THEN("Resolved promise does not contain free disk capacity information") {
|
|
306
|
+
// REQUIRE(args.size() == 0);
|
|
307
|
+
// }
|
|
308
|
+
// }),
|
|
309
|
+
// volta::react::Callback([](auto args) {
|
|
310
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
311
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
312
|
+
// handlePromiseRejection(payload);
|
|
313
|
+
// })));
|
|
314
|
+
// }
|
|
315
|
+
|
|
316
|
+
// Unable to test due to lack of 'Promise' support
|
|
317
|
+
// Sidenote: wifi connection is required to perform below testcase
|
|
318
|
+
// WHEN("downloadAsync is called") {
|
|
319
|
+
// std::vector<utils::json::JsonContainer> headers;
|
|
320
|
+
// auto headerAccept = utils::json::JsonContainer::createJsonObject();
|
|
321
|
+
// headerAccept.insert("Accept", 'application/json');
|
|
322
|
+
// headers.push_back(headerAccept);
|
|
323
|
+
|
|
324
|
+
// auto promise =
|
|
325
|
+
// fileSystemModule
|
|
326
|
+
// .downloadAsync("https://httpbin.org/get", "/data/hello/downloadResponse.json", {}, 1,
|
|
327
|
+
// headers);
|
|
328
|
+
|
|
329
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
330
|
+
// volta::react::Callback([](auto args) {
|
|
331
|
+
// THEN("Resolved promise contains download response") {
|
|
332
|
+
// REQUIRE(args.size() == 1);
|
|
333
|
+
// REQUIRE(std::holds_alternative<volta::react::NativeJSObject>(args[0]));
|
|
334
|
+
|
|
335
|
+
// volta::react::NativeJSObject fileInfo = std::get<volta::react::NativeJSObject>(args[0]);
|
|
336
|
+
|
|
337
|
+
// REQUIRE(std::holds_alternative<std::string>(fileInfo["uri"]));
|
|
338
|
+
|
|
339
|
+
// REQUIRE(std::get<std::string>(fileInfo["uri"]) == "/data/hello/downloadResponse.json");
|
|
340
|
+
// }
|
|
341
|
+
// }),
|
|
342
|
+
// volta::react::Callback([](auto args) {
|
|
343
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
344
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
345
|
+
// handlePromiseRejection(payload);
|
|
346
|
+
// })));
|
|
347
|
+
// }
|
|
348
|
+
|
|
349
|
+
// Unable to test due to lack of 'Promise' support
|
|
350
|
+
// Sidenote: wifi connection is required to perform below testcase
|
|
351
|
+
// WHEN("uploadAsync is called") {
|
|
352
|
+
// std::vector<utils::json::JsonContainer> headers;
|
|
353
|
+
// auto headerAccept = utils::json::JsonContainer::createJsonObject();
|
|
354
|
+
// headerAccept.insert("Accept", 'application/json');
|
|
355
|
+
// headers.push_back(headerAccept);
|
|
356
|
+
|
|
357
|
+
// auto promise =
|
|
358
|
+
// fileSystemModule
|
|
359
|
+
// .uploadAsync("https://httpbin.org/post", "/data/hello/uploadResponse.json", {}, 2,
|
|
360
|
+
// headers);
|
|
361
|
+
|
|
362
|
+
// promise.promiseOperation_(std::make_shared<volta::react::Promise>(
|
|
363
|
+
// volta::react::Callback([](auto args) {
|
|
364
|
+
// THEN("Resolved promise contains upload response") {
|
|
365
|
+
// REQUIRE(args.size() == 1);
|
|
366
|
+
// REQUIRE(std::holds_alternative<volta::react::NativeJSObject>(args[0]));
|
|
367
|
+
|
|
368
|
+
// volta::react::NativeJSObject fileInfo = std::get<volta::react::NativeJSObject>(args[0]);
|
|
369
|
+
|
|
370
|
+
// REQUIRE(std::holds_alternative<std::string>(fileInfo["uri"]));
|
|
371
|
+
|
|
372
|
+
// REQUIRE(std::get<std::string>(fileInfo["uri"]) == "/data/hello/uploadResponse.json");
|
|
373
|
+
// }
|
|
374
|
+
// }),
|
|
375
|
+
// volta::react::Callback([](auto args) {
|
|
376
|
+
// volta::react::NativeJSObject payload = ExpoUtils::convertJsonContainerToNativeJSObject(
|
|
377
|
+
// std::get<utils::json::JsonContainer>(args[0]));
|
|
378
|
+
// handlePromiseRejection(payload);
|
|
379
|
+
// })));
|
|
380
|
+
// }
|
|
381
|
+
|
|
382
|
+
WHEN("aggregateMethods is called") {
|
|
383
|
+
std::weak_ptr<facebook::react::Instance> instance;
|
|
384
|
+
fileSystemModule.triggerMethodAggregation(*fileSystemModule.jsQueue_, instance);
|
|
385
|
+
|
|
386
|
+
THEN("methodContainerMap contains expected methods") {
|
|
387
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("documentDirectory") !=
|
|
388
|
+
fileSystemModule.methodContainerMap_.end());
|
|
389
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("cacheDirectory") !=
|
|
390
|
+
fileSystemModule.methodContainerMap_.end());
|
|
391
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("bundleDirectory") !=
|
|
392
|
+
fileSystemModule.methodContainerMap_.end());
|
|
393
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("getInfoAsync") !=
|
|
394
|
+
fileSystemModule.methodContainerMap_.end());
|
|
395
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("readAsStringAsync") !=
|
|
396
|
+
fileSystemModule.methodContainerMap_.end());
|
|
397
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("getContentUriAsync") !=
|
|
398
|
+
fileSystemModule.methodContainerMap_.end());
|
|
399
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("writeAsStringAsync") !=
|
|
400
|
+
fileSystemModule.methodContainerMap_.end());
|
|
401
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("deleteAsync") !=
|
|
402
|
+
fileSystemModule.methodContainerMap_.end());
|
|
403
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("moveAsync") !=
|
|
404
|
+
fileSystemModule.methodContainerMap_.end());
|
|
405
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("copyAsync") !=
|
|
406
|
+
fileSystemModule.methodContainerMap_.end());
|
|
407
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("makeDirectoryAsync") !=
|
|
408
|
+
fileSystemModule.methodContainerMap_.end());
|
|
409
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("readDirectoryAsync") !=
|
|
410
|
+
fileSystemModule.methodContainerMap_.end());
|
|
411
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("getFreeDiskStorageAsync") !=
|
|
412
|
+
fileSystemModule.methodContainerMap_.end());
|
|
413
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("getTotalDiskCapacityAsync") !=
|
|
414
|
+
fileSystemModule.methodContainerMap_.end());
|
|
415
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("downloadAsync") !=
|
|
416
|
+
fileSystemModule.methodContainerMap_.end());
|
|
417
|
+
REQUIRE(fileSystemModule.methodContainerMap_.find("uploadAsync") !=
|
|
418
|
+
fileSystemModule.methodContainerMap_.end());
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
THEN("size of methodContainerMap is increased by addListener and removeListeners") {
|
|
422
|
+
REQUIRE(fileSystemModule.methodContainerMap_.size() == 19);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name": "@amazon-devices/expo-file-system", "version": "2.0.1758683737+15.8.0", "description": "Provides access to the local file system on the device.", "main": "build/index.js", "types": "build/index.d.ts", "sideEffects": false, "scripts": {"build": "expo-module build", "clean": "expo-module clean", "lint": "expo-module lint", "test": "expo-module test", "expo-module": "expo-module"}, "kepler": {"temporary-alias-for-package": {"name": "expo-file-system", "version": "15.8.0"}}, "keywords": ["react-native", "expo", "file-system", "file"], "repository": {"type": "git", "url": "https://github.com/expo/expo.git", "directory": "packages/expo-file-system"}, "bugs": {"url": "https://github.com/expo/expo/issues"}, "author": "650 Industries, Inc.", "license": "See license in LICENSE", "homepage": "https://docs.expo.dev/versions/latest/sdk/filesystem/", "dependencies": {"uuid": "^3.4.0", "@amazon-devices/keplerscript-turbomodule-api": "^1.0.0"}, "devDependencies": {"expo-module-scripts": "^3.0.0", "jest-expo": "~50.0.0-alpha.0"}, "peerDependencies": {"expo": "*"}}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const config_plugins_1 = require("expo/config-plugins");
|
|
4
|
+
const pkg = require('expo-file-system/package.json');
|
|
5
|
+
const withFileSystem = (config) => {
|
|
6
|
+
return config_plugins_1.AndroidConfig.Permissions.withPermissions(config, [
|
|
7
|
+
'android.permission.READ_EXTERNAL_STORAGE',
|
|
8
|
+
'android.permission.WRITE_EXTERNAL_STORAGE',
|
|
9
|
+
'android.permission.INTERNET',
|
|
10
|
+
]);
|
|
11
|
+
};
|
|
12
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(withFileSystem, pkg.name, pkg.version);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AndroidConfig, ConfigPlugin, createRunOncePlugin } from 'expo/config-plugins';
|
|
2
|
+
|
|
3
|
+
const pkg = require('expo-file-system/package.json');
|
|
4
|
+
|
|
5
|
+
const withFileSystem: ConfigPlugin = (config) => {
|
|
6
|
+
return AndroidConfig.Permissions.withPermissions(config, [
|
|
7
|
+
'android.permission.READ_EXTERNAL_STORAGE',
|
|
8
|
+
'android.permission.WRITE_EXTERNAL_STORAGE',
|
|
9
|
+
'android.permission.INTERNET',
|
|
10
|
+
]);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default createRunOncePlugin(withFileSystem, pkg.name, pkg.version);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/withFileSystem.ts"],"version":"5.7.2"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
dependencies: {
|
|
3
|
+
'@amazon-devices/react-native-kepler': {
|
|
4
|
+
platforms: {
|
|
5
|
+
kepler: null, // do not use platform definition from @react-native-kepler
|
|
6
|
+
},
|
|
7
|
+
},
|
|
8
|
+
},
|
|
9
|
+
dependency: {
|
|
10
|
+
platforms: {
|
|
11
|
+
kepler: {
|
|
12
|
+
autolink: {
|
|
13
|
+
ExponentFileSystemLib: {
|
|
14
|
+
provider: 'system',
|
|
15
|
+
turbomodules: ['ExponentFileSystem'],
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { KeplerTurboModule, TurboModuleRegistry } from '@amazon-devices/keplerscript-turbomodule-api';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DeletingOptions,
|
|
5
|
+
DownloadOptions,
|
|
6
|
+
FileInfo,
|
|
7
|
+
FileSystemDownloadResult,
|
|
8
|
+
FileSystemUploadOptions,
|
|
9
|
+
FileSystemUploadResult,
|
|
10
|
+
InfoOptions,
|
|
11
|
+
MakeDirectoryOptions,
|
|
12
|
+
ReadingOptions,
|
|
13
|
+
RelocatingOptions,
|
|
14
|
+
WritingOptions,
|
|
15
|
+
FileSystemHttpResult,
|
|
16
|
+
} from './FileSystem.types';
|
|
17
|
+
import { ExponentFileSystemModule } from './types';
|
|
18
|
+
|
|
19
|
+
interface Spec extends KeplerTurboModule {
|
|
20
|
+
readonly documentDirectory: () => string;
|
|
21
|
+
readonly cacheDirectory: () => string;
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated this property was removed from expo
|
|
24
|
+
*/
|
|
25
|
+
readonly bundledAssets: () => string;
|
|
26
|
+
readonly bundleDirectory: () => string;
|
|
27
|
+
readonly getInfoAsync: (fileUri: string, options: InfoOptions) => Promise<FileInfo>;
|
|
28
|
+
readonly readAsStringAsync: (fileUri: string, options: ReadingOptions) => Promise<string>;
|
|
29
|
+
readonly getContentUriAsync: (fileUri: string) => Promise<string>;
|
|
30
|
+
readonly writeAsStringAsync: (fileUri: string, contents: string, options: WritingOptions) => Promise<void>;
|
|
31
|
+
readonly writeArrayBufferAsync: (fileUri: string, contents: ArrayBuffer) => Promise<void>;
|
|
32
|
+
readonly deleteAsync: (fileUri: string, options: DeletingOptions) => Promise<void>;
|
|
33
|
+
readonly moveAsync: (options: RelocatingOptions) => Promise<void>;
|
|
34
|
+
readonly copyAsync: (options: RelocatingOptions) => Promise<void>;
|
|
35
|
+
readonly makeDirectoryAsync: (fileUri: string, options: MakeDirectoryOptions) => Promise<void>;
|
|
36
|
+
readonly readDirectoryAsync: (fileUri: string) => Promise<string[]>;
|
|
37
|
+
readonly getFreeDiskStorageAsync: () => Promise<string>;
|
|
38
|
+
readonly getTotalDiskCapacityAsync: () => Promise<string | undefined>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const nativeModule = TurboModuleRegistry.getEnforcing<Spec>('ExponentFileSystem') as Spec;
|
|
42
|
+
const expoCryptoNativeModule = TurboModuleRegistry.getEnforcing<any>('ExpoCrypto');
|
|
43
|
+
|
|
44
|
+
function getHeadersFromResponse(response: Response): FileSystemHttpResult['headers'] {
|
|
45
|
+
const responseHeaders: FileSystemDownloadResult['headers'] = {};
|
|
46
|
+
response.headers.forEach((value: string, key: string) => {
|
|
47
|
+
responseHeaders[key] = value;
|
|
48
|
+
})
|
|
49
|
+
return responseHeaders;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getMimeTypeFromHeaders(responseHeaders: FileSystemHttpResult['headers']): FileSystemHttpResult['mimeType'] {
|
|
53
|
+
const contentTypeKey = Object.keys(responseHeaders).find((key) => key.toLowerCase() === "content-type");
|
|
54
|
+
return contentTypeKey ? responseHeaders[contentTypeKey].split(';')[0] : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const module: ExponentFileSystemModule = {
|
|
58
|
+
get name(): 'ExponentFileSystem' {
|
|
59
|
+
return 'ExponentFileSystem';
|
|
60
|
+
},
|
|
61
|
+
get documentDirectory() {
|
|
62
|
+
return nativeModule.documentDirectory();
|
|
63
|
+
},
|
|
64
|
+
get cacheDirectory() {
|
|
65
|
+
return nativeModule.cacheDirectory();
|
|
66
|
+
},
|
|
67
|
+
/**
|
|
68
|
+
* @deprecated this property was removed from expo
|
|
69
|
+
*/
|
|
70
|
+
get bundledAssets() {
|
|
71
|
+
return null;
|
|
72
|
+
},
|
|
73
|
+
get bundleDirectory() {
|
|
74
|
+
return nativeModule.bundleDirectory();
|
|
75
|
+
},
|
|
76
|
+
async getInfoAsync(fileUri: string, options: InfoOptions) {
|
|
77
|
+
return nativeModule.getInfoAsync(fileUri, options);
|
|
78
|
+
},
|
|
79
|
+
async readAsStringAsync(fileUri: string, options: ReadingOptions) {
|
|
80
|
+
return nativeModule.readAsStringAsync(fileUri, options);
|
|
81
|
+
},
|
|
82
|
+
async getContentUriAsync(fileUri: string) {
|
|
83
|
+
return nativeModule.getContentUriAsync(fileUri);
|
|
84
|
+
},
|
|
85
|
+
async writeAsStringAsync(fileUri: string, contents: string, options: WritingOptions) {
|
|
86
|
+
return nativeModule.writeAsStringAsync(fileUri, contents, options);
|
|
87
|
+
},
|
|
88
|
+
async deleteAsync(fileUri: string, options: DeletingOptions) {
|
|
89
|
+
return nativeModule.deleteAsync(fileUri, options);
|
|
90
|
+
},
|
|
91
|
+
async moveAsync(options: RelocatingOptions) {
|
|
92
|
+
return nativeModule.moveAsync(options);
|
|
93
|
+
},
|
|
94
|
+
async copyAsync(options: RelocatingOptions) {
|
|
95
|
+
return nativeModule.copyAsync(options);
|
|
96
|
+
},
|
|
97
|
+
async makeDirectoryAsync(fileUri: string, options: MakeDirectoryOptions) {
|
|
98
|
+
return nativeModule.makeDirectoryAsync(fileUri, options);
|
|
99
|
+
},
|
|
100
|
+
async readDirectoryAsync(fileUri: string) {
|
|
101
|
+
return nativeModule.readDirectoryAsync(fileUri);
|
|
102
|
+
},
|
|
103
|
+
async getFreeDiskStorageAsync() {
|
|
104
|
+
return nativeModule.getFreeDiskStorageAsync().then(parseInt);
|
|
105
|
+
},
|
|
106
|
+
async getTotalDiskCapacityAsync() {
|
|
107
|
+
const result = await nativeModule.getTotalDiskCapacityAsync();
|
|
108
|
+
return result ? parseInt(result) : 0;
|
|
109
|
+
},
|
|
110
|
+
async downloadAsync(url: string, fileUri: string, options: DownloadOptions): Promise<FileSystemDownloadResult> {
|
|
111
|
+
const { headers, md5 } = options;
|
|
112
|
+
const response = await fetch(url, { headers });
|
|
113
|
+
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
throw new Error('Failed to download the file!');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const data = await response.arrayBuffer();
|
|
119
|
+
|
|
120
|
+
await nativeModule.writeArrayBufferAsync(fileUri, data);
|
|
121
|
+
|
|
122
|
+
const responseHeaders = getHeadersFromResponse(response);
|
|
123
|
+
|
|
124
|
+
let downloadResult: FileSystemDownloadResult = {
|
|
125
|
+
uri: fileUri,
|
|
126
|
+
headers: responseHeaders,
|
|
127
|
+
status: response.status,
|
|
128
|
+
mimeType: getMimeTypeFromHeaders(responseHeaders)
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
if (md5) {
|
|
132
|
+
const md5ArrayBuffer = await expoCryptoNativeModule.digestAsync("MD5", data);
|
|
133
|
+
downloadResult.md5 = [...new Uint8Array(md5ArrayBuffer)]
|
|
134
|
+
.map(x => x.toString(16).padStart(2, '0'))
|
|
135
|
+
.join('');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return downloadResult;
|
|
139
|
+
},
|
|
140
|
+
async uploadAsync(url: string, fileUri: string, options: FileSystemUploadOptions): Promise<FileSystemUploadResult> {
|
|
141
|
+
const { headers, httpMethod } = options;
|
|
142
|
+
const method = httpMethod ?? "POST";
|
|
143
|
+
|
|
144
|
+
const data = await nativeModule.readAsStringAsync(fileUri, {});
|
|
145
|
+
|
|
146
|
+
const response = await fetch(url, { method, headers, body: data });
|
|
147
|
+
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
throw new Error('Failed to upload the file!');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const uploadResult = await response.text();
|
|
153
|
+
|
|
154
|
+
const responseHeaders = getHeadersFromResponse(response);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
body: uploadResult,
|
|
158
|
+
headers: responseHeaders,
|
|
159
|
+
status: response.status,
|
|
160
|
+
mimeType: getMimeTypeFromHeaders(responseHeaders)
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
addListener(eventName: string): void{},
|
|
164
|
+
removeListeners(count: number): void{},
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export default module;
|