Skip to content

Add GetHttpsCallableFromURL to Functions #964

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 26 additions & 13 deletions app/src/util_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ METHOD_LOOKUP_DECLARATION(uribuilder, URI_BUILDER_METHODS)
METHOD_LOOKUP_DEFINITION(uribuilder, "android/net/Uri$Builder",
URI_BUILDER_METHODS)

// Methods of the java.net.URL class.
// clang-format off
#define URL_METHODS(X) \
X(Constructor, "<init>", "(Ljava/lang/String;)V"), \
X(ConstructorWithURL, "<init>", "(Ljava/net/URL;Ljava/lang/String;)V")
// clang-format on
METHOD_LOOKUP_DECLARATION(url, URL_METHODS)
METHOD_LOOKUP_DEFINITION(url, "java/net/URL", URL_METHODS)

// clang-format off
#define FILE_OUTPUT_STREAM_METHODS(X) \
X(ConstructorFile, "<init>", "(Ljava/io/File;)V"), \
Expand Down Expand Up @@ -151,13 +160,6 @@ METHOD_LOOKUP_DECLARATION(url_class_loader, URL_CLASS_LOADER_METHODS)
METHOD_LOOKUP_DEFINITION(url_class_loader, "java/net/URLClassLoader",
URL_CLASS_LOADER_METHODS)

// clang-format off
#define URL_METHODS(X) \
X(Constructor, "<init>", "(Ljava/net/URL;Ljava/lang/String;)V")
// clang-format on
METHOD_LOOKUP_DECLARATION(url, URL_METHODS)
METHOD_LOOKUP_DEFINITION(url, "java/net/URL", URL_METHODS)

