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) }