Skip to content

Commit 680fef2

Browse files
committed
Feat: add UseEmulator to storage api
1 parent f222d7a commit 680fef2

File tree

13 files changed

+141
-24
lines changed

13 files changed

+141
-24
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+
### 13.3.0
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((port > 0),
481+
"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: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,5 +226,10 @@ 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_)
231+
internal_->UseEmulator(host, port);
232+
}
233+
229234
} // namespace storage
230235
} // namespace firebase

storage/src/desktop/metadata_desktop.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ 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_).AsHttpUrl();
137137
if (!token.empty()) http_url += "&token=" + token;
138138
return http_url;
139139
}

storage/src/desktop/storage_desktop.cc

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ StorageInternal::StorageInternal(App* app, const char* url) {
3535

3636
if (url) {
3737
url_ = url;
38-
root_ = StoragePath(url_);
38+
root_ = StoragePath(this, url_);
3939
} else {
4040
const char* bucket = app->options().storage_bucket();
41-
root_ = StoragePath(bucket ? std::string(kGsScheme) + bucket : "");
41+
root_ = StoragePath(this, bucket ? std::string(kGsScheme) + bucket : "");
4242
}
4343

4444
// LINT.IfChange
@@ -76,20 +76,22 @@ StorageInternal::~StorageInternal() {
7676
}
7777

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

8384
// Get a StorageReference for the specified path.
84-
StorageReferenceInternal* StorageInternal::GetReference(
85-
const char* path) const {
85+
StorageReferenceInternal* StorageInternal::GetReference(const char* path) {
86+
configured_ = true;
8687
return new StorageReferenceInternal(root_.GetChild(path),
8788
const_cast<StorageInternal*>(this));
8889
}
8990

9091
// Get a StorageReference for the provided URL.
9192
StorageReferenceInternal* StorageInternal::GetReferenceFromUrl(
92-
const char* url) const {
93+
const char* url) {
94+
configured_ = true;
9395
return new StorageReferenceInternal(url, const_cast<StorageInternal*>(this));
9496
}
9597

@@ -134,6 +136,27 @@ void StorageInternal::CleanupCompletedOperations() {
134136
}
135137
}
136138

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

storage/src/desktop/storage_desktop.h

Lines changed: 21 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,11 @@ 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_;
139+
122140
};
123141

124142
} // namespace internal

storage/src/desktop/storage_path.cc

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
#include <string.h>
1818

1919
#include <string>
20+
#include <iostream>
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,10 @@ 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,
63+
StoragePath::StoragePath(StorageInternal *storage,
64+
const std::string& bucket, const std::string& path,
6065
const std::string& object) {
66+
storage_internal_ = storage;
6167
bucket_ = bucket;
6268
path_ = Path(path).GetChild(object);
6369
}
@@ -97,15 +103,20 @@ void StoragePath::ConstructFromHttpUrl(const std::string& url, int path_start) {
97103
std::string StoragePath::AsHttpUrl() const {
98104
static const char* kUrlEnd = "?alt=media";
99105
// Construct the URL. Final format is:
100-
// https://[projectname].googleapis.com/v0/b/[bucket]/o/[path and/or object]
106+
// http[s]://[host]:[port]/v0/b/[bucket]/o/[path and/or object]
101107
return AsHttpMetadataUrl() + kUrlEnd;
102108
}
103109

104110
std::string StoragePath::AsHttpMetadataUrl() const {
105111
// 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;
112+
// [scheme]://[host]:[port]/v0/b/[bucket]/o/[path and/or object]
113+
114+
std::string result = storage_internal_->get_scheme();
115+
result += "://";
116+
result += storage_internal_->get_host();
117+
result += ":";
118+
result += std::to_string(storage_internal_->get_port());
119+
result += kBucketIdentifierString;
109120
result += bucket_;
110121
result += kBucketEndString;
111122
result += rest::util::EncodeUrl(path_.str());

storage/src/desktop/storage_path.h

Lines changed: 10 additions & 6 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,11 +37,11 @@ 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,
44+
StoragePath(StorageInternal *storage, const std::string& bucket, const std::string& path,
4345
const std::string& object = "");
4446

4547
// The bucket portion of this path.
@@ -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, const Path& path)
88+
: storage_internal_(storage), bucket_(bucket), path_(path) {}
8789

8890
void ConstructFromGsUri(const std::string& uri, int path_start);
8991
void ConstructFromHttpUrl(const std::string& url, int path_start);
9092

9193
std::string bucket_;
9294
Path path_;
95+
StorageInternal* storage_internal_;
96+
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)