// clang-format off
#define JAVA_URI_METHODS(X) X(ToUrl, "toURL", "()Ljava/net/URL;")
// clang-format on
Expand Down Expand Up @@ -395,14 +397,14 @@ static void ReleaseClasses(JNIEnv* env) {
uri::ReleaseClass(env);
object::ReleaseClass(env);
uribuilder::ReleaseClass(env);
url::ReleaseClass(env);
if (g_jniresultcallback_loaded) {
jniresultcallback::ReleaseClass(env);
g_jniresultcallback_loaded = false;
}
JavaThreadContext::Terminate(env);
#if defined(FIREBASE_ANDROID_FOR_DESKTOP)
java_uri::ReleaseClass(env);
url::ReleaseClass(env);
url_class_loader::ReleaseClass(env);
#endif // defined(FIREBASE_ANDROID_FOR_DESKTOP)
}
Expand Down Expand Up @@ -493,7 +495,8 @@ bool Initialize(JNIEnv* env, jobject activity_object) {
throwable::CacheMethodIds(env, activity_object) &&
uri::CacheMethodIds(env, activity_object) &&
object::CacheMethodIds(env, activity_object) &&
uribuilder::CacheMethodIds(env, activity_object))) {
uribuilder::CacheMethodIds(env, activity_object) &&
url::CacheMethodIds(env, activity_object))) {
ReleaseClasses(env);
TerminateActivityClasses(env);
return false;
Expand All @@ -508,7 +511,6 @@ bool Initialize(JNIEnv* env, jobject activity_object) {
#if defined(FIREBASE_ANDROID_FOR_DESKTOP)
// Cache JVM class-loader for desktop.
if (!(java_uri::CacheMethodIds(env, activity_object) &&
url::CacheMethodIds(env, activity_object) &&
url_class_loader::CacheMethodIds(env, activity_object))) {
return false;
}
Expand Down Expand Up @@ -1182,6 +1184,17 @@ jobject ParseUriString(JNIEnv* env, const char* uri_string) {
return uri;
}

// Convert a char array into a jobject of type java.net.URL.
// The caller must call env->DeleteLocalRef() on the returned jobject.
jobject CharsToURL(JNIEnv* env, const char* url_string) {
jobject url_jstring = env->NewStringUTF(url_string);
jobject url = env->NewObject(
url::GetClass(), url::GetMethodId(url::kConstructor), url_jstring);
CheckAndClearJniExceptions(env);
env->DeleteLocalRef(url_jstring);
return url;
}

// Convert a jbyteArray to a vector, releasing the reference to the
// jbyteArray.
std::vector<unsigned char> JniByteArrayToVector(JNIEnv* env, jobject array) {
Expand Down Expand Up @@ -1623,9 +1636,9 @@ jclass FindClassInFiles(
env->NewObjectArray(embedded_files.size(), url::GetClass(), nullptr);
for (int i = 0; i < embedded_files.size(); ++i) {
jstring embedded_file_string = env->NewStringUTF(embedded_files[i].name);
jobject jar_url =
env->NewObject(url::GetClass(), url::GetMethodId(url::kConstructor),
cache_url, embedded_file_string);
jobject jar_url = env->NewObject(url::GetClass(),
url::GetMethodId(url::kConstructorWithURL),
cache_url, embedded_file_string);
env->SetObjectArrayElement(url_path_array, i, jar_url);
env->DeleteLocalRef(jar_url);
env->DeleteLocalRef(embedded_file_string);
Expand Down
4 changes: 4 additions & 0 deletions app/src/util_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,10 @@ jobject CharsToJniUri(JNIEnv* env, const char* uri);
// The caller must call env->DeleteLocalRef() on the returned jobject.
jobject ParseUriString(JNIEnv* env, const char* uri_string);

// Convert a char array into a jobject of type java.net.URL.
// The caller must call env->DeleteLocalRef() on the returned jobject.
jobject CharsToURL(JNIEnv* env, const char* url_string);

// Convert a jbyteArray to a vector, releasing the reference to the jbyteArray.
std::vector<unsigned char> JniByteArrayToVector(JNIEnv* env, jobject array);

Expand Down
71 changes: 64 additions & 7 deletions functions/integration_test/src/integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,27 @@ class FirebaseFunctionsTest : public FirebaseTest {
// Sign in an anonymous user.
void SignIn();

firebase::Future<firebase::functions::HttpsCallableResult> TestFunctionHelper(
const char* function_name,
firebase::functions::HttpsCallableReference& ref,
const firebase::Variant* const function_data,
const firebase::Variant& expected_result,
firebase::functions::Error expected_error =
firebase::functions::kErrorNone);

firebase::Future<firebase::functions::HttpsCallableResult> TestFunction(
const char* function_name, const firebase::Variant* const function_data,
const firebase::Variant& expected_result,
firebase::functions::Error expected_error =
firebase::functions::kErrorNone);

firebase::Future<firebase::functions::HttpsCallableResult>
TestFunctionFromURL(const char* function_url,
const firebase::Variant* const function_data,
const firebase::Variant& expected_result,
firebase::functions::Error expected_error =
firebase::functions::kErrorNone);

bool initialized_;
firebase::auth::Auth* auth_;
firebase::functions::Functions* functions_;
Expand Down Expand Up @@ -181,15 +196,11 @@ void FirebaseFunctionsTest::SignIn() {

// A helper function for calling a Firebase Function and waiting on the result.
firebase::Future<firebase::functions::HttpsCallableResult>
FirebaseFunctionsTest::TestFunction(
const char* function_name, const firebase::Variant* const function_data,
FirebaseFunctionsTest::TestFunctionHelper(
const char* function_name, firebase::functions::HttpsCallableReference& ref,
const firebase::Variant* const function_data,
const firebase::Variant& expected_result,
firebase::functions::Error expected_error) {
// Create a callable that we can run our test with.
LogDebug("Calling %s", function_name);
firebase::functions::HttpsCallableReference ref;
ref = functions_->GetHttpsCallable(function_name);

firebase::Future<firebase::functions::HttpsCallableResult> future;
if (function_data == nullptr) {
future = ref.Call();
Expand All @@ -207,6 +218,34 @@ FirebaseFunctionsTest::TestFunction(
return future;
}

firebase::Future<firebase::functions::HttpsCallableResult>
FirebaseFunctionsTest::TestFunction(
const char* function_name, const firebase::Variant* const function_data,
const firebase::Variant& expected_result,
firebase::functions::Error expected_error) {
// Create a callable that we can run our test with.
LogDebug("Calling %s", function_name);
firebase::functions::HttpsCallableReference ref;
ref = functions_->GetHttpsCallable(function_name);

return TestFunctionHelper(function_name, ref, function_data, expected_result,
expected_error);
}

firebase::Future<firebase::functions::HttpsCallableResult>
FirebaseFunctionsTest::TestFunctionFromURL(
const char* function_url, const firebase::Variant* const function_data,
const firebase::Variant& expected_result,
firebase::functions::Error expected_error) {
// Create a callable that we can run our test with.
LogDebug("Calling by URL %s", function_url);
firebase::functions::HttpsCallableReference ref;
ref = functions_->GetHttpsCallableFromURL(function_url);

return TestFunctionHelper(function_url, ref, function_data, expected_result,
expected_error);
}

TEST_F(FirebaseFunctionsTest, TestInitializeAndTerminate) {
// Already tested via SetUp() and TearDown().
}
Expand Down Expand Up @@ -323,4 +362,22 @@ TEST_F(FirebaseFunctionsTest, TestErrorHandling) {
firebase::functions::kErrorOutOfRange);
}

TEST_F(FirebaseFunctionsTest, TestFunctionFromURL) {
SignIn();

// addNumbers(4, 2) = 6
firebase::Variant data(firebase::Variant::EmptyMap());
data.map()["firstNumber"] = 4;
data.map()["secondNumber"] = 2;
std::string proj = app_->options().project_id();
std::string url =
"https://us-central1-" + proj + ".cloudfunctions.net/addNumbers";
firebase::Variant result =
TestFunctionFromURL(url.c_str(), &data, firebase::Variant::Null())
.result()
->data();
EXPECT_TRUE(result.is_map());
EXPECT_EQ(result.map()["operationResult"], 6);
}

} // namespace firebase_testapp_automated
30 changes: 29 additions & 1 deletion functions/src/android/functions_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ const char kApiIdentifier[] = "Functions";
X(GetHttpsCallable, "getHttpsCallable", \
"(Ljava/lang/String;)" \
"Lcom/google/firebase/functions/HttpsCallableReference;", \
util::kMethodTypeInstance), \
util::kMethodTypeInstance), \
X(GetHttpsCallableFromURL, "getHttpsCallableFromUrl", \
"(Ljava/net/URL;)" \
"Lcom/google/firebase/functions/HttpsCallableReference;", \
util::kMethodTypeInstance), \
X(UseFunctionsEmulator, "useFunctionsEmulator", \
"(Ljava/lang/String;)V", \
util::kMethodTypeInstance)
Expand Down Expand Up @@ -137,6 +141,7 @@ void FunctionsInternal::Terminate(App* app) {
JNIEnv* env = app->GetJNIEnv();
firebase_functions::ReleaseClass(env);
functions_exception::ReleaseClass(env);
functions_exception_code::ReleaseClass(env);

// Call Terminate on all other Functions internal classes.
HttpsCallableReferenceInternal::Terminate(app);
Expand Down Expand Up @@ -199,6 +204,29 @@ HttpsCallableReferenceInternal* FunctionsInternal::GetHttpsCallable(
return internal;
}

HttpsCallableReferenceInternal* FunctionsInternal::GetHttpsCallableFromURL(
const char* url) const {
FIREBASE_ASSERT_RETURN(nullptr, url != nullptr);
JNIEnv* env = app_->GetJNIEnv();
jobject url_object = util::CharsToURL(env, url);
jobject callable_reference_obj =
env->CallObjectMethod(obj_,
firebase_functions::GetMethodId(
firebase_functions::kGetHttpsCallableFromURL),
url_object);
env->DeleteLocalRef(url_object);
if (util::LogException(
env, kLogLevelError,
"Functions::GetHttpsCallableFromURL() (url = %s) failed", url)) {
return nullptr;
}
HttpsCallableReferenceInternal* internal = new HttpsCallableReferenceInternal(
const_cast<FunctionsInternal*>(this), callable_reference_obj);
env->DeleteLocalRef(callable_reference_obj);
util::CheckAndClearJniExceptions(env);
return internal;
}

void FunctionsInternal::UseFunctionsEmulator(const char* origin) {
FIREBASE_ASSERT(origin != nullptr);
JNIEnv* env = app_->GetJNIEnv();
Expand Down
4 changes: 4 additions & 0 deletions functions/src/android/functions_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ class FunctionsInternal {

HttpsCallableReferenceInternal* GetHttpsCallable(const char* name) const;

// Get a FunctionsReference for the specified URL.
HttpsCallableReferenceInternal* GetHttpsCallableFromURL(
const char* url) const;

void UseFunctionsEmulator(const char* origin);

// Convert an error code obtained from a Java FunctionsException into a C++
Expand Down
6 changes: 6 additions & 0 deletions functions/src/common/functions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ HttpsCallableReference Functions::GetHttpsCallable(const char* name) const {
return HttpsCallableReference(internal_->GetHttpsCallable(name));
}

HttpsCallableReference Functions::GetHttpsCallableFromURL(
const char* url) const {
if (!internal_) return HttpsCallableReference();
return HttpsCallableReference(internal_->GetHttpsCallableFromURL(url));
}

void Functions::UseFunctionsEmulator(const char* origin) {
if (!internal_) return;
internal_->UseFunctionsEmulator(origin);
Expand Down
21 changes: 11 additions & 10 deletions functions/src/desktop/callable_reference_desktop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#include "functions/src/desktop/callable_reference_desktop.h"

#include <string>

#include "app/rest/request.h"
#include "app/rest/util.h"
#include "app/src/function_registry.h"
Expand All @@ -32,8 +34,8 @@ enum CallableReferenceFn {
};

HttpsCallableReferenceInternal::HttpsCallableReferenceInternal(
FunctionsInternal* functions, const char* name)
: functions_(functions), name_(name) {
FunctionsInternal* functions, const char* url)
: functions_(functions), url_(url) {
functions_->future_manager().AllocFutureApi(this, kCallableReferenceFnCount);
rest::InitTransportCurl();
transport_.set_is_async(true);
Expand All @@ -46,7 +48,7 @@ HttpsCallableReferenceInternal::~HttpsCallableReferenceInternal() {

HttpsCallableReferenceInternal::HttpsCallableReferenceInternal(
const HttpsCallableReferenceInternal& other)
: functions_(other.functions_), name_(other.name_) {
: functions_(other.functions_), url_(other.url_) {
functions_->future_manager().AllocFutureApi(this, kCallableReferenceFnCount);
rest::InitTransportCurl();
transport_.set_is_async(true);
Expand All @@ -55,14 +57,14 @@ HttpsCallableReferenceInternal::HttpsCallableReferenceInternal(
HttpsCallableReferenceInternal& HttpsCallableReferenceInternal::operator=(
const HttpsCallableReferenceInternal& other) {
functions_ = other.functions_;
name_ = other.name_;
url_ = other.url_;
return *this;
}

#if defined(FIREBASE_USE_MOVE_OPERATORS) || defined(DOXYGEN)
HttpsCallableReferenceInternal::HttpsCallableReferenceInternal(
HttpsCallableReferenceInternal&& other)
: functions_(other.functions_), name_(std::move(other.name_)) {
: functions_(other.functions_), url_(std::move(other.url_)) {
other.functions_ = nullptr;
functions_->future_manager().MoveFutureApi(&other, this);
rest::InitTransportCurl();
Expand All @@ -73,7 +75,7 @@ HttpsCallableReferenceInternal& HttpsCallableReferenceInternal::operator=(
HttpsCallableReferenceInternal&& other) {
functions_ = other.functions_;
other.functions_ = nullptr;
name_ = std::move(other.name_);
url_ = std::move(other.url_);
functions_->future_manager().MoveFutureApi(&other, this);
return *this;
}
Expand Down Expand Up @@ -300,8 +302,7 @@ void HttpsCallableReferenceInternal::ResolveFuture(
Future<HttpsCallableResult> HttpsCallableReferenceInternal::Call(
const Variant& data) {
// Set up the request.
std::string url = functions_->GetUrl(name_);
request_.set_url(url.data());
request_.set_url(url_.data());
request_.set_method(rest::util::kPost);
request_.add_header(rest::util::kContentType, rest::util::kApplicationJson);

Expand All @@ -318,8 +319,8 @@ Future<HttpsCallableResult> HttpsCallableReferenceInternal::Call(
std::string json = util::VariantToJson(body);
request_.set_post_fields(json.data());

firebase::LogDebug("Calling Cloud Function with name: %s\nurl: %s\ndata: %s",
name_.c_str(), url.c_str(), json.c_str());
firebase::LogDebug("Calling Cloud Function with url: %s\ndata: %s",
url_.c_str(), json.c_str());

// Set up the future to resolve when the request is complete.
ReferenceCountedFutureImpl* future_impl = future();
Expand Down
9 changes: 5 additions & 4 deletions functions/src/desktop/callable_reference_desktop.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#ifndef FIREBASE_FUNCTIONS_SRC_DESKTOP_CALLABLE_REFERENCE_DESKTOP_H_
#define FIREBASE_FUNCTIONS_SRC_DESKTOP_CALLABLE_REFERENCE_DESKTOP_H_

#include <string>

#include "app/rest/transport_curl.h"
#include "app/rest/transport_interface.h"
#include "app/src/include/firebase/future.h"
Expand Down Expand Up @@ -52,8 +54,7 @@ class HttpsCallableRequest : public rest::Request {

class HttpsCallableReferenceInternal {
public:
HttpsCallableReferenceInternal(FunctionsInternal* functions,
const char* name);
HttpsCallableReferenceInternal(FunctionsInternal* functions, const char* url);
~HttpsCallableReferenceInternal();

// Copy constructor. It's totally okay (and efficient) to copy
Expand Down Expand Up @@ -108,8 +109,8 @@ class HttpsCallableReferenceInternal {
// Keep track of the Functions object for managing Futures.
FunctionsInternal* functions_;

// The name of the endpoint this reference points to.
std::string name_;
// The URL of the endpoint this reference points to.
std::string url_;

rest::TransportCurl transport_;
// For now, we only allow one request per reference at a time in C++.
Expand Down
Loading