172 lines
4.3 KiB
Go
172 lines
4.3 KiB
Go
package plugins
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
var ErrRepositoryNotFound = errors.New("plugin repository not found")
|
|
|
|
type Repository struct {
|
|
ID int
|
|
URL string
|
|
DisplayName string
|
|
Enabled bool
|
|
LastFetchedAt *time.Time
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
type CreateRepositoryInput struct {
|
|
URL string
|
|
DisplayName string
|
|
Enabled *bool
|
|
}
|
|
|
|
type UpdateRepositoryInput struct {
|
|
URL *string
|
|
DisplayName *string
|
|
Enabled *bool
|
|
LastFetchedAt *time.Time
|
|
}
|
|
|
|
type RepositoryStore struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
func NewRepositoryStore(pool *pgxpool.Pool) *RepositoryStore {
|
|
return &RepositoryStore{pool: pool}
|
|
}
|
|
|
|
const repositoryColumns = `id, url, display_name, enabled, last_fetched_at, created_at, updated_at`
|
|
|
|
func scanRepository(row pgx.Row) (*Repository, error) {
|
|
var repository Repository
|
|
var lastFetchedAt *time.Time
|
|
if err := row.Scan(
|
|
&repository.ID,
|
|
&repository.URL,
|
|
&repository.DisplayName,
|
|
&repository.Enabled,
|
|
&lastFetchedAt,
|
|
&repository.CreatedAt,
|
|
&repository.UpdatedAt,
|
|
); err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, ErrRepositoryNotFound
|
|
}
|
|
return nil, fmt.Errorf("scanning plugin repository: %w", err)
|
|
}
|
|
repository.LastFetchedAt = lastFetchedAt
|
|
return &repository, nil
|
|
}
|
|
|
|
func scanRepositories(rows pgx.Rows) ([]*Repository, error) {
|
|
var repositories []*Repository
|
|
for rows.Next() {
|
|
repository, err := scanRepository(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
repositories = append(repositories, repository)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("iterating plugin repositories: %w", err)
|
|
}
|
|
return repositories, nil
|
|
}
|
|
|
|
func (s *RepositoryStore) Create(ctx context.Context, input CreateRepositoryInput) (*Repository, error) {
|
|
enabled := true
|
|
if input.Enabled != nil {
|
|
enabled = *input.Enabled
|
|
}
|
|
|
|
query := `INSERT INTO plugin_repositories (url, display_name, enabled)
|
|
VALUES ($1, $2, $3)
|
|
RETURNING ` + repositoryColumns
|
|
|
|
return scanRepository(s.pool.QueryRow(ctx, query, input.URL, input.DisplayName, enabled))
|
|
}
|
|
|
|
func (s *RepositoryStore) GetByID(ctx context.Context, id int) (*Repository, error) {
|
|
query := `SELECT ` + repositoryColumns + ` FROM plugin_repositories WHERE id = $1`
|
|
return scanRepository(s.pool.QueryRow(ctx, query, id))
|
|
}
|
|
|
|
func (s *RepositoryStore) List(ctx context.Context) ([]*Repository, error) {
|
|
query := `SELECT ` + repositoryColumns + ` FROM plugin_repositories ORDER BY id ASC`
|
|
rows, err := s.pool.Query(ctx, query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("listing plugin repositories: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
return scanRepositories(rows)
|
|
}
|
|
|
|
func (s *RepositoryStore) Update(ctx context.Context, id int, input UpdateRepositoryInput) error {
|
|
var setClauses []string
|
|
var args []any
|
|
argIndex := 1
|
|
|
|
if input.URL != nil {
|
|
setClauses = append(setClauses, fmt.Sprintf("url = $%d", argIndex))
|
|
args = append(args, *input.URL)
|
|
argIndex++
|
|
}
|
|
if input.DisplayName != nil {
|
|
setClauses = append(setClauses, fmt.Sprintf("display_name = $%d", argIndex))
|
|
args = append(args, *input.DisplayName)
|
|
argIndex++
|
|
}
|
|
if input.Enabled != nil {
|
|
setClauses = append(setClauses, fmt.Sprintf("enabled = $%d", argIndex))
|
|
args = append(args, *input.Enabled)
|
|
argIndex++
|
|
}
|
|
if input.LastFetchedAt != nil {
|
|
setClauses = append(setClauses, fmt.Sprintf("last_fetched_at = $%d", argIndex))
|
|
args = append(args, *input.LastFetchedAt)
|
|
argIndex++
|
|
}
|
|
|
|
if len(setClauses) == 0 {
|
|
_, err := s.GetByID(ctx, id)
|
|
return err
|
|
}
|
|
|
|
setClauses = append(setClauses, "updated_at = NOW()")
|
|
args = append(args, id)
|
|
|
|
query := fmt.Sprintf(
|
|
"UPDATE plugin_repositories SET %s WHERE id = $%d",
|
|
strings.Join(setClauses, ", "),
|
|
argIndex,
|
|
)
|
|
tag, err := s.pool.Exec(ctx, query, args...)
|
|
if err != nil {
|
|
return fmt.Errorf("updating plugin repository: %w", err)
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return ErrRepositoryNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *RepositoryStore) Delete(ctx context.Context, id int) error {
|
|
tag, err := s.pool.Exec(ctx, `DELETE FROM plugin_repositories WHERE id = $1`, id)
|
|
if err != nil {
|
|
return fmt.Errorf("deleting plugin repository: %w", err)
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return ErrRepositoryNotFound
|
|
}
|
|
return nil
|
|
}
|