refactor: Replace interface{} with any type alias across codebase

- Replace interface{} with any in api_integration_test.go makeRequest method and test response maps
- Replace interface{} with any in logging.go WithField method and all logging function signatures
- Replace interface{} with any in middleware etag.go sync.Map.Range callback
- Replace interface{} with any in middleware redis_ratelimit.go RedisClientInterface and result parsing
- Replace interface{} with any in middleware redis_ratelimit_
This commit is contained in:
nutcas3
2026-04-25 06:31:02 +03:00
parent cfd5ca63cf
commit 6cf546712b
12 changed files with 117 additions and 129 deletions
@@ -207,7 +207,7 @@ func setupTest(t *testing.T) *TestSetup {
}
// Helper function to make authenticated requests
func (ts *TestSetup) makeRequest(method, path string, body interface{}, token string) *httptest.ResponseRecorder {
func (ts *TestSetup) makeRequest(method, path string, body any, token string) *httptest.ResponseRecorder {
var reqBody *bytes.Buffer
if body != nil {
jsonBody, _ := json.Marshal(body)
@@ -243,7 +243,7 @@ func TestAuthenticationEndpoints(t *testing.T) {
w := ts.makeRequest("POST", "/v1/auth/login", loginData, "")
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
var response map[string]any
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.Contains(t, response, "access_token")
@@ -293,7 +293,7 @@ func TestRBACPermissions(t *testing.T) {
})
t.Run("Viewer cannot write to plugins", func(t *testing.T) {
pluginData := map[string]interface{}{
pluginData := map[string]any{
"name": "test-plugin",
"version": "1.0.0",
}
@@ -302,7 +302,7 @@ func TestRBACPermissions(t *testing.T) {
})
t.Run("Admin can write to plugins", func(t *testing.T) {
pluginData := map[string]interface{}{
pluginData := map[string]any{
"name": "test-plugin",
"version": "1.0.0",
}
@@ -342,14 +342,14 @@ func TestPluginEndpoints(t *testing.T) {
w := ts.makeRequest("GET", "/v1/plugins", nil, ts.AdminToken)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
var response map[string]any
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.Contains(t, response, "plugins")
})
t.Run("Install plugin without permissions", func(t *testing.T) {
pluginData := map[string]interface{}{
pluginData := map[string]any{
"name": "test-plugin",
"version": "1.0.0",
}
@@ -372,14 +372,14 @@ func TestAutomationEndpoints(t *testing.T) {
w := ts.makeRequest("GET", "/v1/automation", nil, ts.AdminToken)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
var response map[string]any
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.Contains(t, response, "automations")
})
t.Run("Create automation without permissions", func(t *testing.T) {
automationData := map[string]interface{}{
automationData := map[string]any{
"name": "test-automation",
"description": "Test automation",
"type": "scheduled",
@@ -389,7 +389,7 @@ func TestAutomationEndpoints(t *testing.T) {
})
t.Run("Create automation with operator permissions", func(t *testing.T) {
automationData := map[string]interface{}{
automationData := map[string]any{
"name": "test-automation",
"description": "Test automation",
"type": "scheduled",
@@ -410,7 +410,7 @@ func TestConfigurationEndpoints(t *testing.T) {
})
t.Run("Update configuration without permissions", func(t *testing.T) {
configData := map[string]interface{}{
configData := map[string]any{
"section": "test",
"key": "test.config",
"value": "test-value",
@@ -421,7 +421,7 @@ func TestConfigurationEndpoints(t *testing.T) {
})
t.Run("Update configuration with admin permissions", func(t *testing.T) {
configData := map[string]interface{}{
configData := map[string]any{
"section": "test",
"key": "test.config",
"value": "test-value",
@@ -441,7 +441,7 @@ func TestChaosEndpoints(t *testing.T) {
w := ts.makeRequest("GET", "/v1/chaos", nil, ts.AdminToken)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
var response map[string]any
err := json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.Contains(t, response, "active")
@@ -449,7 +449,7 @@ func TestChaosEndpoints(t *testing.T) {
})
t.Run("Run chaos experiment without permissions", func(t *testing.T) {
chaosData := map[string]interface{}{
chaosData := map[string]any{
"name": "test-chaos",
"type": "pod-delete",
"target": "test-pod",
@@ -460,7 +460,7 @@ func TestChaosEndpoints(t *testing.T) {
})
t.Run("Run chaos experiment with admin permissions", func(t *testing.T) {
chaosData := map[string]interface{}{
chaosData := map[string]any{
"name": "test-chaos",
"type": "pod-delete",
"target": "test-pod",
@@ -486,7 +486,7 @@ func TestErrorHandling(t *testing.T) {
})
t.Run("Missing required fields", func(t *testing.T) {
pluginData := map[string]interface{}{
pluginData := map[string]any{
"version": "1.0.0",
// Missing name field
}
+14 -14
View File
@@ -11,9 +11,9 @@ var Logger *logrus.Logger
// Config holds logging configuration
type Config struct {
Level string
Format string // "json" or "text"
Output string // "stdout" or "stderr"
Level string
Format string // "json" or "text"
Output string // "stdout" or "stderr"
}
// DefaultConfig returns default logging configuration
@@ -76,7 +76,7 @@ func InitWithDefaults() error {
}
// WithField returns a logger with a single field
func WithField(key string, value interface{}) *logrus.Entry {
func WithField(key string, value any) *logrus.Entry {
return Logger.WithField(key, value)
}
@@ -91,51 +91,51 @@ func WithError(err error) *logrus.Entry {
}
// Info logs an info message
func Info(args ...interface{}) {
func Info(args ...any) {
Logger.Info(args...)
}
// Infof logs a formatted info message
func Infof(format string, args ...interface{}) {
func Infof(format string, args ...any) {
Logger.Infof(format, args...)
}
// Warn logs a warning message
func Warn(args ...interface{}) {
func Warn(args ...any) {
Logger.Warn(args...)
}
// Warnf logs a formatted warning message
func Warnf(format string, args ...interface{}) {
func Warnf(format string, args ...any) {
Logger.Warnf(format, args...)
}
// Error logs an error message
func Error(args ...interface{}) {
func Error(args ...any) {
Logger.Error(args...)
}
// Errorf logs a formatted error message
func Errorf(format string, args ...interface{}) {
func Errorf(format string, args ...any) {
Logger.Errorf(format, args...)
}
// Fatal logs a fatal message and exits
func Fatal(args ...interface{}) {
func Fatal(args ...any) {
Logger.Fatal(args...)
}
// Fatalf logs a formatted fatal message and exits
func Fatalf(format string, args ...interface{}) {
func Fatalf(format string, args ...any) {
Logger.Fatalf(format, args...)
}
// Debug logs a debug message
func Debug(args ...interface{}) {
func Debug(args ...any) {
Logger.Debug(args...)
}
// Debugf logs a formatted debug message
func Debugf(format string, args ...interface{}) {
func Debugf(format string, args ...any) {
Logger.Debugf(format, args...)
}
+36 -36
View File
@@ -56,33 +56,33 @@ func NewETagGenerator(secretKey string) *ETagGenerator {
func (g *ETagGenerator) GenerateETag(data ETagData) string {
// Create a composite hash from multiple factors
h := sha256.New()
// Include path and method
h.Write([]byte(data.Path))
h.Write([]byte(data.Method))
// Include content if available
if len(data.Content) > 0 {
h.Write(data.Content)
}
// Include timestamp (rounded to second for consistency)
timestampBytes := []byte(fmt.Sprintf("%d", data.Timestamp.Unix()))
timestampBytes := fmt.Appendf(nil, "%d", data.Timestamp.Unix())
h.Write(timestampBytes)
// Include user context for personalized responses
if data.UserID != "" {
h.Write([]byte(data.UserID))
}
// Include query parameters for cache differentiation
if data.Params != "" {
h.Write([]byte(data.Params))
}
// Add secret key for security
h.Write(g.secretKey)
// Generate final ETag
hash := h.Sum(nil)
return fmt.Sprintf("\"%x\"", hash)
@@ -111,7 +111,7 @@ func (g *ETagGenerator) CacheETag(key, etag string) {
// InvalidateCache removes ETag entries matching a pattern
func (g *ETagGenerator) InvalidateCache(pattern string) {
g.cacheStorage.Range(func(key, value interface{}) bool {
g.cacheStorage.Range(func(key, value any) bool {
if keyStr, ok := key.(string); ok {
if strings.Contains(keyStr, pattern) {
g.cacheStorage.Delete(key)
@@ -133,26 +133,26 @@ func generateETag(c *gin.Context) string {
Timestamp: time.Now(),
Params: c.Request.URL.RawQuery,
}
// Add user context if available
if userID, exists := c.Get("user_id"); exists {
if idStr, ok := userID.(string); ok {
data.UserID = idStr
}
}
// Try to get cached ETag first
cacheKey := fmt.Sprintf("%s:%s:%s", data.Method, data.Path, data.Params)
if cached, exists := etagGenerator.GetCachedETag(cacheKey); exists {
return cached
}
// Generate new ETag
etag := etagGenerator.GenerateETag(data)
// Cache the ETag for future use
etagGenerator.CacheETag(cacheKey, etag)
return etag
}
@@ -165,14 +165,14 @@ func generateContentETag(c *gin.Context, content []byte) string {
Timestamp: time.Now(),
Params: c.Request.URL.RawQuery,
}
// Add user context if available
if userID, exists := c.Get("user_id"); exists {
if idStr, ok := userID.(string); ok {
data.UserID = idStr
}
}
return etagGenerator.GenerateETag(data)
}
@@ -184,14 +184,14 @@ func generateWeakETag(c *gin.Context) string {
Timestamp: time.Now(),
Params: c.Request.URL.RawQuery,
}
// Add user context if available
if userID, exists := c.Get("user_id"); exists {
if idStr, ok := userID.(string); ok {
data.UserID = idStr
}
}
return etagGenerator.GenerateWeakETag(data)
}
@@ -203,12 +203,12 @@ func ETagValidationMiddleware() gin.HandlerFunc {
c.Next()
return
}
// Check If-None-Match header (for GET requests)
if noneMatch := c.GetHeader("If-None-Match"); noneMatch != "" {
// Generate current ETag
currentETag := generateETag(c)
// Check if any of the ETags match
if etagMatches(noneMatch, currentETag) {
c.Header("ETag", currentETag)
@@ -217,18 +217,18 @@ func ETagValidationMiddleware() gin.HandlerFunc {
return
}
}
// Check If-Match header (for PUT/PATCH/DELETE requests)
if match := c.GetHeader("If-Match"); match != "" {
currentETag := generateETag(c)
if !etagMatches(match, currentETag) {
c.Status(http.StatusPreconditionFailed)
c.Abort()
return
}
}
c.Next()
}
}
@@ -242,14 +242,14 @@ func ContentBasedETagMiddleware() gin.HandlerFunc {
body: bytes.NewBuffer(nil),
}
c.Writer = wrapper
c.Next()
// Only generate ETags for successful GET requests
if c.Request.Method != "GET" || c.Writer.Status() != http.StatusOK {
return
}
// Generate ETag based on captured response content
responseBody := wrapper.Body()
if len(responseBody) > 0 {
@@ -268,15 +268,15 @@ func CacheInvalidationMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Store original request for post-processing
c.Next()
// Invalidate cache for POST, PUT, PATCH, DELETE requests
if c.Request.Method == "POST" || c.Request.Method == "PUT" ||
c.Request.Method == "PATCH" || c.Request.Method == "DELETE" {
if c.Request.Method == "POST" || c.Request.Method == "PUT" ||
c.Request.Method == "PATCH" || c.Request.Method == "DELETE" {
// Invalidate ETags for this resource pattern
pattern := c.Request.URL.Path
etagGenerator.InvalidateCache(pattern)
// Also invalidate related collection resources
if strings.Contains(pattern, "/") {
parts := strings.Split(strings.Trim(pattern, "/"), "/")
@@ -295,19 +295,19 @@ func etagMatches(headerValue, currentETag string) bool {
if headerValue == "*" {
return true
}
// Parse the header value (can contain multiple ETags)
etags := strings.Split(headerValue, ",")
for _, etag := range etags {
etags := strings.SplitSeq(headerValue, ",")
for etag := range etags {
etag = strings.TrimSpace(etag)
// Remove quotes for comparison
etag = strings.Trim(etag, "\"")
current := strings.Trim(currentETag, "\"W/")
if etag == current || etag == "W/"+current {
return true
}
}
return false
}
@@ -13,7 +13,7 @@ import (
// RedisClientInterface defines the interface for Redis operations
type RedisClientInterface interface {
Eval(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd
Eval(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd
Del(ctx context.Context, keys ...string) *redis.IntCmd
ZCount(ctx context.Context, key, min, max string) *redis.IntCmd
}
@@ -90,7 +90,7 @@ func (r *RedisRateLimiter) Allow(ctx context.Context, key string) (*RateLimitRes
}
// Parse Lua script result
resultSlice, ok := result.([]interface{})
resultSlice, ok := result.([]any)
if !ok || len(resultSlice) < 3 {
return nil, fmt.Errorf("invalid redis result format")
}
@@ -163,7 +163,7 @@ func (r *RedisRateLimiter) AllowTokenBucket(ctx context.Context, key string) (*R
return nil, fmt.Errorf("redis token bucket check failed: %w", err)
}
resultSlice, ok := result.([]interface{})
resultSlice, ok := result.([]any)
if !ok || len(resultSlice) < 3 {
return nil, fmt.Errorf("invalid redis result format")
}
@@ -207,10 +207,7 @@ func (r *RedisRateLimiter) GetStats(ctx context.Context, key string) (*RateLimit
return nil, fmt.Errorf("failed to get rate limit stats: %w", err)
}
remaining := r.limit - int64(count)
if remaining < 0 {
remaining = 0
}
remaining := max(r.limit-int64(count), 0)
return &RateLimitResult{
Allowed: remaining > 0,
@@ -18,8 +18,8 @@ type MockRedisClient struct {
mock.Mock
}
func (m *MockRedisClient) Eval(ctx context.Context, script string, keys []string, args ...interface{}) *redis.Cmd {
argsList := make([]interface{}, 0, len(keys)+len(args)+1)
func (m *MockRedisClient) Eval(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd {
argsList := make([]any, 0, len(keys)+len(args)+1)
argsList = append(argsList, script)
for _, key := range keys {
argsList = append(argsList, key)
@@ -55,11 +55,11 @@ func TestRedisRateLimiter_Allow(t *testing.T) {
mockClient := new(MockRedisClient)
// Mock Redis response based on test case
var mockResult []interface{}
var mockResult []any
if tt.expected {
mockResult = []interface{}{int64(1), int64(tt.limit - 1), int64(time.Now().Unix() + 60)}
mockResult = []any{int64(1), int64(tt.limit - 1), int64(time.Now().Unix() + 60)}
} else {
mockResult = []interface{}{int64(0), int64(0), int64(time.Now().Unix() + 60), int64(30)}
mockResult = []any{int64(0), int64(0), int64(time.Now().Unix() + 60), int64(30)}
}
cmd := redis.NewCmd(context.Background())
@@ -115,7 +115,7 @@ func TestRedisRateLimitMiddleware(t *testing.T) {
// Mock successful rate limit check
cmd := redis.NewCmd(context.Background())
cmd.SetVal([]interface{}{int64(1), int64(4), int64(time.Now().Unix() + 60)})
cmd.SetVal([]any{int64(1), int64(4), int64(time.Now().Unix() + 60)})
mockClient.On("Eval", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("int64"), mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(cmd)
// Create rate limiter with mock client
@@ -267,10 +267,10 @@ func TestMultiTierRateLimiter(t *testing.T) {
// Mock different tier responses
freeCmd := redis.NewCmd(context.Background())
freeCmd.SetVal([]interface{}{int64(1), int64(9), int64(time.Now().Unix() + 60)})
freeCmd.SetVal([]any{int64(1), int64(9), int64(time.Now().Unix() + 60)})
premiumCmd := redis.NewCmd(context.Background())
premiumCmd.SetVal([]interface{}{int64(1), int64(99), int64(time.Now().Unix() + 60)})
premiumCmd.SetVal([]any{int64(1), int64(99), int64(time.Now().Unix() + 60)})
mockClient.On("Eval", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("int64"), mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(freeCmd).Once()
mockClient.On("Eval", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("int64"), mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(premiumCmd).Once()
+7 -7
View File
@@ -20,9 +20,9 @@ type MessageQueue struct {
// Message represents a message to be sent/received
type Message struct {
Type string `json:"type"`
Payload map[string]interface{} `json:"payload"`
Timestamp time.Time `json:"timestamp"`
Type string `json:"type"`
Payload map[string]any `json:"payload"`
Timestamp time.Time `json:"timestamp"`
}
// NewMessageQueue creates a new message queue connection
@@ -162,10 +162,10 @@ func (mq *MessageQueue) PublishToExchange(exchange, routingKey string, msg Messa
err = mq.channel.PublishWithContext(
ctx,
exchange, // exchange
routingKey, // routing key
false, // mandatory
false, // immediate
exchange, // exchange
routingKey, // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "application/json",
DeliveryMode: amqp.Persistent,
@@ -147,8 +147,8 @@ func TestAutomationService_Create(t *testing.T) {
_, service := setupAutomationTest(t)
t.Run("Create automation successfully", func(t *testing.T) {
definition := map[string]interface{}{
"actions": []map[string]interface{}{
definition := map[string]any{
"actions": []map[string]any{
{
"type": "restart",
"target": "service",
@@ -313,7 +313,7 @@ func BenchmarkAutomationService_List(b *testing.B) {
db, service := setupAutomationTest(&testing.T{})
// Create test automations
for i := 0; i < 100; i++ {
for i := range 100 {
automation := models.Automation{
Name: fmt.Sprintf("automation-%d", i),
Description: "Test automation",
@@ -110,7 +110,7 @@ func TestPluginService_Install(t *testing.T) {
_, service := setupPluginTest(t)
t.Run("Install new plugin successfully", func(t *testing.T) {
configJSON, _ := json.Marshal(map[string]interface{}{"key": "value"})
configJSON, _ := json.Marshal(map[string]any{"key": "value"})
input := InstallPluginInput{
Name: "new-plugin",
Version: "1.0.0",
@@ -235,7 +235,7 @@ func BenchmarkPluginService_List(b *testing.B) {
db, service := setupPluginTest(&testing.T{})
// Create test plugins
for i := 0; i < 100; i++ {
for i := range 100 {
plugin := models.Plugin{
Name: fmt.Sprintf("plugin-%d", i),
Version: "1.0.0",
@@ -43,7 +43,7 @@ func CheckConnectivityHandler(client *es2.ES2Client) gin.HandlerFunc {
_, err := client.GetProfileStatus(context.Background(), req)
connected := err == nil
var errMsg interface{}
var errMsg any
if err != nil {
errMsg = err.Error()
}
@@ -71,7 +71,7 @@ func OrderProfileHandlerWithRepo(client *es2.ES2Client, repo repository.ProfileR
// Send webhook notification for successful download
if webhookClient != nil {
go webhookClient.SendProfileDownloaded(context.Background(), order.ICCID, map[string]interface{}{
go webhookClient.SendProfileDownloaded(context.Background(), order.ICCID, map[string]any{
"imsi": order.IMSI,
"profileType": order.ProfileType,
"status": downloadResp.ExecutionStatus,
@@ -141,10 +141,7 @@ func GetProfileHandlerWithRepo(client *es2.ES2Client, repo repository.ProfileRep
func ListProfilesHandlerWithRepo(repo repository.ProfileRepository) gin.HandlerFunc {
return func(c *gin.Context) {
page := parsePositiveInt(c.Query("page"), 1)
limit := parsePositiveInt(c.Query("limit"), 20)
if limit > 100 {
limit = 100
}
limit := min(parsePositiveInt(c.Query("limit"), 20), 100)
Logger.Info().Int("page", page).Int("limit", limit).Msg("Listing eSIM profiles")
@@ -194,7 +191,7 @@ func DeleteProfileHandlerWithRepo(client *es2.ES2Client, repo repository.Profile
// Send webhook notification for profile deletion
if webhookClient != nil {
go webhookClient.SendProfileDeleted(context.Background(), profileID, map[string]interface{}{
go webhookClient.SendProfileDeleted(context.Background(), profileID, map[string]any{
"executionStatus": deleteResp.ExecutionStatus,
"statusMessage": deleteResp.StatusMessage,
})
@@ -29,18 +29,18 @@ func NewWebhookClient(webhookURL, apiKey string) *WebhookClient {
// WebhookEvent represents a webhook notification event
type WebhookEvent struct {
EventType string `json:"event_type"`
Timestamp time.Time `json:"timestamp"`
ProfileICCID string `json:"profile_iccid"`
Data map[string]interface{} `json:"data"`
EventType string `json:"event_type"`
Timestamp time.Time `json:"timestamp"`
ProfileICCID string `json:"profile_iccid"`
Data map[string]any `json:"data"`
}
// Event types
const (
EventProfileDownloaded = "profile.downloaded"
EventProfileActivated = "profile.activated"
EventProfileDeactivated = "profile.deactivated"
EventProfileDeleted = "profile.deleted"
EventProfileActivated = "profile.activated"
EventProfileDeactivated = "profile.deactivated"
EventProfileDeleted = "profile.deleted"
EventProfileStatusChange = "profile.status_changed"
EventDownloadFailed = "download.failed"
)
@@ -80,34 +80,34 @@ func (w *WebhookClient) SendNotification(ctx context.Context, event *WebhookEven
}
// SendProfileDownloaded sends a notification when a profile is downloaded
func (w *WebhookClient) SendProfileDownloaded(ctx context.Context, iccid string, data map[string]interface{}) error {
func (w *WebhookClient) SendProfileDownloaded(ctx context.Context, iccid string, data map[string]any) error {
event := &WebhookEvent{
EventType: EventProfileDownloaded,
Timestamp: time.Now(),
EventType: EventProfileDownloaded,
Timestamp: time.Now(),
ProfileICCID: iccid,
Data: data,
Data: data,
}
return w.SendNotification(ctx, event)
}
// SendProfileActivated sends a notification when a profile is activated
func (w *WebhookClient) SendProfileActivated(ctx context.Context, iccid string, data map[string]interface{}) error {
func (w *WebhookClient) SendProfileActivated(ctx context.Context, iccid string, data map[string]any) error {
event := &WebhookEvent{
EventType: EventProfileActivated,
Timestamp: time.Now(),
EventType: EventProfileActivated,
Timestamp: time.Now(),
ProfileICCID: iccid,
Data: data,
Data: data,
}
return w.SendNotification(ctx, event)
}
// SendProfileDeleted sends a notification when a profile is deleted
func (w *WebhookClient) SendProfileDeleted(ctx context.Context, iccid string, data map[string]interface{}) error {
func (w *WebhookClient) SendProfileDeleted(ctx context.Context, iccid string, data map[string]any) error {
event := &WebhookEvent{
EventType: EventProfileDeleted,
Timestamp: time.Now(),
EventType: EventProfileDeleted,
Timestamp: time.Now(),
ProfileICCID: iccid,
Data: data,
Data: data,
}
return w.SendNotification(ctx, event)
}
@@ -115,10 +115,10 @@ func (w *WebhookClient) SendProfileDeleted(ctx context.Context, iccid string, da
// SendDownloadFailed sends a notification when a profile download fails
func (w *WebhookClient) SendDownloadFailed(ctx context.Context, iccid string, errorMessage string) error {
event := &WebhookEvent{
EventType: EventDownloadFailed,
Timestamp: time.Now(),
EventType: EventDownloadFailed,
Timestamp: time.Now(),
ProfileICCID: iccid,
Data: map[string]interface{}{
Data: map[string]any{
"error": errorMessage,
},
}
+2 -8
View File
@@ -1,9 +1,9 @@
package types
import (
"slices"
"fmt"
"net/url"
"slices"
"strings"
)
@@ -51,13 +51,7 @@ func (c *CLIConfig) Validate() []error {
// Validate theme
validThemes := []string{"default", "light", "dark", "high-contrast"}
if c.Theme != "" {
valid := false
for _, t := range validThemes {
if c.Theme == t {
valid = true
break
}
}
valid := slices.Contains(validThemes, c.Theme)
if !valid {
errors = append(errors, fmt.Errorf("invalid theme '%s', must be one of: %v", c.Theme, validThemes))
}