(req BulkForceRunRequest, requireReason bool)
| 593 | } |
| 594 | |
| 595 | func normalizeBulkForceRunRequest(req BulkForceRunRequest, requireReason bool) (BulkForceRunRequest, error) { |
| 596 | if len(req.RunIDs) == 0 { |
| 597 | return BulkForceRunRequest{}, fmt.Errorf("%w: bulk force run_ids is required", ErrValidation) |
| 598 | } |
| 599 | if len(req.RunIDs) > MaxForceRunBulkIDs { |
| 600 | return BulkForceRunRequest{}, forceRunDiagnosticError( |
| 601 | diagnosticcontract.CodeBulkTooLarge, |
| 602 | "Bulk force operation is too large", |
| 603 | fmt.Sprintf("Bulk force operations accept at most %d run ids.", MaxForceRunBulkIDs), |
| 604 | diagnosticcontract.SeverityError, |
| 605 | "", |
| 606 | map[string]any{"limit": MaxForceRunBulkIDs, "count": len(req.RunIDs)}, |
| 607 | ErrBulkTooLarge, |
| 608 | ) |
| 609 | } |
| 610 | seen := make(map[string]struct{}, len(req.RunIDs)) |
| 611 | ids := make([]string, 0, len(req.RunIDs)) |
| 612 | for _, id := range req.RunIDs { |
| 613 | trimmed := strings.TrimSpace(id) |
| 614 | if trimmed == "" { |
| 615 | return BulkForceRunRequest{}, fmt.Errorf( |
| 616 | "%w: bulk force run_ids must not contain empty values", |
| 617 | ErrValidation, |
| 618 | ) |
| 619 | } |
| 620 | if _, ok := seen[trimmed]; ok { |
| 621 | continue |
| 622 | } |
| 623 | seen[trimmed] = struct{}{} |
| 624 | ids = append(ids, trimmed) |
| 625 | } |
| 626 | req.RunIDs = ids |
| 627 | req.Reason = strings.TrimSpace(req.Reason) |
| 628 | if requireReason && req.Reason == "" { |
| 629 | return BulkForceRunRequest{}, forceRunDiagnosticError( |
| 630 | diagnosticcontract.CodeForceOpRequiresReason, |
| 631 | "Force fail requires a reason", |
| 632 | "Provide a reason so every failed row has clear audit evidence.", |
| 633 | diagnosticcontract.SeverityError, |
| 634 | "agh task fail <run-id...> --reason \"operator recovery\"", |
| 635 | nil, |
| 636 | ErrForceOpRequiresReason, |
| 637 | ) |
| 638 | } |
| 639 | req.Metadata = normalizeRawJSON(req.Metadata) |
| 640 | if err := ValidateMetadataSize(req.Metadata, "bulk_force.metadata"); err != nil { |
| 641 | return BulkForceRunRequest{}, err |
| 642 | } |
| 643 | return req, nil |
| 644 | } |
| 645 | |
| 646 | func forceRunDiagnosticError( |
| 647 | code string, |
no test coverage detected