Skip to content

Commit 1097939

Browse files
Feat: add UseEmulator to storage api (#1795)
* Feat: add UseEmulator to storage api * Fix formating * Update storage/src/desktop/storage_desktop.h Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update release_build_files/readme.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Fix the storage util as storagePath now needs storageinternal ref To get the url to show up correctly the storage path now depends upon the storage internal to query for the host and port * Change the Release Notes and change the UseEmulator to give errors --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 72f627a commit 1097939

File tree

14 files changed

+172
-47
lines changed

14 files changed

+172
-47
lines changed

release_build_files/readme.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,12 @@ workflow use only during the development of your app, not for publicly shipping
613613
code.
614614

615615
## Release Notes
616+
### Upcoming
617+
- Changes
618+
- Storage: Add support for Firebase Storage emulator via `UseEmulator`.
619+
The `UseEmulator` method should be called before invoking any other
620+
methods on a new instance of Storage. Default port is 9199.
621+
616622
### 13.2.0
617623
- Changes
618624
- General (Android): Update to Firebase Android BoM version 34.4.0.

storage/src/android/storage_android.cc

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ namespace internal {
6262
"(Ljava/lang/String;)" \
6363
"Lcom/google/firebase/storage/StorageReference;"), \
6464
X(GetApp, "getApp", \
65-
"()Lcom/google/firebase/FirebaseApp;")
65+
"()Lcom/google/firebase/FirebaseApp;"), \
66+
X(UseEmulator, "useEmulator", \
67+
"(Ljava/lang/String;I)V")
6668
// clang-format on
6769

6870
METHOD_LOOKUP_DECLARATION(firebase_storage, FIREBASE_STORAGE_METHODS)
@@ -471,6 +473,23 @@ void StorageInternal::set_max_operation_retry_time(
471473
millis);
472474
}
473475

476+
void StorageInternal::UseEmulator(const char* host, int port) {
477+
JNIEnv* env = app_->GetJNIEnv();
478+
FIREBASE_ASSERT_MESSAGE_RETURN_VOID((host != nullptr && host[0] != '\0'),
479+
"Emulator host cannot be null or empty.")
480+
FIREBASE_ASSERT_MESSAGE_RETURN_VOID(
481+
(port > 0), "Emulator port must be a positive number.")
482+
483+
jobject host_string = env->NewStringUTF(host);
484+
jint port_num = static_cast<jint>(port);
485+
486+
env->CallVoidMethod(
487+
obj_, firebase_storage::GetMethodId(firebase_storage::kUseEmulator),
488+
host_string, port_num);
489+
env->DeleteLocalRef(host_string);
490+
util::CheckAndClearJniExceptions(env);
491+
}
492+
474493
} // namespace internal
475494
} // namespace storage
476495
} // namespace firebase

storage/src/android/storage_android.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ class StorageInternal {
9292
// if a failure occurs.
9393
void set_max_operation_retry_time(double max_transfer_retry_seconds);
9494

95+
// Configures the Storage SDK to use an emulated backend instead of call the
96+
// default remote backend
97+
void UseEmulator(const char* host, int port);
98+
9599
// Convert an error code obtained from a Java StorageException into a C++
96100
// Error enum.
97101
Error ErrorFromJavaErrorCode(jint java_error_code) const;

storage/src/common/storage.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,5 +226,9 @@ void Storage::set_max_operation_retry_time(double max_transfer_retry_seconds) {
226226
return internal_->set_max_operation_retry_time(max_transfer_retry_seconds);
227227
}
228228

229+
void Storage::UseEmulator(const char* host, int port) {
230+
if (internal_) internal_->UseEmulator(host, port);
231+
}
232+
229233
} // namespace storage
230234
} // namespace firebase

