Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
package shopify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
maxSyncOnlyIDs = 500
|
||||
maxCategoryFilterLen = 200
|
||||
)
|
||||
|
||||
// ProductSyncScope is an optional one-shot filter for EnqueueSync / POST /sync.
|
||||
// Empty fields mean "no filter" (sync recent processed products up to SyncLimit).
|
||||
type ProductSyncScope struct {
|
||||
Limit int `json:"sync_limit,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Category string `json:"category,omitempty"`
|
||||
ProductIDs []string `json:"product_ids,omitempty"`
|
||||
}
|
||||
|
||||
func clearOneShotSyncFilters(opt *SyncOptions) {
|
||||
if opt == nil {
|
||||
return
|
||||
}
|
||||
opt.SyncFilterStatus = ""
|
||||
opt.SyncFilterCategory = ""
|
||||
opt.SyncOnlyIDs = nil
|
||||
}
|
||||
|
||||
func normalizeSyncFilterStatus(status string) (string, error) {
|
||||
s := strings.TrimSpace(strings.ToLower(status))
|
||||
if s == "" {
|
||||
return "", nil
|
||||
}
|
||||
switch s {
|
||||
case "completed", "needs_review", "error", "processing", "processed":
|
||||
return s, nil
|
||||
default:
|
||||
return "", fmt.Errorf("%w: invalid status", ErrInvalidSyncScope)
|
||||
}
|
||||
}
|
||||
|
||||
func applyProductSyncScope(opt *SyncOptions, scope ProductSyncScope) error {
|
||||
if opt == nil {
|
||||
return fmt.Errorf("%w: missing options", ErrInvalidSyncScope)
|
||||
}
|
||||
clearOneShotSyncFilters(opt)
|
||||
if scope.Limit > 0 {
|
||||
if scope.Limit > maxSyncLimit {
|
||||
return fmt.Errorf("%w: sync_limit max %d", ErrInvalidSyncScope, maxSyncLimit)
|
||||
}
|
||||
opt.SyncLimit = scope.Limit
|
||||
}
|
||||
status, err := normalizeSyncFilterStatus(scope.Status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opt.SyncFilterStatus = status
|
||||
cat := strings.TrimSpace(scope.Category)
|
||||
if len(cat) > maxCategoryFilterLen {
|
||||
return fmt.Errorf("%w: category too long", ErrInvalidSyncScope)
|
||||
}
|
||||
opt.SyncFilterCategory = cat
|
||||
if len(scope.ProductIDs) > maxSyncOnlyIDs {
|
||||
return fmt.Errorf("%w: product_ids max %d", ErrInvalidSyncScope, maxSyncOnlyIDs)
|
||||
}
|
||||
cleaned := make([]string, 0, len(scope.ProductIDs))
|
||||
seen := make(map[string]struct{}, len(scope.ProductIDs))
|
||||
for _, raw := range scope.ProductIDs {
|
||||
id := strings.TrimSpace(raw)
|
||||
if id == "" {
|
||||
continue
|
||||
}
|
||||
parsed, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: invalid product_ids", ErrInvalidSyncScope)
|
||||
}
|
||||
key := parsed.String()
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
cleaned = append(cleaned, key)
|
||||
}
|
||||
opt.SyncOnlyIDs = cleaned
|
||||
return nil
|
||||
}
|
||||
|
||||
func pruneSyncOnlyIDs(ids []string, max int) []string {
|
||||
if max <= 0 || len(ids) <= max {
|
||||
return ids
|
||||
}
|
||||
return ids[:max]
|
||||
}
|
||||
Reference in New Issue
Block a user