В предыдущей части мы увидели, как написать приложение NodeJS с помощью Kotlin. Теперь давайте применим эти знания для создания наших первых облачных функций с помощью Kotlin 👌
Создать проект Firebase Cloud Functions
Во-первых, рекомендую вам ознакомиться с документацией по firebase.
Возобновлено двумя простыми командами:
npm install -g firebase-tools firebase init
Переход на Котлин
У нас есть проект с папкой functions
, в которой содержится код, выполняемый Firebase Cloud Functions. Добавим поддержку Kotlin.
Создайте build.gradle
в корне проекта
buildscript { ext.kotlin_version = '1.2.30' repositories { mavenCentral() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } apply plugin: 'kotlin2js' repositories { mavenCentral() } dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-test-js:$kotlin_version" } compileKotlin2Js.kotlinOptions { moduleKind = "commonjs" outputFile = "functions/index.js" sourceMap = true }
Примечание: outputFile
должен указывать на functions/index.js
Инициализация оболочки Gradle:
gradle wrapper
Эта команда создает сценарий gradlew
с папкой gradle
, которая содержит оболочку gradle, чтобы избежать конфликтов.
Создайте исходную папку в корне проекта.
src/main/kotlin
Используйте обертки
Я начинаю с создания набора оболочек для ExpressJS и Firebase. Я использую документацию и определение файлов TypeScript. Эти обертки стараются быть максимально точными.
Оболочки ExpressJS доступны на моем гитхабе здесь.
Оболочки Firebase доступны на моем гитхабе здесь.
Скопируйте все обертки в src/main/kotlin
, мы будем использовать их для создания нашей первой облачной функции.
Первая облачная функция
Давайте создадим бэкэнд ToDo. В приложении ToDo будет 3 API:
PUT /v1/task
: Создать новую задачуGET /v1/task/{id}?
: получить задачу или список задачDELETE /v1/task/{id}
: Удалить задачу
Создать Index.kt
файл в src/main/kotlin
external val exports: dynamic fun main(args: Array<String>) { val app = Express() Admin.initializeApp(Functions.config().firebase) val db = Admin.firestore() // PUT method here // GET method here // DELETE method here exports.v1 = Functions.https.onRequest(app) } data class Task(val id: String? = undefined, val label: String, val time: Double) : DocumentData
Используя оболочки, мы создали приложение ExpressJS и объявили его облачным функциям. У нас есть только один динамический тип для экспорта. Весь код строго проверяется компилятором Kotlin 💪
Создать новую задачу
app.put("/task", { req, res -> val input = req.body.unsafeCast<TaskInput>() val inputTask = Task(label = input.label, time = Date().getTime()) // Hack because firestore checks the object prototype and KotlinJS has his own db.collection("task").add(jsObject { label = inputTask.label time = inputTask.time }).then({ ref -> res.status(201).json(Task(ref.id, inputTodo.label, inputTodo.time)) }).catch({ error -> res.status(500).json(error) }) })
Чтобы добавить новую задачу, мы создали запрос на размещение. В теле req
содержится json, уже проанализированный Firebase. Мы небезопасно помещаем тело в класс данных Kotlin. Параметры доступны через TaskInput
class. Создаем новый документ в коллекции задача. В случае успеха мы возвращаем статус 201 (СОЗДАНО) с содержимым задачи как json.
data class TaskInput(val label: String? = null) inline fun jsObject(init: dynamic.() -> Unit): dynamic { val o = js("{}") init(o) return o }
jsObject
- это хитрость для динамического создания объекта JavaScript. Firebase требует простой объект JavaScript (проверьте прототип), но Kotlin создает объекты с конкретным прототипом…
Получите одну или несколько задач
app.get("/task/:id?", { req, res -> val params = req.params.unsafeCast<Params>() if (params.id != undefined) { db.collection("task").doc(params.id).get().then({ doc -> if (!doc.exists) { res.status(404).json(Message("Task not found!")) } else { val data = doc.data().unsafeCast<Task>() res.status(200).json(Task(doc.id, data.label, data.time)) } }).catch({ err -> res.status(500).json(err) }) } else { db.collection("task").get().then({ snapshot -> val result = snapshot.docs.toList().map { Task(it.id, it.data()!!["label"] as String, it.data()!!["time"] as Double) } res.status(200).json(result) }).catch({ err -> res.status(500).json(err) }) } })
Мы создали метод get
с необязательным параметром id
. Если указан id
, мы получим связанный документ. В противном случае мы получили бы все документы. Как видите, мы используем .toList().map{}
, специфичный для Kotlin, и он отлично работает 👍
data class Params(val id: String? = undefined) data class Message(val msg: String)
Удалить задачу
app.delete("/task/:id", { req, res -> val params = req.params.unsafeCast<Params>() db.collection("task").doc(params.id).delete().then({ res.status(200).json(Message("Task has been deleted")) }) })
Мы создали метод удаления, который удаляет документ с предоставленным идентификатором. Мы не проверяли, было ли удаление успешным, здесь (вы можете улучшить его, если хотите 😉).
Попробуй это
Последний шаг, но не в последнюю очередь! Мы хотим протестировать наши новые облачные функции, и хорошие новости заключаются в том, что это очень просто!
Сгенерируйте код JavaScript:
./gradlew build
Запускаем локальный сервер (из папки functions
)
npm run serve
И протестируйте любые методы, используя одну из этих примеров команд curl:
Создать новую задачу
curl -X PUT \ http://localhost:5000/todomedium/us-central1/v1/task \ -d 'label=New%20task!'
Получить список задач
curl -X GET \ http://localhost:5000/todomedium/us-central1/v1/task
Получите задание с TASK_ID
curl -X GET \ http://localhost:5000/todomedium/us-central1/v1/task/TASK_ID
Удалить задачу с TASK_ID
curl -X DELETE \ http://localhost:5000/todomedium/us-central1/v1/task/TASK_ID
Исходный код доступен в этом проекте на github.
и вуаля! теперь вы можете создавать красивые облачные функции с помощью Kotlin 🎉
Бонус: Kotlinify it!
Надеюсь, вам понравилось, и вы можете поделиться им со своими друзьями и коллегами 😉
… А если есть вопросы, задавайте их в комментариях!