8000 Firestore:Fix Observable in calls instead of wrapping in ApiFuture by abhinav-qlogic · Pull Request #6148 · googleapis/google-cloud-java · GitHub
[go: up one dir, main page]

Skip to content

Conversation

@abhinav-qlogic
Copy link

Fixes #5887

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Aug 23, 2019

/** Fetches the document reference in the form of the stream inside apiStreamObserver */
@Nonnull
public void get(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still lukewarm on adding additional API surface. I think we can live without DocumentReference.get() since you should be able to call getAll().

* @param responseObserver ApiStreamObserver of DocumentSnapshot
* @param fieldMask If set, specifies the subset of fields to return.
*/
void getAll(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please match the signature above and change the order to:

getAll(DocumentReference[] documentReferences, @Nullable FieldMask fieldMask, final ApiStreamObserver<DocumentSnapshot> responseObserver)

Putting the observer last also makes for much more readable code when a Lambda is used.


/**
* Retrieves multiple documents from Firestore, while optionally applying a field mask to reduce
* the amount of data transmitted.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mention that the documents will be returned out of order and that missing documents will not be returned at all.

return futureList;
}

void getAll(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please merge this with the internal method above. You can likely rewrite the existing getAll() to use your new method.

public void onCompleted() {}
},
documentReferences);
Thread.sleep(1000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rewrite this tests to not sleep.

public void onCompleted() {}
};
ref.get(FieldMask.of("foo"), responseObserver, ref);
Thread.sleep(1000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rewrite this tests to not sleep.

},
documentReferences);
Thread.sleep(1000);
assertEquals(map("foo", "bar"), documentSnapshots.get(0).getData());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please assert response size and fetch multiple documents in this test, including non-existing documents.

@Override
public void onCompleted() {}
};
ref.get(FieldMask.of("foo"), responseObserver, ref);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this test will likely go away as per my previous suggestion of removing this API.

@codecov
Copy link
codecov bot commented Aug 28, 2019

Codecov Report

Merging #6148 into master will increase coverage by 0.11%.
The diff coverage is 82.35%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #6148      +/-   ##
============================================
+ Coverage     47.38%    47.5%   +0.11%     
- Complexity    27198    27462     +264     
============================================
  Files          2523     2527       +4     
  Lines        274600   274768     +168     
  Branches      31378    31415      +37     
============================================
+ Hits         130127   130528     +401     
+ Misses       134861   134609     -252     
- Partials       9612     9631      +19
Impacted Files Coverage Δ Complexity Δ
...java/com/google/cloud/firestore/FirestoreImpl.java 82.96% <82.35%> (-0.47%) 23 <1> (+1)
...ud/bigtable/data/v2/stub/EnhancedBigtableStub.java 94.85% <0%> (-3.63%) 22% <0%> (ø)
.../google/cloud/spanner/jdbc/CredentialsService.java 91.11% <0%> (-2.23%) 11% <0%> (-1%)
.../com/google/cloud/spanner/jdbc/ConnectionImpl.java 82.58% <0%> (-1.12%) 155% <0%> (ø)
...a/com/google/cloud/firestore/DocumentSnapshot.java 84.95% <0%> (-0.76%) 55% <0%> (ø)
...m/google/cloud/spanner/jdbc/ConnectionOptions.java 82.42% <0%> (-0.4%) 46% <0%> (+2%)
...ain/java/com/google/cloud/pubsub/v1/Publisher.java 89.11% <0%> (-0.35%) 40% <0%> (ø)
...oogle/cloud/spanner/jdbc/SingleUseTransaction.java 87.5% <0%> (-0.23%) 37% <0%> (-3%)
...om/google/cloud/storage/spi/v1/HttpStorageRpc.java 1.64% <0%> (-0.01%) 1% <0%> (ø)
...rc/main/java/com/google/cloud/storage/Storage.java 79.72% <0%> (ø) 0% <0%> (ø) ⬇️
... and 102 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 002f217...74a4aec. Read the comment docs.

8000

@rahulKQL rahulKQL added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Aug 29, 2019
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Aug 29, 2019
Copy link
Contributor
@schmidt-sebastian schmidt-sebastian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty close now. Thanks for the update!

* will not be returned.
*
* @param documentReferences Array with Document References to fetch.
* @param fieldMask If set, specifies the subset of fields to return.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/set/not null/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

public ApiFuture<List<DocumentSnapshot>> getAll(
@Nonnull DocumentReference... documentReferences) {
return this.getAll(documentReferences, null, null);
return this.getAll(documentReferences, null, null, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For cases like this, it might make sense to add inline comments to describe the arguments:

   return this.getAll(documentReferences, /* foo= */ null, /* bar= */ null, /* baz= */ null);

@Nullable FieldMask fieldMask,
@Nullable ByteString transactionId) {
@Nullable ByteString transactionId,
@Nullable final ApiStreamObserver apiStreamObserver) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we could have one method that adds the ApiStreamObserver, and one that turns it into an ApiFuture?

