diff --git a/firebase-functions/src/androidTest/java/com/google/firebase/functions/CallTest.java b/firebase-functions/src/androidTest/java/com/google/firebase/functions/CallTest.java index a6f38d568b7..9ae3a64bc68 100644 --- a/firebase-functions/src/androidTest/java/com/google/firebase/functions/CallTest.java +++ b/firebase-functions/src/androidTest/java/com/google/firebase/functions/CallTest.java @@ -25,6 +25,7 @@ import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Tasks; import com.google.firebase.FirebaseApp; +import com.google.firebase.emulators.EmulatedServiceSettings; import com.google.firebase.functions.FirebaseFunctionsException.Code; import java.util.Arrays; import java.util.HashMap; @@ -39,6 +40,8 @@ public class CallTest { private static FirebaseApp app; + private static final EmulatedServiceSettings NO_EMULATOR = null; + @BeforeClass public static void setUp() { FirebaseApp.initializeApp(InstrumentationRegistry.getContext()); @@ -90,7 +93,8 @@ public void testToken() throws InterruptedException, ExecutionException { () -> { HttpsCallableContext context = new HttpsCallableContext("token", null); return Tasks.forResult(context); - }); + }, + NO_EMULATOR); HttpsCallableReference function = functions.getHttpsCallable("tokenTest"); Task result = function.call(new HashMap<>()); @@ -110,7 +114,8 @@ public void testInstanceId() throws InterruptedException, ExecutionException { () -> { HttpsCallableContext context = new HttpsCallableContext(null, "iid"); return Tasks.forResult(context); - }); + }, + NO_EMULATOR); HttpsCallableReference function = functions.getHttpsCallable("instanceIdTest"); Task result = function.call(new HashMap<>()); diff --git a/firebase-functions/src/androidTest/java/com/google/firebase/functions/FirebaseFunctionsTest.java b/firebase-functions/src/androidTest/java/com/google/firebase/functions/FirebaseFunctionsTest.java index 41873cdc00f..aa7628e248d 100644 --- a/firebase-functions/src/androidTest/java/com/google/firebase/functions/FirebaseFunctionsTest.java +++ b/firebase-functions/src/androidTest/java/com/google/firebase/functions/FirebaseFunctionsTest.java @@ -15,32 +15,24 @@ package com.google.firebase.functions; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import androidx.test.InstrumentationRegistry; +import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.emulators.EmulatedServiceSettings; +import com.google.firebase.emulators.EmulatorSettings; import java.net.URL; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class FirebaseFunctionsTest { - private final FirebaseApp app = mock(FirebaseApp.class); - private final ContextProvider provider = mock(ContextProvider.class); - - @Before - public void setUp() { - when(app.get(FunctionsMultiResourceComponent.class)) - .thenReturn( - new FunctionsMultiResourceComponent( - InstrumentationRegistry.getTargetContext(), provider, "my-project")); - } @Test public void testGetUrl() { + FirebaseApp app = getApp("testGetUrl"); + FirebaseFunctions functions = FirebaseFunctions.getInstance(app, "my-region"); URL url = functions.getURL("my-endpoint"); assertEquals("https://my-region-my-project.cloudfunctions.net/my-endpoint", url.toString()); @@ -49,4 +41,52 @@ public void testGetUrl() { url = functions.getURL("my-endpoint"); assertEquals("https://us-central1-my-project.cloudfunctions.net/my-endpoint", url.toString()); } + + @Test + public void testGetUrl_withEmulator() { + FirebaseApp app = getApp("testGetUrl_withEmulator"); + + app.enableEmulators( + new EmulatorSettings.Builder() + .addEmulatedService( + FirebaseFunctions.EMULATOR, new EmulatedServiceSettings("10.0.2.2", 5001)) + .build()); + + URL withRegion = FirebaseFunctions.getInstance(app, "my-region").getURL("my-endpoint"); + assertEquals("http://10.0.2.2:5001/my-project/my-region/my-endpoint", withRegion.toString()); + + URL withoutRegion = FirebaseFunctions.getInstance(app).getURL("my-endpoint"); + assertEquals( + "http://10.0.2.2:5001/my-project/us-central1/my-endpoint", withoutRegion.toString()); + } + + @Test + public void testGetUrl_withEmulator_matchesOldImpl() { + FirebaseApp app = getApp("testGetUrl_withEmulator_matchesOldImpl"); + + app.enableEmulators( + new EmulatorSettings.Builder() + .addEmulatedService( + FirebaseFunctions.EMULATOR, new EmulatedServiceSettings("10.0.2.2", 5001)) + .build()); + + FirebaseFunctions functions = FirebaseFunctions.getInstance(app); + URL newImplUrl = functions.getURL("my-endpoint"); + + functions.useFunctionsEmulator("http://10.0.2.2:5001"); + URL oldImplUrl = functions.getURL("my-endpoint"); + + assertEquals(newImplUrl.toString(), oldImplUrl.toString()); + } + + private FirebaseApp getApp(String name) { + return FirebaseApp.initializeApp( + InstrumentationRegistry.getInstrumentation().getTargetContext(), + new FirebaseOptions.Builder() + .setProjectId("my-project") + .setApplicationId("appid") + .setApiKey("apikey") + .build(), + name); + } } diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.java b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.java index ae9137fa09b..43fba4fc3f9 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.java +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.java @@ -27,6 +27,8 @@ import com.google.android.gms.tasks.TaskCompletionSource; import com.google.android.gms.tasks.Tasks; import com.google.firebase.FirebaseApp; +import com.google.firebase.emulators.EmulatedServiceSettings; +import com.google.firebase.emulators.FirebaseEmulator; import com.google.firebase.functions.FirebaseFunctionsException.Code; import java.io.IOException; import java.io.InterruptedIOException; @@ -47,6 +49,17 @@ /** FirebaseFunctions lets you call Cloud Functions for Firebase. */ public class FirebaseFunctions { + /** + * Emulator identifier, see {@link + * com.google.firebase.emulators.EmulatorSettings.Builder#addEmulatedService(FirebaseEmulator, + * EmulatedServiceSettings)} + * + *

TODO(samstern): Un-hide this once Firestore, Database, and Functions are implemented + * + * @hide + */ + public static final FirebaseEmulator EMULATOR = FirebaseEmulator.forName("functions"); + /** A task that will be resolved once ProviderInstaller has installed what it needs to. */ private static final TaskCompletionSource providerInstalled = new TaskCompletionSource<>(); @@ -75,13 +88,26 @@ public class FirebaseFunctions { private String urlFormat = "https://%1$s-%2$s.cloudfunctions.net/%3$s"; FirebaseFunctions( - Context context, String projectId, String region, ContextProvider contextProvider) { + Context context, + String projectId, + String region, + ContextProvider contextProvider, + @Nullable EmulatedServiceSettings emulatorSettings) { this.client = new OkHttpClient(); this.serializer = new Serializer(); this.contextProvider = Preconditions.checkNotNull(contextProvider); this.projectId = Preconditions.checkNotNull(projectId); this.region = Preconditions.checkNotNull(region); + if (emulatorSettings != null) { + urlFormat = + "http://" + + emulatorSettings.getHost() + + ":" + + emulatorSettings.getPort() + + "/%2$s/%1$s/%3$s"; + } + maybeInstallProviders(context); } diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsMultiResourceComponent.java b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsMultiResourceComponent.java index 7f609dd601a..e2e30638e99 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsMultiResourceComponent.java +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsMultiResourceComponent.java @@ -16,6 +16,8 @@ import android.content.Context; import androidx.annotation.GuardedBy; +import com.google.firebase.FirebaseApp; +import com.google.firebase.emulators.EmulatedServiceSettings; import java.util.HashMap; import java.util.Map; @@ -31,19 +33,25 @@ class FunctionsMultiResourceComponent { private final Context applicationContext; private final ContextProvider contextProvider; - private final String projectId; + private final FirebaseApp app; FunctionsMultiResourceComponent( - Context applicationContext, ContextProvider contextProvider, String projectId) { + Context applicationContext, ContextProvider contextProvider, FirebaseApp app) { this.applicationContext = applicationContext; this.contextProvider = contextProvider; - this.projectId = projectId; + this.app = app; } synchronized FirebaseFunctions get(String region) { FirebaseFunctions functions = instances.get(region); + String projectId = app.getOptions().getProjectId(); + EmulatedServiceSettings emulatorSettings = + app.getEmulatorSettings().getServiceSettings(FirebaseFunctions.EMULATOR); + if (functions == null) { - functions = new FirebaseFunctions(applicationContext, projectId, region, contextProvider); + functions = + new FirebaseFunctions( + applicationContext, projectId, region, contextProvider, emulatorSettings); instances.put(region, functions); } return functions; diff --git a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.java b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.java index 95b620cfed0..1b83c573755 100644 --- a/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.java +++ b/firebase-functions/src/main/java/com/google/firebase/functions/FunctionsRegistrar.java @@ -16,7 +16,7 @@ import android.content.Context; import androidx.annotation.Keep; -import com.google.firebase.FirebaseOptions; +import com.google.firebase.FirebaseApp; import com.google.firebase.auth.internal.InternalAuthProvider; import com.google.firebase.components.Component; import com.google.firebase.components.ComponentRegistrar; @@ -48,13 +48,13 @@ public List> getComponents() { Component.builder(FunctionsMultiResourceComponent.class) .add(Dependency.required(Context.class)) .add(Dependency.required(ContextProvider.class)) - .add(Dependency.required(FirebaseOptions.class)) + .add(Dependency.required(FirebaseApp.class)) .factory( c -> new FunctionsMultiResourceComponent( c.get(Context.class), c.get(ContextProvider.class), - c.get(FirebaseOptions.class).getProjectId())) + c.get(FirebaseApp.class))) .build(), LibraryVersionComponent.create("fire-fn", BuildConfig.VERSION_NAME)); }