@react-native-firebase/storage 14.11.1 → 15.1.1

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,30 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [15.1.1](https://github.com/invertase/react-native-firebase/compare/v15.1.0...v15.1.1) (2022-06-28)
7
+
8
+ **Note:** Version bump only for package @react-native-firebase/storage
9
+
10
+ # [15.1.0](https://github.com/invertase/react-native-firebase/compare/v15.0.0...v15.1.0) (2022-06-28)
11
+
12
+ **Note:** Version bump only for package @react-native-firebase/storage
13
+
14
+ # [15.0.0](https://github.com/invertase/react-native-firebase/compare/v14.11.1...v15.0.0) (2022-06-20)
15
+
16
+ ### Bug Fixes
17
+
18
+ - **storage, ios:** correct storage metadata update / delete ([2dcb079](https://github.com/invertase/react-native-firebase/commit/2dcb0790c1812a33100cceea9dcb407d6a64cb87))
19
+ - **storage, ios:** surface underlying reason for unknown errors if possible ([6cd53ea](https://github.com/invertase/react-native-firebase/commit/6cd53eaca16ef52c52a28a7b209a7c8313fef08b))
20
+
21
+ - fix(storage, android)!: android now updates customMetadata as a group ([d602436](https://github.com/invertase/react-native-firebase/commit/d602436795bfb78f24bc69c42880133505738c00))
22
+
23
+ ### BREAKING CHANGES
24
+
25
+ - android works like web+iOS now: customMetadata if passed in will be
26
+ updated as a single atomic unit, all keys at once. Any key you want to keep in customMetadata
27
+ must be passed in during update; any missing keys will be removed. Set customMetadata to null
28
+ in order to remove customMetadata entirely, omit it during update to leave it unchanged.
29
+
6
30
  ## [14.11.1](https://github.com/invertase/react-native-firebase/compare/v14.11.0...v14.11.1) (2022-06-17)
7
31
 
8
32
  **Note:** Version bump only for package @react-native-firebase/storage
@@ -36,9 +36,9 @@ import com.google.firebase.storage.StorageReference;
36
36
  import com.google.firebase.storage.StorageTask;
37
37
  import io.invertase.firebase.app.ReactNativeFirebaseApp;
38
38
  import io.invertase.firebase.common.SharedUtils;
39
+ import java.util.HashMap;
39
40
  import java.util.Locale;
40
41
  import java.util.Map;
41
- import java.util.Objects;
42
42
  import javax.annotation.Nullable;
43
43
 
44
44
  class ReactNativeFirebaseStorageCommon {
@@ -93,15 +93,40 @@ class ReactNativeFirebaseStorageCommon {
93
93
  }
94
94
 
95
95
  /** Converts a RN ReadableMap into a StorageMetadata instance */
96
- static StorageMetadata buildMetadataFromMap(ReadableMap metadataMap, @Nullable Uri file) {
96
+ static StorageMetadata buildMetadataFromMap(
97
+ ReadableMap metadataMap, @Nullable Uri file, @Nullable StorageMetadata existingMetadata) {
97
98
  StorageMetadata.Builder metadataBuilder = new StorageMetadata.Builder();
98
99
 
99
100
  if (metadataMap != null) {
100
- if (metadataMap.hasKey(KEY_CUSTOM_META)) {
101
- ReadableMap customerMetaMap = Objects.requireNonNull(metadataMap.getMap(KEY_CUSTOM_META));
102
- Map<String, Object> customMeta = customerMetaMap.toHashMap();
103
- for (Map.Entry<String, Object> entry : customMeta.entrySet()) {
101
+ if (metadataMap.hasKey(KEY_CUSTOM_META) || (existingMetadata != null)) {
102
+
103
+ Map<String, Object> customMetadata = new HashMap<>();
104
+ ReadableMap freshCustomMetadata = metadataMap.getMap(KEY_CUSTOM_META);
105
+ Map<String, Object> existingCustomMetadata = new HashMap<>();
106
+
107
+ // Our baseline will be any existing custom metadata if it exists
108
+ if (existingMetadata != null) {
109
+ for (String existingKey : existingMetadata.getCustomMetadataKeys()) {
110
+ customMetadata.put(existingKey, existingMetadata.getCustomMetadata(existingKey));
111
+ existingCustomMetadata.put(
112
+ existingKey, existingMetadata.getCustomMetadata(existingKey));
113
+ }
114
+ }
115
+
116
+ // Clobber with any fresh custom metadata if it exists
117
+ if (freshCustomMetadata != null) {
118
+ customMetadata.putAll(freshCustomMetadata.toHashMap());
119
+ }
120
+
121
+ for (Map.Entry<String, Object> entry : customMetadata.entrySet()) {
104
122
  metadataBuilder.setCustomMetadata(entry.getKey(), (String) entry.getValue());
123
+
124
+ // API contract updates custom metadata as a group but android SDK has key granularity
125
+ // So if freshCustomMetadata exists, for any key that in our merged map but not in
126
+ // freshCustomMetadata, set to null to clear
127
+ if (freshCustomMetadata == null || !freshCustomMetadata.hasKey(entry.getKey())) {
128
+ metadataBuilder.setCustomMetadata(entry.getKey(), null);
129
+ }
105
130
  }
106
131
  }
107
132
 
@@ -167,20 +192,24 @@ class ReactNativeFirebaseStorageCommon {
167
192
  if (storageMetadata.getCacheControl() != null
168
193
  && storageMetadata.getCacheControl().length() > 0) {
169
194
  metadata.putString(KEY_CACHE_CONTROL, storageMetadata.getCacheControl());
170
- } else {
171
- metadata.putNull(KEY_CACHE_CONTROL);
172
195
  }
173
196
 
174
197
  if (storageMetadata.getContentLanguage() != null
175
198
  && storageMetadata.getContentLanguage().length() > 0) {
176
199
  metadata.putString(KEY_CONTENT_LANG, storageMetadata.getContentLanguage());
177
- } else {
178
- metadata.putNull(KEY_CONTENT_LANG);
179
200
  }
180
201
 
181
- metadata.putString(KEY_CONTENT_DISPOSITION, storageMetadata.getContentDisposition());
182
- metadata.putString(KEY_CONTENT_ENCODING, storageMetadata.getContentEncoding());
183
- metadata.putString(KEY_CONTENT_TYPE, storageMetadata.getContentType());
202
+ if (storageMetadata.getContentDisposition() != null
203
+ && storageMetadata.getContentDisposition().length() > 0) {
204
+ metadata.putString(KEY_CONTENT_DISPOSITION, storageMetadata.getContentDisposition());
205
+ }
206
+ if (storageMetadata.getContentEncoding() != null
207
+ && storageMetadata.getContentEncoding().length() > 0) {
208
+ metadata.putString(KEY_CONTENT_ENCODING, storageMetadata.getContentEncoding());
209
+ }
210
+ if (storageMetadata.getContentType() != null && storageMetadata.getContentType().length() > 0) {
211
+ metadata.putString(KEY_CONTENT_TYPE, storageMetadata.getContentType());
212
+ }
184
213
 
185
214
  if (storageMetadata.getCustomMetadataKeys().size() > 0) {
186
215
  WritableMap customMetadata = Arguments.createMap();
@@ -188,8 +217,6 @@ class ReactNativeFirebaseStorageCommon {
188
217
  customMetadata.putString(key, storageMetadata.getCustomMetadata(key));
189
218
  }
190
219
  metadata.putMap(KEY_CUSTOM_META, customMetadata);
191
- } else {
192
- metadata.putNull(KEY_CUSTOM_META);
193
220
  }
194
221
 
195
222
  return metadata;
@@ -169,6 +169,24 @@ public class ReactNativeFirebaseStorageModule extends ReactNativeFirebaseModule
169
169
  }
170
170
  }
171
171
 
172
+ // Useful for development / debugging
173
+ private void dumpMetadata(StorageMetadata metadata) {
174
+ System.err.println("STORAGE dumping metadata contents");
175
+ System.err.println("STORAGE - cacheControl " + metadata.getCacheControl());
176
+ System.err.println("STORAGE - contentDisposition " + metadata.getContentDisposition());
177
+ System.err.println("STORAGE - contentEncoding " + metadata.getContentEncoding());
178
+ System.err.println("STORAGE - contentLanguage " + metadata.getContentLanguage());
179
+ System.err.println("STORAGE - contentType " + metadata.getContentType());
180
+ for (String customKey : metadata.getCustomMetadataKeys()) {
181
+ System.err.println(
182
+ "STORAGE - customMetadata: '"
183
+ + customKey
184
+ + "' / '"
185
+ + metadata.getCustomMetadata(customKey)
186
+ + "'");
187
+ }
188
+ }
189
+
172
190
  /**
173
191
  * @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#updateMetadata
174
192
  */
@@ -177,17 +195,35 @@ public class ReactNativeFirebaseStorageModule extends ReactNativeFirebaseModule
177
195
  String appName, String url, ReadableMap metadataMap, final Promise promise) {
178
196
  try {
179
197
  StorageReference reference = getReferenceFromUrl(url, appName);
180
- StorageMetadata metadata = buildMetadataFromMap(metadataMap, null);
181
198
 
182
199
  reference
183
- .updateMetadata(metadata)
200
+ .getMetadata()
184
201
  .addOnCompleteListener(
185
202
  getExecutor(),
186
- task -> {
187
- if (task.isSuccessful()) {
188
- promise.resolve(getMetadataAsMap(task.getResult()));
203
+ getTask -> {
204
+ if (getTask.isSuccessful()) {
205
+
206
+ // dumpMetadata(getTask.getResult());
207
+ StorageMetadata metadata =
208
+ buildMetadataFromMap(metadataMap, null, getTask.getResult());
209
+ // dumpMetadata(metadata);
210
+
211
+ reference
212
+ .updateMetadata(metadata)
213
+ .addOnCompleteListener(
214
+ getExecutor(),
215
+ updateTask -> {
216
+ if (updateTask.isSuccessful()) {
217
+ // dumpMetadata(updateTask.getResult());
218
+ promise.resolve(getMetadataAsMap(updateTask.getResult()));
219
+ } else {
220
+ updateTask.getException().printStackTrace();
221
+ promiseRejectStorageException(promise, updateTask.getException());
222
+ }
223
+ });
224
+
189
225
  } else {
190
- promiseRejectStorageException(promise, task.getException());
226
+ promiseRejectStorageException(promise, getTask.getException());
191
227
  }
192
228
  });
193
229
  } catch (Exception e) {
@@ -186,7 +186,7 @@ class ReactNativeFirebaseStorageUploadTask extends ReactNativeFirebaseStorageTas
186
186
 
187
187
  /** Put String or Data from JavaScript */
188
188
  void begin(ExecutorService executor, String string, String format, ReadableMap metadataMap) {
189
- StorageMetadata metadata = buildMetadataFromMap(metadataMap, null);
189
+ StorageMetadata metadata = buildMetadataFromMap(metadataMap, null, null);
190
190
  uploadTask = storageReference.putBytes(uploadStringToByteArray(string, format), metadata);
191
191
  setStorageTask(uploadTask);
192
192
  addEventListeners(executor);
@@ -195,7 +195,7 @@ class ReactNativeFirebaseStorageUploadTask extends ReactNativeFirebaseStorageTas
195
195
  /** Put File from JavaScript */
196
196
  void begin(ExecutorService executor, String localFilePath, ReadableMap metadataMap) {
197
197
  Uri fileUri = SharedUtils.getUri(localFilePath);
198
- StorageMetadata metadata = buildMetadataFromMap(metadataMap, fileUri);
198
+ StorageMetadata metadata = buildMetadataFromMap(metadataMap, fileUri, null);
199
199
  uploadTask = storageReference.putFile(fileUri, metadata);
200
200
  setStorageTask(uploadTask);
201
201
  addEventListeners(executor);
@@ -56,7 +56,8 @@
56
56
 
57
57
  + (NSString *)getTaskStatus:(FIRStorageTaskStatus)status;
58
58
 
59
- + (FIRStorageMetadata *)buildMetadataFromMap:(NSDictionary *)metadata;
59
+ + (FIRStorageMetadata *)buildMetadataFromMap:(NSDictionary *)metadata
60
+ existingMetadata:(FIRStorageMetadata *)existingMetadata;
60
61
 
61
62
  + (NSArray *)getErrorCodeMessage:(NSError *)error;
62
63
 
@@ -392,9 +392,64 @@
392
392
  }
393
393
  }
394
394
 
395
- + (FIRStorageMetadata *)buildMetadataFromMap:(NSDictionary *)metadata {
396
- FIRStorageMetadata *storageMetadata = [[FIRStorageMetadata alloc] initWithDictionary:metadata];
397
- storageMetadata.customMetadata = [metadata[@"customMetadata"] mutableCopy];
395
+ + (FIRStorageMetadata *)buildMetadataFromMap:(NSDictionary *)metadata
396
+ existingMetadata:(nullable FIRStorageMetadata *)existingMetadata {
397
+ // If an existing metadata was passed in, modify it with our map, otherwise init a fresh copy
398
+ FIRStorageMetadata *storageMetadata = existingMetadata;
399
+ if (storageMetadata == nil) {
400
+ storageMetadata = [[FIRStorageMetadata alloc] init];
401
+ }
402
+
403
+ if (metadata[@"cacheControl"] == [NSNull null]) {
404
+ storageMetadata.cacheControl = nil;
405
+ } else {
406
+ storageMetadata.cacheControl = metadata[@"cacheControl"];
407
+ }
408
+
409
+ if (metadata[@"contentLanguage"] == [NSNull null]) {
410
+ storageMetadata.contentLanguage = nil;
411
+ } else {
412
+ storageMetadata.contentLanguage = metadata[@"contentLanguage"];
413
+ }
414
+
415
+ if (metadata[@"contentEncoding"] == [NSNull null]) {
416
+ storageMetadata.contentEncoding = nil;
417
+ } else {
418
+ storageMetadata.contentEncoding = metadata[@"contentEncoding"];
419
+ }
420
+
421
+ if (metadata[@"contentDisposition"] == [NSNull null]) {
422
+ storageMetadata.contentDisposition = nil;
423
+ } else {
424
+ storageMetadata.contentDisposition = metadata[@"contentDisposition"];
425
+ }
426
+
427
+ if (metadata[@"contentType"] == [NSNull null]) {
428
+ storageMetadata.contentType = nil;
429
+ } else {
430
+ storageMetadata.contentType = metadata[@"contentType"];
431
+ }
432
+
433
+ if (metadata[@"customMetadata"] == [NSNull null]) {
434
+ storageMetadata.customMetadata = @{};
435
+ } else {
436
+ NSMutableDictionary *customMetadata = [metadata[@"customMetadata"] mutableCopy];
437
+ for (NSString *key in customMetadata.allKeys) {
438
+ if (customMetadata[key] == [NSNull null] || customMetadata[key] == nil) {
439
+ [customMetadata removeObjectForKey:key];
440
+ }
441
+ }
442
+ storageMetadata.customMetadata = customMetadata;
443
+ }
444
+
445
+ // md5hash may be settable but is not update-able, so just test for existence and carry it in
446
+ // FIXME this will need a fix related to
447
+ // https://github.com/firebase/firebase-ios-sdk/issues/9849#issuecomment-1159292592 if
448
+ // (metadata[@"md5hash"]) {
449
+ // NSLog(@"STORAGE md5hash was set");
450
+ // storageMetadata.md5Hash = metadata[@"md5hash"];
451
+ // }
452
+
398
453
  return storageMetadata;
399
454
  }
400
455
 
@@ -417,7 +472,13 @@
417
472
  code = @"invalid-device-file-path";
418
473
  message = @"The specified device file path is invalid or is restricted.";
419
474
  } else {
420
- message = @"An unknown error has occurred.";
475
+ if (userInfo[@"ResponseBody"]) {
476
+ message =
477
+ [NSString stringWithFormat:@"An unknown error has occurred. (underlying reason '%@')",
478
+ userInfo[@"ResponseBody"]];
479
+ } else {
480
+ message = @"An unknown error has occurred.";
481
+ }
421
482
  }
422
483
  break;
423
484
  case FIRStorageErrorCodeObjectNotFound:
@@ -143,17 +143,26 @@ RCT_EXPORT_METHOD(updateMetadata
143
143
  : (RCTPromiseResolveBlock)resolve
144
144
  : (RCTPromiseRejectBlock)reject) {
145
145
  FIRStorageReference *storageReference = [self getReferenceFromUrl:url app:firebaseApp];
146
- FIRStorageMetadata *storageMetadata = [RNFBStorageCommon buildMetadataFromMap:metadata];
147
146
 
148
- [storageReference
149
- updateMetadata:storageMetadata
150
- completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
151
- if (error != nil) {
152
- [self promiseRejectStorageException:reject error:error];
153
- } else {
154
- resolve([RNFBStorageCommon metadataToDict:metadata]);
155
- }
156
- }];
147
+ [storageReference metadataWithCompletion:^(FIRStorageMetadata *_Nullable fetchedMetadata,
148
+ NSError *_Nullable error) {
149
+ if (error != nil) {
150
+ [self promiseRejectStorageException:reject error:error];
151
+ } else {
152
+ FIRStorageMetadata *storageMetadata =
153
+ [RNFBStorageCommon buildMetadataFromMap:metadata existingMetadata:fetchedMetadata];
154
+
155
+ [storageReference updateMetadata:storageMetadata
156
+ completion:^(FIRStorageMetadata *_Nullable updatedMetadata,
157
+ NSError *_Nullable error) {
158
+ if (error != nil) {
159
+ [self promiseRejectStorageException:reject error:error];
160
+ } else {
161
+ resolve([RNFBStorageCommon metadataToDict:updatedMetadata]);
162
+ }
163
+ }];
164
+ }
165
+ }];
157
166
  }
158
167
 
159
168
  /**
@@ -391,7 +400,8 @@ RCT_EXPORT_METHOD(putFile
391
400
  : (nonnull NSNumber *)taskId
392
401
  : (RCTPromiseResolveBlock)resolve
393
402
  : (RCTPromiseRejectBlock)reject) {
394
- FIRStorageMetadata *storageMetadata = [RNFBStorageCommon buildMetadataFromMap:metadata];
403
+ FIRStorageMetadata *storageMetadata = [RNFBStorageCommon buildMetadataFromMap:metadata
404
+ existingMetadata:nil];
395
405
  FIRStorageReference *storageReference = [self getReferenceFromUrl:url app:firebaseApp];
396
406
 
397
407
  [RNFBStorageCommon
@@ -462,7 +472,8 @@ RCT_EXPORT_METHOD(putString
462
472
  : (nonnull NSNumber *)taskId
463
473
  : (RCTPromiseResolveBlock)resolve
464
474
  : (RCTPromiseRejectBlock)reject) {
465
- FIRStorageMetadata *storageMetadata = [RNFBStorageCommon buildMetadataFromMap:metadata];
475
+ FIRStorageMetadata *storageMetadata = [RNFBStorageCommon buildMetadataFromMap:metadata
476
+ existingMetadata:nil];
466
477
  FIRStorageReference *storageReference = [self getReferenceFromUrl:url app:firebaseApp];
467
478
 
468
479
  __block FIRStorageUploadTask *uploadTask;
package/lib/index.d.ts CHANGED
@@ -323,7 +323,7 @@ export namespace FirebaseStorageTypes {
323
323
  /**
324
324
  * Additional user-defined custom metadata for this storage object.
325
325
  *
326
- * String values only are supported for custom metadata property values.
326
+ * All values must be strings. Set to null to delete all. Any keys ommitted during update will be removed.
327
327
  *
328
328
  * #### Example
329
329
  *
package/lib/utils.js CHANGED
@@ -88,9 +88,9 @@ export function validateMetadata(metadata, update = true) {
88
88
  `firebase.storage.SettableMetadata invalid property '${key}' should be a string or null value.`,
89
89
  );
90
90
  }
91
- } else if (!isObject(value)) {
91
+ } else if (!isObject(value) && !isNull(value)) {
92
92
  throw new Error(
93
- 'firebase.storage.SettableMetadata.customMetadata must be an object of keys and string values.',
93
+ 'firebase.storage.SettableMetadata.customMetadata must be an object of keys and string values or null value.',
94
94
  );
95
95
  }
96
96
  }
package/lib/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- module.exports = '14.11.1';
2
+ module.exports = '15.1.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-firebase/storage",
3
- "version": "14.11.1",
3
+ "version": "15.1.1",
4
4
  "author": "Invertase <oss@invertase.io> (http://invertase.io)",
5
5
  "description": "React Native Firebase - React Native Firebase provides native integration with Cloud Storage, providing support to upload and download files directly from your device and from your Firebase Cloud Storage bucket.",
6
6
  "main": "lib/index.js",
@@ -29,10 +29,10 @@
29
29
  "download"
30
30
  ],
31
31
  "peerDependencies": {
32
- "@react-native-firebase/app": "14.11.1"
32
+ "@react-native-firebase/app": "15.1.1"
33
33
  },
34
34
  "publishConfig": {
35
35
  "access": "public"
36
36
  },
37
- "gitHead": "b1a430bbf36a41076b33550fec113fa0e44583b4"
37
+ "gitHead": "8d469f19b9faec54c492f83c0a2dba6a52815da3"
38
38
  }