void getAll(
      final DocumentReference[] documentReferences,
      @Nullable FieldMask fieldMask,
      @Nullable ByteString transactionId,
      @Nullable final ApiStreamObserver apiStreamObserver) { ... }

ApiFuture<List<DocumentSnapshot>> getAll(
      final DocumentReference[] documentReferences,
      @Nullable FieldMask fieldMask,
      @Nullable ByteString transactionId) { 
   List results....
   getAll(..., new ApiStreamObserver() -> {
      result.add();
    });
    // Sort and return task
 }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


@Test
public void getAllWithObserver() throws Exception {
DocumentReference ref = randomColl.document("doc1");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/ref/ref1/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

DocumentReference ref = randomColl.document("doc1");
ref.set(ALL_SUPPORTED_TYPES_MAP).get();

DocumentReference ref1 = randomColl.document("doc2");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/ref1/ref2

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


DocumentReference ref2 = randomColl.document("doc3");
ref2.set(ALL_SUPPORTED_TYPES_MAP).get();
ref2.delete();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to set and delete a document in order for it to show up as "missing". You can just query a non-existing document that you never created.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

assertFalse(documentSnapshot.exists());
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd slightly prefer if you checked the document references. Do note that they are returned alphabetically sorted by the backend and you could just verify get them by index.

if (null != documentSnapshot.getData()) {
assertEquals(map("foo", "bar"), documentSnapshot.getData());
} else {
assertFalse(documentSnapshot.exists());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check (getData() == null && ! docSnapshot.exists()) is always true and doesn't quite verify that one of your documents is not returned.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
});

future.get();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for updating this!

if (null != documentSnapshots && documentSnapshots.size() > 0) {
for (DocumentSnapshot documentSnapshot : documentSnapshots) {
if (null != documentSnapshot.getData()) {
assertEquals(map("foo", "bar"), documentSnapshot.getData());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised this work because ALL_SUPPORTED_TYPES_MAP has a lot more fields.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

ref2.set(ALL_SUPPORTED_TYPES_MAP).get();
ref2.delete();

final List<DocumentSnapshot> documentSnapshots = new ArrayList<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be wrapped in a synchronized list since it is being written and read from different threads.

Suggested change
final List<DocumentSnapshot> documentSnapshots = new ArrayList<>();
final List<DocumentSnapshot> documentSnapshots = Collections.synchronizedList(new ArrayList<DocumentSnapshot>());

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


future.get();

if (null != documentSnapshots && documentSnapshots.size() > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if statement doesn't actually do anything. documentSnapshots is never null due to the final initializer above, and the size check before iterating is not needed due to the for-each not executing if the list is empty.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor
@schmidt-sebastian schmidt-sebastian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This looks good. Can you try to remove the "transactionId" argument before merging?

* @param documentReferences Array with Document References to fetch.
* @param fieldMask If not null, specifies the subset of fields to return.
* @param transactionId Id of {@link Transaction}
* @param responseObserver ApiStreamObserver of DocumentSnapshot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is the external Firestore API, do you mind coming up with a more descriptive way to describe responseObserver?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

*
* @param documentReferences Array with Document References to fetch.
* @param fieldMask If not null, specifies the subset of fields to return.
* @param transactionId Id of {@link Transaction}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, the transaction ID handling would not be exposed in the public API. We currently don't use the transaction ID in any of our customer facing APIs, but instead have helper methods that use them internally.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

/**
* Retrieves multiple documents from Firestore while optionally applying a field mask to reduce
* the amount of data transmitted. Returned documents will be out of order and missing documents
* will not be returned.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, it looks like I was wrong and missing documents do get returned. Can you remove the last part of your API doc?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@schmidt-sebastian
Copy link
Contributor

@BenWhitehead This is good to be merged.

@BenWhitehead
Copy link
Contributor

Thanks @schmidt-sebastian, I'll hold off until the builds have completed (there appears to be some slowness with the build queue).

@chingor13 chingor13 merged commit 61a759a into googleapis:master Sep 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: yes This human has signed the Contributor License Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Firestore expose Observable in calls instead of wrapping in ApiFuture

7 participants

0