googleapis / java-firestore

Apache License 2.0
100 stars 66 forks source link

use Emulator with Firestore without credentials #361

Open lechen26 opened 4 years ago

lechen26 commented 4 years ago

Hi

based on the existing issues on the repo and the latest 2.0.0 release i understand that its suppose to be possible to connect to the FireStore emulator without providing a real credentials, as its not really needed.

i've tried to do the following

Firestore FS = FirestoreOptions.getDefaultInstance().toBuilder() .setProjectId("test") .setHost("localhost:8888") .setCredentials(EmulatorCredentials()) .setCredentialsProvider(FixedCredentialsProvider.create(EmulatorCredentials())) .build() .getService();

using the latest 2.0.0 java-cloud-firestore. but, it doesnt seems working and i still get the exception that asks me to provide GOOGLE_APPLICATION_CREDENTIALS.

what am i missing?

i also saw there was another merge on that area, and the additional .setEmulatorHost to the FireStoreOptions but it was not yet published.

thanks, Chen

athakor commented 4 years ago

@lechen26 i try to reproduced it with firestore emulator with 2.0.0 but i am unable to reproduced.

<dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>google-cloud-firestore</artifactId>
       <version>2.0.0</version>
</dependency>

Code Sample :

import com.google.api.gax.core.FixedCredentialsProvider;
import com.google.cloud.ServiceOptions;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreOptions;

public class Test {
    public static void main(String[] args) {
        Firestore FS = FirestoreOptions.getDefaultInstance().toBuilder()
                .setProjectId(ServiceOptions.getDefaultProjectId())
                .setHost("localhost:8888")
                .setCredentials(new FirestoreOptions.EmulatorCredentials())
                .setCredentialsProvider(FixedCredentialsProvider.create(new FirestoreOptions.EmulatorCredentials()))
                .build()
                .getService();
        System.out.println(FS.document("doc/test"));
    }
}

Output : DocumentReference{path=projects/xxxx-xxx-xxx/databases/(default)/documents/doc/test}

lechen26 commented 4 years ago

i was able to change it to the following:

fun initLocalDB(): FirestoreOptions {
        return FirestoreOptions.getDefaultInstance()
            .toBuilder()
            .setProjectId("emulator")
            .setHost("localhost:8888")
            .setCredentials(FirestoreOptions.EmulatorCredentials())
            .build()
    }

we are using spring (kotlin) , it might be related to the way spring initialize the bean and auto configures it. we are still getting the warning of

2020-09-07 15:23:40.005  WARN 73555 --- [           main] o.s.c.g.core.DefaultCredentialsProvider  : No core credentials are set. Service-specific credentials (e.g., spring.cloud.gcp.pubsub.credentials.*) should be used if your app uses services that require credentials.
java.io.IOException: The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.

but i assume its related to the org.springframework.cloud.gcp.autoconfigure.core.GcpContextAutoConfiguration.

thanks!

athakor commented 4 years ago

@lechen26 still i am unable to reproduced with spring(kotlin)

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
        <kotlin.version>1.3.72</kotlin.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-kotlin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>google-cloud-firestore</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
        <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <configuration>
                    <args>
                        <arg>-Xjsr305=strict</arg>
                    </args>
                    <compilerPlugins>
                        <plugin>spring</plugin>
                    </compilerPlugins>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-maven-allopen</artifactId>
                        <version>${kotlin.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>

DemoApplication.kt

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
    SpringApplication.run(DemoApplication::class.java, *args)
}

DemoController.kt

import com.google.api.gax.core.FixedCredentialsProvider
import com.google.cloud.ServiceOptions
import com.google.cloud.firestore.FirestoreOptions
import com.google.cloud.firestore.FirestoreOptions.EmulatorCredentials
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class DemoController {

    @GetMapping("/initLocalDB")
    fun initLocalDB(): String {
        return FirestoreOptions.getDefaultInstance()
                .toBuilder()
                .setProjectId(ServiceOptions.getDefaultProjectId())
                .setHost("localhost:8888")
                .setCredentials(EmulatorCredentials())
                .setCredentialsProvider(FixedCredentialsProvider.create(EmulatorCredentials()))
                .build().service.document("doc/test").toString()
    }
}

Output:

output

huw0 commented 4 years ago

I am experiencing the same issue with the latest Java firestore library.

FIRESTORE_EMULATOR_HOST="localhost:8080"
FIRESTORE_PROJECT_ID="project-id"

With these environment variables set my expectation is that I can instantiate Firestore with the default FirestoreOptions.getDefaultInstance().getService()

However this fails with a NullPointerException.

Caused by: com.google.cloud.firestore.FirestoreException: java.lang.NullPointerException
        at com.google.cloud.firestore.FirestoreException.networkException(FirestoreException.java:89)
        at com.google.cloud.firestore.FirestoreOptions$DefaultFirestoreRpcFactory.create(FirestoreOptions.java:91)
        at com.google.cloud.firestore.FirestoreOptions$DefaultFirestoreRpcFactory.create(FirestoreOptions.java:81)
        at com.google.cloud.ServiceOptions.getRpc(ServiceOptions.java:561)
        at com.google.cloud.firestore.FirestoreOptions.getFirestoreRpc(FirestoreOptions.java:365)
        at com.google.cloud.firestore.FirestoreImpl.<init>(FirestoreImpl.java:65)
        at com.google.cloud.firestore.FirestoreOptions$DefaultFirestoreFactory.create(FirestoreOptions.java:72)
        at com.google.cloud.firestore.FirestoreOptions$DefaultFirestoreFactory.create(FirestoreOptions.java:65)
        at com.google.cloud.ServiceOptions.getService(ServiceOptions.java:541)

Looking in the code there is only one reference to the emulator, which sets the host. https://github.com/googleapis/java-firestore/blob/97037f42f76e9df3ae458d4e2b04336e64b834c3/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java#L217-L233 However nothing is done with FIRESTORE_PROJECT_ID.

For now I'm working around this with a profile based bean that is different for local testing.

FirestoreOptions.getDefaultInstance().toBuilder()
                .setProjectId("project-id")
                .build()
                .getService();

Ultimately there are two issues:

mukul-1985 commented 3 years ago

Hi,

@athakor I started a Firestore emulator using command firebase emulators:start I was able to connect to the emulator, add and read data.

I tried to see the data from firebase emulator ui and data is not visible in the UI. Am I missing something here? do I need to do any configurations to check the data in the UI?

at15 commented 1 year ago

To view the data in firestore emulator UI, you need to set the right project id, i.e. same as .firebaserc https://stackoverflow.com/questions/63036316/why-dont-i-see-data-in-the-firebase-cloud-firestore-emulator-ui and https://github.com/firebase/firebase-tools/issues/2365

{
  "projects": {
    "default": "foo-123ab"
  }
}
Firestore db = FirestoreOptions.getDefaultInstance()
        .toBuilder()
        // https://stackoverflow.com/questions/63036316/why-dont-i-see-data-in-the-firebase-cloud-firestore-emulator-ui
        .setProjectId("foo-123ab")
        .setEmulatorHost("localhost:8080")
        .setCredentials(new FirestoreOptions.EmulatorCredentials())
        .setCredentialsProvider(FixedCredentialsProvider.create(new FirestoreOptions.EmulatorCredentials()))
        .build()
        .getService();

// Creates a new racket
final ApiFuture<WriteResult> update = db.collection("fruits")
        .document("apple")
        .set(Map.of("price", "10"));
try {
    final WriteResult writeResult = update.get();
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}