style: Apply code formatting with go fmt
- Run 'make format' to ensure all Go code follows standard formatting - Maintains consistent code style across the entire codebase - No functional changes, only whitespace and formatting improvements
This commit is contained in:
parent
4fd0846127
commit
2bffa2c418
19 changed files with 543 additions and 527 deletions
|
|
@ -13,12 +13,12 @@ import (
|
|||
type ExternalAPIClient struct {
|
||||
httpClient *http.Client
|
||||
mutex sync.RWMutex
|
||||
|
||||
|
||||
// Configuration
|
||||
timeout time.Duration
|
||||
maxRetries int
|
||||
userAgent string
|
||||
|
||||
|
||||
// Rate limiting
|
||||
lastRequest time.Time
|
||||
minInterval time.Duration
|
||||
|
|
@ -32,28 +32,28 @@ type APIClientConfig struct {
|
|||
}
|
||||
|
||||
type OpenSkyFlightInfo struct {
|
||||
ICAO string `json:"icao"`
|
||||
Callsign string `json:"callsign"`
|
||||
Origin string `json:"origin"`
|
||||
Destination string `json:"destination"`
|
||||
FirstSeen time.Time `json:"first_seen"`
|
||||
LastSeen time.Time `json:"last_seen"`
|
||||
AircraftType string `json:"aircraft_type"`
|
||||
Registration string `json:"registration"`
|
||||
FlightNumber string `json:"flight_number"`
|
||||
Airline string `json:"airline"`
|
||||
ICAO string `json:"icao"`
|
||||
Callsign string `json:"callsign"`
|
||||
Origin string `json:"origin"`
|
||||
Destination string `json:"destination"`
|
||||
FirstSeen time.Time `json:"first_seen"`
|
||||
LastSeen time.Time `json:"last_seen"`
|
||||
AircraftType string `json:"aircraft_type"`
|
||||
Registration string `json:"registration"`
|
||||
FlightNumber string `json:"flight_number"`
|
||||
Airline string `json:"airline"`
|
||||
}
|
||||
|
||||
type APIError struct {
|
||||
Operation string
|
||||
StatusCode int
|
||||
Message string
|
||||
Retryable bool
|
||||
RetryAfter time.Duration
|
||||
Operation string
|
||||
StatusCode int
|
||||
Message string
|
||||
Retryable bool
|
||||
RetryAfter time.Duration
|
||||
}
|
||||
|
||||
func (e *APIError) Error() string {
|
||||
return fmt.Sprintf("API error in %s: %s (status: %d, retryable: %v)",
|
||||
return fmt.Sprintf("API error in %s: %s (status: %d, retryable: %v)",
|
||||
e.Operation, e.Message, e.StatusCode, e.Retryable)
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ func NewExternalAPIClient(config APIClientConfig) *ExternalAPIClient {
|
|||
if config.MinInterval == 0 {
|
||||
config.MinInterval = 1 * time.Second // Default rate limit
|
||||
}
|
||||
|
||||
|
||||
return &ExternalAPIClient{
|
||||
httpClient: &http.Client{
|
||||
Timeout: config.Timeout,
|
||||
|
|
@ -85,7 +85,7 @@ func NewExternalAPIClient(config APIClientConfig) *ExternalAPIClient {
|
|||
func (c *ExternalAPIClient) enforceRateLimit() {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
|
||||
elapsed := time.Since(c.lastRequest)
|
||||
if elapsed < c.minInterval {
|
||||
time.Sleep(c.minInterval - elapsed)
|
||||
|
|
@ -95,18 +95,18 @@ func (c *ExternalAPIClient) enforceRateLimit() {
|
|||
|
||||
func (c *ExternalAPIClient) makeRequest(ctx context.Context, url string) (*http.Response, error) {
|
||||
c.enforceRateLimit()
|
||||
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
req.Header.Set("User-Agent", c.userAgent)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
|
||||
var resp *http.Response
|
||||
var lastErr error
|
||||
|
||||
|
||||
for attempt := 0; attempt <= c.maxRetries; attempt++ {
|
||||
if attempt > 0 {
|
||||
// Exponential backoff
|
||||
|
|
@ -117,16 +117,16 @@ func (c *ExternalAPIClient) makeRequest(ctx context.Context, url string) (*http.
|
|||
case <-time.After(backoff):
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resp, lastErr = c.httpClient.Do(req)
|
||||
if lastErr != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
// Check for retryable status codes
|
||||
if resp.StatusCode >= 500 || resp.StatusCode == 429 {
|
||||
resp.Body.Close()
|
||||
|
||||
|
||||
// Handle rate limiting
|
||||
if resp.StatusCode == 429 {
|
||||
retryAfter := parseRetryAfter(resp.Header.Get("Retry-After"))
|
||||
|
|
@ -140,15 +140,15 @@ func (c *ExternalAPIClient) makeRequest(ctx context.Context, url string) (*http.
|
|||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
// Success or non-retryable error
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
if lastErr != nil {
|
||||
return nil, lastErr
|
||||
}
|
||||
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
|
@ -156,14 +156,14 @@ func (c *ExternalAPIClient) GetFlightInfoFromOpenSky(ctx context.Context, icao s
|
|||
if icao == "" {
|
||||
return nil, fmt.Errorf("empty ICAO code")
|
||||
}
|
||||
|
||||
|
||||
// OpenSky Network API endpoint for flight information
|
||||
apiURL := fmt.Sprintf("https://opensky-network.org/api/flights/aircraft?icao24=%s&begin=%d&end=%d",
|
||||
icao,
|
||||
time.Now().Add(-24*time.Hour).Unix(),
|
||||
time.Now().Unix(),
|
||||
)
|
||||
|
||||
|
||||
resp, err := c.makeRequest(ctx, apiURL)
|
||||
if err != nil {
|
||||
return nil, &APIError{
|
||||
|
|
@ -173,7 +173,7 @@ func (c *ExternalAPIClient) GetFlightInfoFromOpenSky(ctx context.Context, icao s
|
|||
}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return nil, &APIError{
|
||||
|
|
@ -183,7 +183,7 @@ func (c *ExternalAPIClient) GetFlightInfoFromOpenSky(ctx context.Context, icao s
|
|||
Retryable: resp.StatusCode >= 500 || resp.StatusCode == 429,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var flights [][]interface{}
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
if err := decoder.Decode(&flights); err != nil {
|
||||
|
|
@ -193,11 +193,11 @@ func (c *ExternalAPIClient) GetFlightInfoFromOpenSky(ctx context.Context, icao s
|
|||
Retryable: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if len(flights) == 0 {
|
||||
return nil, nil // No flight information available
|
||||
}
|
||||
|
||||
|
||||
// Parse the most recent flight
|
||||
flight := flights[0]
|
||||
if len(flight) < 10 {
|
||||
|
|
@ -207,11 +207,11 @@ func (c *ExternalAPIClient) GetFlightInfoFromOpenSky(ctx context.Context, icao s
|
|||
Retryable: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
info := &OpenSkyFlightInfo{
|
||||
ICAO: icao,
|
||||
}
|
||||
|
||||
|
||||
// Parse fields based on OpenSky API documentation
|
||||
if callsign, ok := flight[1].(string); ok {
|
||||
info.Callsign = callsign
|
||||
|
|
@ -228,7 +228,7 @@ func (c *ExternalAPIClient) GetFlightInfoFromOpenSky(ctx context.Context, icao s
|
|||
if destination, ok := flight[5].(string); ok {
|
||||
info.Destination = destination
|
||||
}
|
||||
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
|
|
@ -236,10 +236,10 @@ func (c *ExternalAPIClient) GetAircraftInfoFromOpenSky(ctx context.Context, icao
|
|||
if icao == "" {
|
||||
return nil, fmt.Errorf("empty ICAO code")
|
||||
}
|
||||
|
||||
|
||||
// OpenSky Network metadata API
|
||||
apiURL := fmt.Sprintf("https://opensky-network.org/api/metadata/aircraft/icao/%s", icao)
|
||||
|
||||
|
||||
resp, err := c.makeRequest(ctx, apiURL)
|
||||
if err != nil {
|
||||
return nil, &APIError{
|
||||
|
|
@ -249,11 +249,11 @@ func (c *ExternalAPIClient) GetAircraftInfoFromOpenSky(ctx context.Context, icao
|
|||
}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, nil // Aircraft not found
|
||||
}
|
||||
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return nil, &APIError{
|
||||
|
|
@ -263,7 +263,7 @@ func (c *ExternalAPIClient) GetAircraftInfoFromOpenSky(ctx context.Context, icao
|
|||
Retryable: resp.StatusCode >= 500 || resp.StatusCode == 429,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var aircraft map[string]interface{}
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
if err := decoder.Decode(&aircraft); err != nil {
|
||||
|
|
@ -273,7 +273,7 @@ func (c *ExternalAPIClient) GetAircraftInfoFromOpenSky(ctx context.Context, icao
|
|||
Retryable: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return aircraft, nil
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +282,7 @@ func (c *ExternalAPIClient) EnhanceCallsignWithExternalData(ctx context.Context,
|
|||
enhancement["callsign"] = callsign
|
||||
enhancement["icao"] = icao
|
||||
enhancement["enhanced"] = false
|
||||
|
||||
|
||||
// Try to get flight information from OpenSky
|
||||
if flightInfo, err := c.GetFlightInfoFromOpenSky(ctx, icao); err == nil && flightInfo != nil {
|
||||
enhancement["flight_info"] = map[string]interface{}{
|
||||
|
|
@ -295,53 +295,53 @@ func (c *ExternalAPIClient) EnhanceCallsignWithExternalData(ctx context.Context,
|
|||
}
|
||||
enhancement["enhanced"] = true
|
||||
}
|
||||
|
||||
|
||||
// Try to get aircraft metadata
|
||||
if aircraftInfo, err := c.GetAircraftInfoFromOpenSky(ctx, icao); err == nil && aircraftInfo != nil {
|
||||
enhancement["aircraft_info"] = aircraftInfo
|
||||
enhancement["enhanced"] = true
|
||||
}
|
||||
|
||||
|
||||
return enhancement, nil
|
||||
}
|
||||
|
||||
func (c *ExternalAPIClient) BatchEnhanceCallsigns(ctx context.Context, callsigns map[string]string) (map[string]map[string]interface{}, error) {
|
||||
results := make(map[string]map[string]interface{})
|
||||
|
||||
|
||||
for callsign, icao := range callsigns {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return results, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
|
||||
enhanced, err := c.EnhanceCallsignWithExternalData(ctx, callsign, icao)
|
||||
if err != nil {
|
||||
// Log error but continue with other callsigns
|
||||
fmt.Printf("Warning: failed to enhance callsign %s (ICAO: %s): %v\n", callsign, icao, err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
results[callsign] = enhanced
|
||||
}
|
||||
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (c *ExternalAPIClient) TestConnection(ctx context.Context) error {
|
||||
// Test with a simple API call
|
||||
testURL := "https://opensky-network.org/api/states?time=0&lamin=0&lomin=0&lamax=1&lomax=1"
|
||||
|
||||
|
||||
resp, err := c.makeRequest(ctx, testURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("connection test failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("connection test returned status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -349,24 +349,24 @@ func parseRetryAfter(header string) time.Duration {
|
|||
if header == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
// Try parsing as seconds
|
||||
if seconds, err := time.ParseDuration(header + "s"); err == nil {
|
||||
return seconds
|
||||
}
|
||||
|
||||
|
||||
// Try parsing as HTTP date
|
||||
if t, err := http.ParseTime(header); err == nil {
|
||||
return time.Until(t)
|
||||
}
|
||||
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// HealthCheck provides information about the client's health
|
||||
func (c *ExternalAPIClient) HealthCheck(ctx context.Context) map[string]interface{} {
|
||||
health := make(map[string]interface{})
|
||||
|
||||
|
||||
// Test connection
|
||||
if err := c.TestConnection(ctx); err != nil {
|
||||
health["status"] = "unhealthy"
|
||||
|
|
@ -374,16 +374,16 @@ func (c *ExternalAPIClient) HealthCheck(ctx context.Context) map[string]interfac
|
|||
} else {
|
||||
health["status"] = "healthy"
|
||||
}
|
||||
|
||||
|
||||
// Add configuration info
|
||||
health["timeout"] = c.timeout.String()
|
||||
health["max_retries"] = c.maxRetries
|
||||
health["min_interval"] = c.minInterval.String()
|
||||
health["user_agent"] = c.userAgent
|
||||
|
||||
|
||||
c.mutex.RLock()
|
||||
health["last_request"] = c.lastRequest
|
||||
c.mutex.RUnlock()
|
||||
|
||||
|
||||
return health
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue