package main import ( "context" "fmt" "time" "github.com/szabadpalya/api/internal/store" "github.com/szabadpalya/api/internal/store/dbgen" "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. // // Four statements rather than a loop over table names: the order matters, and // a name interpolated into SQL is the one thing the generated layer cannot // check. func (s *seeder) reset(ctx context.Context) error { if err := s.q.SeedReset(ctx); err != nil { return err } if err := s.q.SeedResetCampaigns(ctx); err != nil { return err } if err := s.q.SeedResetOwners(ctx); err != nil { return err } return s.q.SeedResetUsers(ctx) } // 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) q := s.q.WithTx(tx) ownerID, err := q.SeedOwner(ctx, dbgen.SeedOwnerParams{ ID: store.NewID(), Slug: c.Owner.Slug, Name: c.Owner.Name, Initials: c.Owner.Initials, Location: c.Owner.Location, Blurb: c.Owner.Blurb, AvatarUrl: c.Owner.AvatarURL, }) if err != nil { return 0, 0, err } campaignID := store.NewID() if err := q.SeedCampaign(ctx, dbgen.SeedCampaignParams{ ID: campaignID, Slug: c.Slug, Title: c.Title, Category: c.Category, Summary: c.Summary, Art: c.Art, GoalAmount: c.GoalAmount, EndsAt: endsAt, Status: c.Status, OwnerID: ownerID, }); err != nil { return 0, 0, err } for i, sec := range c.Story { if err := q.SeedStorySection(ctx, dbgen.SeedStorySectionParams{ ID: store.NewID(), CampaignID: campaignID, Heading: sec.Heading, Body: sec.Body, Position: 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 := q.SeedTier(ctx, dbgen.SeedTierParams{ ID: tierID, CampaignID: campaignID, Type: t.Type, Price: t.Price, ListPrice: t.ListPrice, Title: t.Title, Includes: t.Includes, InventoryLimit: t.InventoryLimit, EstimatedDelivery: delivery, Featured: t.Featured, Position: 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 := q.SeedPledge(ctx, dbgen.SeedPledgeParams{ ID: store.NewID(), CampaignID: campaignID, TierID: tierID, BackerEmail: fmt.Sprintf("tamogato-%s@example.invalid", store.NewID()), Amount: t.Price, Status: status, CapturedAt: capturedAt, }); err != nil { return 0, 0, err } } } for _, u := range c.Updates { if err := q.SeedCampaignUpdate(ctx, dbgen.SeedCampaignUpdateParams{ ID: store.NewID(), CampaignID: campaignID, Title: u.Title, Body: u.Body, }); err != nil { return 0, 0, err } } for _, m := range c.Comments { if err := q.SeedComment(ctx, dbgen.SeedCommentParams{ ID: store.NewID(), CampaignID: campaignID, AuthorName: m.AuthorName, Body: m.Body, }); err != nil { return 0, 0, err } } return pledgeCount, raised, tx.Commit(ctx) }