diff --git a/internal/store/db/accounts.sql.go b/internal/store/db/accounts.sql.go new file mode 100644 index 0000000..c8e7aa3 --- /dev/null +++ b/internal/store/db/accounts.sql.go @@ -0,0 +1,114 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: accounts.sql + +package db + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createAccount = `-- name: CreateAccount :one +INSERT INTO provider_accounts (id, project_id, provider, secret_enc, comment) +VALUES ($1, $2, $3, $4, $5) +RETURNING id, project_id, provider, secret_enc, comment, created_at +` + +type CreateAccountParams struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` + Provider string `json:"provider"` + SecretEnc string `json:"secret_enc"` + Comment string `json:"comment"` +} + +func (q *Queries) CreateAccount(ctx context.Context, arg CreateAccountParams) (ProviderAccount, error) { + row := q.db.QueryRow(ctx, createAccount, + arg.ID, + arg.ProjectID, + arg.Provider, + arg.SecretEnc, + arg.Comment, + ) + var i ProviderAccount + err := row.Scan( + &i.ID, + &i.ProjectID, + &i.Provider, + &i.SecretEnc, + &i.Comment, + &i.CreatedAt, + ) + return i, err +} + +const deleteAccount = `-- name: DeleteAccount :exec +DELETE FROM provider_accounts WHERE id = $1 AND project_id = $2 +` + +type DeleteAccountParams struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` +} + +func (q *Queries) DeleteAccount(ctx context.Context, arg DeleteAccountParams) error { + _, err := q.db.Exec(ctx, deleteAccount, arg.ID, arg.ProjectID) + return err +} + +const getAccount = `-- name: GetAccount :one +SELECT id, project_id, provider, secret_enc, comment, created_at FROM provider_accounts WHERE id = $1 AND project_id = $2 +` + +type GetAccountParams struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` +} + +func (q *Queries) GetAccount(ctx context.Context, arg GetAccountParams) (ProviderAccount, error) { + row := q.db.QueryRow(ctx, getAccount, arg.ID, arg.ProjectID) + var i ProviderAccount + err := row.Scan( + &i.ID, + &i.ProjectID, + &i.Provider, + &i.SecretEnc, + &i.Comment, + &i.CreatedAt, + ) + return i, err +} + +const listAccounts = `-- name: ListAccounts :many +SELECT id, project_id, provider, secret_enc, comment, created_at FROM provider_accounts WHERE project_id = $1 ORDER BY created_at +` + +func (q *Queries) ListAccounts(ctx context.Context, projectID pgtype.UUID) ([]ProviderAccount, error) { + rows, err := q.db.Query(ctx, listAccounts, projectID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ProviderAccount + for rows.Next() { + var i ProviderAccount + if err := rows.Scan( + &i.ID, + &i.ProjectID, + &i.Provider, + &i.SecretEnc, + &i.Comment, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/store/db/check_runs.sql.go b/internal/store/db/check_runs.sql.go new file mode 100644 index 0000000..14a6ab5 --- /dev/null +++ b/internal/store/db/check_runs.sql.go @@ -0,0 +1,36 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: check_runs.sql + +package db + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createCheckRun = `-- name: CreateCheckRun :one +INSERT INTO check_runs (id, domain_id, result) +VALUES ($1, $2, $3) +RETURNING id, domain_id, result, created_at +` + +type CreateCheckRunParams struct { + ID pgtype.UUID `json:"id"` + DomainID pgtype.UUID `json:"domain_id"` + Result []byte `json:"result"` +} + +func (q *Queries) CreateCheckRun(ctx context.Context, arg CreateCheckRunParams) (CheckRun, error) { + row := q.db.QueryRow(ctx, createCheckRun, arg.ID, arg.DomainID, arg.Result) + var i CheckRun + err := row.Scan( + &i.ID, + &i.DomainID, + &i.Result, + &i.CreatedAt, + ) + return i, err +} diff --git a/internal/store/db/db.go b/internal/store/db/db.go new file mode 100644 index 0000000..468d1fa --- /dev/null +++ b/internal/store/db/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package db + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/store/db/domains.sql.go b/internal/store/db/domains.sql.go new file mode 100644 index 0000000..92eb755 --- /dev/null +++ b/internal/store/db/domains.sql.go @@ -0,0 +1,119 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: domains.sql + +package db + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createDomain = `-- name: CreateDomain :one +INSERT INTO domains (id, project_id, provider_account_id, zone_name, zone_id, template_id) +VALUES ($1, $2, $3, $4, $5, $6) +RETURNING id, project_id, provider_account_id, zone_name, zone_id, template_id, created_at +` + +type CreateDomainParams struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` + ProviderAccountID pgtype.UUID `json:"provider_account_id"` + ZoneName string `json:"zone_name"` + ZoneID string `json:"zone_id"` + TemplateID pgtype.UUID `json:"template_id"` +} + +func (q *Queries) CreateDomain(ctx context.Context, arg CreateDomainParams) (Domain, error) { + row := q.db.QueryRow(ctx, createDomain, + arg.ID, + arg.ProjectID, + arg.ProviderAccountID, + arg.ZoneName, + arg.ZoneID, + arg.TemplateID, + ) + var i Domain + err := row.Scan( + &i.ID, + &i.ProjectID, + &i.ProviderAccountID, + &i.ZoneName, + &i.ZoneID, + &i.TemplateID, + &i.CreatedAt, + ) + return i, err +} + +const deleteDomain = `-- name: DeleteDomain :exec +DELETE FROM domains WHERE id = $1 AND project_id = $2 +` + +type DeleteDomainParams struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` +} + +func (q *Queries) DeleteDomain(ctx context.Context, arg DeleteDomainParams) error { + _, err := q.db.Exec(ctx, deleteDomain, arg.ID, arg.ProjectID) + return err +} + +const getDomain = `-- name: GetDomain :one +SELECT id, project_id, provider_account_id, zone_name, zone_id, template_id, created_at FROM domains WHERE id = $1 AND project_id = $2 +` + +type GetDomainParams struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` +} + +func (q *Queries) GetDomain(ctx context.Context, arg GetDomainParams) (Domain, error) { + row := q.db.QueryRow(ctx, getDomain, arg.ID, arg.ProjectID) + var i Domain + err := row.Scan( + &i.ID, + &i.ProjectID, + &i.ProviderAccountID, + &i.ZoneName, + &i.ZoneID, + &i.TemplateID, + &i.CreatedAt, + ) + return i, err +} + +const listDomains = `-- name: ListDomains :many +SELECT id, project_id, provider_account_id, zone_name, zone_id, template_id, created_at FROM domains WHERE project_id = $1 ORDER BY created_at +` + +func (q *Queries) ListDomains(ctx context.Context, projectID pgtype.UUID) ([]Domain, error) { + rows, err := q.db.Query(ctx, listDomains, projectID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Domain + for rows.Next() { + var i Domain + if err := rows.Scan( + &i.ID, + &i.ProjectID, + &i.ProviderAccountID, + &i.ZoneName, + &i.ZoneID, + &i.TemplateID, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/store/db/models.go b/internal/store/db/models.go new file mode 100644 index 0000000..8fff901 --- /dev/null +++ b/internal/store/db/models.go @@ -0,0 +1,59 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package db + +import ( + "github.com/jackc/pgx/v5/pgtype" + dto "github.com/vasyakrg/dns-autoresolver/internal/store/dto" +) + +type CheckRun struct { + ID pgtype.UUID `json:"id"` + DomainID pgtype.UUID `json:"domain_id"` + Result []byte `json:"result"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + +type Domain struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` + ProviderAccountID pgtype.UUID `json:"provider_account_id"` + ZoneName string `json:"zone_name"` + ZoneID string `json:"zone_id"` + TemplateID pgtype.UUID `json:"template_id"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + +type Project struct { + ID pgtype.UUID `json:"id"` + UserID pgtype.UUID `json:"user_id"` + Name string `json:"name"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + +type ProviderAccount struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` + Provider string `json:"provider"` + SecretEnc string `json:"secret_enc"` + Comment string `json:"comment"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + +type Template struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` + Name string `json:"name"` + Doc *dto.TemplateDoc `json:"doc"` + Version int32 `json:"version"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + +type User struct { + ID pgtype.UUID `json:"id"` + Email string `json:"email"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} diff --git a/internal/store/db/templates.sql.go b/internal/store/db/templates.sql.go new file mode 100644 index 0000000..cd8f95d --- /dev/null +++ b/internal/store/db/templates.sql.go @@ -0,0 +1,150 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: templates.sql + +package db + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" + dto "github.com/vasyakrg/dns-autoresolver/internal/store/dto" +) + +const createTemplate = `-- name: CreateTemplate :one +INSERT INTO templates (id, project_id, name, doc, version) +VALUES ($1, $2, $3, $4, 1) +RETURNING id, project_id, name, doc, version, created_at, updated_at +` + +type CreateTemplateParams struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` + Name string `json:"name"` + Doc *dto.TemplateDoc `json:"doc"` +} + +func (q *Queries) CreateTemplate(ctx context.Context, arg CreateTemplateParams) (Template, error) { + row := q.db.QueryRow(ctx, createTemplate, + arg.ID, + arg.ProjectID, + arg.Name, + arg.Doc, + ) + var i Template + err := row.Scan( + &i.ID, + &i.ProjectID, + &i.Name, + &i.Doc, + &i.Version, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const deleteTemplate = `-- name: DeleteTemplate :exec +DELETE FROM templates WHERE id = $1 AND project_id = $2 +` + +type DeleteTemplateParams struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` +} + +func (q *Queries) DeleteTemplate(ctx context.Context, arg DeleteTemplateParams) error { + _, err := q.db.Exec(ctx, deleteTemplate, arg.ID, arg.ProjectID) + return err +} + +const getTemplate = `-- name: GetTemplate :one +SELECT id, project_id, name, doc, version, created_at, updated_at FROM templates WHERE id = $1 AND project_id = $2 +` + +type GetTemplateParams struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` +} + +func (q *Queries) GetTemplate(ctx context.Context, arg GetTemplateParams) (Template, error) { + row := q.db.QueryRow(ctx, getTemplate, arg.ID, arg.ProjectID) + var i Template + err := row.Scan( + &i.ID, + &i.ProjectID, + &i.Name, + &i.Doc, + &i.Version, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const listTemplates = `-- name: ListTemplates :many +SELECT id, project_id, name, doc, version, created_at, updated_at FROM templates WHERE project_id = $1 ORDER BY created_at +` + +func (q *Queries) ListTemplates(ctx context.Context, projectID pgtype.UUID) ([]Template, error) { + rows, err := q.db.Query(ctx, listTemplates, projectID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Template + for rows.Next() { + var i Template + if err := rows.Scan( + &i.ID, + &i.ProjectID, + &i.Name, + &i.Doc, + &i.Version, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateTemplate = `-- name: UpdateTemplate :one +UPDATE templates +SET name = $3, doc = $4, version = version + 1, updated_at = now() +WHERE id = $1 AND project_id = $2 +RETURNING id, project_id, name, doc, version, created_at, updated_at +` + +type UpdateTemplateParams struct { + ID pgtype.UUID `json:"id"` + ProjectID pgtype.UUID `json:"project_id"` + Name string `json:"name"` + Doc *dto.TemplateDoc `json:"doc"` +} + +func (q *Queries) UpdateTemplate(ctx context.Context, arg UpdateTemplateParams) (Template, error) { + row := q.db.QueryRow(ctx, updateTemplate, + arg.ID, + arg.ProjectID, + arg.Name, + arg.Doc, + ) + var i Template + err := row.Scan( + &i.ID, + &i.ProjectID, + &i.Name, + &i.Doc, + &i.Version, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} diff --git a/internal/store/dto/template_doc.go b/internal/store/dto/template_doc.go new file mode 100644 index 0000000..e0a5356 --- /dev/null +++ b/internal/store/dto/template_doc.go @@ -0,0 +1,36 @@ +package dto + +import "github.com/vasyakrg/dns-autoresolver/internal/model" + +// RecordDTO is the JSONB representation of one DNS record in a template. +type RecordDTO struct { + Type string `json:"type"` + Name string `json:"name"` + TTL int `json:"ttl"` + Values []string `json:"values"` +} + +// TemplateDoc is stored in templates.doc (jsonb). +type TemplateDoc struct { + Records []RecordDTO `json:"records"` +} + +func FromModel(recs []model.Record) TemplateDoc { + out := TemplateDoc{Records: make([]RecordDTO, 0, len(recs))} + for _, r := range recs { + out.Records = append(out.Records, RecordDTO{ + Type: string(r.Type), Name: r.Name, TTL: r.TTL, Values: r.Values, + }) + } + return out +} + +func (d TemplateDoc) ToModel() []model.Record { + out := make([]model.Record, 0, len(d.Records)) + for _, r := range d.Records { + out = append(out, model.Record{ + Type: model.RecordType(r.Type), Name: r.Name, TTL: r.TTL, Values: r.Values, + }) + } + return out +} diff --git a/internal/store/dto/template_doc_test.go b/internal/store/dto/template_doc_test.go new file mode 100644 index 0000000..a9b6bec --- /dev/null +++ b/internal/store/dto/template_doc_test.go @@ -0,0 +1,25 @@ +package dto + +import ( + "testing" + + "github.com/vasyakrg/dns-autoresolver/internal/model" +) + +func TestTemplateDocRoundTrip(t *testing.T) { + recs := []model.Record{ + {Type: model.A, Name: "www.example.com.", TTL: 300, Values: []string{"1.2.3.4"}}, + {Type: model.MX, Name: "example.com.", TTL: 3600, Values: []string{"10 mx1.example.com."}}, + } + doc := FromModel(recs) + if len(doc.Records) != 2 { + t.Fatalf("want 2 records, got %d", len(doc.Records)) + } + back := doc.ToModel() + if len(back) != 2 || back[0].Type != model.A || back[1].Type != model.MX { + t.Fatalf("round-trip mismatch: %+v", back) + } + if back[0].Values[0] != "1.2.3.4" || back[1].TTL != 3600 { + t.Fatalf("field mismatch: %+v", back) + } +} diff --git a/internal/store/queries/accounts.sql b/internal/store/queries/accounts.sql new file mode 100644 index 0000000..d407626 --- /dev/null +++ b/internal/store/queries/accounts.sql @@ -0,0 +1,13 @@ +-- name: CreateAccount :one +INSERT INTO provider_accounts (id, project_id, provider, secret_enc, comment) +VALUES ($1, $2, $3, $4, $5) +RETURNING *; + +-- name: GetAccount :one +SELECT * FROM provider_accounts WHERE id = $1 AND project_id = $2; + +-- name: ListAccounts :many +SELECT * FROM provider_accounts WHERE project_id = $1 ORDER BY created_at; + +-- name: DeleteAccount :exec +DELETE FROM provider_accounts WHERE id = $1 AND project_id = $2; diff --git a/internal/store/queries/check_runs.sql b/internal/store/queries/check_runs.sql new file mode 100644 index 0000000..3c5ea29 --- /dev/null +++ b/internal/store/queries/check_runs.sql @@ -0,0 +1,4 @@ +-- name: CreateCheckRun :one +INSERT INTO check_runs (id, domain_id, result) +VALUES ($1, $2, $3) +RETURNING *; diff --git a/internal/store/queries/domains.sql b/internal/store/queries/domains.sql new file mode 100644 index 0000000..4e134ea --- /dev/null +++ b/internal/store/queries/domains.sql @@ -0,0 +1,13 @@ +-- name: CreateDomain :one +INSERT INTO domains (id, project_id, provider_account_id, zone_name, zone_id, template_id) +VALUES ($1, $2, $3, $4, $5, $6) +RETURNING *; + +-- name: GetDomain :one +SELECT * FROM domains WHERE id = $1 AND project_id = $2; + +-- name: ListDomains :many +SELECT * FROM domains WHERE project_id = $1 ORDER BY created_at; + +-- name: DeleteDomain :exec +DELETE FROM domains WHERE id = $1 AND project_id = $2; diff --git a/internal/store/queries/templates.sql b/internal/store/queries/templates.sql new file mode 100644 index 0000000..f08d170 --- /dev/null +++ b/internal/store/queries/templates.sql @@ -0,0 +1,19 @@ +-- name: CreateTemplate :one +INSERT INTO templates (id, project_id, name, doc, version) +VALUES ($1, $2, $3, $4, 1) +RETURNING *; + +-- name: GetTemplate :one +SELECT * FROM templates WHERE id = $1 AND project_id = $2; + +-- name: ListTemplates :many +SELECT * FROM templates WHERE project_id = $1 ORDER BY created_at; + +-- name: UpdateTemplate :one +UPDATE templates +SET name = $3, doc = $4, version = version + 1, updated_at = now() +WHERE id = $1 AND project_id = $2 +RETURNING *; + +-- name: DeleteTemplate :exec +DELETE FROM templates WHERE id = $1 AND project_id = $2; diff --git a/internal/store/store.go b/internal/store/store.go new file mode 100644 index 0000000..9758f0c --- /dev/null +++ b/internal/store/store.go @@ -0,0 +1,20 @@ +package store + +import ( + "github.com/jackc/pgx/v5/pgxpool" + + "github.com/vasyakrg/dns-autoresolver/internal/store/db" +) + +// Store wraps sqlc-generated queries over a pgx pool. +type Store struct { + q *db.Queries + pool *pgxpool.Pool +} + +func New(pool *pgxpool.Pool) *Store { + return &Store{q: db.New(pool), pool: pool} +} + +// Queries exposes the generated queries for callers that need them directly. +func (s *Store) Queries() *db.Queries { return s.q } diff --git a/internal/store/store_test.go b/internal/store/store_test.go new file mode 100644 index 0000000..1e51df9 --- /dev/null +++ b/internal/store/store_test.go @@ -0,0 +1,98 @@ +package store + +import ( + "context" + "testing" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" + "github.com/jackc/pgx/v5/pgxpool" + + "github.com/vasyakrg/dns-autoresolver/internal/store/db" + "github.com/vasyakrg/dns-autoresolver/internal/store/dto" +) + +// defaultProject is the seed default tenant project (see migrations/0001_init.sql). +// +// NOTE: sqlc generated UUID columns as pgtype.UUID (not google/uuid.UUID) — +// pgUUID bridges the two so tests can still use uuid.New()/uuid.MustParse. +var defaultProject = pgUUID(uuid.MustParse("00000000-0000-0000-0000-000000000002")) + +func pgUUID(id uuid.UUID) pgtype.UUID { + return pgtype.UUID{Bytes: id, Valid: true} +} + +func newStore(t *testing.T) (*Store, context.Context) { + dsn := startPostgres(t) + pool, err := pgxpool.New(context.Background(), dsn) + if err != nil { + t.Fatal(err) + } + t.Cleanup(pool.Close) + return New(pool), context.Background() +} + +func TestAccountCRUD(t *testing.T) { + s, ctx := newStore(t) + acc, err := s.Queries().CreateAccount(ctx, db.CreateAccountParams{ + ID: pgUUID(uuid.New()), ProjectID: defaultProject, + Provider: "selectel", SecretEnc: "enc-blob", Comment: "prod", + }) + if err != nil { + t.Fatal(err) + } + got, err := s.Queries().GetAccount(ctx, db.GetAccountParams{ID: acc.ID, ProjectID: defaultProject}) + if err != nil || got.Provider != "selectel" || got.SecretEnc != "enc-blob" { + t.Fatalf("get mismatch: %+v err=%v", got, err) + } + + list, err := s.Queries().ListAccounts(ctx, defaultProject) + if err != nil || len(list) != 1 { + t.Fatalf("list mismatch: %+v err=%v", list, err) + } + + if err := s.Queries().DeleteAccount(ctx, db.DeleteAccountParams{ID: acc.ID, ProjectID: defaultProject}); err != nil { + t.Fatal(err) + } + if _, err := s.Queries().GetAccount(ctx, db.GetAccountParams{ID: acc.ID, ProjectID: defaultProject}); err == nil { + t.Fatal("expected error after delete, got nil") + } +} + +func TestTemplateJSONBRoundTrip(t *testing.T) { + s, ctx := newStore(t) + doc := dto.TemplateDoc{Records: []dto.RecordDTO{ + {Type: "A", Name: "www.example.com.", TTL: 300, Values: []string{"1.2.3.4"}}, + {Type: "SRV", Name: "_autodiscover._tcp.example.com.", TTL: 3600, Values: []string{"0 0 443 mail.example.com."}}, + }} + tpl, err := s.Queries().CreateTemplate(ctx, db.CreateTemplateParams{ + ID: pgUUID(uuid.New()), ProjectID: defaultProject, Name: "base", Doc: &doc, + }) + if err != nil { + t.Fatal(err) + } + got, err := s.Queries().GetTemplate(ctx, db.GetTemplateParams{ID: tpl.ID, ProjectID: defaultProject}) + if err != nil { + t.Fatal(err) + } + if got.Doc == nil || len(got.Doc.Records) != 2 || got.Doc.Records[1].Type != "SRV" { + t.Fatalf("jsonb round-trip failed: %+v", got.Doc) + } + + doc2 := dto.TemplateDoc{Records: []dto.RecordDTO{ + {Type: "A", Name: "www.example.com.", TTL: 60, Values: []string{"5.6.7.8"}}, + }} + updated, err := s.Queries().UpdateTemplate(ctx, db.UpdateTemplateParams{ + ID: tpl.ID, ProjectID: defaultProject, Name: "base-v2", Doc: &doc2, + }) + if err != nil { + t.Fatal(err) + } + if updated.Version != tpl.Version+1 || updated.Doc == nil || len(updated.Doc.Records) != 1 { + t.Fatalf("update mismatch: %+v", updated) + } + + if err := s.Queries().DeleteTemplate(ctx, db.DeleteTemplateParams{ID: tpl.ID, ProjectID: defaultProject}); err != nil { + t.Fatal(err) + } +} diff --git a/sqlc.yaml b/sqlc.yaml new file mode 100644 index 0000000..068a34b --- /dev/null +++ b/sqlc.yaml @@ -0,0 +1,19 @@ +version: "2" +sql: + - engine: postgresql + schema: internal/store/migrations + queries: internal/store/queries + gen: + go: + package: db + out: internal/store/db + sql_package: pgx/v5 + emit_json_tags: true + emit_pointers_for_null_types: true + overrides: + - column: templates.doc + go_type: + import: github.com/vasyakrg/dns-autoresolver/internal/store/dto + package: dto + type: TemplateDoc + pointer: true