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
+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()