[Pinpoint] Expose QueryPairwise RPC in service.proto

The QueryPairwise RPC is fully implemented in service_impl.go but was
missing from the Pinpoint service definition in service.proto.
Consequently, no gRPC or REST gateway endpoints were generated,
making it impossible to query Pairwise/Try Jobs via the public API.

This CL adds the QueryPairwise RPC declaration to service.proto and
regenerates all protobuf and HTTP reverse-proxy Go stubs.

Bug: b/510066948
Change-Id: I647a7216473c75ed604d7a93ed25dfb58d910c59
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/1226889
Reviewed-by: Maxim Sheshukov <maximsheshukov@google.com>
Reviewed-by: Jeff Yoon <jeffyoon@google.com>
Commit-Queue: Eduardo Yap <eduardoyap@google.com>
diff --git a/pinpoint/README.md b/pinpoint/README.md
index 485c296..5424f76 100644
--- a/pinpoint/README.md
+++ b/pinpoint/README.md
@@ -154,13 +154,18 @@
 
 ### C. Via Go Pinpoint REST Query API
 
-Once the workflow completes, retrieve the Wilcoxon Signed-Rank test
-statistical results (p-values, control/treatment medians, and swarming task
-logs) programmatically:
+Once the bisection workflow completes, retrieve the statistical results programmatically:
 
 ```bash
 curl -i -X GET "http://localhost:8080/pinpoint/v1/query?job_id=[your-job-id]"
 ```
 
-_Note: If `QueryBisection` shows unimplemented, use the Temporal CLI or Web
-UI to fetch outcomes natively._
+### D. Via Go Pinpoint REST QueryPairwise API (New)
+
+Once a **Pairwise/Try Job** completes, retrieve the Wilcoxon Signed-Rank
+test statistical results, control/treatment medians, p-values, and all
+swarming task IDs:
+
+```bash
+curl -i -X GET "http://localhost:8080/pinpoint/v1/query-pairwise?job_id=[your-job-id]"
+```
diff --git a/pinpoint/proto/v1/service.pb.go b/pinpoint/proto/v1/service.pb.go
index 413e7ac..03eed79 100644
--- a/pinpoint/proto/v1/service.pb.go
+++ b/pinpoint/proto/v1/service.pb.go
@@ -2787,13 +2787,14 @@
 	"\x1bPAIRWISE_JOB_STATUS_RUNNING\x10\x01\x12!\n" +
 	"\x1dPAIRWISE_JOB_STATUS_COMPLETED\x10\x02\x12\x1e\n" +
 	"\x1aPAIRWISE_JOB_STATUS_FAILED\x10\x03\x12 \n" +
-	"\x1cPAIRWISE_JOB_STATUS_CANCELED\x10\x042\x8d\x06\n" +
+	"\x1cPAIRWISE_JOB_STATUS_CANCELED\x10\x042\x8a\a\n" +
 	"\bPinpoint\x12\x8e\x01\n" +
 	"\x11ScheduleBisection\x12\".pinpoint.v1.ScheduleBisectRequest\x1a\x1c.pinpoint.v1.BisectExecution\"7\x82\xd3\xe4\x93\x021Z\x17\"\x15/pinpoint/v1/schedule\"\x16/pinpoint/v1/bisection\x12g\n" +
 	"\tCancelJob\x12\x1d.pinpoint.v1.CancelJobRequest\x1a\x1e.pinpoint.v1.CancelJobResponse\"\x1b\x82\xd3\xe4\x93\x02\x15\x12\x13/pinpoint/v1/cancel\x12k\n" +
 	"\x0eQueryBisection\x12\x1f.pinpoint.v1.QueryBisectRequest\x1a\x1c.pinpoint.v1.BisectExecution\"\x1a\x82\xd3\xe4\x93\x02\x14\x12\x12/pinpoint/v1/query\x12p\n" +
 	"\x0eLegacyJobQuery\x12\x1d.pinpoint.v1.LegacyJobRequest\x1a\x1e.pinpoint.v1.LegacyJobResponse\"\x1f\x82\xd3\xe4\x93\x02\x19\x12\x17/pinpoint/v1/legacy-job\x12z\n" +
