Skip to content

Commit 70858e1

Browse files
committed
Move pkg/storage/sql to pkg/storage
1 parent 8b2416c commit 70858e1

35 files changed

+199
-262
lines changed

garden-app/pkg/storage/sql/additional_queries.go renamed to garden-app/pkg/storage/additional_queries.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
package sql
1+
package storage
22

33
import (
44
"context"
55
"database/sql"
66
"fmt"
77

88
"github.com/calvinmclean/automated-garden/garden-app/pkg"
9-
"github.com/calvinmclean/automated-garden/garden-app/pkg/storage/sql/db"
9+
"github.com/calvinmclean/automated-garden/garden-app/pkg/storage/db"
1010
)
1111

1212
// AdditionalQueries implements AdditionalQueries interface for SQL storage

garden-app/pkg/storage/client.go

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,36 @@
11
package storage
22

33
import (
4+
"context"
5+
"crypto/rand"
6+
"database/sql"
7+
"embed"
8+
"encoding/hex"
49
"fmt"
510

611
"github.com/calvinmclean/automated-garden/garden-app/pkg"
712
"github.com/calvinmclean/automated-garden/garden-app/pkg/notifications"
8-
"github.com/calvinmclean/automated-garden/garden-app/pkg/storage/sql"
913
"github.com/calvinmclean/automated-garden/garden-app/pkg/weather"
10-
1114
"github.com/calvinmclean/babyapi"
15+
"github.com/golang-migrate/migrate/v4"
16+
"github.com/golang-migrate/migrate/v4/database/sqlite3"
17+
"github.com/golang-migrate/migrate/v4/source/iofs"
18+
"github.com/rs/xid"
19+
20+
// sqlite driver import
21+
_ "modernc.org/sqlite"
1222
)
1323

14-
// Config is used to configure the SQLite storage client
24+
//go:generate sqlc generate
25+
26+
//go:embed migrations/*.sql
27+
var migrationsFS embed.FS
28+
29+
// Config holds configuration for the storage backend
1530
type Config struct {
1631
ConnectionString string `mapstructure:"connection_string" yaml:"connection_string"`
1732
}
1833

19-
// AdditionalQueries are queries that are implemented outside of the base babyapi implementations
20-
type AdditionalQueries interface {
21-
GetZonesUsingWaterSchedule(id string) ([]*pkg.ZoneAndGarden, error)
22-
GetWaterSchedulesUsingWeatherClient(id string) ([]*pkg.WaterSchedule, error)
23-
}
24-
2534
type Client struct {
2635
Gardens babyapi.Storage[*pkg.Garden]
2736
Zones babyapi.Storage[*pkg.Zone]
@@ -30,24 +39,83 @@ type Client struct {
3039
NotificationClientConfigs babyapi.Storage[*notifications.Client]
3140
WaterRoutines babyapi.Storage[*pkg.WaterRoutine]
3241

33-
AdditionalQueries
42+
*AdditionalQueries
3443
}
3544

45+
// NewClient creates a new storage.Client using SQL backend.
46+
// It initializes the database connection using the provided config.
3647
func NewClient(config Config) (*Client, error) {
37-
sqlClient, err := sql.NewClient(sql.Config{
38-
DataSourceName: config.ConnectionString,
39-
})
48+
connectionString := config.ConnectionString
49+
// Use shared cache for in-memory databases to allow multiple connections to share the same database
50+
// Generate a unique database name for each client so tests don't interfere with each other
51+
if connectionString == ":memory:" {
52+
// Generate a random identifier for the database name
53+
randomBytes := make([]byte, 8)
54+
if _, err := rand.Read(randomBytes); err != nil {
55+
return nil, fmt.Errorf("error generating random database name: %w", err)
56+
}
57+
randomName := hex.EncodeToString(randomBytes)
58+
connectionString = fmt.Sprintf("file:mem%s?mode=memory&cache=shared", randomName)
59+
}
60+
61+
db, err := sql.Open("sqlite", connectionString)
4062
if err != nil {
41-
return nil, fmt.Errorf("error creating SQL client: %w", err)
63+
return nil, fmt.Errorf("error opening sqlite database: %w", err)
64+
}
65+
66+
err = runMigrations(db)
67+
if err != nil {
68+
return nil, fmt.Errorf("error running migrations: %w", err)
4269
}
4370

4471
return &Client{
45-
Gardens: sqlClient.Gardens,
46-
Zones: sqlClient.Zones,
47-
WaterSchedules: sqlClient.WaterSchedules,
48-
WeatherClientConfigs: sqlClient.WeatherClientConfigs,
49-
NotificationClientConfigs: sqlClient.NotificationClientConfigs,
50-
WaterRoutines: sqlClient.WaterRoutines,
51-
AdditionalQueries: sqlClient.AdditionalQueries,
72+
Gardens: NewGardenStorage(db),
73+
Zones: NewZoneStorage(db),
74+
WaterSchedules: NewWaterScheduleStorage(db),
75+
WeatherClientConfigs: NewWeatherClientStorage(db),
76+
NotificationClientConfigs: NewNotificationClientStorage(db),
77+
WaterRoutines: NewWaterRoutineStorage(db),
78+
AdditionalQueries: NewAdditionalQueries(db),
5279
}, nil
5380
}
81+
82+
// GetWeatherClient retrieves a WeatherClient by ID and initializes it
83+
func (c *Client) GetWeatherClient(id xid.ID) (weather.Client, error) {
84+
clientConfig, err := c.WeatherClientConfigs.Get(context.Background(), id.String())
85+
if err != nil {
86+
return nil, fmt.Errorf("error getting weather client config: %w", err)
87+
}
88+
89+
if clientConfig == nil {
90+
return nil, fmt.Errorf("weather client config not found")
91+
}
92+
93+
return weather.NewClient(clientConfig, func(weatherClientOptions map[string]any) error {
94+
clientConfig.Options = weatherClientOptions
95+
return c.WeatherClientConfigs.Set(context.Background(), clientConfig)
96+
})
97+
}
98+
99+
// runMigrations executes all pending database migrations
100+
func runMigrations(db *sql.DB) error {
101+
driver, err := sqlite3.WithInstance(db, &sqlite3.Config{})
102+
if err != nil {
103+
return fmt.Errorf("error creating migration driver: %w", err)
104+
}
105+
106+
migrations, err := iofs.New(migrationsFS, "migrations")
107+
if err != nil {
108+
return fmt.Errorf("error creating migration source: %w", err)
109+
}
110+
111+
m, err := migrate.NewWithInstance("iofs", migrations, "sqlite3", driver)
112+
if err != nil {
113+
return fmt.Errorf("error creating migration instance: %w", err)
114+
}
115+
116+
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
117+
return fmt.Errorf("error running migrations: %w", err)
118+
}
119+
120+
return nil
121+
}
File renamed without changes.

garden-app/pkg/storage/sql/db/notification_client_queries.sql.go renamed to garden-app/pkg/storage/db/notification_client_queries.sql.go

File renamed without changes.

garden-app/pkg/storage/sql/db/water_routine_queries.sql.go renamed to garden-app/pkg/storage/db/water_routine_queries.sql.go

File renamed without changes.

garden-app/pkg/storage/sql/db/water_schedule_queries.sql.go renamed to garden-app/pkg/storage/db/water_schedule_queries.sql.go

File renamed without changes.

garden-app/pkg/storage/sql/db/weather_client_queries.sql.go renamed to garden-app/pkg/storage/db/weather_client_queries.sql.go

File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)