szabadpalya/api/test/seed/insert.go

140 lines
4.2 KiB
Go

package main
import (
"context"
"fmt"
"time"
"github.com/szabadpalya/api/internal/store"
"github.com/szabadpalya/api/test/seeddata"
)
// reset clears the campaign side of the database. Relations cascade, so
// campaigns and owners are enough; users go too because the seed's
// pledges are anonymous and any account left behind would be an orphan.
func (s *seeder) reset(ctx context.Context) error {
for _, table := range []string{"Pledge", "Campaign", "Owner", "User"} {
if _, err := s.pool.Exec(ctx, fmt.Sprintf(`DELETE FROM %q`, table)); err != nil {
return err
}
}
return nil
}
// campaign writes one fixture campaign and returns how many pledges it created
// and what they add up to.
func (s *seeder) campaign(ctx context.Context, c seeddata.Campaign) (int, int, error) {
endsAt, err := s.store.EndOfDay(c.EndsAt)
if err != nil {
return 0, 0, err
}
tx, err := s.pool.Begin(ctx)
if err != nil {
return 0, 0, err
}
defer tx.Rollback(ctx)
var ownerID string
if err := tx.QueryRow(ctx, `
INSERT INTO "Owner" ("id","slug","name","initials","location","blurb","avatarUrl","approved")
VALUES ($1,$2,$3,$4,$5,$6,NULLIF($7,''),true)
ON CONFLICT ("slug") DO UPDATE SET "name" = EXCLUDED."name"
RETURNING "id"`,
store.NewID(), c.Owner.Slug, c.Owner.Name, c.Owner.Initials, c.Owner.Location, c.Owner.Blurb,
c.Owner.AvatarURL,
).Scan(&ownerID); err != nil {
return 0, 0, err
}
campaignID := store.NewID()
if _, err := tx.Exec(ctx, `
INSERT INTO "Campaign" ("id","slug","title","category","summary","art","goalAmount",
"endsAt","status","ownerId")
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::"CampaignStatus",$10)`,
campaignID, c.Slug, c.Title, c.Category, c.Summary, c.Art, c.GoalAmount,
endsAt, c.Status, ownerID); err != nil {
return 0, 0, err
}
for i, sec := range c.Story {
if _, err := tx.Exec(ctx, `
INSERT INTO "StorySection" ("id","campaignId","heading","body","position")
VALUES ($1,$2,$3,$4,$5)`,
store.NewID(), campaignID, sec.Heading, sec.Body, i); err != nil {
return 0, 0, err
}
}
// Was the goal reached? That decides whether physical tiers were charged.
raised := 0
for _, t := range c.Tiers {
raised += t.Price * t.Backers
}
goalReached := raised >= c.GoalAmount
pledgeCount := 0
now := time.Now()
for i, t := range c.Tiers {
var delivery *time.Time
if t.EstimatedDelivery != "" {
d, err := s.store.EndOfDay(t.EstimatedDelivery)
if err != nil {
return 0, 0, err
}
delivery = &d
}
tierID := store.NewID()
if _, err := tx.Exec(ctx, `
INSERT INTO "RewardTier" ("id","campaignId","type","price","listPrice","title",
"includes","inventoryLimit","estimatedDelivery",
"featured","position")
VALUES ($1,$2,$3::"TierType",$4,$5,$6,$7,$8,$9,$10,$11)`,
tierID, campaignID, t.Type, t.Price, t.ListPrice, t.Title, t.Includes,
t.InventoryLimit, delivery, t.Featured, i); err != nil {
return 0, 0, err
}
// A physical tier on a campaign that missed its goal is still only
// authorized — that is the fulfillment guard, visible in the fixture.
held := t.Type == "PHYSICAL" && !goalReached
status := "CAPTURED"
var capturedAt *time.Time
if held {
status = "AUTHORIZED"
} else {
capturedAt = &now
}
for n := 0; n < t.Backers; n++ {
pledgeCount++
if _, err := tx.Exec(ctx, `
INSERT INTO "Pledge" ("id","campaignId","tierId","backerEmail","quantity",
"amount","status","capturedAt")
VALUES ($1,$2,$3,$4,1,$5,$6::"PledgeStatus",$7)`,
store.NewID(), campaignID, tierID,
fmt.Sprintf("tamogato-%s@example.invalid", store.NewID()),
t.Price, status, capturedAt); err != nil {
return 0, 0, err
}
}
}
for _, u := range c.Updates {
if _, err := tx.Exec(ctx, `
INSERT INTO "CampaignUpdate" ("id","campaignId","title","body")
VALUES ($1,$2,$3,$4)`, store.NewID(), campaignID, u.Title, u.Body); err != nil {
return 0, 0, err
}
}
for _, m := range c.Comments {
if _, err := tx.Exec(ctx, `
INSERT INTO "Comment" ("id","campaignId","authorName","body")
VALUES ($1,$2,$3,$4)`, store.NewID(), campaignID, m.AuthorName, m.Body); err != nil {
return 0, 0, err
}
}
return pledgeCount, raised, tx.Commit(ctx)
}