From edc2bf7346ea88d8db773535bf62a59dbea4fb20 Mon Sep 17 00:00:00 2001 From: Alex Panchenko <440271+panchenko@users.noreply.github.com> Date: Fri, 4 Apr 2025 16:09:35 +0200 Subject: [PATCH] stub: Utility method StreamObservers.nextAndComplete() that does both onNext and onComplete (#11778) --- .../java/io/grpc/stub/StreamObservers.java | 23 +++++++++--- .../io/grpc/stub/StreamObserversTest.java | 35 +++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 stub/src/test/java/io/grpc/stub/StreamObserversTest.java diff --git a/stub/src/main/java/io/grpc/stub/StreamObservers.java b/stub/src/main/java/io/grpc/stub/StreamObservers.java index 2cc53ea0aa..a421d3eca2 100644 --- a/stub/src/main/java/io/grpc/stub/StreamObservers.java +++ b/stub/src/main/java/io/grpc/stub/StreamObservers.java @@ -23,12 +23,21 @@ import java.util.Iterator; /** * Utility functions for working with {@link StreamObserver} and it's common subclasses like * {@link CallStreamObserver}. - * - * @deprecated Of questionable utility and generally not used. */ -@Deprecated -@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4694") public final class StreamObservers { + // Prevent instantiation + private StreamObservers() { } + + /** + * Utility method to call {@link StreamObserver#onNext(Object)} and + * {@link StreamObserver#onCompleted()} on the specified responseObserver. + */ + @ExperimentalApi("https://github.com/grpc/grpc-java/issues/10957") + public static void nextAndComplete(StreamObserver responseObserver, T response) { + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + /** * Copy the values of an {@link Iterator} to the target {@link CallStreamObserver} while properly * accounting for outbound flow-control. After calling this method, {@code target} should no @@ -40,7 +49,10 @@ public final class StreamObservers { * * @param source of values expressed as an {@link Iterator}. * @param target {@link CallStreamObserver} which accepts values from the source. + * @deprecated Of questionable utility and generally not used. */ + @Deprecated + @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4694") public static void copyWithFlowControl(final Iterator source, final CallStreamObserver target) { Preconditions.checkNotNull(source, "source"); @@ -80,7 +92,10 @@ public final class StreamObservers { * * @param source of values expressed as an {@link Iterable}. * @param target {@link CallStreamObserver} which accepts values from the source. + * @deprecated Of questionable utility and generally not used. */ + @Deprecated + @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4694") public static void copyWithFlowControl(final Iterable source, CallStreamObserver target) { Preconditions.checkNotNull(source, "source"); diff --git a/stub/src/test/java/io/grpc/stub/StreamObserversTest.java b/stub/src/test/java/io/grpc/stub/StreamObserversTest.java new file mode 100644 index 0000000000..86a1aba2e7 --- /dev/null +++ b/stub/src/test/java/io/grpc/stub/StreamObserversTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2025 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.stub; + +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; + +public class StreamObserversTest { + + @Test + public void nextAndComplete() { + @SuppressWarnings("unchecked") + StreamObserver observer = Mockito.mock(StreamObserver.class); + InOrder inOrder = Mockito.inOrder(observer); + StreamObservers.nextAndComplete(observer, "TEST"); + inOrder.verify(observer).onNext("TEST"); + inOrder.verify(observer).onCompleted(); + inOrder.verifyNoMoreInteractions(); + } +}