storage/src/desktop/metadata_desktop.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ const char* MetadataInternal::download_url() const {
133133

134134
std::string MetadataInternal::GetPathFromToken(const std::string& token) const {
135135
std::string http_url =
136-
StoragePath("gs://" + bucket_ + "/" + path_).AsHttpUrl();
136+
StoragePath(storage_internal_, "gs://" + bucket_ + "/" + path_)
137+
.AsHttpUrl();
137138
if (!token.empty()) http_url += "&token=" + token;
138139
return http_url;
139140
}

storage/src/desktop/storage_desktop.cc

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "app/src/app_common.h"
2424
#include "app/src/function_registry.h"
2525
#include "app/src/include/firebase/app.h"
26+
#include "app/src/log.h"
2627
#include "storage/src/desktop/rest_operation.h"
2728
#include "storage/src/desktop/storage_reference_desktop.h"
2829

@@ -35,10 +36,10 @@ StorageInternal::StorageInternal(App* app, const char* url) {
3536

3637
if (url) {
3738
url_ = url;
38-
root_ = StoragePath(url_);
39+
root_ = StoragePath(this, url_);
3940
} else {
4041
const char* bucket = app->options().storage_bucket();
41-
root_ = StoragePath(bucket ? std::string(kGsScheme) + bucket : "");
42+
root_ = StoragePath(this, bucket ? std::string(kGsScheme) + bucket : "");
4243
}
4344

4445
// LINT.IfChange
@@ -76,20 +77,22 @@ StorageInternal::~StorageInternal() {
7677
}
7778

7879
// Get a StorageReference to the root of the database.
79-
StorageReferenceInternal* StorageInternal::GetReference() const {
80+
StorageReferenceInternal* StorageInternal::GetReference() {
81+
configured_ = true;
8082
return new StorageReferenceInternal(url_, const_cast<StorageInternal*>(this));
8183
}
8284

8385
// Get a StorageReference for the specified path.
84-
StorageReferenceInternal* StorageInternal::GetReference(
85-
const char* path) const {
86+
StorageReferenceInternal* StorageInternal::GetReference(const char* path) {
87+
configured_ = true;
8688
return new StorageReferenceInternal(root_.GetChild(path),
8789
const_cast<StorageInternal*>(this));
8890
}
8991

9092
// Get a StorageReference for the provided URL.
9193
StorageReferenceInternal* StorageInternal::GetReferenceFromUrl(
92-
const char* url) const {
94+
const char* url) {
95+
configured_ = true;
9396
return new StorageReferenceInternal(url, const_cast<StorageInternal*>(this));
9497
}
9598

@@ -134,6 +137,30 @@ void StorageInternal::CleanupCompletedOperations() {
134137
}
135138
}
136139

140+
void StorageInternal::UseEmulator(const char* host, int port) {
141+
if (host == nullptr || host[0] == '\0') {
142+
LogError("Emulator host cannot be null or empty.");
143+
return;
144+
}
145+
146+
if (port <= 0) {
147+
LogError("Emulator port must be a positive number.");
148+
return;
149+
}
150+
151+
if (configured_) {
152+
LogError(
153+
"Cannot connect to emulator after Storage SDK initialization. "
154+
"Call use_emulator(host, port) before creating a Storage "
155+
"reference or trying to load data.");
156+
return;
157+
}
158+
159+
scheme_ = "http";
160+
port_ = port;
161+
host_ = host;
162+
}
163+
137164
} // namespace internal
138165
} // namespace storage
139166
} // namespace firebase

