Skip to content

Commit c93c6cd

Browse files
review changes part 2
1 parent b548ddf commit c93c6cd

23 files changed

Lines changed: 227 additions & 174 deletions

File tree

entity/request.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,18 @@ const (
3333
RequestStateError RequestState = "error"
3434
)
3535

36-
// Change represents a set of related code changes identified by one or more URIs from a particular code change provider, like Github Pull Requests.
37-
// The object is immutable after creation.
36+
// Change represents a code change identified by a URI from a code change provider (e.g., GitHub Pull Request, Phabricator Diff).
37+
// The provider is extracted from the URI scheme. The object is immutable after creation.
3838
type Change struct {
39-
// Provider is the code change provider that maps to a registered provider (e.g., "github", "github-enterprise", "phabricator").
40-
Provider string `json:"provider"`
41-
// URIs is a list of change URIs that should be landed together. The format is determined by the change-provider implementation.
42-
// Default format: "github.com/<org>/<repo>/<pr>/<hash>" (e.g., "github.com/uber/submitqueue/123/abc123def")
43-
URIs []string `json:"uris"`
39+
// URI identifies the change(s) to land (RFC 3986 compliant).
40+
// The scheme identifies the change provider, and the path contains provider-specific resource identifiers.
41+
//
42+
// By default, the GitHub format is supported (though other providers can be added):
43+
// Single PR: "github://<org>/<repo>/pull/<pr>/<hash>"
44+
// Stacked PRs: "github://<org>/<repo>/pull/<pr1>/<hash1>/<pr2>/<hash2>/..."
45+
// Example: "github://uber/submitqueue/pull/123/abc123def"
46+
//
47+
URI string `json:"uri"`
4448
}
4549

4650
// Request defines a request to land (merge into target branch of the source control repository) a set of code changes.

