MalikDe
MalikDe

Reputation: 157

Kotlin and Firebase cloud functions update() method

I am using Kotlin to generate Javascript code for Firebase cloud function.

I would like to call update method, passing some values as arguments. In Kotlin, I have to pass a Map as an argument of update():

val map = mutableMapOf<String,Int>() //also tried with a Hashmap: same result

//generate some values
for(i in 0 until 3){ 
    map.put("key$i", i)
}
console.log(map.keys.toList().toString()) //log map keys
console.log(map.values.toList().toString()) //log map values

ref.child("values").update(map)

Kotlin generates that javascript code :

var LinkedHashMap_init = Kotlin.kotlin.collections.LinkedHashMap_init_q3lmfv$;
function main$lambda(event){
    var map = LinkedHashMap_init();
    for (var i = 0; i < 3; i++) {
      map.put_xwzc9p$('key' + i, i);
    }
    console.log(toList(map.keys).toString());
    console.log(toList(map.values).toString());
    ref.child('values').update(map);
}

In my Firebase console, here is the result of the 2 logs. It seems to indicate that the map is correct:

[key0, key1, key2]
[0, 1, 2]

But update() is not working: functions logs display that message:

Error: Reference.update failed: First argument  contains an invalid key (this$AbstractMutableMap) in property 'games.test.values._keys_qe2m0n$_0'.  Keys must be non-empty strings and can't contain ".", "#", "$", "/", "[", or "]"
    at /user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/cjs/src/core/util/validation.js:139:27
    at Object.exports.forEach (/user_code/node_modules/firebase-admin/node_modules/@firebase/util/dist/cjs/src/obj.js:37:13)
    at Object.exports.validateFirebaseData (/user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/cjs/src/core/util/validation.js:132:16)
    at /user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/cjs/src/core/util/validation.js:223:17
    at Object.exports.forEach (/user_code/node_modules/firebase-admin/node_modules/@firebase/util/dist/cjs/src/obj.js:37:13)
    at Object.exports.validateFirebaseMergeDataArg (/user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/cjs/src/core/util/validation.js:221:12)
    at Reference.update (/user_code/node_modules/firebase-admin/node_modules/@firebase/database/dist/cjs/src/api/Reference.js:140:22)
    at main$lambda (/user_code/index.js:87:25)
    at Object.<anonymous> (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:59:27)
    at next (native)

But finally, if I manually write js code into the function, it works:

var map = {}
for (var i = 0; i < 3; i++) {
    map["key"+i] = i
}
ref.child("values").update(map)

Regards.

Upvotes: 2

Views: 718

Answers (2)

MalikDe
MalikDe

Reputation: 157

Thanks to Doug's answer, I wrote that little snippet to wrap a simple key/value javascript object to a Kotlin typed object:

class JsWrapperMap<K, V> {
    private val map: dynamic = js("({})")

    operator fun get(key: K): V = map[key]
    operator fun set(key: K, value: V) {
        map[key] = value
    }

    fun toJs(): dynamic = map
}

/**
 * Returns an empty new [JsWrapperMap]
 */
fun <K, V> jsMapOf(): JsWrapperMap<K, V> = JsWrapperMap()

/**
 * Returns an empty new [JsWrapperMap] of type <Any, Any>
 */
fun jsMapOfAny(): JsWrapperMap<Any, Any> = JsWrapperMap()

It can be used like this :

val map = jsMapOf<String, Int>()
map["key_0"] = 12
map["key_1"] = 36
ref.child("values").update(map.toJs()) 

I am open to any suggestions on that code :)

Upvotes: 4

Doug Stevenson
Doug Stevenson

Reputation: 317477

Kotlin is apparently making a LinkedHashMap from your call to mutableMapOf.

update() takes a JavaScript object whose ordinary, simple properties should be used to update the document.

I'm fairly certain that the implementation of LinkedHashMap in JavaScript has a lot of object properties that are going to violate the stated constraint, as you can see from the error message.

You can't simply pass any kind of object you want to update(). You need to pass an object that contains only the keys and values you want to update(). The method simply isn't expecting something complicated like a LinkedHashMap. Instead, you'll need to write something that compiles down to a simple JavaScript object.

Maybe this would be helpful to read.

Upvotes: 5

Related Questions