Skip to content

Commit 9f22651

Browse files
committed
feat(entities) Adds build store interface and mysql implementation
1 parent e848990 commit 9f22651

7 files changed

Lines changed: 155 additions & 0 deletions

File tree

extension/storage/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go_library(
55
srcs = [
66
"batch_dependent_store.go",
77
"batch_store.go",
8+
"build_store.go",
89
"change_provider_store.go",
910
"request_store.go",
1011
"storage.go",

extension/storage/build_store.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package storage
2+
3+
import (
4+
"context"
5+
6+
"github.com/uber/submitqueue/entity"
7+
)
8+
9+
// BuildStore is an interface that defines methods for managing builds in the database.
10+
type BuildStore interface {
11+
// Get retrieves a build by ID. Returns ErrNotFound if the build is not found.
12+
Get(ctx context.Context, id string) (entity.Build, error)
13+
14+
// Create creates a new build. The build must have a unique ID already assigned.
15+
// Returns ErrAlreadyExists if a build with the same ID already exists.
16+
Create(ctx context.Context, build entity.Build) error
17+
18+
// UpdateStatus updates the status of a build.
19+
UpdateStatus(ctx context.Context, id string, newStatus entity.BuildStatus) error
20+
}

extension/storage/mysql/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go_library(
55
srcs = [
66
"batch_dependent_store.go",
77
"batch_store.go",
8+
"build_store.go",
89
"change_provider_store.go",
910
"request_store.go",
1011
"storage.go",
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package mysql
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"encoding/json"
7+
"errors"
8+
"fmt"
9+
10+
"github.com/go-sql-driver/mysql"
11+
12+
"github.com/uber/submitqueue/entity"
13+
"github.com/uber/submitqueue/extension/storage"
14+
)
15+
16+
type buildStore struct {
17+
db *sql.DB
18+
}
19+
20+
// NewBuildStore creates a new MySQL-backed BuildStore.
21+
func NewBuildStore(db *sql.DB) storage.BuildStore {
22+
return &buildStore{db: db}
23+
}
24+
25+
// Get retrieves a build by ID. Returns ErrNotFound if the build is not found.
26+
func (s *buildStore) Get(ctx context.Context, id string) (entity.Build, error) {
27+
var build entity.Build
28+
var speculationPathJSON []byte
29+
30+
err := s.db.QueryRowContext(ctx,
31+
"SELECT id, batch_id, speculation_path, score, status FROM build WHERE id = ?",
32+
id,
33+
).Scan(&build.ID, &build.BatchID, &speculationPathJSON, &build.Score, &build.Status)
34+
35+
if errors.Is(err, sql.ErrNoRows) {
36+
return entity.Build{}, storage.WrapNotFound(err)
37+
}
38+
if err != nil {
39+
return entity.Build{}, fmt.Errorf("failed to get build entity id=%s from the database: %w", id, err)
40+
}
41+
42+
if err := json.Unmarshal(speculationPathJSON, &build.SpeculationPath); err != nil {
43+
return entity.Build{}, fmt.Errorf("failed to unmarshal speculation_path for build entity id=%s from the database: %w", id, err)
44+
}
45+
46+
return build, nil
47+
}
48+
49+
// Create creates a new build. The build must have a unique ID already assigned. Returns ErrAlreadyExists if the build ID already exists.
50+
func (s *buildStore) Create(ctx context.Context, build entity.Build) error {
51+
speculationPathJSON, err := json.Marshal(build.SpeculationPath)
52+
if err != nil {
53+
return fmt.Errorf("failed to marshal speculation_path id=%s for Create build entity: %w", build.ID, err)
54+
}
55+
56+
_, err = s.db.ExecContext(ctx,
57+
"INSERT INTO build (id, batch_id, speculation_path, score, status) VALUES (?, ?, ?, ?, ?)",
58+
build.ID, build.BatchID, speculationPathJSON, build.Score, build.Status,
59+
)
60+
if err != nil {
61+
var mysqlErr *mysql.MySQLError
62+
if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
63+
return fmt.Errorf("build entity id=%s: %w", build.ID, storage.ErrAlreadyExists)
64+
}
65+
return fmt.Errorf("failed to insert build entity id=%s: %w", build.ID, err)
66+
}
67+
68+
return nil
69+
}
70+
71+
// UpdateStatus updates the status of a build. Returns ErrNotFound if the build is not found.
72+
func (s *buildStore) UpdateStatus(ctx context.Context, id string, newStatus entity.BuildStatus) error {
73+
result, err := s.db.ExecContext(ctx,
74+
"UPDATE build SET status = ? WHERE id = ?",
75+
newStatus, id,
76+
)
77+
if err != nil {
78+
return fmt.Errorf("failed to update build status for id=%q newStatus=%v: %w", id, newStatus, err)
79+
}
80+
81+
rowsAffected, err := result.RowsAffected()
82+
if err != nil {
83+
return fmt.Errorf("failed to get rows affected from update for id=%q newStatus=%v: %w", id, newStatus, err)
84+
}
85+
86+
if rowsAffected != 1 {
87+
return storage.WrapNotFound(fmt.Errorf("build entity id=%s", id))
88+
}
89+
90+
return nil
91+
}

extension/storage/mysql/storage.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type mysqlStorage struct {
1414
changeProviderStore storage.ChangeProviderStore
1515
batchStore storage.BatchStore
1616
batchDependentStore storage.BatchDependentStore
17+
buildStore storage.BuildStore
1718
}
1819

1920
// NewStorage creates a new MySQL storage.
@@ -24,6 +25,7 @@ func NewStorage(db *sql.DB) (storage.Storage, error) {
2425
changeProviderStore: NewChangeProviderStore(db),
2526
batchStore: NewBatchStore(db),
2627
batchDependentStore: NewBatchDependentStore(db),
28+
buildStore: NewBuildStore(db),
2729
}, nil
2830
}
2931

@@ -47,6 +49,11 @@ func (f *mysqlStorage) GetBatchDependentStore() storage.BatchDependentStore {
4749
return f.batchDependentStore
4850
}
4951

52+
// GetBuildStore returns the MySQL-backed BuildStore.
53+
func (f *mysqlStorage) GetBuildStore() storage.BuildStore {
54+
return f.buildStore
55+
}
56+
5057
// Close closes the underlying database connection.
5158
func (f *mysqlStorage) Close() error {
5259
return f.db.Close()

extension/storage/storage.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ type Storage interface {
3838
// GetBatchDependentStore returns the BatchDependentStore instance.
3939
GetBatchDependentStore() BatchDependentStore
4040

41+
// GetBuildStore returns the BuildStore instance.
42+
GetBuildStore() BuildStore
43+
4144
// Close closes the storage and all underlying connections. Should only be called once at the end of the program.
4245
Close() error
4346
}

gateway/controller/land_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,39 @@ func (m *mockBatchDependentStore) Create(ctx context.Context, batchDependent ent
9797
return nil
9898
}
9999

100+
type mockBuildStore struct {
101+
createFunc func(ctx context.Context, build entity.Build) error
102+
getFunc func(ctx context.Context, id string) (entity.Build, error)
103+
updateStatusFunc func(ctx context.Context, id string, newStatus entity.BuildStatus) error
104+
}
105+
106+
func (m *mockBuildStore) Get(ctx context.Context, id string) (entity.Build, error) {
107+
if m.getFunc != nil {
108+
return m.getFunc(ctx, id)
109+
}
110+
return entity.Build{}, nil
111+
}
112+
113+
func (m *mockBuildStore) Create(ctx context.Context, build entity.Build) error {
114+
if m.createFunc != nil {
115+
return m.createFunc(ctx, build)
116+
}
117+
return nil
118+
}
119+
120+
func (m *mockBuildStore) UpdateStatus(ctx context.Context, id string, newStatus entity.BuildStatus) error {
121+
if m.updateStatusFunc != nil {
122+
return m.updateStatusFunc(ctx, id, newStatus)
123+
}
124+
return nil
125+
}
126+
100127
type mockStorage struct {
101128
requestStore storage.RequestStore
102129
changeProviderStore storage.ChangeProviderStore
103130
batchStore storage.BatchStore
104131
batchDependentStore storage.BatchDependentStore
132+
buildStore storage.BuildStore
105133
}
106134

107135
func (m *mockStorage) GetRequestStore() storage.RequestStore {
@@ -120,6 +148,10 @@ func (m *mockStorage) GetBatchDependentStore() storage.BatchDependentStore {
120148
return m.batchDependentStore
121149
}
122150

151+
func (m *mockStorage) GetBuildStore() storage.BuildStore {
152+
return m.buildStore
153+
}
154+
123155
func (m *mockStorage) Close() error {
124156
return nil
125157
}

0 commit comments

Comments
 (0)