3

問題

Ktor チュートリアルに従ってKtor Kotlin アプリケーションを AppEngine にデプロイすると、Firestore サーバー認証が機能しないため、指定された Firestore データベースにデータが書き込まれません。

アプリが IntelliJ IDE で直接実行されている場合と、コマンドを介してktorの実装で実行されている場合の両方で、データは期待どおりに Firestore に書き込まれgradle appengineRunます。

ステージング環境と本番環境の両方に、AppEngine/Firebase プロジェクトの 2 つのセットがあります。コマンドを使用してデプロイする前に、gradle appengineDeployコマンドを使用して正しい SDK 構成がアクティブ化および検証されているgcloud config configurations list.

ここに画像の説明を入力

奇妙な点は、これらの戦略でデプロイされたいくつかのアプリが Firestore に書き込みを行いましたが、アプリを再度デプロイすると、Firestore は新しいデータが書き込まれていることを示しませんでした。

実装

Ktor セットアップ

標準のktorに必要なファイルがあります。古い実装からの古いMANIFEST.MFファイルもあります。それが問題を引き起こしている可能性はありますか?

src/main/resources/application.conf

ktor {
  application {
  modules = [ Initialization.main ]
  }
}

src/main/resources/webapp/WEB-INF/

appengine-web.xml

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    <threadsafe>true</threadsafe>
    <runtime>java8</runtime>
</appengine-web-app>

web.xml

<?xml version="1.0" encoding="ISO-8859-1" ?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<servlet>
    <display-name>KtorServlet</display-name>
    <servlet-name>KtorServlet</servlet-name>
    <servlet-class>io.ktor.server.servlet.ServletApplicationEngine</servlet-class>
    <!-- path to application.conf file, required -->
    <init-param>
        <param-name>io.ktor.config</param-name>
        <param-value>application.conf</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>KtorServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

logging.properties

.level = INFO

src/main/META-INF/MANIFEST>MF

Manifest-Version: 1.0
Main-Class: Initialization

依存関係

#1-3で概説する認証戦略では、Firebase Admin ライブラリが使用されます。compile 'com.google.firebase:firebase-admin:6.5.0'

認証戦略#4では、Google Cloud Firestore ライブラリが使用されます。compile 'com.google.cloud:google-cloud-firestore:0.58.0-beta'

build.gradle

group 'coinverse'
version '1.0-SNAPSHOT'

buildscript {
    ext.kotlin_version = '1.2.61'
    ext.junitJupiterVersion  = '5.0.3'
    ext.ktor_version = '0.9.4'
    ext.appengine_version = '1.9.60'
    ext.appengine_plugin_version = '1.3.4'

repositories {
    mavenCentral()
    jcenter()
}
dependencies {
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.3'
    classpath "com.google.cloud.tools:appengine-gradle-plugin:$appengine_plugin_version"
    }
}

apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'war'
apply plugin: 'com.google.cloud.tools.appengine'

sourceSets {
    main.kotlin.srcDirs = [ 'src/main/kotlin' ]
}

sourceCompatibility = 1.8

repositories {
mavenCentral()
jcenter()
maven { url "https://kotlin.bintray.com/ktor" }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile("org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}")
        testRuntime("org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}")
        testCompile("org.assertj:assertj-core:3.10.0")
        testCompileOnly('org.apiguardian:apiguardian-api:1.0.0')
        compile 'com.squareup.retrofit2:retrofit:2.3.0'
        compile 'com.squareup.retrofit2:converter-gson:2.3.0'
        compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
        compile 'io.reactivex.rxjava2:rxjava:2.2.0'
        compile 'com.google.cloud:google-cloud-firestore:0.58.0-beta'
        // Or compile 'com.google.cloud:google-cloud-firestore:0.58.0-beta'
        compile 'com.google.firebase:firebase-admin:6.5.0'
        compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
        compile "io.ktor:ktor-server-servlet:$ktor_version"
        compile "io.ktor:ktor-html-builder:$ktor_version"
        providedCompile "com.google.appengine:appengine:$appengine_version"
}

kotlin.experimental.coroutines = 'enable'

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

task run(dependsOn: appengineRun)

appengine {
    deploy {
        version = 'price-staging-1021653pm'
        stopPreviousVersion = false
    }
}

Firebase 戦略の初期化

1. Google Cloud Platform で初期化する

資格情報が自動的に管理されるため、この方法は有望です。