-	"\x10SchedulePairwise\x12$.pinpoint.v1.SchedulePairwiseRequest\x1a\x1e.pinpoint.v1.PairwiseExecution\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/pinpoint/v1/pairwise\x12\xab\x01\n" +
+	"\x10SchedulePairwise\x12$.pinpoint.v1.SchedulePairwiseRequest\x1a\x1e.pinpoint.v1.PairwiseExecution\" \x82\xd3\xe4\x93\x02\x1a:\x01*\"\x15/pinpoint/v1/pairwise\x12{\n" +
+	"\rQueryPairwise\x12!.pinpoint.v1.QueryPairwiseRequest\x1a\".pinpoint.v1.QueryPairwiseResponse\"#\x82\xd3\xe4\x93\x02\x1d\x12\x1b/pinpoint/v1/query-pairwise\x12\xab\x01\n" +
 	"\x15ScheduleCulpritFinder\x12).pinpoint.v1.ScheduleCulpritFinderRequest\x1a#.pinpoint.v1.CulpritFinderExecution\"B\x82\xd3\xe4\x93\x02<Z\x1d\"\x1b/pinpoint/v1/culprit-finder\"\x1b/pinpoint/v1/culprit_finderB0Z.go.skia.org/infra/pinpoint/proto/v1;pinpointpbb\x06proto3"
 
 var (
@@ -2886,15 +2887,17 @@
 	3,  // 36: pinpoint.v1.Pinpoint.QueryBisection:input_type -> pinpoint.v1.QueryBisectRequest
 	19, // 37: pinpoint.v1.Pinpoint.LegacyJobQuery:input_type -> pinpoint.v1.LegacyJobRequest
 	12, // 38: pinpoint.v1.Pinpoint.SchedulePairwise:input_type -> pinpoint.v1.SchedulePairwiseRequest
-	16, // 39: pinpoint.v1.Pinpoint.ScheduleCulpritFinder:input_type -> pinpoint.v1.ScheduleCulpritFinderRequest
-	11, // 40: pinpoint.v1.Pinpoint.ScheduleBisection:output_type -> pinpoint.v1.BisectExecution
-	5,  // 41: pinpoint.v1.Pinpoint.CancelJob:output_type -> pinpoint.v1.CancelJobResponse
-	11, // 42: pinpoint.v1.Pinpoint.QueryBisection:output_type -> pinpoint.v1.BisectExecution
-	20, // 43: pinpoint.v1.Pinpoint.LegacyJobQuery:output_type -> pinpoint.v1.LegacyJobResponse
-	15, // 44: pinpoint.v1.Pinpoint.SchedulePairwise:output_type -> pinpoint.v1.PairwiseExecution
-	18, // 45: pinpoint.v1.Pinpoint.ScheduleCulpritFinder:output_type -> pinpoint.v1.CulpritFinderExecution
-	40, // [40:46] is the sub-list for method output_type
-	34, // [34:40] is the sub-list for method input_type
+	13, // 39: pinpoint.v1.Pinpoint.QueryPairwise:input_type -> pinpoint.v1.QueryPairwiseRequest
+	16, // 40: pinpoint.v1.Pinpoint.ScheduleCulpritFinder:input_type -> pinpoint.v1.ScheduleCulpritFinderRequest
+	11, // 41: pinpoint.v1.Pinpoint.ScheduleBisection:output_type -> pinpoint.v1.BisectExecution
+	5,  // 42: pinpoint.v1.Pinpoint.CancelJob:output_type -> pinpoint.v1.CancelJobResponse
+	11, // 43: pinpoint.v1.Pinpoint.QueryBisection:output_type -> pinpoint.v1.BisectExecution
+	20, // 44: pinpoint.v1.Pinpoint.LegacyJobQuery:output_type -> pinpoint.v1.LegacyJobResponse
+	15, // 45: pinpoint.v1.Pinpoint.SchedulePairwise:output_type -> pinpoint.v1.PairwiseExecution
+	14, // 46: pinpoint.v1.Pinpoint.QueryPairwise:output_type -> pinpoint.v1.QueryPairwiseResponse
+	18, // 47: pinpoint.v1.Pinpoint.ScheduleCulpritFinder:output_type -> pinpoint.v1.CulpritFinderExecution
+	41, // [41:48] is the sub-list for method output_type
+	34, // [34:41] is the sub-list for method input_type
 	34, // [34:34] is the sub-list for extension type_name
 	34, // [34:34] is the sub-list for extension extendee
 	0,  // [0:34] is the sub-list for field type_name
diff --git a/pinpoint/proto/v1/service.pb.gw.go b/pinpoint/proto/v1/service.pb.gw.go
index d8694d9..7cdf4fb 100644
--- a/pinpoint/proto/v1/service.pb.gw.go
+++ b/pinpoint/proto/v1/service.pb.gw.go
@@ -238,6 +238,42 @@
 }
 
 var (
+	filter_Pinpoint_QueryPairwise_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_Pinpoint_QueryPairwise_0(ctx context.Context, marshaler runtime.Marshaler, client PinpointClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryPairwiseRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Pinpoint_QueryPairwise_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := client.QueryPairwise(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Pinpoint_QueryPairwise_0(ctx context.Context, marshaler runtime.Marshaler, server PinpointServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryPairwiseRequest
+	var metadata runtime.ServerMetadata
+
+	if err := req.ParseForm(); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Pinpoint_QueryPairwise_0); err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	msg, err := server.QueryPairwise(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+var (
 	filter_Pinpoint_ScheduleCulpritFinder_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
 )
 
@@ -465,6 +501,31 @@
 
 	})
 
+	mux.Handle("GET", pattern_Pinpoint_QueryPairwise_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		var err error
+		var annotatedContext context.Context
+		annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/pinpoint.v1.Pinpoint/QueryPairwise", runtime.WithHTTPPathPattern("/pinpoint/v1/query-pairwise"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Pinpoint_QueryPairwise_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+		if err != nil {
+			runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Pinpoint_QueryPairwise_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	mux.Handle("POST", pattern_Pinpoint_ScheduleCulpritFinder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(req.Context())
 		defer cancel()
@@ -688,6 +749,28 @@
 
 	})
 
+	mux.Handle("GET", pattern_Pinpoint_QueryPairwise_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		var err error
+		var annotatedContext context.Context
+		annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/pinpoint.v1.Pinpoint/QueryPairwise", runtime.WithHTTPPathPattern("/pinpoint/v1/query-pairwise"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Pinpoint_QueryPairwise_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+		annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+		if err != nil {
+			runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Pinpoint_QueryPairwise_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	mux.Handle("POST", pattern_Pinpoint_ScheduleCulpritFinder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(req.Context())
 		defer cancel()
@@ -748,6 +831,8 @@
 
 	pattern_Pinpoint_SchedulePairwise_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"pinpoint", "v1", "pairwise"}, ""))
 
+	pattern_Pinpoint_QueryPairwise_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"pinpoint", "v1", "query-pairwise"}, ""))
+
 	pattern_Pinpoint_ScheduleCulpritFinder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"pinpoint", "v1", "culprit_finder"}, ""))
 
 	pattern_Pinpoint_ScheduleCulpritFinder_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"pinpoint", "v1", "culprit-finder"}, ""))
