Skip to content

Commit 0beff58

Browse files
[feat] Proto API for Gateway service (#11)
API object model for the controller implementing getting a SubmitQueue land request Co-authored-by: sergeyb <sergeyb@uber.com>
1 parent 9cb133f commit 0beff58

11 files changed

Lines changed: 672 additions & 51 deletions

File tree

gateway/controller/BUILD.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ load("@rules_go//go:def.bzl", "go_library", "go_test")
22

33
go_library(
44
name = "controller",
5-
srcs = ["ping.go"],
5+
srcs = ["ping.go", "land.go"],
66
importpath = "github.com/uber/submitqueue/gateway/controller",
77
visibility = ["//visibility:public"],
88
deps = [
@@ -14,7 +14,7 @@ go_library(
1414

1515
go_test(
1616
name = "controller_test",
17-
srcs = ["ping_test.go"],
17+
srcs = ["ping_test.go", "land_test.go"],
1818
embed = [":controller"],
1919
deps = ["//gateway/protopb"],
2020
)

gateway/controller/land.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package controller
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"github.com/uber-go/tally/v4"
9+
pb "github.com/uber/submitqueue/gateway/protopb"
10+
"go.uber.org/zap"
11+
)
12+
13+
// LandController handles land business logic for the gateway
14+
type LandController struct {
15+
logger *zap.Logger
16+
metricsScope tally.Scope
17+
}
18+
19+
// NewLandController creates a new instance of the gateway land controller
20+
func NewLandController(logger *zap.Logger, scope tally.Scope) *LandController {
21+
if logger == nil {
22+
logger = zap.NewNop()
23+
}
24+
if scope == nil {
25+
scope = tally.NoopScope
26+
}
27+
28+
return &LandController{
29+
logger: logger,
30+
metricsScope: scope,
31+
}
32+
}
33+
34+
// Land handles the land request and returns a response
35+
func (c *LandController) Land(ctx context.Context, req *pb.LandRequest) (*pb.LandResponse, error) {
36+
start := time.Now()
37+
defer func() {
38+
c.metricsScope.Timer("land_request_latency").Record(time.Since(start))
39+
}()
40+
41+
c.metricsScope.Counter("land_request_count").Inc(1)
42+
43+
// TODO: Implement proper SQID generation and send the request to the appropriate queue. So far unix time to make it sequential.
44+
sqid := fmt.Sprintf("%d", time.Now().Unix())
45+
46+
c.logger.Debug("land request received",
47+
zap.String("queue", req.Queue),
48+
zap.String("sqid", sqid),
49+
)
50+
51+
return &pb.LandResponse{
52+
Sqid: sqid,
53+
}, nil
54+
}

gateway/controller/land_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package controller
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
pb "github.com/uber/submitqueue/gateway/protopb"
8+
)
9+
10+
func TestNewLandController(t *testing.T) {
11+
controller := NewLandController(nil, nil)
12+
if controller == nil {
13+
t.Fatal("NewLandController() returned nil")
14+
}
15+
}
16+
17+
func TestLand_ReturnsSqid(t *testing.T) {
18+
controller := NewLandController(nil, nil)
19+
ctx := context.Background()
20+
21+
req := &pb.LandRequest{
22+
Queue: "test-queue",
23+
Change: &pb.Change{Source: "github", Ids: []string{"123"}},
24+
}
25+
resp, err := controller.Land(ctx, req)
26+
27+
if err != nil {
28+
t.Fatalf("Land() returned error: %v", err)
29+
}
30+
31+
if resp.Sqid == "" {
32+
t.Fatal("Expected sqid to be set, got empty string")
33+
}
34+
}

gateway/proto/gateway.proto

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ option java_multiple_files = true;
77
option java_outer_classname = "GatewayProto";
88
option java_package = "com.uber.devexp.submitqueue.gateway";
99

10+
// ***************
11+
// API domain definitions.
12+
// ***************
13+
1014
// PingRequest is the request for the Ping method
1115
message PingRequest {
1216
// Optional message to include in the ping
@@ -25,8 +29,72 @@ message PingResponse {
2529
string hostname = 4;
2630
}
2731

32+
// Strategy defines the possible source control integration methods
33+
enum Strategy {
34+
// Default strategy (let server decide based on configuration)
35+
STRATEGY_DEFAULT = 0;
36+
// Rebase commits onto target branch before landing
37+
STRATEGY_REBASE = 1;
38+
// Same as REBASE but squash commits into a single commit before rebase
39+
STRATEGY_SQUASH_REBASE = 2;
40+
// Merge commits into the target branch by creating a separate merge commit, preserving the commit history along with hashes
41+
STRATEGY_MERGE = 3;
42+
}
43+
44+
// Change represents a set of related code changes identified by one or more IDs from a particular code change provider, like Github Pull Requests.
45+
message Change {
46+
// The code change provider (e.g., "github", "gerrit", "phabricator").
47+
string source = 1;
48+
// List of change IDs, in a format specific to the code change provider, that should be landed together. SubmitQueue guarantees that the changes are landed in the order of the list,
49+
// and no other changes are landed in between. SubmitQueue does not guarantee that each change is individually valid, but produces a special validity
50+
// marker on such changes. The user is responsible to include all changes in a change stack in the order of the list, starting from the earlist change.
51+
repeated string ids = 2;
52+
}
53+
54+
// LandRequest defines a request to land (merge into target branch of the source control repository) a set of code changes.
55+
message LandRequest {
56+
// Name of the queue processing the land request. The queue should be defined in the configuration, otherwise an error is returned.
57+
string queue = 1;
58+
// Change (such as a pull request) to land into the target branch. Target branch is defined by the queue configuration.
59+
Change change = 2;
60+
// Source control integration strategy to use for this land operation. If not specified, the default queue strategy is used.
61+
Strategy strategy = 4;
62+
}
63+
64+
// LandResponse defines the response to a land request.
65+
message LandResponse {
66+
// Globally unique identifier for the land request. Used to track the land request lifecycle.
67+
string sqid = 1;
68+
}
69+
70+
// ***************
71+
// Error messages, returned as `google.rpc.Status` messages.
72+
// ***************
73+
74+
// Generic error with metadata. Each custom error type should extend this message.
75+
message Error {
76+
// Free text error message describing the error.
77+
string message = 1;
78+
}
79+
80+
// UnrecognizedQueueError is returned when a queue name is not recognized. Typically this indicates a typo in the queue name or server misconfiguration.
81+
message UnrecognizedQueueError {
82+
// Free text error message describing the error.
83+
Error error = 1;
84+
// Name of the queue that was not recognized.
85+
string queue = 2;
86+
}
87+
88+
// ***************
89+
// Service definitions.
90+
// ***************
91+
2892
// SubmitQueueGateway provides the gateway API
2993
service SubmitQueueGateway {
3094
// Ping returns a response indicating the service is alive
3195
rpc Ping(PingRequest) returns (PingResponse) {}
96+
97+
// Land lands a set of code changes into a target branch, performing the necessary validations across all other changes in the queue.
98+
// The processing is asynchronous and returns a LandResponse immediately. The land request is processed in the background.
99+
rpc Land(LandRequest) returns (LandResponse) {}
32100
}

0 commit comments

Comments
 (0)