// Use the application default credentials
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
FirebaseOptions options = new FirebaseOptions.Builder()
    .setCredentials(credentials)
    .setProjectId(projectId)
    .build();
FirebaseApp.initializeApp(options);

Firestore db = FirestoreClient.getFirestore();

2.独自のサーバーで初期化する

GCP の [ IAM と管理者] > [サービス アカウント]で、キー ID が認証に使用されている Json オブジェクトと一致することを確認しました。

この戦略を、AppEngine にデプロイされた別の Firestore 接続アプリでうまく使用しています。作業中のアプリは .Jar としてビルドされ、ktorを使用せずに AppEngine に直接デプロイされますが、ここで説明する手順に従います。

// Use a service account
InputStream serviceAccount = new FileInputStream("path/to/serviceAccount.json");
GoogleCredentials credentials = GoogleCredentials.fromStream(serviceAccount);
FirebaseOptions options = new FirebaseOptions.Builder()
    .setCredentials(credentials)
    .build();
FirebaseApp.initializeApp(options);

Firestore db = FirestoreClient.getFirestore();

私の作業中の.Jarビルドアプリでは、ファイルが見つからないという問題を回避するために、 Jsonオブジェクトをプログラムで渡しています。このktorアプリケーションに対して、同じプログラムによる実装を試みました。展開されたときは機能しましたが、機能しgradle appengineRunませんでした。

val credentials = GoogleCredentials.fromStream(Gson().toJson(FirebaseCredentials(
            "service_account",
            "project-name",
            "asdfghjkl",
            "keyStringHere",
            "firebase-adminsdk-dhr30@project-name.iam.gserviceaccount.com",
            "1234567890",
            "https://accounts.google.com/o/oauth2/auth",
            "https://oauth2.googleapis.com/token",
            "https://www.googleapis.com/oauth2/v1/certs",
           "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-dhr30%40project-name-staging.iam.gserviceaccount.com"
    )).byteInputStream())
    val options = FirebaseOptions.Builder()
            .setCredentials(credentials)
            .setDatabaseUrl("https://project-name-staging.firebaseio.com")
            .build()

    FirebaseApp.initializeApp(options)

3. 独自のサーバーで初期化 (Firebase コンソールのセットアップ)

#2の唯一の違いは、このセットアップが追加すること.setDatabaseUrl("https://yourProjectName.firebaseio.com")です。

ここに画像の説明を入力

4.クラウド Firestore を初期化する

FirestoreOptions firestoreOptions =
FirestoreOptions.getDefaultInstance().toBuilder()
    .setProjectId(projectId)
    .build();
Firestore db = firestoreOptions.getService();

Firestore オブジェクトへのアクセス

#1-3では、Firebase アプリはアプリケーションのmain()メソッドですぐに初期化されます。次に、オブジェクトからFirestoreオブジェクトにアクセスします。

FirebaseClient.Kt

object FirebaseClient {
    val firestore: Firestore
    init {
        firestore = FirestoreClient.getFirestore()
    }
}

#4では、Firestore オブジェクトが Kotlin オブジェクトで作成さinit{...}れ、値としてオブジェクトに格納されます。

FirebaseClient.Kt

object FirebaseClient {
    val firestore: Firestore

    init {
        val firestoreOptions = FirestoreOptions.getDefaultInstance().toBuilder()
            .setTimestampsInSnapshotsEnabled(true)
            .setProjectId("project-name")
            .build()
        firestore = firestoreOptions.service
    }
}

Firestore への書き込み

FirebaseClient.firestore.collection(someCollection).document(someDocument).collection(anotherCollection).add(someObject)

4

1 に答える 1

1

別のプロジェクトで Firebase 認証を利用した後、これは Firebase 認証の問題ではなく、アプリケーションのメインメソッドの問題であることがわかりました。したがって、Firebase 認証の上記のさまざまな実装は、AppEngine にデプロイされたときに期待どおりに機能します。

解決

アプリが AppEngine にデプロイされると、アプリケーションのメインメソッドが実行されることを期待していました。これは、IntelliJ で実行されたときにアプリケーションのメイン メソッドが呼び出される方法と似ています。ただし、メインは、アプリのホストされたルートが呼び出されたときにのみ呼び出されることに気付きました。

すなわち:https://[yourProjectName].appspot.com

Ktor アプリのmainメソッドをデプロイ後に自動的に実行する方法を決定するために、新しいStackOverflow投稿を作成しました。

于 2018-12-01T21:08:18.447 に答える