@@ -766,6 +851,8 @@
 
 	forward_Pinpoint_SchedulePairwise_0 = runtime.ForwardResponseMessage
 
+	forward_Pinpoint_QueryPairwise_0 = runtime.ForwardResponseMessage
+
 	forward_Pinpoint_ScheduleCulpritFinder_0 = runtime.ForwardResponseMessage
 
 	forward_Pinpoint_ScheduleCulpritFinder_1 = runtime.ForwardResponseMessage
diff --git a/pinpoint/proto/v1/service.proto b/pinpoint/proto/v1/service.proto
index 2029608..9857a9d 100644
--- a/pinpoint/proto/v1/service.proto
+++ b/pinpoint/proto/v1/service.proto
@@ -540,6 +540,12 @@
 		};
 	}
 
+	rpc QueryPairwise(QueryPairwiseRequest) returns (QueryPairwiseResponse) {
+		option (google.api.http) = {
+			get: "/pinpoint/v1/query-pairwise"
+		};
+	}
+
 	// culprit-finder (a.k.a) sandwich verification
 	rpc ScheduleCulpritFinder(ScheduleCulpritFinderRequest) returns (CulpritFinderExecution) {
 		option (google.api.http) = {
diff --git a/pinpoint/proto/v1/service_grpc.pb.go b/pinpoint/proto/v1/service_grpc.pb.go
index f1504b3..871ed03 100644
--- a/pinpoint/proto/v1/service_grpc.pb.go
+++ b/pinpoint/proto/v1/service_grpc.pb.go
@@ -28,6 +28,7 @@
 	Pinpoint_QueryBisection_FullMethodName        = "/pinpoint.v1.Pinpoint/QueryBisection"
 	Pinpoint_LegacyJobQuery_FullMethodName        = "/pinpoint.v1.Pinpoint/LegacyJobQuery"
 	Pinpoint_SchedulePairwise_FullMethodName      = "/pinpoint.v1.Pinpoint/SchedulePairwise"
+	Pinpoint_QueryPairwise_FullMethodName         = "/pinpoint.v1.Pinpoint/QueryPairwise"
 	Pinpoint_ScheduleCulpritFinder_FullMethodName = "/pinpoint.v1.Pinpoint/ScheduleCulpritFinder"
 )
 
@@ -40,6 +41,7 @@
 	QueryBisection(ctx context.Context, in *QueryBisectRequest, opts ...grpc.CallOption) (*BisectExecution, error)
 	LegacyJobQuery(ctx context.Context, in *LegacyJobRequest, opts ...grpc.CallOption) (*LegacyJobResponse, error)
 	SchedulePairwise(ctx context.Context, in *SchedulePairwiseRequest, opts ...grpc.CallOption) (*PairwiseExecution, error)
+	QueryPairwise(ctx context.Context, in *QueryPairwiseRequest, opts ...grpc.CallOption) (*QueryPairwiseResponse, error)
 	// culprit-finder (a.k.a) sandwich verification
 	ScheduleCulpritFinder(ctx context.Context, in *ScheduleCulpritFinderRequest, opts ...grpc.CallOption) (*CulpritFinderExecution, error)
 }
