Create Compatible Cloud Functions for Firebase With Python (Callable from Cloud Functions for Firebase client SDK)

April 26, 2019
using Google Cloud Functions

Yes, the terminology is very confusing. Refer to Google Cloud Functions vs Cloud Functions for Firebase (and Cloud Functions for Firebase client SDK).

Cloud Functions for Firebase client SDK is a wrapper client to help to call Cloud Functions for Firebase from iOS/Android/Web. Of course you can write your own http call or use Retrofit, but the client SDK offer convinience plus a few firebase integration like Firebase Authentication.

Officially, Cloud Functions for Firebase can only be coded with JavaScript or TypeScript.

This article will guide you on making Google Cloud Functions compatible with Cloud Functions for Firebase, and making it possible to call Google Cloud Functions using Cloud Functions for Firebase client SDK.

We shall write a cloud function which

  • Firebase Authentication
  • Access a json parameter
  • Return a json response

Code explanation

from flask import jsonify, abort
from firebase_admin import auth
from functools import wraps

def json_abort(status_code, message):
    data = {
        'error': {
            'code': status_code,
            'message': message
        }
    }
    response = jsonify(data)
    response.status_code = status_code
    abort(response)


def firebase_auth_required(f):
    @wraps(f)
    def wrapper(request):
        authorization = request.headers.get('Authorization')
        id_token = None
        if authorization and authorization.startswith('Bearer '):
            id_token = authorization.split('Bearer ')[1]
        else:
            json_abort(401, message="Invalid authorization")

        try:
            decoded_token = auth.verify_id_token(id_token)
        except Exception as e: # ValueError or auth.AuthError
            json_abort(401, message="Invalid authorization")
        return f(request, decoded_token)
    return wrapper

@firebase_auth_required
def test_firebase_function(request, decoded_token = None):
    data = request.json.get('data')
    if 'name' not in data:
        json_abort(400, message="Missing name")
    name = data['name']

    return jsonify({
        'data': {
            'name': name
        }
    })

Deploy test_firebase_function cloud functions.

gcloud functions deploy test_firebase_function --runtime python37 --trigger-http --project [PROJECT_ID]

Test call Cloud Functions from Android

Include dependencies.

dependencies {
    implementation 'com.google.firebase:firebase-functions:16.3.0'
}

NOTE: There might be some firebase prerequisites.

Code

fun getFirebaseFunction(): Task<String> {
    val functions = FirebaseFunctions.getInstance()

    val data = mapOf(
        "name" to "Desmond"
    )

    return functions.getHttpsCallable("test_firebase_function")
        .call(data)
        .continueWith { task ->
            val result = task.result?.data as HashMap<*, *>
            result["name"] as String
        }
}
getFirebaseFunction().addOnCompleteListener { task ->
    if (task.isSuccessful) {
        val name = task.result
        Timber.d("name=$name")
    }
    else {
        Timber.e(task.exception)
    }
}

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.