diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java index ba9a2e9856..38e037b33d 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java @@ -21,8 +21,12 @@ import com.google.api.core.NanoClock; import com.google.api.core.SettableApiFuture; import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.BidiStreamObserver; import com.google.api.gax.rpc.BidiStreamingCallable; +import com.google.api.gax.rpc.ClientStream; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; +import com.google.api.gax.rpc.StreamController; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.Timestamp; import com.google.cloud.firestore.spi.v1.FirestoreRpc; @@ -201,7 +205,6 @@ public ApiFuture> getAll( return this.getAll(documentReferences, fieldMask, (ByteString) null); } - @Nonnull @Override public void getAll( final @Nonnull DocumentReference[] documentReferences, @@ -216,12 +219,15 @@ void getAll( @Nullable ByteString transactionId, final ApiStreamObserver apiStreamObserver) { - ApiStreamObserver responseObserver = - new ApiStreamObserver() { + ResponseObserver responseObserver = + new ResponseObserver() { int numResponses; @Override - public void onNext(BatchGetDocumentsResponse response) { + public void onStart(StreamController streamController) {} + + @Override + public void onResponse(BatchGetDocumentsResponse response) { DocumentReference documentReference; DocumentSnapshot documentSnapshot; @@ -270,7 +276,7 @@ public void onError(Throwable throwable) { } @Override - public void onCompleted() { + public void onComplete() { tracer .getCurrentSpan() .addAnnotation(TraceUtil.SPAN_NAME_BATCHGETDOCUMENTS + ": Complete"); @@ -433,19 +439,19 @@ public ApiFuture sendRequest( @Override public void streamRequest( RequestT requestT, - ApiStreamObserver responseObserverT, + ResponseObserver responseObserverT, ServerStreamingCallable callable) { Preconditions.checkState(!closed, "Firestore client has already been closed"); - callable.serverStreamingCall(requestT, responseObserverT); + callable.call(requestT, responseObserverT); } /** Request funnel for all bidirectional streaming requests. */ @Override - public ApiStreamObserver streamRequest( - ApiStreamObserver responseObserverT, + public ClientStream streamRequest( + BidiStreamObserver responseObserverT, BidiStreamingCallable callable) { Preconditions.checkState(!closed, "Firestore client has already been closed"); - return callable.bidiStreamingCall(responseObserverT); + return callable.splitCall(responseObserverT); } @Override diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreRpcContext.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreRpcContext.java index 981d81fdd9..5207bd7701 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreRpcContext.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreRpcContext.java @@ -20,8 +20,10 @@ import com.google.api.core.ApiFuture; import com.google.api.core.InternalApi; import com.google.api.core.InternalExtensionOnly; -import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.BidiStreamObserver; import com.google.api.gax.rpc.BidiStreamingCallable; +import com.google.api.gax.rpc.ClientStream; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.firestore.spi.v1.FirestoreRpc; @@ -48,10 +50,10 @@ ApiFuture sendRequest( void streamRequest( RequestT requestT, - ApiStreamObserver responseObserverT, + ResponseObserver responseObserverT, ServerStreamingCallable callable); - ApiStreamObserver streamRequest( - ApiStreamObserver responseObserverT, + ClientStream streamRequest( + BidiStreamObserver responseObserverT, BidiStreamingCallable callable); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java index 8b5e876932..c8dac33964 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java @@ -32,7 +32,9 @@ import com.google.api.core.InternalExtensionOnly; import com.google.api.core.SettableApiFuture; import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.StatusCode; +import com.google.api.gax.rpc.StreamController; import com.google.auto.value.AutoValue; import com.google.cloud.Timestamp; import com.google.cloud.firestore.Query.QueryOptions.Builder; @@ -1502,14 +1504,17 @@ private void internalStream( final AtomicReference lastReceivedDocument = new AtomicReference<>(); - ApiStreamObserver observer = - new ApiStreamObserver() { + ResponseObserver observer = + new ResponseObserver() { Timestamp readTime; boolean firstResponse; int numDocuments; @Override - public void onNext(RunQueryResponse response) { + public void onStart(StreamController streamController) {} + + @Override + public void onResponse(RunQueryResponse response) { if (!firstResponse) { firstResponse = true; Tracing.getTracer().getCurrentSpan().addAnnotation("Firestore.Query: First response"); @@ -1557,7 +1562,7 @@ public void onError(Throwable throwable) { } @Override - public void onCompleted() { + public void onComplete() { Tracing.getTracer() .getCurrentSpan() .addAnnotation( diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Watch.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Watch.java index f655b58c40..8f07b1332a 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Watch.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Watch.java @@ -21,7 +21,9 @@ import com.google.api.gax.retrying.ExponentialRetryAlgorithm; import com.google.api.gax.retrying.TimedAttemptSettings; import com.google.api.gax.rpc.ApiException; -import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.BidiStreamObserver; +import com.google.api.gax.rpc.ClientStream; +import com.google.api.gax.rpc.StreamController; import com.google.cloud.Timestamp; import com.google.cloud.firestore.DocumentChange.Type; import com.google.common.base.Preconditions; @@ -57,7 +59,7 @@ * It synchronizes on its own instance so it is advisable not to use this class for external * synchronization. */ -class Watch implements ApiStreamObserver { +class Watch implements BidiStreamObserver { /** * Target ID used by watch. Watch uses a fixed target id since we only support one target per * stream. The actual target ID we use is arbitrary. @@ -71,7 +73,7 @@ class Watch implements ApiStreamObserver { private final ExponentialRetryAlgorithm backoff; private final Target target; private TimedAttemptSettings nextAttempt; - private ApiStreamObserver stream; + private ClientStream stream; /** The sorted tree of DocumentSnapshots as sent in the last snapshot. */ private DocumentSet documentSet; @@ -167,7 +169,13 @@ static Watch forQuery(Query query) { } @Override - public synchronized void onNext(ListenResponse listenResponse) { + public void onStart(StreamController streamController) {} + + @Override + public void onReady(ClientStream clientStream) {} + + @Override + public synchronized void onResponse(ListenResponse listenResponse) { switch (listenResponse.getResponseTypeCase()) { case TARGET_CHANGE: TargetChange change = listenResponse.getTargetChange(); @@ -258,7 +266,7 @@ public synchronized void onError(Throwable throwable) { } @Override - public synchronized void onCompleted() { + public synchronized void onComplete() { maybeReopenStream(new StatusException(Status.fromCode(Code.UNKNOWN))); } @@ -289,7 +297,7 @@ ListenerRegistration runWatch( .execute( () -> { synchronized (Watch.this) { - stream.onCompleted(); + stream.closeSend(); stream = null; } }); @@ -321,7 +329,7 @@ private void resetDocs() { /** Closes the stream and calls onError() if the stream is still active. */ private void closeStream(final Throwable throwable) { if (stream != null) { - stream.onCompleted(); + stream.closeSend(); stream = null; } @@ -363,7 +371,7 @@ private void maybeReopenStream(Throwable throwable) { /** Helper to restart the outgoing stream to the backend. */ private void resetStream() { if (stream != null) { - stream.onCompleted(); + stream.closeSend(); stream = null; } @@ -399,7 +407,7 @@ private void initStream() { request.getAddTargetBuilder().setResumeToken(resumeToken); } - stream.onNext(request.build()); + stream.send(request.build()); } } catch (Throwable throwable) { onError(throwable); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/BulkWriterTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/BulkWriterTest.java index 9a6e248ed4..631c543038 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/BulkWriterTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/BulkWriterTest.java @@ -70,7 +70,6 @@ import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; @RunWith(MockitoJUnitRunner.class) public class BulkWriterTest { @@ -1119,11 +1118,10 @@ public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) }; doAnswer( - (Answer>) - mock -> { - retryAttempts[0]++; - return RETRYABLE_FAILED_FUTURE; - }) + mock -> { + retryAttempts[0]++; + return RETRYABLE_FAILED_FUTURE; + }) .when(firestoreMock) .sendRequest( batchWriteCapture.capture(), @@ -1170,11 +1168,10 @@ public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) }; doAnswer( - (Answer>) - mock -> { - retryAttempts[0]++; - return RESOURCE_EXHAUSTED_FAILED_FUTURE; - }) + mock -> { + retryAttempts[0]++; + return RESOURCE_EXHAUSTED_FAILED_FUTURE; + }) .when(firestoreMock) .sendRequest( batchWriteCapture.capture(), diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/ConformanceTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/ConformanceTest.java index d6d4500859..dd5b215950 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/ConformanceTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/ConformanceTest.java @@ -31,8 +31,10 @@ import com.google.api.core.ApiFuture; import com.google.api.core.SettableApiFuture; -import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.BidiStreamObserver; import com.google.api.gax.rpc.BidiStreamingCallable; +import com.google.api.gax.rpc.ClientStream; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.Timestamp; @@ -63,7 +65,6 @@ import com.google.firestore.v1.ListenRequest; import com.google.firestore.v1.ListenResponse; import com.google.firestore.v1.RunQueryRequest; -import com.google.protobuf.AbstractMessage; import com.google.protobuf.Message; import com.google.protobuf.util.JsonFormat; import java.io.IOException; @@ -90,7 +91,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; @RunWith(AllTests.class) public class ConformanceTest { @@ -251,7 +251,7 @@ final CollectionReference collection(final String absolutePath) { private static final class ConformanceGetTestRunner extends BaseConformanceTestRunner { @Captor private ArgumentCaptor getAllCapture; - @Captor private ArgumentCaptor> streamObserverCapture; + @Captor private ArgumentCaptor> streamObserverCapture; private ConformanceGetTestRunner(String description, GetTest testParameters) { super(description, testParameters); @@ -480,7 +480,7 @@ private static final class ConformanceQueryTestRunner extends BaseConformanceTestRunner { @Captor private ArgumentCaptor runQueryCapture; - @Captor private ArgumentCaptor> streamObserverCapture; + @Captor private ArgumentCaptor> streamObserverCapture; private ConformanceQueryTestRunner( String description, TestDefinition.QueryTest testParameters) { @@ -624,8 +624,8 @@ private DocumentSnapshot convertDocument(DocSnapshot snapshot) { private static final class ConformanceListenTestRunner extends BaseConformanceTestRunner { - @Captor private ArgumentCaptor> streamObserverCapture; - @Mock private ApiStreamObserver noOpRequestObserver; + @Captor private ArgumentCaptor> streamObserverCapture; + @Mock private ClientStream noOpRequestObserver; private final Query watchQuery; @@ -641,11 +641,10 @@ public void runTest() throws Throwable { final SettableApiFuture testCaseFinished = SettableApiFuture.create(); doAnswer( - (Answer>) - invocationOnMock -> { - testCaseStarted.set(null); - return noOpRequestObserver; - }) + invocationOnMock -> { + testCaseStarted.set(null); + return noOpRequestObserver; + }) .when(firestore) .streamRequest( streamObserverCapture.capture(), Matchers.any(BidiStreamingCallable.class)); @@ -680,7 +679,7 @@ public void runTest() throws Throwable { testCaseStarted.get(); for (ListenResponse response : testParameters.getResponsesList()) { - streamObserverCapture.getValue().onNext(response); + streamObserverCapture.getValue().onResponse(response); } testCaseFinished.get(); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/DocumentReferenceTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/DocumentReferenceTest.java index 7cc0832ac8..aecc8b6bea 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/DocumentReferenceTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/DocumentReferenceTest.java @@ -64,7 +64,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; -import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.Timestamp; @@ -111,7 +111,7 @@ public class DocumentReferenceTest { @Captor private ArgumentCaptor getAllCapture; - @Captor private ArgumentCaptor streamObserverCapture; + @Captor private ArgumentCaptor> streamObserverCapture; private DocumentReference documentReference; diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/FirestoreTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/FirestoreTest.java index c90692b4ba..9d685022d9 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/FirestoreTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/FirestoreTest.java @@ -32,14 +32,14 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; -import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.firestore.spi.v1.FirestoreRpc; import com.google.firestore.v1.BatchGetDocumentsRequest; import com.google.firestore.v1.CommitRequest; import com.google.firestore.v1.CommitResponse; -import com.google.firestore.v1.ListCollectionIdsRequest; +import com.google.protobuf.Message; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -64,9 +64,7 @@ public class FirestoreTest { @Captor private ArgumentCaptor getAllCapture; - @Captor private ArgumentCaptor listCollectionIdsCapture; - - @Captor private ArgumentCaptor streamObserverCapture; + @Captor private ArgumentCaptor> streamObserverCapture; @Captor private ArgumentCaptor commitCapture; diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/LocalFirestoreHelper.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/LocalFirestoreHelper.java index 4cc8eda767..e73241a002 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/LocalFirestoreHelper.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/LocalFirestoreHelper.java @@ -22,7 +22,7 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; import com.google.api.gax.retrying.RetrySettings; -import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.Timestamp; import com.google.common.base.Preconditions; @@ -316,14 +316,14 @@ public static Answer streamingResponse( final T[] response, @Nullable final Throwable throwable) { return invocation -> { Object[] args = invocation.getArguments(); - ApiStreamObserver observer = (ApiStreamObserver) args[1]; + ResponseObserver observer = (ResponseObserver) args[1]; for (T resp : response) { - observer.onNext(resp); + observer.onResponse(resp); } if (throwable != null) { observer.onError(throwable); } - observer.onCompleted(); + observer.onComplete(); return null; }; } diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/PartitionQuery.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/PartitionQuery.java index 26d7c4cde6..04203313be 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/PartitionQuery.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/PartitionQuery.java @@ -26,7 +26,7 @@ import static org.mockito.Mockito.when; import com.google.api.core.ApiFutures; -import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.firestore.spi.v1.FirestoreRpc; @@ -75,7 +75,7 @@ public class PartitionQuery { @Mock private PartitionQueryPage queryPage; @Captor private ArgumentCaptor runQuery; - @Captor private ArgumentCaptor streamObserverCapture; + @Captor private ArgumentCaptor> streamObserverCapture; @Captor private ArgumentCaptor requestCaptor; @Test diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/QueryTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/QueryTest.java index 23673507f8..90520e3c5c 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/QueryTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/QueryTest.java @@ -42,6 +42,7 @@ import com.google.api.core.ApiClock; import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.cloud.Timestamp; import com.google.cloud.firestore.Query.ComparisonFilter; @@ -74,7 +75,6 @@ import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; import org.threeten.bp.Duration; @RunWith(MockitoJUnitRunner.class) @@ -106,7 +106,7 @@ public long millisTime() { @Captor private ArgumentCaptor runQuery; - @Captor private ArgumentCaptor streamObserverCapture; + @Captor private ArgumentCaptor> streamObserverCapture; private Query query; @@ -957,20 +957,19 @@ public void retriesAfterRetryableError() throws Exception { final boolean[] returnError = new boolean[] {true}; doAnswer( - (Answer) - invocation -> { - if (returnError[0]) { - returnError[0] = false; - return queryResponse( - FirestoreException.forServerRejection( - Status.DEADLINE_EXCEEDED, "Simulated test failure"), - DOCUMENT_NAME + "1", - DOCUMENT_NAME + "2") - .answer(invocation); - } else { - return queryResponse(DOCUMENT_NAME + "3").answer(invocation); - } - }) + invocation -> { + if (returnError[0]) { + returnError[0] = false; + return queryResponse( + FirestoreException.forServerRejection( + Status.DEADLINE_EXCEEDED, "Simulated test failure"), + DOCUMENT_NAME + "1", + DOCUMENT_NAME + "2") + .answer(invocation); + } else { + return queryResponse(DOCUMENT_NAME + "3").answer(invocation); + } + }) .when(firestoreMock) .streamRequest( runQuery.capture(), @@ -1080,22 +1079,21 @@ public void retriesWithoutTimeout() throws Exception { final boolean[] returnError = new boolean[] {true}; doAnswer( - (Answer) - invocation -> { - // Advance clock by an hour - clock.advance(Duration.ofHours(1).toNanos()); - - if (returnError[0]) { - returnError[0] = false; - return queryResponse( - FirestoreException.forServerRejection( - Status.DEADLINE_EXCEEDED, "Simulated test failure"), - DOCUMENT_NAME + "1") - .answer(invocation); - } else { - return queryResponse(DOCUMENT_NAME + "2").answer(invocation); - } - }) + invocation -> { + // Advance clock by an hour + clock.advance(Duration.ofHours(1).toNanos()); + + if (returnError[0]) { + returnError[0] = false; + return queryResponse( + FirestoreException.forServerRejection( + Status.DEADLINE_EXCEEDED, "Simulated test failure"), + DOCUMENT_NAME + "1") + .answer(invocation); + } else { + return queryResponse(DOCUMENT_NAME + "2").answer(invocation); + } + }) .when(firestoreMock) .streamRequest( runQuery.capture(), @@ -1113,18 +1111,17 @@ public void doesNotRetryWithTimeout() { doReturn(Duration.ofMinutes(1)).when(firestoreMock).getTotalRequestTimeout(); doAnswer( - (Answer) - invocation -> { - // Advance clock by an hour - clock.advance(Duration.ofHours(1).toNanos()); - - return queryResponse( - FirestoreException.forServerRejection( - Status.DEADLINE_EXCEEDED, "Simulated test failure"), - DOCUMENT_NAME + "1", - DOCUMENT_NAME + "2") - .answer(invocation); - }) + invocation -> { + // Advance clock by an hour + clock.advance(Duration.ofHours(1).toNanos()); + + return queryResponse( + FirestoreException.forServerRejection( + Status.DEADLINE_EXCEEDED, "Simulated test failure"), + DOCUMENT_NAME + "1", + DOCUMENT_NAME + "2") + .answer(invocation); + }) .when(firestoreMock) .streamRequest( runQuery.capture(), diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/RecursiveDeleteTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/RecursiveDeleteTest.java index 5d8392a548..84713a679c 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/RecursiveDeleteTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/RecursiveDeleteTest.java @@ -44,7 +44,7 @@ import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; import com.google.api.core.SettableApiFuture; -import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.firestore.LocalFirestoreHelper.ResponseStubber; @@ -58,6 +58,7 @@ import com.google.firestore.v1.StructuredQuery.FieldFilter.Operator; import com.google.firestore.v1.Value; import com.google.firestore.v1.Write; +import com.google.protobuf.Message; import com.google.rpc.Code; import io.grpc.Status; import java.util.ArrayList; @@ -105,7 +106,7 @@ public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) @Captor private ArgumentCaptor batchWriteCapture; @Captor private ArgumentCaptor runQueryCapture; - @Captor private ArgumentCaptor streamObserverCapture; + @Captor private ArgumentCaptor> streamObserverCapture; private BulkWriter bulkWriter; private ResponseStubber responseStubber; @@ -295,7 +296,7 @@ public void createsRetryQueryAfterStreamExceptionWithLastReceivedDoc() throws Ex streamObserverCapture.capture(), Matchers.any()); - doAnswer((Answer>) mock -> successResponse(1)) + doAnswer(mock -> successResponse(1)) .when(firestoreMock) .sendRequest( batchWriteCapture.capture(), @@ -355,15 +356,14 @@ public void createsSecondQueryWithCorrectStartAfter() throws Exception { doAnswer(queryResponse(firstStream.toArray(new String[0]))) .doAnswer( - (Answer) - invocation -> { - secondQueryFuture.set(null); - Object[] args = invocation.getArguments(); - ApiStreamObserver observer = - (ApiStreamObserver) args[1]; - observer.onCompleted(); - return null; - }) + invocation -> { + secondQueryFuture.set(null); + Object[] args = invocation.getArguments(); + ResponseObserver observer = + (ResponseObserver) args[1]; + observer.onComplete(); + return null; + }) .when(firestoreMock) .streamRequest( runQueryCapture.capture(), @@ -371,32 +371,31 @@ public void createsSecondQueryWithCorrectStartAfter() throws Exception { Matchers.any()); doAnswer( - (Answer>) - mock -> { - if (numDeletesBuffered[0] < cutoff) { - numDeletesBuffered[0] += batchWriteResponse.size(); - // By waiting for `bufferFuture` to complete, we can guarantee that the writes - // complete after all documents are streamed. Without this future, the test can - // race and complete the writes before the stream is finished, which is a - // different scenario this test is not for. - return ApiFutures.transformAsync( - bufferFuture, - (ApiAsyncFunction) - unused -> mergeResponses(batchWriteResponse.toArray(new ApiFuture[0])), - MoreExecutors.directExecutor()); - } else { - // Once there are `cutoff` pending deletes, completing the future allows enough - // responses to be returned such that the number of pending deletes should be - // less than `minPendingOps`. This allows us to test that the second query is - // made. - bufferFuture.set(null); - return ApiFutures.transformAsync( - secondQueryFuture, - (ApiAsyncFunction) - unused -> mergeResponses(batchWriteResponse.toArray(new ApiFuture[0])), - MoreExecutors.directExecutor()); - } - }) + mock -> { + if (numDeletesBuffered[0] < cutoff) { + numDeletesBuffered[0] += batchWriteResponse.size(); + // By waiting for `bufferFuture` to complete, we can guarantee that the writes + // complete after all documents are streamed. Without this future, the test can + // race and complete the writes before the stream is finished, which is a + // different scenario this test is not for. + return ApiFutures.transformAsync( + bufferFuture, + (ApiAsyncFunction) + unused -> mergeResponses(batchWriteResponse.toArray(new ApiFuture[0])), + MoreExecutors.directExecutor()); + } else { + // Once there are `cutoff` pending deletes, completing the future allows enough + // responses to be returned such that the number of pending deletes should be + // less than `minPendingOps`. This allows us to test that the second query is + // made. + bufferFuture.set(null); + return ApiFutures.transformAsync( + secondQueryFuture, + (ApiAsyncFunction) + unused -> mergeResponses(batchWriteResponse.toArray(new ApiFuture[0])), + MoreExecutors.directExecutor()); + } + }) .when(firestoreMock) .sendRequest( batchWriteCapture.capture(), @@ -677,11 +676,10 @@ public void usesSameBulkWriterInstanceAcrossCalls() throws Exception { final int[] callCount = {0}; final BulkWriter bulkWriter = firestoreMock.bulkWriter(); doAnswer( - (Answer) - mock -> { - callCount[0]++; - return bulkWriter; - }) + mock -> { + callCount[0]++; + return bulkWriter; + }) .when(firestoreMock) .bulkWriter(); diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java index 9e35ee1b77..87c0e3866a 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/TransactionTest.java @@ -47,7 +47,7 @@ import com.google.api.core.ApiFutures; import com.google.api.gax.grpc.GrpcStatusCode; import com.google.api.gax.rpc.ApiException; -import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallable; import com.google.cloud.Timestamp; @@ -100,7 +100,7 @@ public class TransactionTest { firestoreRpc); @Captor private ArgumentCaptor requestCapture; - @Captor private ArgumentCaptor> streamObserverCapture; + @Captor private ArgumentCaptor> streamObserverCapture; private DocumentReference documentReference; private Query queryReference; diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java index 4d6f5d44a7..3ec83f1a1b 100644 --- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java +++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java @@ -29,12 +29,12 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; -import com.google.api.gax.rpc.ApiStreamObserver; +import com.google.api.gax.rpc.BidiStreamObserver; import com.google.api.gax.rpc.BidiStreamingCallable; +import com.google.api.gax.rpc.ClientStream; import com.google.cloud.firestore.Query.Direction; import com.google.cloud.firestore.WatchTest.SnapshotDocument.ChangeType; import com.google.cloud.firestore.spi.v1.FirestoreRpc; @@ -103,7 +103,8 @@ public class WatchTest { .build(), firestoreRpc); - @Captor private ArgumentCaptor> streamObserverCapture; + @Captor + private ArgumentCaptor> streamObserverCapture; /** Executor that executes delayed tasks without delay. */ private final ScheduledExecutorService immediateExecutor = @@ -272,6 +273,17 @@ public void queryWatchRemoveTargetWithStatus() throws InterruptedException { awaitException(Code.ABORTED); } + @Test + public void queryWatchShutsDownStreamOnPermissionDenied() throws InterruptedException { + addQueryListener(); + + awaitAddTarget(); + send(removeTarget(Code.PERMISSION_DENIED)); + awaitClose(); + + awaitException(Code.PERMISSION_DENIED); + } + @Test public void queryWatchReopensOnUnexceptedStreamEnd() throws InterruptedException { addQueryListener(); @@ -975,7 +987,7 @@ private ListenResponse docRemove(String docPath) { } private void send(ListenResponse response) { - streamObserverCapture.getValue().onNext(response); + streamObserverCapture.getValue().onResponse(response); } private void destroy(Code code) { @@ -983,27 +995,30 @@ private void destroy(Code code) { } private void close() { - streamObserverCapture.getValue().onCompleted(); + streamObserverCapture.getValue().onComplete(); } /** Returns a new request observer that persists its input. */ - private Answer newRequestObserver() { + private Answer> newRequestObserver() { return invocationOnMock -> - new ApiStreamObserver() { + new ClientStream() { @Override - public void onNext(ListenRequest listenRequest) { + public void send(ListenRequest listenRequest) { requests.add(listenRequest); } @Override - public void onError(Throwable throwable) { - fail("Received unexpected error"); - } + public void closeSendWithError(Throwable throwable) {} @Override - public void onCompleted() { + public void closeSend() { closes.release(); } + + @Override + public boolean isSendReady() { + return true; + } }; } }