@@ -102,6 +104,16 @@
 	return out, nil
 }
 
+func (c *pinpointClient) QueryPairwise(ctx context.Context, in *QueryPairwiseRequest, opts ...grpc.CallOption) (*QueryPairwiseResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+	out := new(QueryPairwiseResponse)
+	err := c.cc.Invoke(ctx, Pinpoint_QueryPairwise_FullMethodName, in, out, cOpts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 func (c *pinpointClient) ScheduleCulpritFinder(ctx context.Context, in *ScheduleCulpritFinderRequest, opts ...grpc.CallOption) (*CulpritFinderExecution, error) {
 	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CulpritFinderExecution)
@@ -121,6 +133,7 @@
 	QueryBisection(context.Context, *QueryBisectRequest) (*BisectExecution, error)
 	LegacyJobQuery(context.Context, *LegacyJobRequest) (*LegacyJobResponse, error)
 	SchedulePairwise(context.Context, *SchedulePairwiseRequest) (*PairwiseExecution, error)
+	QueryPairwise(context.Context, *QueryPairwiseRequest) (*QueryPairwiseResponse, error)
 	// culprit-finder (a.k.a) sandwich verification
 	ScheduleCulpritFinder(context.Context, *ScheduleCulpritFinderRequest) (*CulpritFinderExecution, error)
 	mustEmbedUnimplementedPinpointServer()
@@ -148,6 +161,9 @@
 func (UnimplementedPinpointServer) SchedulePairwise(context.Context, *SchedulePairwiseRequest) (*PairwiseExecution, error) {
 	return nil, status.Error(codes.Unimplemented, "method SchedulePairwise not implemented")
 }
+func (UnimplementedPinpointServer) QueryPairwise(context.Context, *QueryPairwiseRequest) (*QueryPairwiseResponse, error) {
+	return nil, status.Error(codes.Unimplemented, "method QueryPairwise not implemented")
+}
 func (UnimplementedPinpointServer) ScheduleCulpritFinder(context.Context, *ScheduleCulpritFinderRequest) (*CulpritFinderExecution, error) {
 	return nil, status.Error(codes.Unimplemented, "method ScheduleCulpritFinder not implemented")
 }
@@ -262,6 +278,24 @@
 	return interceptor(ctx, in, info, handler)
 }
 
+func _Pinpoint_QueryPairwise_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryPairwiseRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(PinpointServer).QueryPairwise(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: Pinpoint_QueryPairwise_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(PinpointServer).QueryPairwise(ctx, req.(*QueryPairwiseRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 func _Pinpoint_ScheduleCulpritFinder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(ScheduleCulpritFinderRequest)
 	if err := dec(in); err != nil {
@@ -308,6 +342,10 @@
 			Handler:    _Pinpoint_SchedulePairwise_Handler,
 		},
 		{
+			MethodName: "QueryPairwise",
+			Handler:    _Pinpoint_QueryPairwise_Handler,
+		},
+		{
 			MethodName: "ScheduleCulpritFinder",
 			Handler:    _Pinpoint_ScheduleCulpritFinder_Handler,
 		},