mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-04-21 10:34:39 +08:00
feat(functions): support function timeout (#3534)
Added support for configurable function timeout which allows you to set the timeout for a cloud function call in milliseconds.
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
## Change logs
|
||||
|
||||
See [rnfirebase.io/releases](https://rnfirebase.io/releases).
|
||||
@@ -19,13 +19,17 @@ package io.invertase.firebase.functions;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.gms.tasks.Tasks;
|
||||
import com.google.firebase.FirebaseApp;
|
||||
import com.google.firebase.functions.FirebaseFunctions;
|
||||
import com.google.firebase.functions.HttpsCallableReference;
|
||||
|
||||
import io.invertase.firebase.common.UniversalFirebaseModule;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class UniversalFirebaseFunctionsModule extends UniversalFirebaseModule {
|
||||
public static final String DATA_KEY = "data";
|
||||
@@ -42,17 +46,24 @@ public class UniversalFirebaseFunctionsModule extends UniversalFirebaseModule {
|
||||
String region,
|
||||
String origin,
|
||||
String name,
|
||||
Object data
|
||||
Object data,
|
||||
ReadableMap options
|
||||
) {
|
||||
return Tasks.call(getExecutor(), () -> {
|
||||
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
|
||||
FirebaseFunctions functionsInstance = FirebaseFunctions.getInstance(firebaseApp, region);
|
||||
|
||||
HttpsCallableReference httpReference = functionsInstance.getHttpsCallable(name);
|
||||
|
||||
if (options.hasKey("timeout")) {
|
||||
httpReference.setTimeout((long) options.getInt("timeout"), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
if (origin != null) {
|
||||
functionsInstance.useFunctionsEmulator(origin);
|
||||
}
|
||||
|
||||
return Tasks.await(functionsInstance.getHttpsCallable(name).call(data)).getData();
|
||||
return Tasks.await(httpReference.call(data)).getData();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ public class ReactNativeFirebaseFunctionsModule extends ReactNativeFirebaseModul
|
||||
String origin,
|
||||
String name,
|
||||
ReadableMap wrapper,
|
||||
ReadableMap options,
|
||||
Promise promise
|
||||
) {
|
||||
Task<Object> callMethodTask = module.httpsCallable(
|
||||
@@ -59,7 +60,8 @@ public class ReactNativeFirebaseFunctionsModule extends ReactNativeFirebaseModul
|
||||
region,
|
||||
origin,
|
||||
name,
|
||||
wrapper.toHashMap().get(DATA_KEY)
|
||||
wrapper.toHashMap().get(DATA_KEY),
|
||||
options
|
||||
);
|
||||
|
||||
// resolve
|
||||
@@ -78,7 +80,10 @@ public class ReactNativeFirebaseFunctionsModule extends ReactNativeFirebaseModul
|
||||
details = functionsException.getDetails();
|
||||
code = functionsException.getCode().name();
|
||||
message = functionsException.getMessage();
|
||||
if (functionsException.getCause() instanceof IOException) {
|
||||
String timeout = FirebaseFunctionsException.Code.DEADLINE_EXCEEDED.name();
|
||||
Boolean isTimeout = code.contains(timeout);
|
||||
|
||||
if (functionsException.getCause() instanceof IOException && !isTimeout) {
|
||||
// return UNAVAILABLE for network io errors, to match iOS
|
||||
code = FirebaseFunctionsException.Code.UNAVAILABLE.name();
|
||||
message = FirebaseFunctionsException.Code.UNAVAILABLE.name();
|
||||
|
||||
@@ -286,5 +286,21 @@ describe('functions()', () => {
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
it('HttpsCallableOptions.timeout will error when timeout is exceeded', async () => {
|
||||
const fnName = 'invertaseReactNativeFirebaseFunctionsEmulator';
|
||||
const region = 'europe-west2';
|
||||
|
||||
const functions = firebase.app().functions(region);
|
||||
functions.useFunctionsEmulator('http://api.rnfirebase.io');
|
||||
|
||||
try {
|
||||
await functions.httpsCallable(fnName, { timeout: 1000 })({ testTimeout: '3000' });
|
||||
return Promise.reject(new Error('Did not throw an Error.'));
|
||||
} catch (error) {
|
||||
error.message.should.containEql('DEADLINE').containEql('EXCEEDED');
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
(NSString *) name
|
||||
wrapper:
|
||||
(NSDictionary *) wrapper
|
||||
options:
|
||||
(NSDictionary *) options
|
||||
resolver:
|
||||
(RCTPromiseResolveBlock) resolve
|
||||
rejecter:
|
||||
@@ -54,6 +56,10 @@
|
||||
|
||||
FIRHTTPSCallable *callable = [functions HTTPSCallableWithName:name];
|
||||
|
||||
if (options[@"timeout"]) {
|
||||
callable.timeoutInterval = [options[@"timeout"] doubleValue];
|
||||
}
|
||||
|
||||
[callable callWithObject:[wrapper valueForKey:@"data"] completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
|
||||
if (error) {
|
||||
NSObject *details = [NSNull null];
|
||||
|
||||
26
packages/functions/lib/index.d.ts
vendored
26
packages/functions/lib/index.d.ts
vendored
@@ -144,6 +144,30 @@ export namespace FirebaseFunctionsTypes {
|
||||
(data?: any): Promise<HttpsCallableResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
* An HttpsCallableOptions object that can be passed as the second argument to `firebase.functions().httpsCallable(name, HttpsCallableOptions)`.
|
||||
**/
|
||||
export interface HttpsCallableOptions {
|
||||
/**
|
||||
* The timeout property allows you to control how long the application will wait for the cloud function to respond in milliseconds.
|
||||
*
|
||||
* #### Example
|
||||
*
|
||||
*```js
|
||||
* // The below will wait 7 seconds for a response from the cloud function before an error is thrown
|
||||
* try {
|
||||
* const instance = firebase.functions().httpsCallable('order', { timeout: 7000 });
|
||||
* const response = await instance({
|
||||
* id: '12345',
|
||||
* });
|
||||
* } catch (e) {
|
||||
* console.log(e);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* An HttpsError wraps a single error from a function call.
|
||||
*
|
||||
@@ -312,7 +336,7 @@ export namespace FirebaseFunctionsTypes {
|
||||
* @param name The name of the https callable function.
|
||||
* @return The `HttpsCallable` instance.
|
||||
*/
|
||||
httpsCallable(name: string): HttpsCallable;
|
||||
httpsCallable(name: string, options?: HttpsCallableOptions): HttpsCallable;
|
||||
|
||||
/**
|
||||
* Changes this instance to point to a Cloud Functions emulator running
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { isAndroid } from '@react-native-firebase/app/lib/common';
|
||||
import { isAndroid, isNumber } from '@react-native-firebase/app/lib/common';
|
||||
import {
|
||||
createModuleNamespace,
|
||||
FirebaseModule,
|
||||
@@ -59,11 +59,24 @@ class FirebaseFunctionsModule extends FirebaseModule {
|
||||
this._useFunctionsEmulatorOrigin = null;
|
||||
}
|
||||
|
||||
httpsCallable(name) {
|
||||
httpsCallable(name, options = {}) {
|
||||
if (options.timeout) {
|
||||
if (isNumber(options.timeout)) {
|
||||
options.timeout = options.timeout / 1000;
|
||||
} else {
|
||||
throw new Error('HttpsCallableOptions.timeout expected a Number in milliseconds');
|
||||
}
|
||||
}
|
||||
|
||||
return data => {
|
||||
const nativePromise = this.native.httpsCallable(this._useFunctionsEmulatorOrigin, name, {
|
||||
data,
|
||||
});
|
||||
const nativePromise = this.native.httpsCallable(
|
||||
this._useFunctionsEmulatorOrigin,
|
||||
name,
|
||||
{
|
||||
data,
|
||||
},
|
||||
options,
|
||||
);
|
||||
return nativePromise.catch(nativeError => {
|
||||
const { code, message, details } = nativeError.userInfo || {};
|
||||
return Promise.reject(
|
||||
|
||||
Reference in New Issue
Block a user