147 lines
3.5 KiB
Go
147 lines
3.5 KiB
Go
package personagen
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/lib/pq"
|
|
)
|
|
|
|
// ErrPersonaNotFound is returned when a persona ID does not exist.
|
|
var ErrPersonaNotFound = errors.New("persona not found")
|
|
|
|
// personaRow is the database scan target for persona queries.
|
|
type personaRow struct {
|
|
ID string `db:"id"`
|
|
Name string `db:"name"`
|
|
Handle string `db:"handle"`
|
|
Gender string `db:"gender"`
|
|
Description string `db:"description"`
|
|
Tags pq.StringArray `db:"tags"`
|
|
SpecJSON []byte `db:"spec_json"`
|
|
AnchorURL *string `db:"anchor_url"`
|
|
AvatarURL *string `db:"avatar_url"`
|
|
BannerURL *string `db:"banner_url"`
|
|
ImageURLs pq.StringArray `db:"image_urls"`
|
|
VideoURLs pq.StringArray `db:"video_urls"`
|
|
Status string `db:"status"`
|
|
}
|
|
|
|
func (r *personaRow) toRecord() *PersonaRecord {
|
|
rec := &PersonaRecord{
|
|
ID: r.ID,
|
|
Name: r.Name,
|
|
Handle: r.Handle,
|
|
Gender: r.Gender,
|
|
Description: r.Description,
|
|
Tags: []string(r.Tags),
|
|
Status: r.Status,
|
|
ImageURLs: []string(r.ImageURLs),
|
|
VideoURLs: []string(r.VideoURLs),
|
|
}
|
|
if rec.Tags == nil {
|
|
rec.Tags = []string{}
|
|
}
|
|
if rec.ImageURLs == nil {
|
|
rec.ImageURLs = []string{}
|
|
}
|
|
if rec.VideoURLs == nil {
|
|
rec.VideoURLs = []string{}
|
|
}
|
|
if len(r.SpecJSON) > 0 {
|
|
rec.SpecJSON = r.SpecJSON
|
|
}
|
|
if r.AnchorURL != nil {
|
|
rec.AnchorURL = *r.AnchorURL
|
|
}
|
|
if r.AvatarURL != nil {
|
|
rec.AvatarURL = *r.AvatarURL
|
|
}
|
|
if r.BannerURL != nil {
|
|
rec.BannerURL = *r.BannerURL
|
|
}
|
|
return rec
|
|
}
|
|
|
|
// PostgresPersonaStore implements PersonaStore using PostgreSQL/CockroachDB.
|
|
type PostgresPersonaStore struct {
|
|
db *sqlx.DB
|
|
}
|
|
|
|
// Compile-time interface check.
|
|
var _ PersonaStore = (*PostgresPersonaStore)(nil)
|
|
|
|
// NewPostgresPersonaStore creates a PersonaStore backed by a SQL database.
|
|
func NewPostgresPersonaStore(db *sqlx.DB) *PostgresPersonaStore {
|
|
return &PostgresPersonaStore{db: db}
|
|
}
|
|
|
|
func (s *PostgresPersonaStore) GetByID(ctx context.Context, id string) (*PersonaRecord, error) {
|
|
var row personaRow
|
|
err := s.db.QueryRowxContext(ctx, `
|
|
SELECT id, name, handle, gender, description, tags, spec_json,
|
|
anchor_url, avatar_url, banner_url, image_urls, video_urls, status
|
|
FROM personas WHERE id = $1
|
|
`, id).StructScan(&row)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, ErrPersonaNotFound
|
|
}
|
|
return nil, fmt.Errorf("get persona: %w", err)
|
|
}
|
|
return row.toRecord(), nil
|
|
}
|
|
|
|
func (s *PostgresPersonaStore) Update(ctx context.Context, p *PersonaRecord) error {
|
|
var specJSON []byte
|
|
if p.SpecJSON != nil {
|
|
specJSON = []byte(p.SpecJSON)
|
|
}
|
|
|
|
var anchorURL, avatarURL, bannerURL *string
|
|
if p.AnchorURL != "" {
|
|
anchorURL = &p.AnchorURL
|
|
}
|
|
if p.AvatarURL != "" {
|
|
avatarURL = &p.AvatarURL
|
|
}
|
|
if p.BannerURL != "" {
|
|
bannerURL = &p.BannerURL
|
|
}
|
|
|
|
result, err := s.db.ExecContext(ctx, `
|
|
UPDATE personas
|
|
SET name = $2, handle = $3, tags = $4, spec_json = $5,
|
|
anchor_url = $6, avatar_url = $7, banner_url = $8,
|
|
image_urls = $9, video_urls = $10, status = $11
|
|
WHERE id = $1
|
|
`,
|
|
p.ID,
|
|
p.Name,
|
|
p.Handle,
|
|
pq.StringArray(p.Tags),
|
|
specJSON,
|
|
anchorURL,
|
|
avatarURL,
|
|
bannerURL,
|
|
pq.StringArray(p.ImageURLs),
|
|
pq.StringArray(p.VideoURLs),
|
|
p.Status,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("update persona: %w", err)
|
|
}
|
|
|
|
rows, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("update persona rows affected: %w", err)
|
|
}
|
|
if rows == 0 {
|
|
return ErrPersonaNotFound
|
|
}
|
|
return nil
|
|
}
|