1

I followed some guides and wrote a cloud function. Now I need to call it from within my app. I did the following:

Kotlin code:

class ActivitySignup : AppCompatActivity() {

    private lateinit var functions: FirebaseFunctions
    private lateinit var user: String

    override fun onCreate(savedInstanceState: Bundle?) {
    ...
    functions = Firebase.functions
    ...
    submitbutton.setOnClickListener() {
            Log.e(tag,"Clicked submit")
            userEditTxt = findViewById(R.id.et_user)
            user = userEditTxt.text.toString().trim()

         auth.createUserWithEmailAndPassword(email, password)
            .addOnCompleteListener(this) { task ->
                if (task.isSuccessful) {
                    // Sign in success, update UI with the signed-in user's information
                    Log.i(tag, "User created")

                    functions.getHttpsCallable("addUser")
                    .call(user)
                    .continueWith { task ->
                        // This continuation runs on either success or failure, but if the task
                        // has failed then result will throw an Exception which will be
                        // propagated down.
                        val result = task.result?.data as String
                        Log.e("result", result)
                        result
                    }

                    val intent = Intent(this, ActivityGroups::class.java)
                    startActivity(intent)
                    finish()

                } else {
                    // If sign in fails, display a message to the user.
                    Log.w(tag, "failure", task.exception)
                    Toast.makeText(baseContext, "Authentication failed.",
                        Toast.LENGTH_SHORT).show()
                }

        }

    }

The cloud function in index.ts:

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();

exports.addUser = functions.https.onCall((data, context) => {
  console.log('addUser: ', data.username);

  const username = data.username
  functions.auth.user().onCreate(user => {
    const doc = admin.firestore().collection('users').doc();
    return doc.set({
      createDate: admin.firestore.FieldValue.serverTimestamp(),
      modifiedDate: admin.firestore.FieldValue.serverTimestamp(), 
      username: username,
      email: user.email,    
      stat: 1, //0 = banned, 1 = normal
      uid: user.uid,
      rowpointer: doc.id,
    });
  });

However, there are 2 problems:

  1. Android studio is highlighting the "functions" part of Firebase.functions in red. The error is Unresolved reference: functions

  2. When I did firebase serve in Visual Studio, I got the following:

  • functions[addUser]: http function initialized (http://localhost:5000/APPNAME-cf4da/us-central1/addUser).

i functions: Beginning execution of "addUser"

{"severity":"WARNING","message":"Request has invalid method. GET"}

{"severity":"ERROR","message":"Invalid request, unable to process."}

i functions: Finished "addUser" in ~1s

I'm pretty new to Android dev/cloud functions, so I feel like I'm just making a rookie mistake somewhere...

whatwhatwhat
  • 1,991
  • 4
  • 31
  • 50
  • I don't see any call to the Callable function in the Android code you shared. Calls look like the examples in the documenttion here: https://firebase.google.com/docs/functions/callable#call_the_function Are you sure this is all that is needed to reproduce the error? – Frank van Puffelen Feb 01 '21 at 18:07
  • @FrankvanPuffelen sorry about that! I added where the callable will be in the Kotlin code. And I've been told in the past that I put too much code in my questions...so I tried to put only the important things in my question. Anything else is standard to a Kotlin app? – whatwhatwhat Feb 01 '21 at 18:43
  • @FrankvanPuffelen that documentation you linked is exactly what I followed to get me to this point. It's this part that's giving me the problem: https://firebase.google.com/docs/functions/callable#initialize_the_client_sdk – whatwhatwhat Feb 01 '21 at 18:44
  • Thanks for the Android code that calls the Function. What is `.call(user)`? – Frank van Puffelen Feb 01 '21 at 18:54
  • @FrankvanPuffelen from the docs, it's just `.call(data)`, which is any data that is relevant to the function. In my case, all I need is the username. – whatwhatwhat Feb 01 '21 at 19:15
  • I understood that. But we need to see what you are passing in, and you're not showing how `user` is initialized. – Frank van Puffelen Feb 01 '21 at 19:17
  • 1
    Fixed! Sorry about that. – whatwhatwhat Feb 01 '21 at 19:28

1 Answers1

1

This nesting makes no sense:

exports.addUser = functions.https.onCall((data, context) => {
  console.log('addUser: ', data.username);

  const username = data.username
  functions.auth.user().onCreate(user => {
    const doc = admin.firestore().collection('users').doc();
    return doc.set({
      createDate: admin.firestore.FieldValue.serverTimestamp(),
      modifiedDate: admin.firestore.FieldValue.serverTimestamp(), 
      username: username,
      email: user.email,    
      stat: 1, //0 = banned, 1 = normal
      uid: user.uid,
      rowpointer: doc.id,
    });
  });

You seem to be trying to register a functions.auth.user().onCreate function inside a functions.https.onCall function, which is not possible. All Cloud Functions need to be top-level exports of your index.js file.

My best guess is that you want to pass information about the just created user from your Android code to the Cloud Function, in which case that should be in the datsa parameter that is passed to onCall(data, context). If you actually "just" want to know the current user, you can also get that from context.auth as shown in documentation on writing and deploying a callable Cloud Function.

This is probably closer, although there may have been more problems in your original code:

exports.addUser = functions.https.onCall((data, context) => {
  console.log('addUser: ', data.username);
  const username = data.username;
  const email = data.email;
  const uid = context.auth.uid; //  get uid from context
  const doc = admin.firestore().collection('users').doc(uid); //  use uid as document ID
  return doc.set({
      createDate: admin.firestore.FieldValue.serverTimestamp(),
      modifiedDate: admin.firestore.FieldValue.serverTimestamp(), 
      username: username,
      email: email,    
      stat: 1, //0 = banned, 1 = normal
      uid: uid
  });
});
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • This is giving an error pointing to `context.auth.uid`. The error is `Object is possibly 'undefined'`. – whatwhatwhat Feb 01 '21 at 20:03
  • 1
    That could indeed be, as I mostly focused on trying to show your an outline of what to do. Treat this as pseudo-code showing you some key points of why your code doen't worjk. I recommend you search for more information about the error message, which is not Firebase-specific. – Frank van Puffelen Feb 01 '21 at 21:27