entity/request_test.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func TestRequest_ToBytes(t *testing.T) {
1111
req := Request{
1212
ID: "test-queue/123",
1313
Queue: "test-queue",
14-
Change: Change{Provider: "github", URIs: []string{"github.com/uber/submitqueue/456/abc123def", "github.com/uber/submitqueue/789/def456abc"}},
14+
Change: Change{URI: "github://uber/submitqueue/pull/456/abc123def/789/def456abc"},
1515
LandStrategy: RequestLandStrategyRebase,
1616
State: RequestStateNew,
1717
Version: 1,
@@ -24,8 +24,7 @@ func TestRequest_ToBytes(t *testing.T) {
2424
// Verify JSON contains expected fields
2525
jsonStr := string(data)
2626
assert.Contains(t, jsonStr, "test-queue/123")
27-
assert.Contains(t, jsonStr, "github")
28-
assert.Contains(t, jsonStr, "github.com/uber/submitqueue/456/abc123def")
27+
assert.Contains(t, jsonStr, "github://uber/submitqueue/pull/456/abc123def/789/def456abc")
2928
assert.Contains(t, jsonStr, "rebase")
3029
assert.Contains(t, jsonStr, "new")
3130
}
@@ -34,7 +33,7 @@ func TestRequestFromBytes(t *testing.T) {
3433
original := Request{
3534
ID: "my-queue/999",
3635
Queue: "my-queue",
37-
Change: Change{Provider: "phabricator", URIs: []string{"phabricator.uber.com/D111/fedcba987"}},
36+
Change: Change{URI: "code.uber.internal.com/D111"},
3837
LandStrategy: RequestLandStrategyMerge,
3938
State: RequestStateProcessing,
4039
Version: 3,
@@ -51,8 +50,7 @@ func TestRequestFromBytes(t *testing.T) {
5150
// Verify all fields match
5251
assert.Equal(t, original.ID, deserialized.ID)
5352
assert.Equal(t, original.Queue, deserialized.Queue)
54-
assert.Equal(t, original.Change.Provider, deserialized.Change.Provider)
55-
assert.Equal(t, original.Change.URIs, deserialized.Change.URIs)
53+
assert.Equal(t, original.Change.URI, deserialized.Change.URI)
5654
assert.Equal(t, original.LandStrategy, deserialized.LandStrategy)
5755
assert.Equal(t, original.State, deserialized.State)
5856
assert.Equal(t, original.Version, deserialized.Version)
@@ -85,11 +83,11 @@ func TestRequest_SerializationRoundTrip(t *testing.T) {
8583
req Request
8684
}{
8785
{
88-
name: "full request with multiple PRs",
86+
name: "github stacked diff",
8987
req: Request{
9088
ID: "queue1/100",
9189
Queue: "queue1",
92-
Change: Change{Provider: "github", URIs: []string{"github.com/uber/repo-a/101/aaa111", "github.com/uber/repo-a/102/bbb222", "github.com/uber/repo-a/103/ccc333"}},
90+
Change: Change{URI: "github://uber/repo-a/pull/101/aaa111/102/bbb222/103/ccc333"},
9391
LandStrategy: RequestLandStrategySquashRebase,
9492
State: RequestStateLanded,
9593
Version: 5,
@@ -100,7 +98,7 @@ func TestRequest_SerializationRoundTrip(t *testing.T) {
10098
req: Request{
10199
ID: "queue2/200",
102100
Queue: "queue2",
103-
Change: Change{Provider: "phabricator", URIs: []string{"phabricator.uber.com/D12345/abc123def456"}},
101+
Change: Change{URI: "code.uber.internal.com/D12345"},
104102
LandStrategy: RequestLandStrategyRebase,
105103
State: RequestStateNew,
106104
Version: 1,
@@ -111,7 +109,7 @@ func TestRequest_SerializationRoundTrip(t *testing.T) {
111109
req: Request{
112110
ID: "queue3/300",
113111
Queue: "queue3",
114-
Change: Change{Provider: "github-enterprise", URIs: []string{"github.uber.com/internal/service/999/deadbeef12"}},
112+
Change: Change{URI: "github.uber.com/internal/service/999/deadbeef12"},
115113
LandStrategy: RequestLandStrategyMerge,
116114
State: RequestStateError,
117115
Version: 10,
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
load("@rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "changeprovider",
5+
srcs = ["change_provider.go"],
6+
importpath = "github.com/uber/submitqueue/extension/changeprovider",
7+
visibility = ["//visibility:public"],
8+
)

extension/changeprovider/change_provider.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,15 @@ type ChangeInfo struct {
3434
ChangedFiles []ChangedFile
3535
}
3636

37-
// ChangeProvider fetches change information from external code review providers.
37+
// ChangeProvider fetches change metadata from external systems
3838
// Each implementation is configured for a specific provider (GitHub, GitLab, Phabricator).
3939
type ChangeProvider interface {
40-
// Get retrieves change information for the provided URIs.
41-
// The URI format is determined by the implementation.
42-
// Default format: "github.com/<org>/<repo>/<pr>/<hash>" (e.g., "github.com/uber/submitqueue/123/abc123def")
40+
// Get retrieves change information for the provided URI (RFC 3986 compliant).
41+
// The URI scheme identifies the provider, and the path contains provider-specific resource identifiers.
42+
//
43+
// By default, the GitHub format is supported (though other providers can be added):
44+
// Single PR: "github://<org>/<repo>/pull/<pr>/<hash>"
45+
// Stacked PRs: "github://<org>/<repo>/pull/<pr1>/<hash1>/<pr2>/<hash2>/..."
4346
// Returns the change info containing metadata and file changes.
44-
Get(ctx context.Context, uris []string) (ChangeInfo, error)
47+
Get(ctx context.Context, uri string) (ChangeInfo, error)
4548
}

extension/storage/mysql/request_store.go

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package mysql
33
import (
44
"context"
55
"database/sql"
6-
"encoding/json"
76
"errors"
87
"fmt"
98

@@ -25,12 +24,11 @@ func NewRequestStore(db *sql.DB) storage.RequestStore {
2524
// Get retrieves a land request by ID. Returns ErrNotFound if the request is not found.
2625
func (r *requestStore) Get(ctx context.Context, id string) (entity.Request, error) {
2726
var req entity.Request
28-
var changeURIsJSON []byte
2927

3028
err := r.db.QueryRowContext(ctx,
31-
"SELECT id, queue, change_source, change_uris, land_strategy, state, version FROM request WHERE id = ?",
29+
"SELECT id, queue, change_uri, land_strategy, state, version FROM request WHERE id = ?",
3230
id,
33-
).Scan(&req.ID, &req.Queue, &req.Change.Provider, &changeURIsJSON, &req.LandStrategy, &req.State, &req.Version)
31+
).Scan(&req.ID, &req.Queue, &req.Change.URI, &req.LandStrategy, &req.State, &req.Version)
3432

3533
if errors.Is(err, sql.ErrNoRows) {
3634
return entity.Request{}, storage.WrapNotFound(err)
@@ -39,23 +37,14 @@ func (r *requestStore) Get(ctx context.Context, id string) (entity.Request, erro
3937
return entity.Request{}, fmt.Errorf("failed to get request entity id=%s from the database: %w", id, err)
4038
}
4139

42-
if err := json.Unmarshal(changeURIsJSON, &req.Change.URIs); err != nil {
43-
return entity.Request{}, fmt.Errorf("failed to unmarshal change URIs for request entity id=%s from the database: %w", id, err)
44-
}
45-
4640
return req, nil
4741
}
4842

4943
// Create creates a new land request. The request must have a unique ID already assigned. Returns ErrAlreadyExists if the request ID already exists.
5044
func (r *requestStore) Create(ctx context.Context, request entity.Request) error {
51-
changeURIsJSON, err := json.Marshal(request.Change.URIs)
52-
if err != nil {
53-
return fmt.Errorf("failed to marshal change URIs=%v id=%s for Create request entity: %w", request.Change.URIs, request.ID, err)
54-
}
55-
56-
_, err = r.db.ExecContext(ctx,
57-
"INSERT INTO request (id, queue, change_source, change_uris, land_strategy, state, version) VALUES (?, ?, ?, ?, ?, ?, ?)",
58-
request.ID, request.Queue, request.Change.Provider, changeURIsJSON, request.LandStrategy, request.State, request.Version,
45+
_, err := r.db.ExecContext(ctx,
46+
"INSERT INTO request (id, queue, change_uri, land_strategy, state, version) VALUES (?, ?, ?, ?, ?, ?)",
47+
request.ID, request.Queue, request.Change.URI, request.LandStrategy, request.State, request.Version,
5948
)
6049
if err != nil {
6150
var mysqlErr *mysql.MySQLError

extension/storage/mysql/schema/request.sql

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
CREATE TABLE IF NOT EXISTS request (
22
id VARCHAR(255) NOT NULL,
33
queue VARCHAR(255) NOT NULL,
4-
change_source VARCHAR(255) NOT NULL,
5-
change_uris JSON NOT NULL,
4+
change_uri TEXT NOT NULL,
65
land_strategy VARCHAR(64) NOT NULL,
76
state VARCHAR(64) NOT NULL,
87
version INT NOT NULL,

gateway/controller/land.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,12 @@ func (c *LandController) Land(ctx context.Context, req *pb.LandRequest) (*pb.Lan
6161
if req.Queue == "" {
6262
return nil, fmt.Errorf("LandController requires the request to have a queue name specified: %w", ErrInvalidRequest)
6363
}
64-
if req.Change == nil || req.Change.Source == "" {
65-
return nil, fmt.Errorf("LandController requires the request to have a change source specified: %w", ErrInvalidRequest)
66-
}
67-
if len(req.Change.GetUris()) == 0 {
68-
return nil, fmt.Errorf("LandController requires the request to have at least one change URI specified: %w", ErrInvalidRequest)
64+
if req.Change == nil || req.Change.Uri == "" {
65+
return nil, fmt.Errorf("LandController requires the request to have a change URI specified: %w", ErrInvalidRequest)
6966
}
7067

7168
change := entity.Change{
72-
Provider: req.Change.GetSource(),
73-
URIs: req.Change.GetUris(),
69+
URI: req.Change.GetUri(),
7470
}
7571

7672
// TODO: validate that queue is configured. Return error if not.
@@ -104,8 +100,7 @@ func (c *LandController) Land(ctx context.Context, req *pb.LandRequest) (*pb.Lan
104100
c.logger.Debugw("land request created",
105101
"queue", req.Queue,
106102
"sqid", request.ID,
107-
"change_source", change.Provider,
108-
"change_uris", change.URIs,
103+
"change_uri", change.URI,
109104
"strategy", string(strategy),
110105
)
111106

gateway/controller/land_test.go

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ func TestLand_ReturnsSqid(t *testing.T) {
216216

217217
req := &pb.LandRequest{
218218
Queue: "test-queue",
219-
Change: &pb.Change{Source: "github", Uris: []string{"github.com/uber/test-repo/123/abc123def"}},
219+
Change: &pb.Change{Uri: "github://uber/test-repo/pull/123/abc123def"},
220220
}
221221
resp, err := controller.Land(ctx, req)
222222

@@ -248,16 +248,15 @@ func TestLand_PassesCorrectParametersToStore(t *testing.T) {
248248

249249
req := &pb.LandRequest{
250250
Queue: "my-queue",
251-
Change: &pb.Change{Source: "github", Uris: []string{"github.com/uber/myservice/1/abc111", "github.com/uber/myservice/2/def222"}},
251+
Change: &pb.Change{Uri: "github://uber/myservice/pull/1/abc111/2/def222"},
252252
Strategy: pb.Strategy_REBASE,
253253
}
254254
resp, err := controller.Land(ctx, req)
255255

256256
require.NoError(t, err)
257257
assert.Equal(t, "my-queue/42", capturedRequest.ID)
258258
assert.Equal(t, "my-queue", capturedRequest.Queue)
259-
assert.Equal(t, "github", capturedRequest.Change.Provider)
260-
assert.Equal(t, []string{"github.com/uber/myservice/1/abc111", "github.com/uber/myservice/2/def222"}, capturedRequest.Change.URIs)
259+
assert.Equal(t, "github://uber/myservice/pull/1/abc111/2/def222", capturedRequest.Change.URI)
261260
assert.Equal(t, entity.RequestLandStrategyRebase, capturedRequest.LandStrategy)
262261
assert.Equal(t, entity.RequestStateNew, capturedRequest.State)
263262
assert.Equal(t, int32(1), capturedRequest.Version)
@@ -285,7 +284,7 @@ func TestLand_ReturnsErrorOnStorageFailure(t *testing.T) {
285284

286285
req := &pb.LandRequest{
287286
Queue: "test-queue",
288-
Change: &pb.Change{Source: "github", Uris: []string{"github.com/uber/test-repo/123/abc123def"}},
287+
Change: &pb.Change{Uri: "github://uber/test-repo/pull/123/abc123def"},
289288
}
290289
_, err := controller.Land(ctx, req)
291290

@@ -313,7 +312,7 @@ func TestLand_ReturnsErrorOnCounterFailure(t *testing.T) {
313312

314313
req := &pb.LandRequest{
315314
Queue: "test-queue",
316-
Change: &pb.Change{Source: "github", Uris: []string{"github.com/uber/test-repo/123/abc123def"}},
315+
Change: &pb.Change{Uri: "github://uber/test-repo/pull/123/abc123def"},
317316
}
318317
_, err := controller.Land(ctx, req)
319318

@@ -344,7 +343,7 @@ func TestLand_CounterDomainIncludesQueue(t *testing.T) {
344343

345344
req := &pb.LandRequest{
346345
Queue: "my-queue",
347-
Change: &pb.Change{Source: "github", Uris: []string{"github.com/uber/test-repo/123/abc123def"}},
346+
Change: &pb.Change{Uri: "github://uber/test-repo/pull/123/abc123def"},
348347
}
349348
_, err := controller.Land(ctx, req)
350349

@@ -366,15 +365,15 @@ func TestLand_ReturnsErrorOnEmptyQueue(t *testing.T) {
366365

367366
req := &pb.LandRequest{
368367
Queue: "",
369-
Change: &pb.Change{Source: "github", Uris: []string{"github.com/uber/test-repo/123/abc123def"}},
368+
Change: &pb.Change{Uri: "github://uber/test-repo/pull/123/abc123def"},
370369
}
371370
_, err := controller.Land(ctx, req)
372371

373372
require.Error(t, err)
374373
assert.True(t, IsInvalidRequest(err))
375374
}
376375

377-
func TestLand_ReturnsErrorOnEmptyChangeSource(t *testing.T) {
376+
func TestLand_ReturnsErrorOnEmptyChangeUri(t *testing.T) {
378377
store := &mockStorage{requestStore: &mockRequestStore{
379378
createFunc: func(ctx context.Context, request entity.Request) error {
380379
return nil
@@ -388,7 +387,7 @@ func TestLand_ReturnsErrorOnEmptyChangeSource(t *testing.T) {
388387

389388
req := &pb.LandRequest{
390389
Queue: "test-queue",
391-
Change: &pb.Change{Source: "", Uris: []string{"github.com/uber/test-repo/123/abc123def"}},
390+
Change: &pb.Change{Uri: ""},
392391
}
393392
_, err := controller.Land(ctx, req)
394393

@@ -418,28 +417,6 @@ func TestLand_ReturnsErrorOnNilChange(t *testing.T) {
418417
assert.True(t, IsInvalidRequest(err))
419418
}
420419

421-
func TestLand_ReturnsErrorOnEmptyChangeIDs(t *testing.T) {
422-
store := &mockStorage{requestStore: &mockRequestStore{
423-
createFunc: func(ctx context.Context, request entity.Request) error {
424-
return nil
425-
},
426-
}}
427-
cnt := &mockCounter{nextFunc: func(ctx context.Context, domain string) (int64, error) {
428-
return 1, nil
429-
}}
430-
controller := NewLandController(zap.NewNop().Sugar(), tally.NoopScope, store, cnt, noopPublisher(), "request")
431-
ctx := context.Background()
432-
433-
req := &pb.LandRequest{
434-
Queue: "test-queue",
435-
Change: &pb.Change{Source: "github", Uris: []string{}},
436-
}
437-
_, err := controller.Land(ctx, req)
438-
439-
require.Error(t, err)
440-
assert.True(t, IsInvalidRequest(err))
441-
}
442-
443420
func TestLand_PublishesToQueue(t *testing.T) {
444421
var publishedTopic string
445422
var publishedMessage queue.Message
@@ -463,7 +440,7 @@ func TestLand_PublishesToQueue(t *testing.T) {
463440

464441
req := &pb.LandRequest{
465442
Queue: "test-queue",
466-
Change: &pb.Change{Source: "github", Uris: []string{"github.com/uber/backend/456/fed987cba"}},
443+
Change: &pb.Change{Uri: "github://uber/backend/pull/456/fed987cba"},
467444
Strategy: pb.Strategy_REBASE,
468445
}
469446
resp, err := controller.Land(ctx, req)
@@ -481,8 +458,7 @@ func TestLand_PublishesToQueue(t *testing.T) {
481458
require.NoError(t, err)
482459
assert.Equal(t, "test-queue/123", deserializedReq.ID)
483460
assert.Equal(t, "test-queue", deserializedReq.Queue)
484-
assert.Equal(t, "github", deserializedReq.Change.Provider)
485-
assert.Equal(t, []string{"github.com/uber/backend/456/fed987cba"}, deserializedReq.Change.URIs)
461+
assert.Equal(t, "github://uber/backend/pull/456/fed987cba", deserializedReq.Change.URI)
486462
assert.Equal(t, entity.RequestLandStrategyRebase, deserializedReq.LandStrategy)
487463
assert.Equal(t, entity.RequestStateNew, deserializedReq.State)
488464
assert.Equal(t, int32(1), deserializedReq.Version)
@@ -506,7 +482,7 @@ func TestLand_ContinuesWhenPublishFails(t *testing.T) {
506482

507483
req := &pb.LandRequest{
508484
Queue: "test-queue",
509-
Change: &pb.Change{Source: "github", Uris: []string{"github.com/uber/service/1/abc123def"}},
485+
Change: &pb.Change{Uri: "github://uber/service/pull/1/abc123def"},
510486
}
511487
_, err := controller.Land(ctx, req)
512488

gateway/proto/gateway.proto

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,21 @@ enum Strategy {
4141
MERGE = 3;
4242
}
4343

44-
// Change represents a set of related code changes identified by one or more URIs from a particular code change provider, like Github Pull Requests.
44+
// Change represents a code change identified by a URI from a code change provider (e.g., GitHub Pull Request, Phabricator Diff).
45+
// The provider is extracted from the URI scheme.
4546
message Change {
46-
// The code change provider ID that maps to a registered provider (e.g., "github", "github-enterprise", "phabricator").
47-
string source = 1;
48-
// List of change URIs that should be landed together. The format is determined by the change-provider implementation.
49-
// Default format: "github.com/<org>/<repo>/<pr>/<hash>" (e.g., "github.com/uber/submitqueue/123/abc123def")
50-
// SubmitQueue guarantees that the changes are landed in the order of the list, and no other changes are landed in between.
51-
// SubmitQueue does not guarantee that each change is individually valid, but produces a special validity marker on such changes.
52-
// The user is responsible to include all changes in a change stack in the order of the list, starting from the earliest change.
53-
repeated string uris = 2;
47+
// URI identifying the change(s) to land (RFC 3986 compliant).
48+
// The scheme identifies the change provider, and the path contains provider-specific resource identifiers.
49+
//
50+
// By default, the GitHub format is supported (though other providers can be added):
51+
// Single PR: "github://<org>/<repo>/pull/<pr>/<hash>"
52+
// Stacked PRs: "github://<org>/<repo>/pull/<pr1>/<hash1>/<pr2>/<hash2>/..."
53+
// Example: "github://uber/submitqueue/pull/123/abc123def"
54+
//
55+
// SubmitQueue guarantees changes are landed in order with no other changes in between.
56+
// SubmitQueue does not guarantee each change is individually valid, but produces a validity marker on such changes.
57+
// The user is responsible for including all changes in a stack in order, starting from the earliest change.
58+
string uri = 1;
5459
}
5560

5661
// LandRequest defines a request to land (merge into target branch of the source control repository) a set of code changes.

0 commit comments

Comments
 (0)