mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-04-06 22:38:47 +08:00
Merge pull request #1056 from invertase/functions
Functions Implementation
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -96,3 +96,4 @@ tests/ios/Fabric.framework/Fabric
|
||||
bridge/android/app/.classpath
|
||||
bridge/android/app/.project
|
||||
**/.vscode
|
||||
bridge/functions/firebase-debug.log
|
||||
|
||||
@@ -102,6 +102,7 @@ dependencies {
|
||||
compileOnly "com.google.firebase:firebase-ads:$firebaseVersion"
|
||||
compileOnly "com.google.firebase:firebase-firestore:$firebaseVersion"
|
||||
compileOnly "com.google.firebase:firebase-invites:$firebaseVersion"
|
||||
compileOnly "com.google.firebase:firebase-functions:$firebaseVersion"
|
||||
compileOnly('com.crashlytics.sdk.android:crashlytics:2.9.1@aar') {
|
||||
transitive = true
|
||||
}
|
||||
|
||||
@@ -4,38 +4,24 @@ import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
|
||||
import com.facebook.react.bridge.ReadableType;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class Utils {
|
||||
private static final String TAG = "Utils";
|
||||
|
||||
// TODO NOTE
|
||||
public static void todoNote(final String tag, final String name, final Callback callback) {
|
||||
Log.e(tag, "The method " + name + " has not yet been implemented.");
|
||||
Log.e(tag, "Feel free to contribute to finish the method in the source.");
|
||||
|
||||
WritableMap errorMap = Arguments.createMap();
|
||||
errorMap.putString("error", "unimplemented");
|
||||
callback.invoke(null, errorMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* send a JS event
|
||||
**/
|
||||
@@ -50,160 +36,114 @@ public class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key map key
|
||||
* @param value map value
|
||||
* @param map map
|
||||
* Takes a value and calls the appropriate setter for its type on the target map + key
|
||||
*
|
||||
* @param key String key to set on target map
|
||||
* @param value Object value to set on target map
|
||||
* @param map WritableMap target map to write the value to
|
||||
*/
|
||||
public static void mapPutValue(String key, Object value, WritableMap map) {
|
||||
String type = value != null ? value.getClass().getName() : "";
|
||||
switch (type) {
|
||||
case "java.lang.Boolean":
|
||||
map.putBoolean(key, (Boolean) value);
|
||||
break;
|
||||
case "java.lang.Long":
|
||||
Long longVal = (Long) value;
|
||||
map.putDouble(key, (double) longVal);
|
||||
break;
|
||||
case "java.lang.Double":
|
||||
map.putDouble(key, (Double) value);
|
||||
break;
|
||||
case "java.lang.String":
|
||||
map.putString(key, (String) value);
|
||||
break;
|
||||
default:
|
||||
map.putString(key, null);
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void mapPutValue(String key, @Nullable Object value, WritableMap map) {
|
||||
if (value == null) {
|
||||
map.putNull(key);
|
||||
} else {
|
||||
String type = value.getClass().getName();
|
||||
switch (type) {
|
||||
case "java.lang.Boolean":
|
||||
map.putBoolean(key, (Boolean) value);
|
||||
break;
|
||||
case "java.lang.Long":
|
||||
Long longVal = (Long) value;
|
||||
map.putDouble(key, (double) longVal);
|
||||
break;
|
||||
case "java.lang.Float":
|
||||
float floatVal = (float) value;
|
||||
map.putDouble(key, (double) floatVal);
|
||||
break;
|
||||
case "java.lang.Double":
|
||||
map.putDouble(key, (Double) value);
|
||||
break;
|
||||
case "java.lang.Integer":
|
||||
map.putInt(key, (int) value);
|
||||
break;
|
||||
case "java.lang.String":
|
||||
map.putString(key, (String) value);
|
||||
break;
|
||||
default:
|
||||
if (List.class.isAssignableFrom(value.getClass())) {
|
||||
map.putArray(key, Arguments.makeNativeArray((List<Object>) value));
|
||||
} else if (Map.class.isAssignableFrom(value.getClass())) {
|
||||
map.putMap(key, Arguments.makeNativeMap((Map<String, Object>) value));
|
||||
} else {
|
||||
Log.d(TAG, "utils:mapPutValue:unknownType:" + type);
|
||||
map.putNull(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a ReadableMap to a WritableMap for the purposes of re-sending back to JS
|
||||
* TODO This is now a legacy util - internally uses RN functionality
|
||||
*
|
||||
* @param map
|
||||
* @return
|
||||
* @param map ReadableMap
|
||||
* @return WritableMap
|
||||
*/
|
||||
public static WritableMap readableMapToWritableMap(ReadableMap map) {
|
||||
WritableMap writableMap = Arguments.createMap();
|
||||
|
||||
ReadableMapKeySetIterator iterator = map.keySetIterator();
|
||||
while (iterator.hasNextKey()) {
|
||||
String key = iterator.nextKey();
|
||||
ReadableType type = map.getType(key);
|
||||
switch (type) {
|
||||
case Null:
|
||||
writableMap.putNull(key);
|
||||
break;
|
||||
case Boolean:
|
||||
writableMap.putBoolean(key, map.getBoolean(key));
|
||||
break;
|
||||
case Number:
|
||||
writableMap.putDouble(key, map.getDouble(key));
|
||||
break;
|
||||
case String:
|
||||
writableMap.putString(key, map.getString(key));
|
||||
break;
|
||||
case Map:
|
||||
writableMap.putMap(key, readableMapToWritableMap(map.getMap(key)));
|
||||
break;
|
||||
case Array:
|
||||
// TODO writableMap.putArray(key, readableArrayToWritableArray(map.getArray(key)));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Could not convert object with key: " + key + ".");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeMap.java#L54
|
||||
writableMap.merge(map);
|
||||
return writableMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a ReadableMap into a native Java Map
|
||||
* TODO This is now a legacy util - internally uses RN functionality
|
||||
*
|
||||
* @param readableMap ReadableMap
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, Object> recursivelyDeconstructReadableMap(ReadableMap readableMap) {
|
||||
Map<String, Object> deconstructedMap = new HashMap<>();
|
||||
if (readableMap == null) {
|
||||
return deconstructedMap;
|
||||
}
|
||||
|
||||
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
|
||||
while (iterator.hasNextKey()) {
|
||||
String key = iterator.nextKey();
|
||||
ReadableType type = readableMap.getType(key);
|
||||
switch (type) {
|
||||
case Null:
|
||||
deconstructedMap.put(key, null);
|
||||
break;
|
||||
case Boolean:
|
||||
deconstructedMap.put(key, readableMap.getBoolean(key));
|
||||
break;
|
||||
case Number:
|
||||
deconstructedMap.put(key, readableMap.getDouble(key));
|
||||
break;
|
||||
case String:
|
||||
deconstructedMap.put(key, readableMap.getString(key));
|
||||
break;
|
||||
case Map:
|
||||
deconstructedMap.put(key, Utils.recursivelyDeconstructReadableMap(readableMap.getMap(key)));
|
||||
break;
|
||||
case Array:
|
||||
deconstructedMap.put(key, Utils.recursivelyDeconstructReadableArray(readableMap.getArray(key)));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Could not convert object with key: " + key + ".");
|
||||
}
|
||||
|
||||
}
|
||||
return deconstructedMap;
|
||||
// https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java#L216
|
||||
return readableMap.toHashMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a ReadableArray into a native Java Map
|
||||
* TODO This is now a legacy util - internally uses RN functionality
|
||||
*
|
||||
* @param readableArray ReadableArray
|
||||
* @return List<Object>
|
||||
*/
|
||||
public static List<Object> recursivelyDeconstructReadableArray(ReadableArray readableArray) {
|
||||
List<Object> deconstructedList = new ArrayList<>(readableArray.size());
|
||||
for (int i = 0; i < readableArray.size(); i++) {
|
||||
ReadableType indexType = readableArray.getType(i);
|
||||
switch (indexType) {
|
||||
case Null:
|
||||
deconstructedList.add(i, null);
|
||||
break;
|
||||
case Boolean:
|
||||
deconstructedList.add(i, readableArray.getBoolean(i));
|
||||
break;
|
||||
case Number:
|
||||
deconstructedList.add(i, readableArray.getDouble(i));
|
||||
break;
|
||||
case String:
|
||||
deconstructedList.add(i, readableArray.getString(i));
|
||||
break;
|
||||
case Map:
|
||||
deconstructedList.add(i, Utils.recursivelyDeconstructReadableMap(readableArray.getMap(i)));
|
||||
break;
|
||||
case Array:
|
||||
deconstructedList.add(i, Utils.recursivelyDeconstructReadableArray(readableArray.getArray(i)));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Could not convert object at index " + i + ".");
|
||||
}
|
||||
}
|
||||
return deconstructedList;
|
||||
// https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java#L175
|
||||
return readableArray.toArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to check if app is in foreground otherwise the app will crash.
|
||||
* http://stackoverflow.com/questions/8489993/check-android-application-is-in-foreground-or-not
|
||||
*
|
||||
* @param context Context
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isAppInForeground(Context context) {
|
||||
/**
|
||||
We need to check if app is in foreground otherwise the app will crash.
|
||||
http://stackoverflow.com/questions/8489993/check-android-application-is-in-foreground-or-not
|
||||
**/
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningAppProcessInfo> appProcesses =
|
||||
activityManager.getRunningAppProcesses();
|
||||
if (appProcesses == null) {
|
||||
return false;
|
||||
}
|
||||
if (activityManager == null) return false;
|
||||
|
||||
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
|
||||
if (appProcesses == null) return false;
|
||||
|
||||
final String packageName = context.getPackageName();
|
||||
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
|
||||
if (appProcess.importance ==
|
||||
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
|
||||
appProcess.processName.equals(packageName)) {
|
||||
if (
|
||||
appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|
||||
&& appProcess.processName.equals(packageName)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package io.invertase.firebase.functions;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.google.android.gms.tasks.OnFailureListener;
|
||||
import com.google.android.gms.tasks.OnSuccessListener;
|
||||
import com.google.firebase.functions.FirebaseFunctions;
|
||||
import com.google.firebase.functions.FirebaseFunctionsException;
|
||||
import com.google.firebase.functions.HttpsCallableReference;
|
||||
import com.google.firebase.functions.HttpsCallableResult;
|
||||
|
||||
import io.invertase.firebase.Utils;
|
||||
|
||||
public class RNFirebaseFunctions extends ReactContextBaseJavaModule {
|
||||
|
||||
private static final String DATA_KEY = "data";
|
||||
private static final String CODE_KEY = "code";
|
||||
private static final String MSG_KEY = "message";
|
||||
private static final String ERROR_KEY = "__error";
|
||||
private static final String DETAILS_KEY = "details";
|
||||
private static final String TAG = "RNFirebaseFunctions";
|
||||
|
||||
RNFirebaseFunctions(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
Log.d(TAG, "New instance");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void httpsCallable(final String name, ReadableMap wrapper, final Promise promise) {
|
||||
Object input = wrapper.toHashMap().get(DATA_KEY);
|
||||
Log.d(TAG, "function:call:input:" + name + ":" + (input != null ? input.toString() : "null"));
|
||||
|
||||
HttpsCallableReference httpsCallableReference = FirebaseFunctions
|
||||
.getInstance()
|
||||
.getHttpsCallable(name);
|
||||
|
||||
httpsCallableReference
|
||||
.call(input)
|
||||
.addOnSuccessListener(new OnSuccessListener<HttpsCallableResult>() {
|
||||
@Override
|
||||
public void onSuccess(HttpsCallableResult httpsCallableResult) {
|
||||
WritableMap map = Arguments.createMap();
|
||||
Object result = httpsCallableResult.getData();
|
||||
|
||||
Log.d(
|
||||
TAG,
|
||||
"function:call:onSuccess:" + name
|
||||
);
|
||||
Log.d(
|
||||
TAG,
|
||||
"function:call:onSuccess:result:type:" + name + ":" + (result != null ? result.getClass().getName() : "null")
|
||||
);
|
||||
Log.d(
|
||||
TAG,
|
||||
"function:call:onSuccess:result:data:" + name + ":" + (result != null ? result.toString() : "null")
|
||||
);
|
||||
|
||||
Utils.mapPutValue(DATA_KEY, result, map);
|
||||
promise.resolve(map);
|
||||
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(new OnFailureListener() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Exception exception) {
|
||||
Log.d(TAG, "function:call:onFailure:" + name, exception);
|
||||
|
||||
String message;
|
||||
Object details = null;
|
||||
String code = "UNKNOWN";
|
||||
WritableMap map = Arguments.createMap();
|
||||
|
||||
if (exception instanceof FirebaseFunctionsException) {
|
||||
FirebaseFunctionsException ffe = (FirebaseFunctionsException) exception;
|
||||
details = ffe.getDetails();
|
||||
code = ffe.getCode().name();
|
||||
message = ffe.getLocalizedMessage();
|
||||
} else {
|
||||
message = exception.getLocalizedMessage();
|
||||
}
|
||||
|
||||
Utils.mapPutValue(CODE_KEY, code, map);
|
||||
Utils.mapPutValue(MSG_KEY, message, map);
|
||||
Utils.mapPutValue(ERROR_KEY, true, map);
|
||||
Utils.mapPutValue(DETAILS_KEY, details, map);
|
||||
promise.resolve(map);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.invertase.firebase.functions;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class RNFirebaseFunctionsPackage implements ReactPackage {
|
||||
public RNFirebaseFunctionsPackage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reactContext react application context that can be used to create modules
|
||||
* @return list of native modules to register with the newly created catalyst instance
|
||||
*/
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
modules.add(new RNFirebaseFunctions(reactContext));
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reactContext
|
||||
* @return a list of view managers that should be registered with {@link UIManagerModule}
|
||||
*/
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
5
bridge/.firebaserc
Normal file
5
bridge/.firebaserc
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"projects": {
|
||||
"default": "rnfirebase-b9ad4"
|
||||
}
|
||||
}
|
||||
@@ -89,23 +89,24 @@ dependencies {
|
||||
|
||||
implementation project(':bridge')
|
||||
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.google.android.gms:play-services-base:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-ads:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-auth:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-config:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-core:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-crash:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-database:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-messaging:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-perf:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-storage:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-firestore:$firebaseVersion"
|
||||
compile "com.google.firebase:firebase-invites:$firebaseVersion"
|
||||
compile('com.crashlytics.sdk.android:crashlytics:2.9.1@aar') {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation "com.google.android.gms:play-services-base:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-ads:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-auth:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-config:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-core:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-crash:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-database:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-messaging:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-perf:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-storage:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-firestore:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-invites:$firebaseVersion"
|
||||
implementation "com.google.firebase:firebase-functions:$firebaseVersion"
|
||||
implementation('com.crashlytics.sdk.android:crashlytics:2.9.1@aar') {
|
||||
transitive = true
|
||||
}
|
||||
compile "com.android.support:appcompat-v7:27.1.0"
|
||||
implementation "com.android.support:appcompat-v7:27.1.0"
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
androidTestImplementation(project(path: ":detox"))
|
||||
androidTestImplementation 'junit:junit:4.12'
|
||||
|
||||
@@ -18,6 +18,7 @@ import io.invertase.firebase.crash.RNFirebaseCrashPackage;
|
||||
import io.invertase.firebase.database.RNFirebaseDatabasePackage;
|
||||
import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage;
|
||||
import io.invertase.firebase.firestore.RNFirebaseFirestorePackage;
|
||||
import io.invertase.firebase.functions.RNFirebaseFunctionsPackage;
|
||||
import io.invertase.firebase.instanceid.RNFirebaseInstanceIdPackage;
|
||||
import io.invertase.firebase.invites.RNFirebaseInvitesPackage;
|
||||
import io.invertase.firebase.links.RNFirebaseLinksPackage;
|
||||
@@ -51,6 +52,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
new RNFirebaseCrashlyticsPackage(),
|
||||
new RNFirebaseDatabasePackage(),
|
||||
new RNFirebaseFirestorePackage(),
|
||||
new RNFirebaseFunctionsPackage(),
|
||||
new RNFirebaseInstanceIdPackage(),
|
||||
new RNFirebaseInvitesPackage(),
|
||||
new RNFirebaseLinksPackage(),
|
||||
|
||||
202
bridge/e2e/functions/functions.e2e.js
Normal file
202
bridge/e2e/functions/functions.e2e.js
Normal file
@@ -0,0 +1,202 @@
|
||||
const TEST_DATA = TestHelpers.functions.data;
|
||||
|
||||
describe('functions()', () => {
|
||||
describe('httpsCallable(fnName)(args)', () => {
|
||||
it('accepts primitive args: undefined', async () => {
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
const response = await functionRunner();
|
||||
response.data.should.equal('null');
|
||||
});
|
||||
|
||||
it('accepts primitive args: string', async () => {
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
const response = await functionRunner('hello');
|
||||
response.data.should.equal('string');
|
||||
});
|
||||
|
||||
it('accepts primitive args: number', async () => {
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
const response = await functionRunner(123);
|
||||
response.data.should.equal('number');
|
||||
});
|
||||
|
||||
it('accepts primitive args: boolean', async () => {
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
const response = await functionRunner(true);
|
||||
response.data.should.equal('boolean');
|
||||
});
|
||||
|
||||
it('accepts primitive args: null', async () => {
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
const response = await functionRunner(null);
|
||||
response.data.should.equal('null');
|
||||
});
|
||||
|
||||
it('accepts array args', async () => {
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
const response = await functionRunner([1, 2, 3, 4]);
|
||||
response.data.should.equal('array');
|
||||
});
|
||||
|
||||
it('accepts object args', async () => {
|
||||
const type = 'simpleObject';
|
||||
const inputData = TEST_DATA[type];
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
const { data: outputData } = await functionRunner({
|
||||
type,
|
||||
inputData,
|
||||
});
|
||||
should.deepEqual(outputData, inputData);
|
||||
});
|
||||
|
||||
it('accepts complex nested objects', async () => {
|
||||
const type = 'advancedObject';
|
||||
const inputData = TEST_DATA[type];
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
const { data: outputData } = await functionRunner({
|
||||
type,
|
||||
inputData,
|
||||
});
|
||||
should.deepEqual(outputData, inputData);
|
||||
});
|
||||
|
||||
it('accepts complex nested arrays', async () => {
|
||||
const type = 'advancedArray';
|
||||
const inputData = TEST_DATA[type];
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
const { data: outputData } = await functionRunner({
|
||||
type,
|
||||
inputData,
|
||||
});
|
||||
should.deepEqual(outputData, inputData);
|
||||
});
|
||||
});
|
||||
|
||||
describe('HttpsError', () => {
|
||||
it('errors return instance of HttpsError', async () => {
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
try {
|
||||
await functionRunner({});
|
||||
return Promise.reject(new Error('Function did not reject with error.'));
|
||||
} catch (e) {
|
||||
should.equal(e.details, null);
|
||||
e.code.should.equal('invalid-argument');
|
||||
e.message.should.equal('Invalid test requested.');
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
it('HttpsError.details -> allows returning complex data', async () => {
|
||||
let type = 'advancedObject';
|
||||
let inputData = TEST_DATA[type];
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
try {
|
||||
await functionRunner({
|
||||
type,
|
||||
inputData,
|
||||
asError: true,
|
||||
});
|
||||
return Promise.reject(new Error('Function did not reject with error.'));
|
||||
} catch (e) {
|
||||
should.deepEqual(e.details, inputData);
|
||||
e.code.should.equal('cancelled');
|
||||
e.message.should.equal(
|
||||
'Response data was requested to be sent as part of an Error payload, so here we are!'
|
||||
);
|
||||
}
|
||||
|
||||
type = 'advancedArray';
|
||||
inputData = TEST_DATA[type];
|
||||
try {
|
||||
await functionRunner({
|
||||
type,
|
||||
inputData,
|
||||
asError: true,
|
||||
});
|
||||
return Promise.reject(new Error('Function did not reject with error.'));
|
||||
} catch (e) {
|
||||
should.deepEqual(e.details, inputData);
|
||||
e.code.should.equal('cancelled');
|
||||
e.message.should.equal(
|
||||
'Response data was requested to be sent as part of an Error payload, so here we are!'
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
it('HttpsError.details -> allows returning primitives', async () => {
|
||||
let type = 'number';
|
||||
let inputData = TEST_DATA[type];
|
||||
const functionRunner = firebase.functions().httpsCallable('runTest');
|
||||
try {
|
||||
await functionRunner({
|
||||
type,
|
||||
inputData,
|
||||
asError: true,
|
||||
});
|
||||
return Promise.reject(new Error('Function did not reject with error.'));
|
||||
} catch (e) {
|
||||
e.code.should.equal('cancelled');
|
||||
e.message.should.equal(
|
||||
'Response data was requested to be sent as part of an Error payload, so here we are!'
|
||||
);
|
||||
should.deepEqual(e.details, inputData);
|
||||
}
|
||||
|
||||
type = 'string';
|
||||
inputData = TEST_DATA[type];
|
||||
try {
|
||||
await functionRunner({
|
||||
type,
|
||||
inputData,
|
||||
asError: true,
|
||||
});
|
||||
return Promise.reject(new Error('Function did not reject with error.'));
|
||||
} catch (e) {
|
||||
should.deepEqual(e.details, inputData);
|
||||
e.code.should.equal('cancelled');
|
||||
e.message.should.equal(
|
||||
'Response data was requested to be sent as part of an Error payload, so here we are!'
|
||||
);
|
||||
}
|
||||
|
||||
type = 'boolean';
|
||||
inputData = TEST_DATA[type];
|
||||
try {
|
||||
await functionRunner({
|
||||
type,
|
||||
inputData,
|
||||
asError: true,
|
||||
});
|
||||
return Promise.reject(new Error('Function did not reject with error.'));
|
||||
} catch (e) {
|
||||
should.deepEqual(e.details, inputData);
|
||||
e.code.should.equal('cancelled');
|
||||
e.message.should.equal(
|
||||
'Response data was requested to be sent as part of an Error payload, so here we are!'
|
||||
);
|
||||
}
|
||||
|
||||
type = 'null';
|
||||
inputData = TEST_DATA[type];
|
||||
try {
|
||||
await functionRunner({
|
||||
type,
|
||||
inputData,
|
||||
asError: true,
|
||||
});
|
||||
return Promise.reject(new Error('Function did not reject with error.'));
|
||||
} catch (e) {
|
||||
should.deepEqual(e.details, inputData);
|
||||
e.code.should.equal('cancelled');
|
||||
e.message.should.equal(
|
||||
'Response data was requested to be sent as part of an Error payload, so here we are!'
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
--recursive
|
||||
--timeout 120000
|
||||
--reporter list
|
||||
--slow 600
|
||||
--bail
|
||||
--exit
|
||||
|
||||
1
bridge/firebase.json
Normal file
1
bridge/firebase.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
64
bridge/functions/index.js
Normal file
64
bridge/functions/index.js
Normal file
@@ -0,0 +1,64 @@
|
||||
const assert = require('assert');
|
||||
const functions = require('firebase-functions');
|
||||
|
||||
const TEST_DATA = require('./test-data');
|
||||
|
||||
exports.runTest = functions.https.onCall(data => {
|
||||
console.log(Date.now(), data);
|
||||
|
||||
if (typeof data === 'undefined') {
|
||||
return 'undefined';
|
||||
}
|
||||
|
||||
if (typeof data === 'string') {
|
||||
return 'string';
|
||||
}
|
||||
|
||||
if (typeof data === 'number') {
|
||||
return 'number';
|
||||
}
|
||||
|
||||
if (typeof data === 'boolean') {
|
||||
return 'boolean';
|
||||
}
|
||||
|
||||
if (data === null) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return 'array';
|
||||
}
|
||||
|
||||
const { type, asError, inputData } = data;
|
||||
if (!Object.hasOwnProperty.call(TEST_DATA, type)) {
|
||||
throw new functions.https.HttpsError(
|
||||
'invalid-argument',
|
||||
'Invalid test requested.'
|
||||
);
|
||||
}
|
||||
|
||||
const outputData = TEST_DATA[type];
|
||||
|
||||
try {
|
||||
assert.deepEqual(outputData, inputData);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new functions.https.HttpsError(
|
||||
'invalid-argument',
|
||||
'Input and Output types did not match.',
|
||||
e.message
|
||||
);
|
||||
}
|
||||
|
||||
// all good
|
||||
if (asError) {
|
||||
throw new functions.https.HttpsError(
|
||||
'cancelled',
|
||||
'Response data was requested to be sent as part of an Error payload, so here we are!',
|
||||
outputData
|
||||
);
|
||||
}
|
||||
|
||||
return outputData;
|
||||
});
|
||||
3866
bridge/functions/package-lock.json
generated
Normal file
3866
bridge/functions/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
bridge/functions/package.json
Normal file
16
bridge/functions/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "functions",
|
||||
"description": "Cloud Functions for Firebase",
|
||||
"scripts": {
|
||||
"serve": "firebase serve --only functions",
|
||||
"shell": "firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"deploy": "firebase deploy --only functions",
|
||||
"logs": "firebase functions:log"
|
||||
},
|
||||
"dependencies": {
|
||||
"firebase-admin": "~5.12.0",
|
||||
"firebase-functions": "^1.0.1"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
41
bridge/functions/test-data.js
Normal file
41
bridge/functions/test-data.js
Normal file
@@ -0,0 +1,41 @@
|
||||
module.exports = {
|
||||
number: 1234,
|
||||
string: 'acde',
|
||||
boolean: true,
|
||||
null: null,
|
||||
simpleObject: {
|
||||
number: 1234,
|
||||
string: 'acde',
|
||||
boolean: true,
|
||||
null: null,
|
||||
},
|
||||
simpleArray: [1234, 'acde', true, null],
|
||||
advancedObject: {
|
||||
array: [1234, 'acde', false, null],
|
||||
object: {
|
||||
number: 1234,
|
||||
string: 'acde',
|
||||
boolean: true,
|
||||
null: null,
|
||||
array: [1234, 'acde', true, null],
|
||||
},
|
||||
number: 1234,
|
||||
string: 'acde',
|
||||
boolean: true,
|
||||
null: null,
|
||||
},
|
||||
advancedArray: [
|
||||
1234,
|
||||
'acde',
|
||||
true,
|
||||
null,
|
||||
[1234, 'acde', true, null],
|
||||
{
|
||||
number: 1234,
|
||||
string: 'acde',
|
||||
boolean: true,
|
||||
null: null,
|
||||
array: [1234, 'acde', true, null],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -94,6 +94,9 @@ console.log = (...args) => {
|
||||
};
|
||||
|
||||
global.TestHelpers = {
|
||||
functions: {
|
||||
data: require('./../functions/test-data'),
|
||||
},
|
||||
firestore: require('./firestore'),
|
||||
database: require('./database'),
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ target 'testing' do
|
||||
pod 'Firebase/Core'
|
||||
pod 'Firebase/Crash'
|
||||
pod 'Firebase/Database'
|
||||
pod 'Firebase/Functions'
|
||||
pod 'Firebase/DynamicLinks'
|
||||
pod 'Firebase/Firestore'
|
||||
pod 'Firebase/Invites'
|
||||
|
||||
@@ -29,6 +29,9 @@ PODS:
|
||||
- Firebase/Firestore (4.13.0):
|
||||
- Firebase/Core
|
||||
- FirebaseFirestore (= 0.11.0)
|
||||
- Firebase/Functions (4.13.0):
|
||||
- Firebase/Core
|
||||
- FirebaseFunctions (= 1.0.0)
|
||||
- Firebase/Invites (4.13.0):
|
||||
- Firebase/Core
|
||||
- FirebaseInvites (= 2.0.2)
|
||||
@@ -76,6 +79,10 @@ PODS:
|
||||
- gRPC-ProtoRPC (~> 1.0)
|
||||
- leveldb-library (~> 1.18)
|
||||
- Protobuf (~> 3.5)
|
||||
- FirebaseFunctions (1.0.0):
|
||||
- FirebaseAnalytics (~> 4.1)
|
||||
- FirebaseCore (~> 4.0)
|
||||
- GTMSessionFetcher/Core (~> 1.1)
|
||||
- FirebaseInstanceID (2.0.10):
|
||||
- FirebaseCore (~> 4.0)
|
||||
- FirebaseInvites (2.0.2):
|
||||
@@ -201,7 +208,8 @@ PODS:
|
||||
- React/Core
|
||||
- React/fishhook
|
||||
- React/RCTBlob
|
||||
- RNFirebase (4.0.4):
|
||||
- RNFirebase (4.0.7):
|
||||
- Firebase/Core
|
||||
- React
|
||||
- yoga (0.55.3.React)
|
||||
|
||||
@@ -215,6 +223,7 @@ DEPENDENCIES:
|
||||
- Firebase/Database
|
||||
- Firebase/DynamicLinks
|
||||
- Firebase/Firestore
|
||||
- Firebase/Functions
|
||||
- Firebase/Invites
|
||||
- Firebase/Messaging
|
||||
- Firebase/Performance
|
||||
@@ -248,6 +257,7 @@ SPEC CHECKSUMS:
|
||||
FirebaseDatabase: 5f0bc6134c5c237cf55f9e1249d406770a75eafd
|
||||
FirebaseDynamicLinks: 38b68641d24e78d0277a9205d988ce22875d5a25
|
||||
FirebaseFirestore: e92a096ce80c7b4b905d4e9d41dbd944adc9d2a5
|
||||
FirebaseFunctions: 3745fada03bd706a9b5c0b9ae7b2d490fa594d21
|
||||
FirebaseInstanceID: 8d20d890d65c917f9f7d9950b6e10a760ad34321
|
||||
FirebaseInvites: ae15e0636f9eb42bdf5c1ef4c8f7bd4a88f9878b
|
||||
FirebaseMessaging: 75cdb862e86c30e0913a2ff307e48d49357c5b73
|
||||
@@ -269,9 +279,9 @@ SPEC CHECKSUMS:
|
||||
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
|
||||
Protobuf: 8a9838fba8dae3389230e1b7f8c104aa32389c03
|
||||
React: 573d89cf10312b17920df6328eaf9ab8059283bf
|
||||
RNFirebase: 8d3b25b57e3fb7afa371959147d23d39e70e150a
|
||||
RNFirebase: 05ef1e1c1681af418f3500fda194a2d17d45be07
|
||||
yoga: 9403c2451c1b47d8cee3e4f1b6fd0ececc63839c
|
||||
|
||||
PODFILE CHECKSUM: d75cf458a6205ebf9ad72c6f3bba5d5d338f9e60
|
||||
PODFILE CHECKSUM: 582ceaad051470812ad9203e13b5ea8ad20c78ac
|
||||
|
||||
COCOAPODS: 1.3.1
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
17AF4F6B1F59CDBF00C02336 /* RNFirebaseLinks.m in Sources */ = {isa = PBXBuildFile; fileRef = 17AF4F6A1F59CDBF00C02336 /* RNFirebaseLinks.m */; };
|
||||
27540F9A209F3641001F4AF4 /* RNFirebaseFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 27540F99209F3641001F4AF4 /* RNFirebaseFunctions.m */; };
|
||||
8300A7AE1F31E143001B16AB /* RNFirebaseDatabaseReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8300A7AD1F31E143001B16AB /* RNFirebaseDatabaseReference.m */; };
|
||||
8323CF061F6FBD870071420B /* BannerComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CEFF1F6FBD870071420B /* BannerComponent.m */; };
|
||||
8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF011F6FBD870071420B /* NativeExpressComponent.m */; };
|
||||
@@ -51,6 +52,8 @@
|
||||
134814201AA4EA6300B7C361 /* libRNFirebase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFirebase.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
17AF4F691F59CDBF00C02336 /* RNFirebaseLinks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseLinks.h; sourceTree = "<group>"; };
|
||||
17AF4F6A1F59CDBF00C02336 /* RNFirebaseLinks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseLinks.m; sourceTree = "<group>"; };
|
||||
27540F98209F361B001F4AF4 /* RNFirebaseFunctions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFunctions.h; sourceTree = "<group>"; };
|
||||
27540F99209F3641001F4AF4 /* RNFirebaseFunctions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFunctions.m; sourceTree = "<group>"; };
|
||||
8300A7AC1F31E143001B16AB /* RNFirebaseDatabaseReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseDatabaseReference.h; sourceTree = "<group>"; };
|
||||
8300A7AD1F31E143001B16AB /* RNFirebaseDatabaseReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseDatabaseReference.m; sourceTree = "<group>"; };
|
||||
8323CEFE1F6FBD870071420B /* BannerComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BannerComponent.h; sourceTree = "<group>"; };
|
||||
@@ -133,9 +136,20 @@
|
||||
path = RNFirebase/links;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
27540F97209F35DF001F4AF4 /* functions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
27540F98209F361B001F4AF4 /* RNFirebaseFunctions.h */,
|
||||
27540F99209F3641001F4AF4 /* RNFirebaseFunctions.m */,
|
||||
);
|
||||
name = functions;
|
||||
path = RNFirebase/functions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
27540F97209F35DF001F4AF4 /* functions */,
|
||||
83AAA0762063DEC2007EC5F7 /* invites */,
|
||||
838E372420231E15004DCD3A /* notifications */,
|
||||
838E372020231DF0004DCD3A /* instanceid */,
|
||||
@@ -381,6 +395,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
27540F9A209F3641001F4AF4 /* RNFirebaseFunctions.m in Sources */,
|
||||
838E372320231DF0004DCD3A /* RNFirebaseInstanceId.m in Sources */,
|
||||
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */,
|
||||
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */,
|
||||
|
||||
20
ios/RNFirebase/functions/RNFirebaseFunctions.h
Normal file
20
ios/RNFirebase/functions/RNFirebaseFunctions.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef RNFirebaseFunctions_h
|
||||
#define RNFirebaseFunctions_h
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if __has_include(<FirebaseFunctions/FIRFunctions.h>)
|
||||
#import <React/RCTBridgeModule.h>
|
||||
|
||||
@interface RNFirebaseFunctions : NSObject <RCTBridgeModule> {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
@interface RNFirebaseFunctions : NSObject
|
||||
@end
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
114
ios/RNFirebase/functions/RNFirebaseFunctions.m
Normal file
114
ios/RNFirebase/functions/RNFirebaseFunctions.m
Normal file
@@ -0,0 +1,114 @@
|
||||
#import "RNFirebaseFunctions.h"
|
||||
|
||||
#if __has_include(<FirebaseFunctions/FIRFunctions.h>)
|
||||
#import <FirebaseFunctions/FIRFunctions.h>
|
||||
#import <FirebaseFunctions/FIRHTTPSCallable.h>
|
||||
#import <FirebaseFunctions/FIRError.h>
|
||||
|
||||
@implementation RNFirebaseFunctions
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
RCT_EXPORT_METHOD(httpsCallable:
|
||||
(NSString *) name
|
||||
wrapper:
|
||||
(NSDictionary *) wrapper
|
||||
resolver:
|
||||
(RCTPromiseResolveBlock) resolve
|
||||
rejecter:
|
||||
(RCTPromiseRejectBlock) reject
|
||||
) {
|
||||
FIRFunctions *functions = [FIRFunctions functions];
|
||||
|
||||
[[functions HTTPSCallableWithName:name] callWithObject:[wrapper valueForKey:@"data"] completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
NSObject *details = [NSNull null];
|
||||
NSString *message = error.localizedDescription;
|
||||
if (error.domain == FIRFunctionsErrorDomain) {
|
||||
details = error.userInfo[FIRFunctionsErrorDetailsKey];
|
||||
if (details == nil) {
|
||||
details = [NSNull null];
|
||||
}
|
||||
}
|
||||
|
||||
resolve(@{
|
||||
@"__error": @true,
|
||||
@"code": [self getErrorCodeName:error],
|
||||
@"message": message,
|
||||
@"details": details
|
||||
});
|
||||
} else {
|
||||
resolve(@{ @"data": [result data] });
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (NSString *)getErrorCodeName:(NSError *)error {
|
||||
NSString *code = @"UNKNOWN";
|
||||
switch (error.code) {
|
||||
case FIRFunctionsErrorCodeOK:
|
||||
code = @"OK";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeCancelled:
|
||||
code = @"CANCELLED";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeUnknown:
|
||||
code = @"UNKNOWN";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeInvalidArgument:
|
||||
code = @"INVALID_ARGUMENT";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeDeadlineExceeded:
|
||||
code = @"DEADLINE_EXCEEDED";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeNotFound:
|
||||
code = @"NOT_FOUND";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeAlreadyExists:
|
||||
code = @"ALREADY_EXISTS";
|
||||
break;
|
||||
case FIRFunctionsErrorCodePermissionDenied:
|
||||
code = @"PERMISSION_DENIED";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeResourceExhausted:
|
||||
code = @"RESOURCE_EXHAUSTED";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeFailedPrecondition:
|
||||
code = @"FAILED_PRECONDITION";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeAborted:
|
||||
code = @"ABORTED";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeOutOfRange:
|
||||
code = @"OUT_OF_RANGE";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeUnimplemented:
|
||||
code = @"UNIMPLEMENTED";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeInternal:
|
||||
code = @"INTERNAL";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeUnavailable:
|
||||
code = @"UNAVAILABLE";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeDataLoss:
|
||||
code = @"DATA_LOSS";
|
||||
break;
|
||||
case FIRFunctionsErrorCodeUnauthenticated:
|
||||
code = @"UNAUTHENTICATED";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
@implementation RNFirebaseFunctions
|
||||
@end
|
||||
#endif
|
||||
|
||||
@@ -16,6 +16,7 @@ import Crash, { NAMESPACE as CrashNamespace } from '../crash';
|
||||
import Crashlytics, { NAMESPACE as CrashlyticsNamespace } from '../crashlytics';
|
||||
import Database, { NAMESPACE as DatabaseNamespace } from '../database';
|
||||
import Firestore, { NAMESPACE as FirestoreNamespace } from '../firestore';
|
||||
import Functions, { NAMESPACE as FunctionsNamespace } from '../functions';
|
||||
import InstanceId, { NAMESPACE as InstanceIdNamespace } from '../iid';
|
||||
import Invites, { NAMESPACE as InvitesNamespace } from '../invites';
|
||||
import Links, { NAMESPACE as LinksNamespace } from '../links';
|
||||
@@ -45,6 +46,7 @@ export default class App {
|
||||
crashlytics: () => Crashlytics;
|
||||
database: () => Database;
|
||||
firestore: () => Firestore;
|
||||
functions: () => Functions;
|
||||
iid: () => InstanceId;
|
||||
invites: () => Invites;
|
||||
links: () => Links;
|
||||
@@ -85,6 +87,7 @@ export default class App {
|
||||
this.crashlytics = APPS.appModule(this, CrashlyticsNamespace, Crashlytics);
|
||||
this.database = APPS.appModule(this, DatabaseNamespace, Database);
|
||||
this.firestore = APPS.appModule(this, FirestoreNamespace, Firestore);
|
||||
this.functions = APPS.appModule(this, FunctionsNamespace, Functions);
|
||||
this.iid = APPS.appModule(this, InstanceIdNamespace, InstanceId);
|
||||
this.invites = APPS.appModule(this, InvitesNamespace, Invites);
|
||||
this.links = APPS.appModule(this, LinksNamespace, Links);
|
||||
|
||||
@@ -38,6 +38,10 @@ import {
|
||||
statics as FirestoreStatics,
|
||||
MODULE_NAME as FirestoreModuleName,
|
||||
} from '../firestore';
|
||||
import {
|
||||
statics as FunctionsStatics,
|
||||
MODULE_NAME as FunctionsModuleName,
|
||||
} from '../functions';
|
||||
import {
|
||||
statics as InstanceIdStatics,
|
||||
MODULE_NAME as InstanceIdModuleName,
|
||||
@@ -81,6 +85,7 @@ import type {
|
||||
DatabaseModule,
|
||||
FirebaseOptions,
|
||||
FirestoreModule,
|
||||
FunctionsModule,
|
||||
InstanceIdModule,
|
||||
InvitesModule,
|
||||
LinksModule,
|
||||
@@ -102,6 +107,7 @@ class Firebase {
|
||||
crashlytics: CrashlyticsModule;
|
||||
database: DatabaseModule;
|
||||
firestore: FirestoreModule;
|
||||
functions: FunctionsModule;
|
||||
iid: InstanceIdModule;
|
||||
invites: InvitesModule;
|
||||
links: LinksModule;
|
||||
@@ -146,6 +152,11 @@ class Firebase {
|
||||
FirestoreStatics,
|
||||
FirestoreModuleName
|
||||
);
|
||||
this.functions = APPS.moduleAndStatics(
|
||||
'functions',
|
||||
FunctionsStatics,
|
||||
FunctionsModuleName
|
||||
);
|
||||
this.iid = APPS.moduleAndStatics(
|
||||
'iid',
|
||||
InstanceIdStatics,
|
||||
|
||||
11
lib/modules/functions/HttpsError.js
Normal file
11
lib/modules/functions/HttpsError.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { FunctionsErrorCode } from './types.flow';
|
||||
|
||||
export default class HttpsError extends Error {
|
||||
+details: ?any;
|
||||
+code: FunctionsErrorCode;
|
||||
constructor(code: FunctionsErrorCode, message?: string, details?: any) {
|
||||
super(message);
|
||||
this.details = details;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
89
lib/modules/functions/index.js
Normal file
89
lib/modules/functions/index.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @flow
|
||||
* Functions representation wrapper
|
||||
*/
|
||||
import ModuleBase from '../../utils/ModuleBase';
|
||||
import { isObject } from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
import type App from '../core/app';
|
||||
import HttpsError from './HttpsError';
|
||||
|
||||
import type {
|
||||
HttpsCallable,
|
||||
HttpsErrorCode,
|
||||
HttpsCallablePromise,
|
||||
} from './types.flow';
|
||||
|
||||
export const NAMESPACE = 'functions';
|
||||
export const MODULE_NAME = 'RNFirebaseFunctions';
|
||||
|
||||
export default class Functions extends ModuleBase {
|
||||
constructor(app: App) {
|
||||
super(app, {
|
||||
multiApp: false,
|
||||
hasShards: false,
|
||||
namespace: NAMESPACE,
|
||||
moduleName: MODULE_NAME,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------
|
||||
* PUBLIC API
|
||||
* -------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a reference to the callable https trigger with the given name.
|
||||
* @param name The name of the trigger.
|
||||
*/
|
||||
httpsCallable(name: string): HttpsCallable {
|
||||
return (data?: any): HttpsCallablePromise => {
|
||||
const promise = getNativeModule(this).httpsCallable(name, { data });
|
||||
return promise.then(this._errorOrResult);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* -------------
|
||||
* INTERNALS
|
||||
* -------------
|
||||
*/
|
||||
_errorOrResult(possibleError): HttpsCallablePromise {
|
||||
if (isObject(possibleError) && possibleError.__error) {
|
||||
const { code, message, details } = possibleError;
|
||||
return Promise.reject(
|
||||
new HttpsError(
|
||||
statics.HttpsErrorCode[code] || statics.HttpsErrorCode.UNKNOWN,
|
||||
message,
|
||||
details
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve(possibleError);
|
||||
}
|
||||
}
|
||||
|
||||
export const statics: { HttpsErrorCode: HttpsErrorCode } = {
|
||||
HttpsErrorCode: {
|
||||
OK: 'ok',
|
||||
CANCELLED: 'cancelled',
|
||||
UNKNOWN: 'unknown',
|
||||
INVALID_ARGUMENT: 'invalid-argument',
|
||||
DEADLINE_EXCEEDED: 'deadline-exceeded',
|
||||
NOT_FOUND: 'not-found',
|
||||
ALREADY_EXISTS: 'already-exists',
|
||||
PERMISSION_DENIED: 'permission-denied',
|
||||
UNAUTHENTICATED: 'unauthenticated',
|
||||
RESOURCE_EXHAUSTED: 'resource-exhausted',
|
||||
FAILED_PRECONDITION: 'failed-precondition',
|
||||
ABORTED: 'aborted',
|
||||
OUT_OF_RANGE: 'out-of-range',
|
||||
UNIMPLEMENTED: 'unimplemented',
|
||||
INTERNAL: 'internal',
|
||||
UNAVAILABLE: 'unavailable',
|
||||
DATA_LOSS: 'data-loss',
|
||||
},
|
||||
};
|
||||
30
lib/modules/functions/types.flow.js
Normal file
30
lib/modules/functions/types.flow.js
Normal file
@@ -0,0 +1,30 @@
|
||||
export type HttpsCallableResult = {
|
||||
data: Object,
|
||||
};
|
||||
|
||||
export type FunctionsErrorCode =
|
||||
| 'ok'
|
||||
| 'cancelled'
|
||||
| 'unknown'
|
||||
| 'invalid-argument'
|
||||
| 'deadline-exceeded'
|
||||
| 'not-found'
|
||||
| 'already-exists'
|
||||
| 'permission-denied'
|
||||
| 'resource-exhausted'
|
||||
| 'failed-precondition'
|
||||
| 'aborted'
|
||||
| 'out-of-range'
|
||||
| 'unimplemented'
|
||||
| 'internal'
|
||||
| 'unavailable'
|
||||
| 'data-loss'
|
||||
| 'unauthenticated';
|
||||
|
||||
export type HttpsCallablePromise =
|
||||
| Promise<HttpsCallableResult>
|
||||
| Promise<HttpsError>;
|
||||
|
||||
export type HttpsCallable = (data?: any) => HttpsCallablePromise;
|
||||
|
||||
export type HttpsErrorCode = { [name: string]: FunctionsErrorCode };
|
||||
@@ -15,6 +15,8 @@ import type Database from '../modules/database';
|
||||
import { typeof statics as DatabaseStatics } from '../modules/database';
|
||||
import type Firestore from '../modules/firestore';
|
||||
import { typeof statics as FirestoreStatics } from '../modules/firestore';
|
||||
import type Functions from '../modules/functions';
|
||||
import { typeof statics as FunctionsStatics } from '../modules/functions';
|
||||
import type InstanceId from '../modules/iid';
|
||||
import { typeof statics as InstanceIdStatics } from '../modules/iid';
|
||||
import type Invites from '../modules/invites';
|
||||
@@ -63,6 +65,7 @@ export type FirebaseModuleName =
|
||||
| 'RNFirebaseCrashlytics'
|
||||
| 'RNFirebaseDatabase'
|
||||
| 'RNFirebaseFirestore'
|
||||
| 'RNFirebaseFunctions'
|
||||
| 'RNFirebaseInstanceId'
|
||||
| 'RNFirebaseInvites'
|
||||
| 'RNFirebaseLinks'
|
||||
@@ -81,6 +84,7 @@ export type FirebaseNamespace =
|
||||
| 'crashlytics'
|
||||
| 'database'
|
||||
| 'firestore'
|
||||
| 'functions'
|
||||
| 'iid'
|
||||
| 'invites'
|
||||
| 'links'
|
||||
@@ -171,6 +175,13 @@ export type FirestoreModule = {
|
||||
nativeModuleExists: boolean,
|
||||
} & FirestoreStatics;
|
||||
|
||||
/* Functions types */
|
||||
|
||||
export type FunctionsModule = {
|
||||
(): Functions,
|
||||
nativeModuleExists: boolean,
|
||||
} & FunctionsStatics;
|
||||
|
||||
/* InstanceId types */
|
||||
|
||||
export type InstanceIdModule = {
|
||||
|
||||
@@ -107,7 +107,7 @@ export default {
|
||||
const _name = (name || DEFAULT_APP_NAME).toUpperCase();
|
||||
|
||||
// return an existing app if found
|
||||
// todo in v4 remove deprecation and throw an error
|
||||
// TODO in v5 remove deprecation and throw an error
|
||||
if (APPS[_name]) {
|
||||
console.warn(INTERNALS.STRINGS.WARN_INITIALIZE_DEPRECATION);
|
||||
return APPS[_name];
|
||||
|
||||
Reference in New Issue
Block a user