mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-04 16:02:03 +00:00
feat: record no creator on a placement monitor set by an API key, and refuse a cloud-panel placement test on the linked workspace's allowance or the sender's daily limit before the cloud opens and charges it
This commit is contained in:
@@ -49,7 +49,7 @@ func (s *service) GetMonitor(ctx context.Context, orgID, campaignID uuid.UUID) (
|
||||
}
|
||||
|
||||
// PutMonitor creates or updates a campaign's monitor. A new or re-enabled
|
||||
// monitor runs its first test within the hour, not an interval from now.
|
||||
// monitor runs its first test within minutes, not an interval from now.
|
||||
func (s *service) PutMonitor(ctx context.Context, orgID, userID, campaignID uuid.UUID, in MonitorInput) (*models.PlacementMonitor, *errx.Error) {
|
||||
if _, xerr := s.ownedCampaign(ctx, orgID, campaignID); xerr != nil {
|
||||
return nil, xerr
|
||||
@@ -59,10 +59,15 @@ func (s *service) PutMonitor(ctx context.Context, orgID, userID, campaignID uuid
|
||||
errs.CaptureException(err)
|
||||
return nil, errx.InternalError()
|
||||
}
|
||||
// An API key has no user behind it.
|
||||
var createdBy *uuid.UUID
|
||||
if userID != uuid.Nil {
|
||||
createdBy = &userID
|
||||
}
|
||||
m := models.PlacementMonitor{
|
||||
OrganizationID: orgID,
|
||||
CampaignID: campaignID,
|
||||
CreatedBy: &userID,
|
||||
CreatedBy: createdBy,
|
||||
Enabled: true,
|
||||
IntervalDays: config.PlacementMonitorIntervalDaysDef,
|
||||
Panel: models.PlacementPanelInstance,
|
||||
|
||||
@@ -338,6 +338,17 @@ func (s *service) CreateTests(ctx context.Context, in CreateInput) ([]TestView,
|
||||
if s.Cloud == nil {
|
||||
return nil, placementErr(errx.Conflict, "placement_panel_unavailable", "Link this instance to Warmbly Cloud to test on its seed panel.")
|
||||
}
|
||||
// The cloud charges its allowance when it opens the test, so what can
|
||||
// be refused here is refused before that.
|
||||
if panel, xerr := s.Cloud.PlacementPanel(ctx); xerr == nil && panel != nil {
|
||||
if lim := panel.Usage.Limit; lim != nil && panel.Usage.Used+len(variants) > *lim {
|
||||
return nil, placementErr(errx.PaymentRequired, "placement_quota_exceeded",
|
||||
"The linked Warmbly Cloud workspace has used its placement tests for the month.")
|
||||
}
|
||||
if xerr := s.checkBudget(ctx, sender, len(variants)*min(panel.Panel.Seeds, pol.SeedsPerTest)); xerr != nil {
|
||||
return nil, xerr
|
||||
}
|
||||
}
|
||||
start, xerr := s.Cloud.StartPlacement(ctx, models.PlacementCloudStartRequest{SenderDomain: senderDomain, Tests: len(variants)})
|
||||
if xerr != nil {
|
||||
return nil, xerr
|
||||
@@ -356,18 +367,8 @@ func (s *service) CreateTests(ctx context.Context, in CreateInput) ([]TestView,
|
||||
}
|
||||
|
||||
// Budget: every probe is a send from the sender's day.
|
||||
probes := len(seeds) * len(variants)
|
||||
if s.Tasks != nil {
|
||||
sent, err := s.Tasks.CountCampaignEmailsSentToday(ctx, sender.ID)
|
||||
if err != nil {
|
||||
errs.CaptureException(err)
|
||||
return nil, errx.InternalError()
|
||||
}
|
||||
if left := sender.CampaignLimit - sent; probes > left {
|
||||
return nil, placementErr(errx.Conflict, "placement_daily_budget",
|
||||
"This test sends "+strconv.Itoa(probes)+" emails from "+sender.Email+", which has "+strconv.Itoa(max(0, left))+
|
||||
" left of its daily limit today.")
|
||||
}
|
||||
if xerr := s.checkBudget(ctx, sender, len(seeds)*len(variants)); xerr != nil {
|
||||
return nil, xerr
|
||||
}
|
||||
|
||||
// Build the tests, their probes and one task per probe.
|
||||
@@ -451,6 +452,25 @@ func (s *service) CreateTests(ctx context.Context, in CreateInput) ([]TestView,
|
||||
return views, nil
|
||||
}
|
||||
|
||||
// checkBudget refuses a test whose copies do not fit in what is left of the
|
||||
// sender's daily campaign limit.
|
||||
func (s *service) checkBudget(ctx context.Context, sender *models.Email, probes int) *errx.Error {
|
||||
if s.Tasks == nil {
|
||||
return nil
|
||||
}
|
||||
sent, err := s.Tasks.CountCampaignEmailsSentToday(ctx, sender.ID)
|
||||
if err != nil {
|
||||
errs.CaptureException(err)
|
||||
return errx.InternalError()
|
||||
}
|
||||
if left := sender.CampaignLimit - sent; probes > left {
|
||||
return placementErr(errx.Conflict, "placement_daily_budget",
|
||||
"This test sends "+strconv.Itoa(probes)+" emails from "+sender.Email+", which has "+strconv.Itoa(max(0, left))+
|
||||
" left of its daily limit today.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// enqueue hands each probe's task to the scheduler. The local scheduler picks
|
||||
// due pending rows on its own; Cloud Tasks needs the handle stored.
|
||||
func (s *service) enqueue(ctx context.Context, tasks []repository.Task, results []models.PlacementResult) {
|
||||
|
||||
Reference in New Issue
Block a user