Skip to content

Commit 4ed7258

Browse files
interface for change provider and update request to include uri
1 parent 753235b commit 4ed7258

15 files changed

Lines changed: 139 additions & 96 deletions

File tree

entity/request.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ const (
3333
RequestStateError RequestState = "error"
3434
)
3535

36-
// Change represents a set of related code changes identified by one or more IDs from a particular code change provider, like Github Pull Requests.
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.
3737
// The object is immutable after creation.
3838
type Change struct {
39-
// Source is the code change provider (e.g., "github", "gerrit", "phabricator").
39+
// Source is the code change provider ID that maps to a registered provider (e.g., "github", "github-enterprise", "phabricator").
4040
Source string `json:"source"`
41-
// IDs is a list of change IDs, in a format specific to the code change provider, that should be landed together.
42-
IDs []string `json:"ids"`
41+
// URIs is a list of change URIs that should be landed together. The format is provider-specific:
42+
// - GitHub: "owner/repo/pr_number@commit_hash" (e.g., "uber/submitqueue/123@abc123def")
43+
// - Phabricator: "revision_id@commit_hash" (e.g., "D12345@abc123def")
44+
URIs []string `json:"uris"`
4345
}
4446

4547
// 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: 10 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{Source: "github", IDs: []string{"PR-456", "PR-789"}},
14+
Change: Change{Source: "github", URIs: []string{"uber/submitqueue/456@abc123def", "uber/submitqueue/789@def456abc"}},
1515
LandStrategy: RequestLandStrategyRebase,
1616
State: RequestStateNew,
1717
Version: 1,
@@ -25,7 +25,7 @@ func TestRequest_ToBytes(t *testing.T) {
2525
jsonStr := string(data)
2626
assert.Contains(t, jsonStr, "test-queue/123")
2727
assert.Contains(t, jsonStr, "github")
28-
assert.Contains(t, jsonStr, "PR-456")
28+
assert.Contains(t, jsonStr, "uber/submitqueue/456@abc123def")
2929
assert.Contains(t, jsonStr, "rebase")
3030
assert.Contains(t, jsonStr, "new")
3131
}
@@ -34,7 +34,7 @@ func TestRequestFromBytes(t *testing.T) {
3434
original := Request{
3535
ID: "my-queue/999",
3636
Queue: "my-queue",
37-
Change: Change{Source: "gerrit", IDs: []string{"CL-111"}},
37+
Change: Change{Source: "phabricator", URIs: []string{"D111@fedcba987"}},
3838
LandStrategy: RequestLandStrategyMerge,
3939
State: RequestStateProcessing,
4040
Version: 3,
@@ -52,7 +52,7 @@ func TestRequestFromBytes(t *testing.T) {
5252
assert.Equal(t, original.ID, deserialized.ID)
5353
assert.Equal(t, original.Queue, deserialized.Queue)
5454
assert.Equal(t, original.Change.Source, deserialized.Change.Source)
55-
assert.Equal(t, original.Change.IDs, deserialized.Change.IDs)
55+
assert.Equal(t, original.Change.URIs, deserialized.Change.URIs)
5656
assert.Equal(t, original.LandStrategy, deserialized.LandStrategy)
5757
assert.Equal(t, original.State, deserialized.State)
5858
assert.Equal(t, original.Version, deserialized.Version)
@@ -85,33 +85,33 @@ func TestRequest_SerializationRoundTrip(t *testing.T) {
8585
req Request
8686
}{
8787
{
88-
name: "full request",
88+
name: "full request with multiple PRs",
8989
req: Request{
9090
ID: "queue1/100",
9191
Queue: "queue1",
92-
Change: Change{Source: "github", IDs: []string{"PR-1", "PR-2", "PR-3"}},
92+
Change: Change{Source: "github", URIs: []string{"uber/repo-a/101@aaa111", "uber/repo-a/102@bbb222", "uber/repo-a/103@ccc333"}},
9393
LandStrategy: RequestLandStrategySquashRebase,
9494
State: RequestStateLanded,
9595
Version: 5,
9696
},
9797
},
9898
{
99-
name: "minimal request",
99+
name: "phabricator revision",
100100
req: Request{
101101
ID: "queue2/200",
102102
Queue: "queue2",
103-
Change: Change{Source: "phabricator", IDs: []string{"D123"}},
103+
Change: Change{Source: "phabricator", URIs: []string{"D12345@abc123def456"}},
104104
LandStrategy: RequestLandStrategyRebase,
105105
State: RequestStateNew,
106106
Version: 1,
107107
},
108108
},
109109
{
110-
name: "error state request",
110+
name: "github enterprise request",
111111
req: Request{
112112
ID: "queue3/300",
113113
Queue: "queue3",
114-
Change: Change{Source: "github", IDs: []string{"PR-999"}},
114+
Change: Change{Source: "github-enterprise", URIs: []string{"internal/service/999@deadbeef12"}},
115115
LandStrategy: RequestLandStrategyMerge,
116116
State: RequestStateError,
117117
Version: 10,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package changeprovider
2+
3+
import "context"
4+
5+
// FileChangeStatus defines the type of modification made to a file.
6+
type FileChangeStatus string
7+
8+
const (
9+
// FileChangeStatusAdded indicates a new file was added.
10+
FileChangeStatusAdded FileChangeStatus = "added"
11+
// FileChangeStatusModified indicates an existing file was modified.
12+
FileChangeStatusModified FileChangeStatus = "modified"
13+
// FileChangeStatusDeleted indicates a file was deleted.
14+
FileChangeStatusDeleted FileChangeStatus = "deleted"
15+
)
16+
17+
// FileChange represents a single file modification in a change.
18+
type FileChange struct {
19+
// Path is the file path relative to the repository root.
20+
Path string
21+
// Status is the type of change made to the file.
22+
Status FileChangeStatus
23+
}
24+
25+
// ChangeProvider fetches the list of changed files from external code review providers.
26+
// Each implementation is configured for a specific provider (GitHub, GitLab, Phabricator).
27+
type ChangeProvider interface {
28+
// Get retrieves the list of files changed across all provided URIs.
29+
// The URI format is provider-specific:
30+
// - GitHub: "owner/repo/pr_number@commit_hash" (e.g., "uber/submitqueue/123@abc123def")
31+
// - Phabricator: "revision_id@commit_hash" (e.g., "D12345@abc123def")
32+
// Returns the aggregated list of changed files across all URIs.
33+
Get(ctx context.Context, uris []string) ([]FileChange, error)
34+
}

extension/storage/mysql/request_store.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ func NewRequestStore(db *sql.DB) storage.RequestStore {
2525
// Get retrieves a land request by ID. Returns ErrNotFound if the request is not found.
2626
func (r *requestStore) Get(ctx context.Context, id string) (entity.Request, error) {
2727
var req entity.Request
28-
var changeIDsJSON []byte
28+
var changeURIsJSON []byte
2929

3030
err := r.db.QueryRowContext(ctx,
31-
"SELECT id, queue, change_source, change_ids, land_strategy, state, version FROM request WHERE id = ?",
31+
"SELECT id, queue, change_source, change_uris, land_strategy, state, version FROM request WHERE id = ?",
3232
id,
33-
).Scan(&req.ID, &req.Queue, &req.Change.Source, &changeIDsJSON, &req.LandStrategy, &req.State, &req.Version)
33+
).Scan(&req.ID, &req.Queue, &req.Change.Source, &changeURIsJSON, &req.LandStrategy, &req.State, &req.Version)
3434

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

42-
if err := json.Unmarshal(changeIDsJSON, &req.Change.IDs); err != nil {
43-
return entity.Request{}, fmt.Errorf("failed to unmarshal change IDs for request entity id=%s from the database: %w", id, err)
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)
4444
}
4545

4646
return req, nil
4747
}
4848

4949
// Create creates a new land request. The request must have a unique ID already assigned. Returns ErrAlreadyExists if the request ID already exists.
5050
func (r *requestStore) Create(ctx context.Context, request entity.Request) error {
51-
changeIDsJSON, err := json.Marshal(request.Change.IDs)
51+
changeURIsJSON, err := json.Marshal(request.Change.URIs)
5252
if err != nil {
53-
return fmt.Errorf("failed to marshal change IDs=%v id=%s for Create request entity: %w", request.Change.IDs, request.ID, err)
53+
return fmt.Errorf("failed to marshal change URIs=%v id=%s for Create request entity: %w", request.Change.URIs, request.ID, err)
5454
}
5555

5656
_, err = r.db.ExecContext(ctx,
57-
"INSERT INTO request (id, queue, change_source, change_ids, land_strategy, state, version) VALUES (?, ?, ?, ?, ?, ?, ?)",
58-
request.ID, request.Queue, request.Change.Source, changeIDsJSON, request.LandStrategy, request.State, request.Version,
57+
"INSERT INTO request (id, queue, change_source, change_uris, land_strategy, state, version) VALUES (?, ?, ?, ?, ?, ?, ?)",
58+
request.ID, request.Queue, request.Change.Source, changeURIsJSON, request.LandStrategy, request.State, request.Version,
5959
)
6060
if err != nil {
6161
var mysqlErr *mysql.MySQLError

extension/storage/mysql/schema/request.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CREATE TABLE IF NOT EXISTS request (
22
id VARCHAR(255) NOT NULL,
33
queue VARCHAR(255) NOT NULL,
44
change_source VARCHAR(255) NOT NULL,
5-
change_ids JSON NOT NULL,
5+
change_uris JSON NOT NULL,
66
land_strategy VARCHAR(64) NOT NULL,
77
state VARCHAR(64) NOT NULL,
88
version INT NOT NULL,

gateway/controller/land.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ func (c *LandController) Land(ctx context.Context, req *pb.LandRequest) (*pb.Lan
6464
if req.Change == nil || req.Change.Source == "" {
6565
return nil, fmt.Errorf("LandController requires the request to have a change source specified: %w", ErrInvalidRequest)
6666
}
67-
if len(req.Change.GetIds()) == 0 {
68-
return nil, fmt.Errorf("LandController requires the request to have at least one change ID specified: %w", ErrInvalidRequest)
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)
6969
}
7070

7171
change := entity.Change{
7272
Source: req.Change.GetSource(),
73-
IDs: req.Change.GetIds(),
73+
URIs: req.Change.GetUris(),
7474
}
7575

7676
// TODO: validate that queue is configured. Return error if not.
@@ -105,7 +105,7 @@ func (c *LandController) Land(ctx context.Context, req *pb.LandRequest) (*pb.Lan
105105
"queue", req.Queue,
106106
"sqid", request.ID,
107107
"change_source", change.Source,
108-
"change_ids", change.IDs,
108+
"change_uris", change.URIs,
109109
"strategy", string(strategy),
110110
)
111111

gateway/controller/land_test.go

Lines changed: 12 additions & 12 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", Ids: []string{"123"}},
219+
Change: &pb.Change{Source: "github", Uris: []string{"uber/test-repo/123@abc123def"}},
220220
}
221221
resp, err := controller.Land(ctx, req)
222222

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

249249
req := &pb.LandRequest{
250250
Queue: "my-queue",
251-
Change: &pb.Change{Source: "github", Ids: []string{"pr-1", "pr-2"}},
251+
Change: &pb.Change{Source: "github", Uris: []string{"uber/myservice/1@abc111", "uber/myservice/2@def222"}},
252252
Strategy: pb.Strategy_REBASE,
253253
}
254254
resp, err := controller.Land(ctx, req)
@@ -257,7 +257,7 @@ func TestLand_PassesCorrectParametersToStore(t *testing.T) {
257257
assert.Equal(t, "my-queue/42", capturedRequest.ID)
258258
assert.Equal(t, "my-queue", capturedRequest.Queue)
259259
assert.Equal(t, "github", capturedRequest.Change.Source)
260-
assert.Equal(t, []string{"pr-1", "pr-2"}, capturedRequest.Change.IDs)
260+
assert.Equal(t, []string{"uber/myservice/1@abc111", "uber/myservice/2@def222"}, capturedRequest.Change.URIs)
261261
assert.Equal(t, entity.RequestLandStrategyRebase, capturedRequest.LandStrategy)
262262
assert.Equal(t, entity.RequestStateNew, capturedRequest.State)
263263
assert.Equal(t, int32(1), capturedRequest.Version)
@@ -285,7 +285,7 @@ func TestLand_ReturnsErrorOnStorageFailure(t *testing.T) {
285285

286286
req := &pb.LandRequest{
287287
Queue: "test-queue",
288-
Change: &pb.Change{Source: "github", Ids: []string{"123"}},
288+
Change: &pb.Change{Source: "github", Uris: []string{"uber/test-repo/123@abc123def"}},
289289
}
290290
_, err := controller.Land(ctx, req)
291291

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

314314
req := &pb.LandRequest{
315315
Queue: "test-queue",
316-
Change: &pb.Change{Source: "github", Ids: []string{"123"}},
316+
Change: &pb.Change{Source: "github", Uris: []string{"uber/test-repo/123@abc123def"}},
317317
}
318318
_, err := controller.Land(ctx, req)
319319

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

345345
req := &pb.LandRequest{
346346
Queue: "my-queue",
347-
Change: &pb.Change{Source: "github", Ids: []string{"123"}},
347+
Change: &pb.Change{Source: "github", Uris: []string{"uber/test-repo/123@abc123def"}},
348348
}
349349
_, err := controller.Land(ctx, req)
350350

@@ -366,7 +366,7 @@ func TestLand_ReturnsErrorOnEmptyQueue(t *testing.T) {
366366

367367
req := &pb.LandRequest{
368368
Queue: "",
369-
Change: &pb.Change{Source: "github", Ids: []string{"123"}},
369+
Change: &pb.Change{Source: "github", Uris: []string{"uber/test-repo/123@abc123def"}},
370370
}
371371
_, err := controller.Land(ctx, req)
372372

@@ -388,7 +388,7 @@ func TestLand_ReturnsErrorOnEmptyChangeSource(t *testing.T) {
388388

389389
req := &pb.LandRequest{
390390
Queue: "test-queue",
391-
Change: &pb.Change{Source: "", Ids: []string{"123"}},
391+
Change: &pb.Change{Source: "", Uris: []string{"uber/test-repo/123@abc123def"}},
392392
}
393393
_, err := controller.Land(ctx, req)
394394

@@ -432,7 +432,7 @@ func TestLand_ReturnsErrorOnEmptyChangeIDs(t *testing.T) {
432432

433433
req := &pb.LandRequest{
434434
Queue: "test-queue",
435-
Change: &pb.Change{Source: "github", Ids: []string{}},
435+
Change: &pb.Change{Source: "github", Uris: []string{}},
436436
}
437437
_, err := controller.Land(ctx, req)
438438

@@ -463,7 +463,7 @@ func TestLand_PublishesToQueue(t *testing.T) {
463463

464464
req := &pb.LandRequest{
465465
Queue: "test-queue",
466-
Change: &pb.Change{Source: "github", Ids: []string{"PR-456"}},
466+
Change: &pb.Change{Source: "github", Uris: []string{"uber/backend/456@fed987cba"}},
467467
Strategy: pb.Strategy_REBASE,
468468
}
469469
resp, err := controller.Land(ctx, req)
@@ -482,7 +482,7 @@ func TestLand_PublishesToQueue(t *testing.T) {
482482
assert.Equal(t, "test-queue/123", deserializedReq.ID)
483483
assert.Equal(t, "test-queue", deserializedReq.Queue)
484484
assert.Equal(t, "github", deserializedReq.Change.Source)
485-
assert.Equal(t, []string{"PR-456"}, deserializedReq.Change.IDs)
485+
assert.Equal(t, []string{"uber/backend/456@fed987cba"}, deserializedReq.Change.URIs)
486486
assert.Equal(t, entity.RequestLandStrategyRebase, deserializedReq.LandStrategy)
487487
assert.Equal(t, entity.RequestStateNew, deserializedReq.State)
488488
assert.Equal(t, int32(1), deserializedReq.Version)
@@ -506,7 +506,7 @@ func TestLand_ContinuesWhenPublishFails(t *testing.T) {
506506

507507
req := &pb.LandRequest{
508508
Queue: "test-queue",
509-
Change: &pb.Change{Source: "github", Ids: []string{"PR-1"}},
509+
Change: &pb.Change{Source: "github", Uris: []string{"uber/service/1@abc123def"}},
510510
}
511511
_, err := controller.Land(ctx, req)
512512

gateway/proto/gateway.proto

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

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.
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.
4545
message Change {
46-
// The code change provider (e.g., "github", "gerrit", "phabricator").
46+
// The code change provider ID that maps to a registered provider (e.g., "github", "github-enterprise", "phabricator").
4747
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;
48+
// List of change URIs that should be landed together. The format is provider-specific:
49+
// - GitHub: "owner/repo/pr_number@commit_hash" (e.g., "uber/submitqueue/123@abc123def")
50+
// - Phabricator: "revision_id@commit_hash" (e.g., "D12345@abc123def")
51+
// SubmitQueue guarantees that the changes are landed in the order of the list, and no other changes are landed in between.
52+
// SubmitQueue does not guarantee that each change is individually valid, but produces a special validity marker on such changes.
53+
// The user is responsible to include all changes in a change stack in the order of the list, starting from the earliest change.
54+
repeated string uris = 2;
5255
}
5356

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

gateway/protopb/gateway.pb.go

Lines changed: 14 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)