Initial MVP commit
All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
This commit is contained in:
71
internal/platform/datasource/postgres/factory.go
Normal file
71
internal/platform/datasource/postgres/factory.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/ejr/weatherapi/internal/platform/config"
|
||||
"gitea.maximumdirect.net/ejr/weatherapi/internal/platform/datasource"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Factory struct{}
|
||||
|
||||
func (Factory) Driver() string {
|
||||
return "postgres"
|
||||
}
|
||||
|
||||
func (Factory) Open(ctx context.Context, cfg config.DatabaseConfig) (datasource.DataSource, error) {
|
||||
dsn, err := buildDSN(cfg.Params.URI, cfg.Params.Username, cfg.Params.Password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open postgres datasource %q: %w", cfg.Name, err)
|
||||
}
|
||||
|
||||
pool, err := pgxpool.New(ctx, dsn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open postgres datasource %q: %w", cfg.Name, err)
|
||||
}
|
||||
|
||||
if err := pool.Ping(ctx); err != nil {
|
||||
pool.Close()
|
||||
return nil, fmt.Errorf("open postgres datasource %q: ping: %w", cfg.Name, err)
|
||||
}
|
||||
|
||||
return &DataSource{pool: pool}, nil
|
||||
}
|
||||
|
||||
type DataSource struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func (d *DataSource) Pool() *pgxpool.Pool {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
return d.pool
|
||||
}
|
||||
|
||||
func (d *DataSource) Close() {
|
||||
if d == nil || d.pool == nil {
|
||||
return
|
||||
}
|
||||
d.pool.Close()
|
||||
}
|
||||
|
||||
func buildDSN(uri, username, password string) (string, error) {
|
||||
u, err := url.Parse(strings.TrimSpace(uri))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid params.uri: %w", err)
|
||||
}
|
||||
if strings.TrimSpace(u.Scheme) == "" {
|
||||
return "", fmt.Errorf("invalid params.uri: missing scheme")
|
||||
}
|
||||
if strings.TrimSpace(u.Host) == "" {
|
||||
return "", fmt.Errorf("invalid params.uri: missing host")
|
||||
}
|
||||
|
||||
u.User = url.UserPassword(strings.TrimSpace(username), strings.TrimSpace(password))
|
||||
return u.String(), nil
|
||||
}
|
||||
64
internal/platform/datasource/registry.go
Normal file
64
internal/platform/datasource/registry.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package datasource
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/ejr/weatherapi/internal/platform/config"
|
||||
)
|
||||
|
||||
// DataSource is a generic opened datasource handle.
|
||||
type DataSource interface {
|
||||
Close()
|
||||
}
|
||||
|
||||
// Factory constructs datasource handles for a specific driver.
|
||||
type Factory interface {
|
||||
Driver() string
|
||||
Open(ctx context.Context, cfg config.DatabaseConfig) (DataSource, error)
|
||||
}
|
||||
|
||||
// Registry maps driver names to datasource factories.
|
||||
type Registry struct {
|
||||
factories map[string]Factory
|
||||
}
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{factories: map[string]Factory{}}
|
||||
}
|
||||
|
||||
func (r *Registry) Register(factory Factory) error {
|
||||
if factory == nil {
|
||||
return fmt.Errorf("register datasource factory: factory is nil")
|
||||
}
|
||||
driver := normalizeDriver(factory.Driver())
|
||||
if driver == "" {
|
||||
return fmt.Errorf("register datasource factory: factory driver is empty")
|
||||
}
|
||||
if _, exists := r.factories[driver]; exists {
|
||||
return fmt.Errorf("register datasource factory: driver %q already registered", driver)
|
||||
}
|
||||
r.factories[driver] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) Open(ctx context.Context, cfg config.DatabaseConfig) (DataSource, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("open datasource: registry is nil")
|
||||
}
|
||||
driver := normalizeDriver(cfg.Driver)
|
||||
factory, ok := r.factories[driver]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("open datasource: unsupported driver %q", cfg.Driver)
|
||||
}
|
||||
ds, err := factory.Open(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func normalizeDriver(driver string) string {
|
||||
return strings.ToLower(strings.TrimSpace(driver))
|
||||
}
|
||||
Reference in New Issue
Block a user