storage/src/desktop/storage_desktop.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ class StorageInternal {
4343
std::string url() { return url_; }
4444

4545
// Get a StorageReference to the root of the database.
46-
StorageReferenceInternal* GetReference() const;
46+
StorageReferenceInternal* GetReference();
4747

4848
// Get a StorageReference for the specified path.
49-
StorageReferenceInternal* GetReference(const char* path) const;
49+
StorageReferenceInternal* GetReference(const char* path);
5050

5151
// Get a StorageReference for the provided URL.
52-
StorageReferenceInternal* GetReferenceFromUrl(const char* url) const;
52+
StorageReferenceInternal* GetReferenceFromUrl(const char* url);
5353

5454
// Returns the maximum time (in seconds) to retry a download if a failure
5555
// occurs.
@@ -99,6 +99,19 @@ class StorageInternal {
9999
// Remove an operation from the list of outstanding operations.
100100
void RemoveOperation(RestOperation* operation);
101101

102+
// Configures the Storage SDK to use an emulated backend instead of call the
103+
// default remote backend
104+
void UseEmulator(const char* host, int port);
105+
106+
// Returns the Host for the storage backend
107+
std::string get_host() { return host_; }
108+
109+
// Returns the Port for the storage backend
110+
int get_port() { return port_; }
111+
112+
// Returns the url scheme currenly in use for the storage backend
113+
std::string get_scheme() { return scheme_; }
114+
102115
private:
103116
// Clean up completed operations.
104117
void CleanupCompletedOperations();
@@ -119,6 +132,10 @@ class StorageInternal {
119132
std::string user_agent_;
120133
Mutex operations_mutex_;
121134
std::vector<RestOperation*> operations_;
135+
std::string host_ = "firebasestorage.googleapis.com";
136+
std::string scheme_ = "https";
137+
int port_ = 443;
138+
bool configured_ = false;
122139
};
123140

124141
} // namespace internal

storage/src/desktop/storage_path.cc

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
#include <string.h>
1818

19+
#include <iostream>
1920
#include <string>
2021

2122
#include "app/rest/util.h"
2223
#include "app/src/include/firebase/internal/common.h"
24+
#include "storage/src/desktop/storage_desktop.h"
2325

2426
namespace firebase {
2527
namespace storage {
@@ -38,8 +40,10 @@ const char kBucketStartString[] = "firebasestorage.googleapis.com/v0/b/";
3840
const size_t kBucketStartStringLength = FIREBASE_STRLEN(kBucketStartString);
3941
const char kBucketEndString[] = "/o/";
4042
const size_t kBucketEndStringLength = FIREBASE_STRLEN(kBucketEndString);
43+
const char kBucketIdentifierString[] = "/v0/b/";
4144

42-
StoragePath::StoragePath(const std::string& path) {
45+
StoragePath::StoragePath(StorageInternal* storage, const std::string& path) {
46+
storage_internal_ = storage;
4347
bucket_ = "";
4448
path_ = Path("");
4549
if (path.compare(0, kGsSchemeLength, kGsScheme) == 0) {
@@ -56,8 +60,9 @@ StoragePath::StoragePath(const std::string& path) {
5660

5761
// Constructs a storage path, based on raw strings for the bucket, path, and
5862
// object.
59-
StoragePath::StoragePath(const std::string& bucket, const std::string& path,
60-
const std::string& object) {
63+
StoragePath::StoragePath(StorageInternal* storage, const std::string& bucket,
64+
const std::string& path, const std::string& object) {
65+
storage_internal_ = storage;
6166
bucket_ = bucket;
6267
path_ = Path(path).GetChild(object);
6368
}
@@ -97,15 +102,20 @@ void StoragePath::ConstructFromHttpUrl(const std::string& url, int path_start) {
97102
std::string StoragePath::AsHttpUrl() const {
98103
static const char* kUrlEnd = "?alt=media";
99104
// Construct the URL. Final format is:
100-
// https://[projectname].googleapis.com/v0/b/[bucket]/o/[path and/or object]
105+
// http[s]://[host]:[port]/v0/b/[bucket]/o/[path and/or object]
101106
return AsHttpMetadataUrl() + kUrlEnd;
102107
}
103108

104109
std::string StoragePath::AsHttpMetadataUrl() const {
105110
// Construct the URL. Final format is:
106-
// https://[projectname].googleapis.com/v0/b/[bucket]/o/[path and/or object]
107-
std::string result = kHttpsScheme;
108-
result += kBucketStartString;
111+
// [scheme]://[host]:[port]/v0/b/[bucket]/o/[path and/or object]
112+
113+
std::string result = storage_internal_->get_scheme();
114+
result += "://";
115+
result += storage_internal_->get_host();
116+
result += ":";
117+
result += std::to_string(storage_internal_->get_port());
118+
result += kBucketIdentifierString;
109119
result += bucket_;
110120
result += kBucketEndString;
111121
result += rest::util::EncodeUrl(path_.str());

storage/src/desktop/storage_path.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ namespace internal {
2525

2626
extern const char kGsScheme[];
2727

28+
class StorageInternal;
29+
2830
// Class for managing paths for firebase storage.
2931
// Storage paths are made up of a bucket, a path,
3032
// and (optionally) an object, located at that path.
@@ -35,12 +37,12 @@ class StoragePath {
3537

3638
// Constructs a storage path, based on an input URL. The URL can either be
3739
// an HTTP[s] link, or a gs URI.
38-
explicit StoragePath(const std::string& path);
40+
explicit StoragePath(StorageInternal* storage, const std::string& path);
3941

4042
// Constructs a storage path, based on raw strings for the bucket, path, and
4143
// object.
42-
StoragePath(const std::string& bucket, const std::string& path,
43-
const std::string& object = "");
44+
StoragePath(StorageInternal* storage, const std::string& bucket,
45+
const std::string& path, const std::string& object = "");
4446

4547
// The bucket portion of this path.
4648
// In the path: MyBucket/folder/object, it would return "MyBucket".
@@ -60,14 +62,14 @@ class StoragePath {
6062
// in a path where bucket is "bucket", local_path is "path/otherchild/" and
6163
// object is an empty string.
6264
StoragePath GetChild(const std::string& path) const {
63-
return StoragePath(bucket_, path_.GetChild(path));
65+
return StoragePath(storage_internal_, bucket_, path_.GetChild(path));
6466
}
6567

6668
// Returns the location one folder up from the current location. If the
6769
// path is at already at the root level, this returns the path unchanged.
6870
// The Object in the result is always set to empty.
6971
StoragePath GetParent() const {
70-
return StoragePath(bucket_, path_.GetParent());
72+
return StoragePath(storage_internal_, bucket_, path_.GetParent());
7173
}
7274

7375
// Returns the path as a HTTP URL to the asset.
@@ -82,14 +84,16 @@ class StoragePath {
8284
private:
8385
static const char* const kSeparator;
8486

85-
StoragePath(const std::string& bucket, const Path& path)
86-
: bucket_(bucket), path_(path) {}
87+
StoragePath(StorageInternal* storage, const std::string& bucket,
88+
const Path& path)
89+
: storage_internal_(storage), bucket_(bucket), path_(path) {}
8790

8891
void ConstructFromGsUri(const std::string& uri, int path_start);
8992
void ConstructFromHttpUrl(const std::string& url, int path_start);
9093

9194
std::string bucket_;
9295
Path path_;
96+
StorageInternal* storage_internal_;
9397
};
9498

9599
} // namespace internal

storage/src/desktop/storage_reference_desktop.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ namespace internal {
4848

4949
StorageReferenceInternal::StorageReferenceInternal(
5050
const std::string& storageUri, StorageInternal* storage)
51-
: storage_(storage), storageUri_(storageUri) {
51+
: storage_(storage), storageUri_(storage, storageUri) {
5252
storage_->future_manager().AllocFutureApi(this, kStorageReferenceFnCount);
5353
}
5454

0 commit comments

Comments
 (0)