Files

77 lines
2.4 KiB
Go
Raw Permalink Normal View History

package billing
import (
"context"
"errors"
"fmt"
"strings"
"github.com/google/uuid"
)
// FeatureKeyFromError extracts the feature key from an ErrFeatureDisabled wrap
// ("feature_disabled: marketing.campaigns.generate_ai").
func FeatureKeyFromError(err error) string {
if err == nil || !errors.Is(err, ErrFeatureDisabled) {
return ""
}
msg := err.Error()
const prefix = "feature_disabled:"
idx := strings.Index(strings.ToLower(msg), prefix)
if idx < 0 {
return ""
}
return strings.TrimSpace(msg[idx+len(prefix):])
}
// FeatureKeysForProcessingType maps a processing job type to registry keys that
// must be effective before StartJob may proceed.
func FeatureKeysForProcessingType(processingType string) []string {
switch strings.ToLower(strings.TrimSpace(processingType)) {
case "title":
return []string{"capability.ai_processing", "catalog.products.process_ai_titles"}
case "description":
return []string{"capability.ai_processing", "catalog.products.process_ai_descriptions"}
case "enhance", "enhance_only", "enhance-only", "seo", "seo_ai":
return []string{"capability.ai_processing"}
case "eprel", "eprel_only":
return []string{"capability.eprel"}
case "email_campaign", "email_campaign_ai", "campaign_ai":
return []string{"capability.campaign_ai", "marketing.campaigns.generate_ai"}
case "normalize", "specs", "fill", "categories", "attributes", "full", "":
return []string{"capability.normalize_specs_fill"}
default:
return []string{"capability.normalize_specs_fill"}
}
}
// AssertFeatures fails closed on the first disabled key.
// Loads Capabilities once for the whole key set (avoids N×CapabilitiesForCompany).
func (s *Service) AssertFeatures(ctx context.Context, companyID uuid.UUID, keys ...string) error {
if len(keys) == 0 {
return nil
}
caps, err := s.CapabilitiesForCompany(ctx, companyID)
if err != nil {
return err
}
for _, key := range keys {
key = strings.TrimSpace(key)
if key == "" {
continue
}
if caps.Features == nil || !caps.Features[key] {
return fmt.Errorf("%w: %s", ErrFeatureDisabled, key)
}
}
return nil
}
// AssertProcessingFeatures enforces plan ∩ global feature keys for a job type.
func (s *Service) AssertProcessingFeatures(ctx context.Context, companyID uuid.UUID, processingType string) error {
if s == nil {
return nil
}
return s.AssertFeatures(ctx, companyID, FeatureKeysForProcessingType(processingType)...)
}