@capacitor-community/sqlite 4.1.1 → 4.2.0

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/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
  <a href="https://www.npmjs.com/package/@capacitor-community/sqlite"><img src="https://img.shields.io/npm/dw/@capacitor-community/sqlite?style=flat-square" /></a>
17
17
  <a href="https://www.npmjs.com/package/@capacitor-community/sqlite"><img src="https://img.shields.io/npm/v/@capacitor-community/sqlite?style=flat-square" /></a>
18
18
  <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
19
- <a href="#contributors-"><img src="https://img.shields.io/badge/all%20contributors-17-orange?style=flat-square" /></a>
19
+ <a href="#contributors-"><img src="https://img.shields.io/badge/all%20contributors-22-orange?style=flat-square" /></a>
20
20
  <!-- ALL-CONTRIBUTORS-BADGE:END -->
21
21
  </p>
22
22
 
@@ -78,6 +78,7 @@ See [#301](https://github.com/capacitor-community/sqlite/issues/301) and [SO que
78
78
  cd electron
79
79
  npm install --save sqlite3
80
80
  npm install --save jszip
81
+ npm install --save node-fetch
81
82
  npm install --save-dev @types/sqlite3
82
83
  ```
83
84
 
@@ -137,7 +138,7 @@ npm install --save-dev @types/sqlite3
137
138
  | closeNCConnection | ✅ | ✅ | ❌ | ❌ |
138
139
  | isNCDatabase | ✅ | ✅ | ❌ | ❌ |
139
140
  | transaction | ✅ | ✅ | ✅ | ✅ |
140
- | getFromHTTPRequest | | | ✅ | ✅ | since 4.1.1
141
+ | getFromHTTPRequest | | | ✅ | ✅ | since 4.2.0
141
142
 
142
143
 
143
144
  ## Documentation & APIs
@@ -210,7 +211,7 @@ npm install --save-dev @types/sqlite3
210
211
 
211
212
  The iOS and Android codes are using `SQLCipher` allowing for database encryption.
212
213
  The iOS codes is using `ZIPFoundation` for unzipping assets files
213
- The Electron code is using `sqlite3`.
214
+ The Electron code is using `sqlite3` and `node-fetch` from 4.2.0.
214
215
  The Web code is using the Stencil component `jeep-sqlite` based on `sql.js`, `localforage`. and `jszip`
215
216
 
216
217
  ## Contributors ✨
@@ -242,6 +243,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
242
243
  <a href="https://github.com/antoniovlx" title="antoniovlx"><img src="https://github.com/antoniovlx.png?size=100" width="50" height="50" /></a>
243
244
  <a href="https://github.com/HarelM" title="HarelM"><img src="https://github.com/HarelM.png?size=100" width="50" height="50" /></a>
244
245
  <a href="https://github.com/rdlabo" title="rdlabo"><img src="https://github.com/rdlabo.png?size=100" width="50" height="50" /></a>
246
+ <a href="https://github.com/axkristiansen" title="rdlabo"><img src="https://github.com/axkristiansen.png?size=100" width="50" height="50" /></a>
245
247
  </p>
246
248
 
247
249
  <!-- markdownlint-enable -->
@@ -17,6 +17,7 @@ import com.getcapacitor.community.database.sqlite.SQLite.ImportExportJson.JsonSQ
17
17
  import com.getcapacitor.community.database.sqlite.SQLite.ImportExportJson.UtilsJson;
18
18
  import com.getcapacitor.community.database.sqlite.SQLite.SqliteConfig;
19
19
  import com.getcapacitor.community.database.sqlite.SQLite.UtilsBiometric;
20
+ import com.getcapacitor.community.database.sqlite.SQLite.UtilsDownloadFromHTTP;
20
21
  import com.getcapacitor.community.database.sqlite.SQLite.UtilsFile;
21
22
  import com.getcapacitor.community.database.sqlite.SQLite.UtilsMigrate;
22
23
  import com.getcapacitor.community.database.sqlite.SQLite.UtilsNCDatabase;
@@ -46,6 +47,7 @@ public class CapacitorSQLite {
46
47
  private final UtilsJson uJson = new UtilsJson();
47
48
  private final UtilsMigrate uMigrate = new UtilsMigrate();
48
49
  private final UtilsNCDatabase uNCDatabase = new UtilsNCDatabase();
50
+ private final UtilsDownloadFromHTTP uHTTP = new UtilsDownloadFromHTTP();
49
51
  private UtilsSecret uSecret;
50
52
  private SharedPreferences sharedPreferences = null;
51
53
  private MasterKey masterKeyAlias;
@@ -537,6 +539,14 @@ public class CapacitorSQLite {
537
539
  }
538
540
  }
539
541
 
542
+ public void getFromHTTPRequest(String url) throws Exception {
543
+ try {
544
+ uHTTP.download(context, url);
545
+ } catch (Exception e) {
546
+ throw new Exception(e.getMessage());
547
+ }
548
+ }
549
+
540
550
  public Boolean checkConnectionsConsistency(JSArray dbNames, JSArray openModes) throws Exception {
541
551
  Set<String> keys = new HashSet<String>(Collections.list(dbDict.keys()));
542
552
  String msg = "All Native Connections released";
@@ -12,12 +12,10 @@ import com.getcapacitor.annotation.CapacitorPlugin;
12
12
  import com.getcapacitor.community.database.sqlite.SQLite.Database;
13
13
  import com.getcapacitor.community.database.sqlite.SQLite.ImportExportJson.JsonSQLite;
14
14
  import com.getcapacitor.community.database.sqlite.SQLite.SqliteConfig;
15
-
16
15
  import java.util.Collections;
17
16
  import java.util.Dictionary;
18
17
  import java.util.Hashtable;
19
18
  import java.util.List;
20
-
21
19
  import org.json.JSONArray;
22
20
  import org.json.JSONException;
23
21
  import org.json.JSONObject;
@@ -1225,9 +1223,9 @@ public class CapacitorSQLitePlugin extends Plugin {
1225
1223
  try {
1226
1224
  Dictionary<Integer, JSONObject> upgDict = implementation.addUpgradeStatement(upgrade);
1227
1225
 
1228
- if (versionUpgrades.get(dbName) != null){
1226
+ if (versionUpgrades.get(dbName) != null) {
1229
1227
  List<Integer> keys = Collections.list(upgDict.keys());
1230
- for (Integer versionKey : keys) {
1228
+ for (Integer versionKey : keys) {
1231
1229
  JSONObject upgObj = upgDict.get(versionKey);
1232
1230
 
1233
1231
  versionUpgrades.get(dbName).put(versionKey, upgObj);
@@ -1362,7 +1360,7 @@ public class CapacitorSQLitePlugin extends Plugin {
1362
1360
  JSObject retObj = new JSObject();
1363
1361
  JsonSQLite retJson = new JsonSQLite();
1364
1362
  if (!call.getData().has("database")) {
1365
- String msg = "ExportToJson: Must provide a database name";
1363
+ String msg = "DeleteExportedRows: Must provide a database name";
1366
1364
  rHandler.retResult(call, null, msg);
1367
1365
  return;
1368
1366
  }
@@ -1418,7 +1416,62 @@ public class CapacitorSQLitePlugin extends Plugin {
1418
1416
  */
1419
1417
  @PluginMethod
1420
1418
  public void getFromHTTPRequest(PluginCall call) {
1421
- call.unimplemented("Not implemented on Android.");
1419
+ if (!call.getData().has("url")) {
1420
+ String msg = "GetFromHTTPRequest: Must provide a database url";
1421
+ rHandler.retResult(call, null, msg);
1422
+ return;
1423
+ }
1424
+ String url = call.getString("url");
1425
+ if (implementation != null) {
1426
+ Runnable setHTTPRunnable = new Runnable() {
1427
+ @Override
1428
+ public void run() {
1429
+ android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
1430
+ try {
1431
+ implementation.getFromHTTPRequest(url);
1432
+ getActivity()
1433
+ .runOnUiThread(
1434
+ new Runnable() {
1435
+ @Override
1436
+ public void run() {
1437
+ rHandler.retResult(call, null, null);
1438
+ return;
1439
+ }
1440
+ }
1441
+ );
1442
+ } catch (Exception e) {
1443
+ getActivity()
1444
+ .runOnUiThread(
1445
+ new Runnable() {
1446
+ @Override
1447
+ public void run() {
1448
+ String msg = "GetFromHTTPRequest: " + e.getMessage();
1449
+ rHandler.retResult(call, null, msg);
1450
+ return;
1451
+ }
1452
+ }
1453
+ );
1454
+ }
1455
+ }
1456
+ };
1457
+ Thread myHttpThread = new Thread(setHTTPRunnable);
1458
+ myHttpThread.start();
1459
+ while (myHttpThread.isAlive());
1460
+ System.out.println("Thread Exiting!");
1461
+ /* try {
1462
+ implementation.getFromHTTPRequest(url);
1463
+ rHandler.retResult(call, null, null);
1464
+ return;
1465
+ } catch (Exception e) {
1466
+ String msg = "GetFromHTTPRequest: " + e.getMessage();
1467
+ rHandler.retResult(call, null, msg);
1468
+ return;
1469
+ }
1470
+ */
1471
+ } else {
1472
+ rHandler.retResult(call, null, loadMessage);
1473
+ return;
1474
+ }
1422
1475
  }
1423
1476
 
1424
1477
  private void AddObserversToNotificationCenter() {
@@ -0,0 +1,116 @@
1
+ package com.getcapacitor.community.database.sqlite.SQLite;
2
+
3
+ import android.content.Context;
4
+ import android.content.res.AssetManager;
5
+ import java.io.File;
6
+ import java.io.FileOutputStream;
7
+ import java.io.IOException;
8
+ import java.io.InputStream;
9
+ import java.net.HttpURLConnection;
10
+ import java.net.URL;
11
+
12
+ public class UtilsDownloadFromHTTP {
13
+
14
+ private static final String TAG = UtilsDownloadFromHTTP.class.getName();
15
+ private static final int BUFFER_SIZE = 4096;
16
+ private final UtilsFile _uFile = new UtilsFile();
17
+
18
+ public void download(Context context, String fileUrl) throws Exception {
19
+ AssetManager assetManager = context.getAssets();
20
+ String fileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
21
+ String dbName = fileName;
22
+ Boolean isZip = false;
23
+ if (!fileName.contains("SQLite.db")) {
24
+ if (_uFile.getFileExtension((fileName)).equals("db")) {
25
+ dbName = fileName.substring(0, fileName.length() - 3) + "SQLite.db";
26
+ }
27
+ if (_uFile.isLast(fileName, ".zip")) {
28
+ isZip = true;
29
+ }
30
+ }
31
+ File cacheDir = context.getCacheDir();
32
+ String cachePath = cacheDir.getAbsolutePath();
33
+ String tmpFilePath = cachePath + File.separator + dbName;
34
+ String databasePath = _uFile.getDatabaseDirectoryPath(context);
35
+ File databaseDir = new File(databasePath);
36
+ try {
37
+ // delete file if exists in cache
38
+ Boolean isExists = _uFile.isPathExists(tmpFilePath);
39
+ if (isExists) {
40
+ _uFile.deleteFile(cachePath, dbName);
41
+ }
42
+ downloadFileToCache(fileUrl, cachePath);
43
+ if (isZip) {
44
+ _uFile.unzipCopyDatabase(cachePath, null, tmpFilePath, true);
45
+ // delete zip file from cache
46
+ _uFile.deleteFile(cachePath, dbName);
47
+ }
48
+ // move files to database folder
49
+ _uFile.moveAllDBs(cacheDir, databaseDir);
50
+ } catch (Exception e) {
51
+ throw new Exception(e.getMessage());
52
+ }
53
+ }
54
+
55
+ public static void downloadFileToCache(String fileURL, String cacheDir) throws Exception {
56
+ HttpURLConnection httpConn = null;
57
+ try {
58
+ URL url = new URL(fileURL);
59
+ httpConn = (HttpURLConnection) url.openConnection();
60
+ int responseCode = httpConn.getResponseCode();
61
+ if (responseCode == HttpURLConnection.HTTP_OK) {
62
+ String fileName = "";
63
+ String disposition = httpConn.getHeaderField("Content-Disposition");
64
+ // String contentType = httpConn.getContentType();
65
+ int contentLength = httpConn.getContentLength();
66
+
67
+ if (disposition != null) {
68
+ // extracts file name from header field
69
+ int index = disposition.indexOf("filename=");
70
+ if (index > 0) {
71
+ fileName = disposition.substring(index + 10, disposition.length() - 1);
72
+ }
73
+ } else {
74
+ // extracts file name from URL
75
+ fileName = fileURL.substring(fileURL.lastIndexOf("/") + 1);
76
+ }
77
+ String dbName = fileName;
78
+ if (!fileName.contains("SQLite.db")) {
79
+ if (fileName.substring(fileName.length() - 3).equals(".db")) {
80
+ dbName = fileName.substring(0, fileName.length() - 3) + "SQLite.db";
81
+ }
82
+ }
83
+
84
+ // System.out.println("Content-Type = " + contentType);
85
+ // System.out.println("Content-Disposition = " + disposition);
86
+ // System.out.println("Content-Length = " + contentLength);
87
+ // System.out.println("fileName = " + fileName);
88
+ // opens input stream from the HTTP connection
89
+ InputStream inputStream = httpConn.getInputStream();
90
+ // create temporary file path
91
+ String tmpFilePath = cacheDir + File.separator + dbName;
92
+ // opens an output stream to save into file
93
+ FileOutputStream outputStream = new FileOutputStream(tmpFilePath);
94
+ int bytesRead = -1;
95
+ byte[] buffer = new byte[BUFFER_SIZE];
96
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
97
+ outputStream.write(buffer, 0, bytesRead);
98
+ }
99
+
100
+ outputStream.close();
101
+ inputStream.close();
102
+
103
+ System.out.println("File " + fileName + " downloaded (" + contentLength + ")");
104
+ } else {
105
+ String msg = "No file to download. Server replied HTTP code: " + responseCode;
106
+ throw new IOException(msg);
107
+ }
108
+ } catch (IOException e) {
109
+ throw new Exception(e);
110
+ } finally {
111
+ if (httpConn != null) {
112
+ httpConn.disconnect();
113
+ }
114
+ }
115
+ }
116
+ }
@@ -2,7 +2,9 @@ package com.getcapacitor.community.database.sqlite.SQLite;
2
2
 
3
3
  import android.content.Context;
4
4
  import android.content.res.AssetManager;
5
+ import android.os.Build;
5
6
  import android.util.Log;
7
+ import androidx.annotation.RequiresApi;
6
8
  import java.io.File;
7
9
  import java.io.FileInputStream;
8
10
  import java.io.FileOutputStream;
@@ -10,8 +12,16 @@ import java.io.IOException;
10
12
  import java.io.InputStream;
11
13
  import java.io.OutputStream;
12
14
  import java.nio.channels.FileChannel;
15
+ import java.nio.file.DirectoryStream;
16
+ import java.nio.file.Files;
17
+ import java.nio.file.Path;
18
+ import java.nio.file.Paths;
13
19
  import java.util.ArrayList;
20
+ import java.util.HashSet;
14
21
  import java.util.List;
22
+ import java.util.Set;
23
+ import java.util.stream.Collectors;
24
+ import java.util.stream.Stream;
15
25
  import java.util.zip.ZipEntry;
16
26
  import java.util.zip.ZipInputStream;
17
27
 
@@ -33,6 +43,10 @@ public class UtilsFile {
33
43
  }
34
44
  }
35
45
 
46
+ public String getDatabaseDirectoryPath(Context context) {
47
+ return context != null && context.getDatabasePath("x") != null ? context.getDatabasePath("x").getParent() : "";
48
+ }
49
+
36
50
  public String[] getListOfFiles(Context context) {
37
51
  String[] files = context.databaseList();
38
52
  List<String> dbs = new ArrayList<>();
@@ -64,6 +78,10 @@ public class UtilsFile {
64
78
  return file.delete();
65
79
  }
66
80
 
81
+ public Boolean deleteFile(File file) {
82
+ return file.delete();
83
+ }
84
+
67
85
  public void copyFromAssetsToDatabase(Context context, Boolean overwrite) throws Exception {
68
86
  AssetManager assetManager = context.getAssets();
69
87
  String assetsDatabasePath = "public/assets/databases";
@@ -99,7 +117,8 @@ public class UtilsFile {
99
117
  if (isLast(fileName, ".zip")) {
100
118
  // unzip file and extract databases
101
119
  String zipPathName = assetsDatabasePath + "/" + fileName;
102
- unzipCopyDatabase(context, assetManager, zipPathName, assetsDatabasePath, overwrite);
120
+ String databasePath = getDatabaseDirectoryPath(context);
121
+ unzipCopyDatabase(databasePath, assetManager, zipPathName, overwrite);
103
122
  }
104
123
  }
105
124
  return;
@@ -109,26 +128,33 @@ public class UtilsFile {
109
128
  }
110
129
  }
111
130
 
112
- public void unzipCopyDatabase(Context context, AssetManager asm, String zipPath, String assetsDatabasePath, Boolean overwrite)
113
- throws IOException {
114
- InputStream is;
131
+ public void unzipCopyDatabase(String databasePath, AssetManager asm, String zipPath, Boolean overwrite) throws IOException {
132
+ InputStream is = null;
133
+ FileInputStream isF = null;
134
+ ZipInputStream zis;
115
135
  byte[] buffer = new byte[1024];
116
- try {
117
- is = asm.open(zipPath);
118
- ZipInputStream zis = new ZipInputStream(is);
119
136
 
137
+ try {
138
+ if (asm != null) {
139
+ is = asm.open(zipPath);
140
+ zis = new ZipInputStream(is);
141
+ } else {
142
+ File zipFile = new File(zipPath);
143
+ isF = new FileInputStream(zipFile);
144
+ zis = new ZipInputStream(isF);
145
+ }
120
146
  ZipEntry ze = zis.getNextEntry();
121
147
  while (ze != null) {
122
148
  String fileName = ze.getName();
123
149
  if (isLast(fileName, ".db")) {
124
150
  String toFileName = addSQLiteSuffix(fileName);
125
- boolean isExist = isFileExists(context, toFileName);
151
+ String dbPath = databasePath + File.separator + toFileName;
152
+ boolean isExist = isPathExists(dbPath);
126
153
  if (!isExist || overwrite) {
127
154
  if (overwrite && isExist) {
128
- deleteDatabase(context, toFileName);
155
+ deleteFile(databasePath, toFileName);
129
156
  }
130
- String toPathName = context.getDatabasePath(toFileName).getAbsolutePath();
131
- File newFile = new File(toPathName);
157
+ File newFile = new File(dbPath);
132
158
  System.out.println("Unzipping to " + newFile.getAbsolutePath());
133
159
  FileOutputStream fos = new FileOutputStream(newFile);
134
160
  int len;
@@ -145,7 +171,11 @@ public class UtilsFile {
145
171
  //close last ZipEntry
146
172
  zis.closeEntry();
147
173
  zis.close();
148
- is.close();
174
+ if (asm != null && is != null) {
175
+ is.close();
176
+ } else if (isF != null) {
177
+ isF.close();
178
+ }
149
179
  } catch (IOException e) {
150
180
  throw new IOException("in unzipCopyDatabase " + e.getLocalizedMessage());
151
181
  }
@@ -294,6 +324,53 @@ public class UtilsFile {
294
324
  }
295
325
  }
296
326
 
327
+ public void moveAllDBs(File fromDir, File toDir) throws Exception {
328
+ // get the file List from fromDir
329
+ List<String> fileList;
330
+ String fromDirPath = fromDir.getAbsolutePath();
331
+ String toDirPath = toDir.getAbsolutePath();
332
+
333
+ try {
334
+ fileList = listDatabases(fromDir);
335
+ for (String fileName : fileList) {
336
+ // Check if the file exists in toDir
337
+ File toFile = new File(toDirPath, fileName);
338
+ Boolean isPath = isPathExists(toFile.getAbsolutePath());
339
+ if (isPath) {
340
+ deleteFile(toFile);
341
+ }
342
+ File fromFile = new File(fromDirPath, fileName);
343
+
344
+ Boolean success = fromFile.renameTo(toFile);
345
+ if (!success) {
346
+ throw new Exception("moveAllDBs: move file " + fileName + " failed");
347
+ }
348
+ }
349
+ } catch (Exception e) {
350
+ throw new Exception(e.getMessage());
351
+ }
352
+ }
353
+
354
+ public List<String> listDatabases(File fileDir) throws Exception {
355
+ List<String> fileList = new ArrayList<>();
356
+ if (!fileDir.exists()) {
357
+ throw new Exception("File " + fileDir.getAbsolutePath() + " does not exist");
358
+ }
359
+ if (!fileDir.isDirectory()) {
360
+ throw new Exception("File " + fileDir.getAbsolutePath() + " is not a directory");
361
+ }
362
+ File[] fList = fileDir.listFiles();
363
+ for (File file : fList) {
364
+ if (file.isFile()) {
365
+ String fileName = file.getName();
366
+ if (getFileExtension((fileName)).equals("db")) {
367
+ fileList.add(fileName);
368
+ }
369
+ }
370
+ }
371
+ return fileList;
372
+ }
373
+
297
374
  private static void copyFileFromFile(File sourceFile, File destFile) throws IOException {
298
375
  if (!destFile.getParentFile().exists()) destFile.getParentFile().mkdirs();
299
376
 
@@ -10,6 +10,7 @@ enum CapacitorSQLiteError: Error {
10
10
  private var config: SqliteConfig
11
11
  private var dbDict: [String: Database] = [:]
12
12
  private var databaseLocation: String = "Documents"
13
+ private let retHandler: ReturnHandler = ReturnHandler()
13
14
  private var initMessage: String = ""
14
15
  private var isInit: Bool = false
15
16
  private var isEncryption: Bool = true
@@ -457,6 +458,33 @@ enum CapacitorSQLiteError: Error {
457
458
  }
458
459
  }
459
460
 
461
+ // MARK: - GetFromHTTPRequest
462
+
463
+ @objc public func getFromHTTPRequest(_ call: CAPPluginCall, url: String) throws {
464
+ if isInit {
465
+
466
+ UtilsDownloadFromHTTP.download(databaseLocation: databaseLocation,
467
+ url: url) { ( result) in
468
+ switch result {
469
+ case .success(_):
470
+ self.retHandler.rResult(call: call)
471
+ return
472
+ case .failure(let error):
473
+
474
+ if error == .downloadFromHTTPFailed {
475
+ let msg = "Download from HTTP failed"
476
+ self.retHandler.rResult(call: call, message: msg)
477
+ return
478
+ }
479
+
480
+ }
481
+
482
+ }
483
+ } else {
484
+ throw CapacitorSQLiteError.failed(message: initMessage)
485
+ }
486
+ }
487
+
460
488
  // MARK: - Close Connection
461
489
 
462
490
  @objc public func closeConnection(_ dbName: String, readonly: Bool) throws {
@@ -1246,9 +1274,9 @@ enum CapacitorSQLiteError: Error {
1246
1274
 
1247
1275
  // check if the assets/database folder exists
1248
1276
  do {
1249
- let assetsDbPath: URL = try
1277
+ let assetsDbURL: URL = try
1250
1278
  UtilsFile.getAssetsDatabasesPath()
1251
- let aPath: String = assetsDbPath.path
1279
+ let aPath: String = assetsDbURL.path
1252
1280
  let bRes: Bool = UtilsFile.isDirExist(dirPath: aPath)
1253
1281
  if bRes {
1254
1282
  // get the database files from assets
@@ -1274,9 +1302,11 @@ enum CapacitorSQLiteError: Error {
1274
1302
  for zip in zipList {
1275
1303
  // for each zip uncompress the file to the Application
1276
1304
  // database folder
1277
- _ = try UtilsFile
1278
- .unzipFromAssetToDatabase(databaseLocation: databaseLocation,
1279
- zip: zip, overwrite: overwrite)
1305
+ _ = try UtilsFile.unzipToDatabase(
1306
+ fromURL: assetsDbURL,
1307
+ databaseLocation: databaseLocation,
1308
+ zip: zip,
1309
+ overwrite: overwrite)
1280
1310
  }
1281
1311
  return
1282
1312
  } else {
@@ -1285,7 +1315,7 @@ enum CapacitorSQLiteError: Error {
1285
1315
  }
1286
1316
  } catch UtilsFileError.copyFromAssetToDatabaseFailed(let message) {
1287
1317
  throw CapacitorSQLiteError.failed(message: message)
1288
- } catch UtilsFileError.unzipFromAssetToDatabaseFailed(let message) {
1318
+ } catch UtilsFileError.unzipToDatabaseFailed(let message) {
1289
1319
  throw CapacitorSQLiteError.failed(message: message)
1290
1320
  } catch let error {
1291
1321
  let msg: String = "\(error)"
@@ -1158,11 +1158,10 @@ public class CapacitorSQLitePlugin: CAPPlugin {
1158
1158
  if let upgVersionDict: [Int: [String: Any]] = try
1159
1159
  implementation?.addUpgradeStatement(dbName,
1160
1160
  upgrade: upgrade) {
1161
- if (
1162
- versionUpgrades[dbName] != nil
1163
- ){
1161
+ if
1162
+ versionUpgrades[dbName] != nil {
1164
1163
  for (versionKey, upgObj) in upgVersionDict {
1165
- versionUpgrades[dbName]![versionKey] = upgObj
1164
+ versionUpgrades[dbName]?[versionKey] = upgObj
1166
1165
  }
1167
1166
  } else {
1168
1167
  versionUpgrades = ["\(dbName)": upgVersionDict]
@@ -1323,13 +1322,42 @@ public class CapacitorSQLitePlugin: CAPPlugin {
1323
1322
  }
1324
1323
 
1325
1324
  }
1326
-
1325
+
1327
1326
  // MARK: - GetFromHTTPRequest
1328
1327
 
1329
1328
  @objc func getFromHTTPRequest(_ call: CAPPluginCall) {
1330
- call.unimplemented("Not implemented on iOS.")
1331
- }
1329
+ guard let url = call.options["url"] as? String else {
1330
+ retHandler.rResult(
1331
+ call: call,
1332
+ message: "GetFromHTTPRequest: Must provide a database url")
1333
+ return
1334
+ }
1335
+ DispatchQueue.global(qos: .background).async {
1336
+
1337
+ do {
1338
+ try self.implementation?.getFromHTTPRequest(call, url: url)
1339
+ DispatchQueue.main.async {
1340
+ self.retHandler.rResult(call: call)
1341
+ return
1342
+ }
1343
+ } catch CapacitorSQLiteError.failed(let message) {
1332
1344
 
1345
+ DispatchQueue.main.async {
1346
+ let msg = "GetFromHTTPRequest: \(message)"
1347
+ self.retHandler.rResult(call: call, message: msg)
1348
+ return
1349
+ }
1350
+ } catch let error {
1351
+ DispatchQueue.main.async {
1352
+ let msg = "GetFromHTTPRequest: " +
1353
+ "\(error.localizedDescription)"
1354
+ self.retHandler.rResult(call: call, message: msg)
1355
+ return
1356
+ }
1357
+ }
1358
+ }
1359
+
1360
+ }
1333
1361
 
1334
1362
  // MARK: - Add Observers
1335
1363
 
@@ -1380,31 +1408,28 @@ public class CapacitorSQLitePlugin: CAPPlugin {
1380
1408
  config.iosIsEncryption = 1
1381
1409
  config.biometricAuth = 0
1382
1410
  config.iosKeychainPrefix = ""
1383
- if let keychainPrefix = getConfigValue("iosKeychainPrefix") as? String {
1411
+ let configPlugin = getConfig()
1412
+ if let keychainPrefix = configPlugin.getString("iosKeychainPrefix") {
1384
1413
  config.iosKeychainPrefix = keychainPrefix
1385
1414
  }
1386
- if let iosDatabaseLocation = getConfigValue("iosDatabaseLocation")
1387
- as? String {
1415
+ if let iosDatabaseLocation = configPlugin.getString("iosDatabaseLocation") {
1388
1416
  config.iosDatabaseLocation = iosDatabaseLocation
1389
1417
  }
1390
- if let isEncryption = getConfigValue("iosIsEncryption") as? Bool {
1391
- if !isEncryption {
1392
- config.iosIsEncryption = 0
1393
- }
1418
+ let isEncryption = configPlugin.getBoolean("iosIsEncryption", false)
1419
+ if !isEncryption {
1420
+ config.iosIsEncryption = 0
1394
1421
  }
1395
1422
  if config.iosIsEncryption == 1 {
1396
- if let iosBiometric = getConfigValue("iosBiometric") as? [String: Any] {
1397
- if let bioAuth = iosBiometric["biometricAuth"] as? Bool {
1398
- if bioAuth {
1399
- config.biometricAuth = 1
1400
- if let bioTitle = iosBiometric["biometricTitle"] as? String {
1401
- config.biometricTitle = bioTitle.count > 0
1402
- ? bioTitle
1403
- : "Biometric login for capacitor sqlite"
1404
- }
1423
+ let iosBiometric = configPlugin.getObject("iosBiometric")
1424
+ if let bioAuth = iosBiometric?["biometricAuth"] as? Bool {
1425
+ if bioAuth {
1426
+ config.biometricAuth = 1
1427
+ if let bioTitle = iosBiometric?["biometricTitle"] as? String {
1428
+ config.biometricTitle = bioTitle.count > 0
1429
+ ? bioTitle
1430
+ : "Biometric login for capacitor sqlite"
1405
1431
  }
1406
1432
  }
1407
-
1408
1433
  }
1409
1434
  }
1410
1435
  return config
@@ -0,0 +1,122 @@
1
+ //
2
+ // UtilsDownloadFromHTTP.swift
3
+ // CapacitorCommunitySqlite
4
+ //
5
+ // Created by Quéau Jean Pierre on 05/10/2022.
6
+ //
7
+
8
+ import Foundation
9
+ import ZIPFoundation
10
+
11
+ enum UtilsDownloadError: Error {
12
+ case downloadFromHTTPFailed
13
+ }
14
+ class UtilsDownloadFromHTTP {
15
+ // swiftlint:disable cyclomatic_complexity
16
+ // swiftlint:disable function_body_length
17
+ class func download(databaseLocation: String, url: String,
18
+ completion: @escaping (Result<Bool, UtilsDownloadError>)
19
+ -> Void) {
20
+ if let mUrl: URL = URL(string: url) {
21
+ let fileName = mUrl.lastPathComponent
22
+ var dbName = fileName
23
+ var isZip = false
24
+ if !fileName.contains("SQLite.db") {
25
+ if String(fileName.suffix(3)) == ".db" {
26
+ dbName = fileName.replacingOccurrences(
27
+ of: ".db", with: "SQLite.db")
28
+ }
29
+ if String(fileName.suffix(4)) == ".zip" {
30
+ isZip = true
31
+ }
32
+ }
33
+ // compute a path to this Url in the cache
34
+ let cacheURL = UtilsFile.getTmpURL()
35
+ let tmp = cacheURL.lastPathComponent
36
+ let fileCacheURL = FileManager.default.temporaryDirectory
37
+ .appendingPathComponent(
38
+ dbName,
39
+ isDirectory: false
40
+ )
41
+
42
+ let task = URLSession.shared.downloadTask(with: mUrl) {
43
+ (tempURL, response, error) in
44
+ // Early exit on error
45
+ guard let tempURL = tempURL else {
46
+ let msg = "\(String(describing: error?.localizedDescription))"
47
+ print("\(msg)")
48
+ completion(.failure(UtilsDownloadError.downloadFromHTTPFailed))
49
+ return
50
+ }
51
+ if let httpResponse = response as? HTTPURLResponse {
52
+ switch httpResponse.statusCode {
53
+ case 200:
54
+ do {
55
+ // Remove any existing document at file
56
+ if FileManager.default.fileExists(
57
+ atPath: fileCacheURL.path) {
58
+ try FileManager.default.removeItem(
59
+ atPath: fileCacheURL.path)
60
+ }
61
+
62
+ // Copy the tempURL to file
63
+ try FileManager.default.copyItem(
64
+ at: tempURL,
65
+ to: fileCacheURL
66
+ )
67
+ // Delete the tempUrl file
68
+ try FileManager.default.removeItem(at: tempURL)
69
+ let dbURL = try UtilsFile.getDatabaseLocationURL(
70
+ databaseLocation: databaseLocation)
71
+ if isZip {
72
+ // get the zip files
73
+ let zipList: [String] = try UtilsFile
74
+ .getFileList(path: cacheURL.path,
75
+ ext: ".zip")
76
+ // loop through the database files
77
+ for zip in zipList {
78
+ _ = try UtilsFile.unzipToDatabase(
79
+ fromURL: cacheURL,
80
+ databaseLocation: tmp,
81
+ zip: zip,
82
+ overwrite: true)
83
+ }
84
+ // Delete the zip file
85
+ try FileManager.default.removeItem(
86
+ at: fileCacheURL)
87
+
88
+ }
89
+ try UtilsFile.moveAllDBSQLite(
90
+ fromURL: cacheURL,
91
+ dirUrl: dbURL)
92
+ completion(.success(true))
93
+ return
94
+ }
95
+
96
+ // Handle potential file system errors
97
+ catch let error {
98
+ let msg = "\(error.localizedDescription)"
99
+ print("\(msg)")
100
+ completion(.failure(UtilsDownloadError.downloadFromHTTPFailed))
101
+ return
102
+ }
103
+ default:
104
+ let msg = "Download: GET resquest not successful. http status code \(httpResponse.statusCode)"
105
+ print("\(msg)")
106
+ completion(.failure(UtilsDownloadError.downloadFromHTTPFailed))
107
+ return
108
+ }
109
+ } else {
110
+ let msg = "Download: not a valid http response"
111
+ print("\(msg)")
112
+ completion(.failure(UtilsDownloadError.downloadFromHTTPFailed))
113
+ return
114
+ }
115
+ }
116
+ // Start the download
117
+ task.resume()
118
+ }
119
+ }
120
+ // swiftlint:enable cyclomatic_complexity
121
+ // swiftlint:enable function_body_length
122
+ }
@@ -25,12 +25,13 @@ enum UtilsFileError: Error {
25
25
  case getLibraryURLFailed
26
26
  case getFileListFailed
27
27
  case copyFromAssetToDatabaseFailed(message: String)
28
- case unzipFromAssetToDatabaseFailed(message: String)
28
+ case unzipToDatabaseFailed(message: String)
29
29
  case copyFromNamesFailed
30
30
  case getFolderURLFailed(message: String)
31
31
  case createDirFailed(message: String)
32
32
  case moveAllDBSQLiteFailed(message: String)
33
33
  case createDatabaseLocationFailed(message: String)
34
+ case getDatabaseLocationURLFailed(message: String)
34
35
  }
35
36
  // swiftlint:disable file_length
36
37
  // swiftlint:disable type_body_length
@@ -51,12 +52,17 @@ class UtilsFile {
51
52
  // move all existing dbs from "Documents" to location folder
52
53
  if location.prefix(9) != "Documents" &&
53
54
  location.prefix(7) != "default" {
55
+ let databaseURL: URL = try UtilsFile
56
+ .getDatabasesUrl().absoluteURL
54
57
 
55
- try UtilsFile.moveAllDBSQLite(dirUrl: dirUrl)
58
+ try UtilsFile.moveAllDBSQLite(fromURL: databaseURL,
59
+ dirUrl: dirUrl)
56
60
  }
57
61
  }
58
62
  } catch UtilsFileError.getFolderURLFailed(let message) {
59
63
  throw UtilsFileError.createDatabaseLocationFailed(message: message)
64
+ } catch UtilsFileError.getDatabasesURLFailed {
65
+ throw UtilsFileError.createDatabaseLocationFailed(message: "getDatabasesURLFailed")
60
66
  } catch UtilsFileError.moveAllDBSQLiteFailed(let message) {
61
67
  throw UtilsFileError.createDatabaseLocationFailed(message: message)
62
68
  } catch let error {
@@ -77,16 +83,15 @@ class UtilsFile {
77
83
 
78
84
  // MARK: - moveAllDBSQLite
79
85
 
80
- class func moveAllDBSQLite(dirUrl: URL) throws {
86
+ class func moveAllDBSQLite(fromURL: URL, dirUrl: URL) throws {
81
87
  // get the db list from Documents folder
82
88
  do {
83
- let databaseURL: URL = try UtilsFile
84
- .getDatabasesUrl().absoluteURL
89
+
85
90
  let fileList: [String] = try UtilsFile
86
- .getFileList(path: databaseURL.path,
91
+ .getFileList(path: fromURL.path,
87
92
  ext: "SQLite.db")
88
93
  for file in fileList {
89
- let fromFileURL: URL = databaseURL
94
+ let fromFileURL: URL = fromURL
90
95
  .appendingPathComponent(file).absoluteURL
91
96
  let toFileURL = dirUrl
92
97
  .appendingPathComponent(file).absoluteURL
@@ -149,6 +154,8 @@ class UtilsFile {
149
154
  dbPathURL = try UtilsFile.getApplicationURL().absoluteURL
150
155
  } else if first[0] == "Library" {
151
156
  dbPathURL = try UtilsFile.getLibraryURL().absoluteURL
157
+ } else if first[0] == "tmp" {
158
+ dbPathURL = UtilsFile.getTmpURL().absoluteURL
152
159
  } else if first[0].caseInsensitiveCompare("cache") == .orderedSame {
153
160
  dbPathURL = try UtilsFile.getCacheURL().absoluteURL
154
161
  } else if first[0] == "Documents" || first[0] == "default" {
@@ -216,6 +223,19 @@ class UtilsFile {
216
223
  }
217
224
  }
218
225
 
226
+ // MARK: - getDatabaseLocationURL
227
+
228
+ class func getDatabaseLocationURL(databaseLocation: String) throws -> URL {
229
+ do {
230
+ let url: URL = try UtilsFile
231
+ .getFolderURL(folderPath: databaseLocation)
232
+
233
+ return url
234
+ } catch UtilsFileError.getFolderURLFailed(let message) {
235
+ throw UtilsFileError.getDatabaseLocationURLFailed(message: message)
236
+ }
237
+ }
238
+
219
239
  // MARK: - getApplicationURL
220
240
 
221
241
  class func getApplicationURL() throws -> URL {
@@ -255,6 +275,12 @@ class UtilsFile {
255
275
  }
256
276
  }
257
277
 
278
+ // MARK: - getTmpURL
279
+
280
+ class func getTmpURL() -> URL {
281
+ return FileManager.default.temporaryDirectory
282
+ }
283
+
258
284
  // MARK: - getLibraryURL
259
285
 
260
286
  class func getLibraryURL() throws -> URL {
@@ -395,15 +421,14 @@ class UtilsFile {
395
421
 
396
422
  }
397
423
 
398
- class func unzipFromAssetToDatabase(databaseLocation: String, zip: String,
399
- overwrite: Bool) throws {
424
+ class func unzipToDatabase(fromURL: URL, databaseLocation: String, zip: String,
425
+ overwrite: Bool) throws {
400
426
  do {
401
- let zipAsset: URL = try getAssetsDatabasesPath()
402
- .appendingPathComponent(zip)
427
+ let zipAsset: URL = fromURL.appendingPathComponent(zip)
403
428
  guard let archive = Archive(url: zipAsset, accessMode: .read) else {
404
429
  let msg = "Error: Read Archive: \(zipAsset) failed"
405
430
  print("\(msg)")
406
- throw UtilsFileError.unzipFromAssetToDatabaseFailed(message: msg)
431
+ throw UtilsFileError.unzipToDatabaseFailed(message: msg)
407
432
  }
408
433
  let uDb: URL = try getFolderURL(folderPath: databaseLocation)
409
434
  for entry in archive {
@@ -421,20 +446,16 @@ class UtilsFile {
421
446
  } catch {
422
447
  let msg = "Error: Extracting \(entry.path) from archive failed \(error.localizedDescription)"
423
448
  print("\(msg)")
424
- throw UtilsFileError.unzipFromAssetToDatabaseFailed(message: msg)
449
+ throw UtilsFileError.unzipToDatabaseFailed(message: msg)
425
450
  }
426
451
  }
427
- } catch UtilsFileError.getAssetsDatabasesPathFailed {
428
- let msg = "Error: getAssetsDatabasesPath Failed"
429
- print("\(msg)")
430
- throw UtilsFileError.unzipFromAssetToDatabaseFailed(message: msg)
431
452
  } catch UtilsFileError.getFolderURLFailed(let message) {
432
453
  print("Error: getFolderUrl Failed \(message)")
433
- throw UtilsFileError.unzipFromAssetToDatabaseFailed(message: message)
454
+ throw UtilsFileError.unzipToDatabaseFailed(message: message)
434
455
  } catch let error {
435
456
  let msg = "Error: \(error)"
436
457
  print("\(msg)")
437
- throw UtilsFileError.unzipFromAssetToDatabaseFailed(message: msg)
458
+ throw UtilsFileError.unzipToDatabaseFailed(message: msg)
438
459
  }
439
460
  }
440
461
 
@@ -238,7 +238,7 @@ class UtilsMigrate {
238
238
  if !toFile.isEmpty {
239
239
  let uFrom: URL = dbPathURL.appendingPathComponent(fromFile)
240
240
  let uTo: URL = databaseURL.appendingPathComponent(toFile)
241
- try UtilsFile.moveFile(pathName: uFrom.path, toPathName: uTo.path, overwrite: true)
241
+ _ = try UtilsFile.moveFile(pathName: uFrom.path, toPathName: uTo.path, overwrite: true)
242
242
  }
243
243
  }
244
244
  }
@@ -27,7 +27,7 @@ class UtilsUpgrade {
27
27
  for (versionKey, upgrade) in Array(upgDict).sorted(by: {$0.0 < $1.0}) {
28
28
  if versionKey > currentVersion && versionKey <= targetVersion {
29
29
  print("- UtilsUpgrade.onUpgrade toVersion: \(versionKey)")
30
-
30
+
31
31
  guard let statements = upgrade["statements"] as? [String] else {
32
32
  let msg: String = "Error: onUpgrade statements not given"
33
33
  throw UtilsUpgradeError.onUpgradeFailed(message: msg)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor-community/sqlite",
3
- "version": "4.1.1",
3
+ "version": "4.2.0",
4
4
  "description": "Community plugin for native & electron SQLite databases",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -55,10 +55,10 @@
55
55
  "prepublishOnly": "npm run build && npm run build-electron && npm run docgen"
56
56
  },
57
57
  "devDependencies": {
58
- "@capacitor/android": "^4.1.0",
59
- "@capacitor/core": "^4.1.0",
58
+ "@capacitor/android": "^4.2.0",
59
+ "@capacitor/core": "^4.2.0",
60
60
  "@capacitor/docgen": "^0.0.17",
61
- "@capacitor/ios": "^4.1.0",
61
+ "@capacitor/ios": "^4.2.0",
62
62
  "@ionic/eslint-config": "^0.3.0",
63
63
  "@ionic/prettier-config": "^1.0.1",
64
64
  "@ionic/swiftlint-config": "^1.1.2",