- Document all exported types, functions, methods, and constants across 52 files - Add package-level doc comments to client, models, utils, and ynison packages - Document HTTP method and endpoint path for each API client method - Document model struct fields and DeJSON/DeList/ToJSON/ToMap methods - Fix duplicate package doc comment in internal/models/base.go Co-Authored-By: Claude Code <noreply@anthropic.com>
125 lines
4.3 KiB
Go
125 lines
4.3 KiB
Go
// Package utils provides utility functions for the Yandex Music API client,
|
|
// including JSON handling, request signing, schema mismatch tracking, and error types.
|
|
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// SchemaMismatch represents a mismatch between expected and actual API response.
|
|
// It captures details about missing fields, unknown fields, and unknown values.
|
|
type SchemaMismatch struct {
|
|
// ModelName is the name of the model that had the mismatch.
|
|
ModelName string
|
|
// MissingFields lists fields that were expected but not found in the response.
|
|
MissingFields []string
|
|
// UnknownFields lists fields that were not expected but found in the response.
|
|
UnknownFields []string
|
|
// UnknownValues maps unknown field names to their values.
|
|
UnknownValues map[string]any
|
|
// Endpoint is the API endpoint where the mismatch occurred.
|
|
Endpoint string
|
|
// LibraryVersion is the version of the library when the mismatch occurred.
|
|
LibraryVersion string
|
|
}
|
|
|
|
// Describe returns a human-readable description of the schema mismatch.
|
|
func (sm *SchemaMismatch) Describe() string {
|
|
var parts []string
|
|
parts = append(parts, fmt.Sprintf("Model: %s", sm.ModelName))
|
|
if len(sm.MissingFields) > 0 {
|
|
parts = append(parts, fmt.Sprintf("Missing fields: %v", sm.MissingFields))
|
|
}
|
|
if len(sm.UnknownFields) > 0 {
|
|
parts = append(parts, fmt.Sprintf("Unknown fields: %v", sm.UnknownFields))
|
|
}
|
|
if len(sm.UnknownValues) > 0 {
|
|
parts = append(parts, fmt.Sprintf("Unknown field structures: %v", sm.UnknownValues))
|
|
}
|
|
parts = append(parts, fmt.Sprintf("Endpoint: %s", sm.Endpoint))
|
|
parts = append(parts, fmt.Sprintf("Library version: %s", sm.LibraryVersion))
|
|
return strings.Join(parts, "\n")
|
|
}
|
|
|
|
// SchemaMismatchHandler is a callback for handling schema mismatches.
|
|
// It is invoked when a schema mismatch is detected, if configured.
|
|
type SchemaMismatchHandler func(*SchemaMismatch)
|
|
|
|
// SchemaMismatchConfig holds configuration for schema mismatch handling.
|
|
type SchemaMismatchConfig struct {
|
|
// ReportUnknownFields indicates whether unknown fields should be reported.
|
|
ReportUnknownFields bool
|
|
// Strict indicates whether schema mismatches should be treated as errors.
|
|
Strict bool
|
|
// OnSchemaMismatch is an optional callback for custom mismatch handling.
|
|
OnSchemaMismatch SchemaMismatchHandler
|
|
}
|
|
|
|
// DefaultSchemaMismatchConfig returns the default configuration for schema mismatch handling.
|
|
// The default config does not report unknown fields, is not strict, and has no callback.
|
|
func DefaultSchemaMismatchConfig() SchemaMismatchConfig {
|
|
return SchemaMismatchConfig{
|
|
ReportUnknownFields: false,
|
|
Strict: false,
|
|
OnSchemaMismatch: nil,
|
|
}
|
|
}
|
|
|
|
// ReportSchemaMismatch reports a schema mismatch according to the provided config.
|
|
// If a callback is configured, it is invoked with the mismatch details.
|
|
// If strict mode is enabled, an error is returned. Otherwise, a warning is logged.
|
|
func ReportSchemaMismatch(config SchemaMismatchConfig, modelName, endpoint string, missingFields, unknownFields []string, unknownValues map[string]any) error {
|
|
mismatch := &SchemaMismatch{
|
|
ModelName: modelName,
|
|
MissingFields: missingFields,
|
|
UnknownFields: unknownFields,
|
|
UnknownValues: unknownValues,
|
|
Endpoint: endpoint,
|
|
LibraryVersion: "go-port-0.1.0",
|
|
}
|
|
|
|
if config.OnSchemaMismatch != nil {
|
|
config.OnSchemaMismatch(mismatch)
|
|
return nil
|
|
}
|
|
|
|
msg := mismatch.Describe()
|
|
if config.Strict {
|
|
return fmt.Errorf("schema mismatch: %s", msg)
|
|
}
|
|
|
|
log.Printf("WARNING: Schema mismatch for %s:\n%s", modelName, msg)
|
|
return nil
|
|
}
|
|
|
|
// SchemaMismatchTracker tracks reported mismatches to avoid duplicate reports.
|
|
// It is safe for concurrent use.
|
|
type SchemaMismatchTracker struct {
|
|
mu sync.Mutex
|
|
reported map[string]bool
|
|
}
|
|
|
|
// NewSchemaMismatchTracker creates a new SchemaMismatchTracker.
|
|
func NewSchemaMismatchTracker() *SchemaMismatchTracker {
|
|
return &SchemaMismatchTracker{
|
|
reported: make(map[string]bool),
|
|
}
|
|
}
|
|
|
|
// ShouldReport returns true if the given model and fields have not been reported before.
|
|
// It marks them as reported so subsequent calls return false for the same key.
|
|
func (t *SchemaMismatchTracker) ShouldReport(modelName string, fields []string) bool {
|
|
t.mu.Lock()
|
|
defer t.mu.Unlock()
|
|
|
|
key := modelName + ":" + strings.Join(fields, ",")
|
|
if t.reported[key] {
|
|
return false
|
|
}
|
|
t.reported[key] = true
|
|
return true
|
|
}
|