2024-11-16 19:02:46 +00:00
|
|
|
package eia
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-12-05 17:59:02 +00:00
|
|
|
"errors"
|
2024-11-19 22:00:05 +00:00
|
|
|
"fmt"
|
2024-11-16 19:02:46 +00:00
|
|
|
"net/url"
|
|
|
|
"slices"
|
2024-12-05 17:59:02 +00:00
|
|
|
"strings"
|
2024-11-16 19:02:46 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/deepmap/oapi-codegen/pkg/securityprovider"
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
|
2024-12-17 21:02:58 +00:00
|
|
|
eiaapi "gitea.libretechconsulting.com/rmcguire/eia-api-go/api"
|
2024-11-16 19:02:46 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
defaultBaseURL = "https://api.eia.gov"
|
|
|
|
defaultPingTimeout = 10 * time.Second
|
|
|
|
defaultLogLevel = zerolog.DebugLevel
|
|
|
|
)
|
|
|
|
|
|
|
|
// Simple wrapper around generated EIA API client
|
|
|
|
// that embeds a client with a bearer auth interceptor
|
|
|
|
// and optional logging middleware
|
|
|
|
//
|
|
|
|
// Performs a basic availability check against the API
|
|
|
|
// when creating a new client, and exposes a Ping() method
|
|
|
|
// for health / readiness probes
|
|
|
|
type Client struct {
|
|
|
|
ctx context.Context
|
|
|
|
apiKey string
|
|
|
|
healthCheckTimeout time.Duration
|
2024-11-19 22:00:05 +00:00
|
|
|
*eiaapi.Client
|
2024-11-19 13:42:18 +00:00
|
|
|
*eiaapi.ClientWithResponses
|
2024-11-16 19:02:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type ClientOpts struct {
|
|
|
|
Context context.Context // Base context for requests
|
|
|
|
APIKey string // API Key [EIA Opendata API v2](https://www.eia.gov/opendata/index.php)
|
|
|
|
Logger *zerolog.Logger // Optional logger, if set is injected into logging middleware
|
|
|
|
LogLevel *zerolog.Level // Optional log level, default is zerolog.DebugLevel
|
|
|
|
BaseURL *url.URL // Optional, default is https://api.eia.gov
|
|
|
|
HealthCheckTimeout *time.Duration // Timeout for Ping() function, default is 10s
|
|
|
|
}
|
|
|
|
|
2024-11-19 22:00:05 +00:00
|
|
|
type Facet struct {
|
|
|
|
Name string
|
|
|
|
Data interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewFacets(facets ...*Facet) *eiaapi.Facets {
|
|
|
|
newFacets := make(map[string]interface{}, len(facets))
|
|
|
|
for _, f := range facets {
|
2024-11-26 15:54:55 +00:00
|
|
|
for i := 0; ; i++ {
|
|
|
|
if _, set := newFacets[fmt.Sprintf("facets[%s][%d]", f.Name, i)]; set {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
newFacets[fmt.Sprintf("facets[%s][%d]", f.Name, i)] = f.Data
|
|
|
|
break
|
|
|
|
}
|
2024-11-19 22:00:05 +00:00
|
|
|
}
|
|
|
|
return &newFacets
|
|
|
|
}
|
|
|
|
|
2024-11-16 19:02:46 +00:00
|
|
|
func NewClient(opts *ClientOpts) (*Client, error) {
|
|
|
|
baseURL := defaultBaseURL
|
|
|
|
if opts.BaseURL != nil {
|
2024-12-05 17:59:02 +00:00
|
|
|
if !strings.HasPrefix(opts.BaseURL.Scheme, "http") {
|
|
|
|
return nil, errors.New("invalid scheme, only http or https supported")
|
|
|
|
}
|
2024-11-16 19:02:46 +00:00
|
|
|
baseURL = opts.BaseURL.String()
|
2024-12-05 17:59:02 +00:00
|
|
|
|
|
|
|
if _, err := url.Parse(baseURL); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-11-16 19:02:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hcTimeout := defaultPingTimeout
|
|
|
|
if opts.HealthCheckTimeout != nil && *opts.HealthCheckTimeout > time.Duration(0) {
|
|
|
|
hcTimeout = *opts.HealthCheckTimeout
|
|
|
|
}
|
|
|
|
|
|
|
|
middlewares := make([]eiaapi.ClientOption, 0, 2)
|
|
|
|
|
|
|
|
// Injects Authorization: Bearer <APIKey> header into
|
|
|
|
// outbound API calls
|
2024-11-19 13:42:18 +00:00
|
|
|
// basicAuth, err := securityprovider.NewSecurityProviderBearerToken(opts.APIKey)
|
|
|
|
// if err != nil {
|
|
|
|
// return nil, err
|
|
|
|
// }
|
|
|
|
// middlewares = append(middlewares, eiaapi.WithRequestEditorFn(basicAuth.Intercept))
|
|
|
|
|
|
|
|
// Injects API key as query parameter: ?api_key=<apiKey>
|
|
|
|
paramAuth, err := securityprovider.NewSecurityProviderApiKey("query", "api_key", opts.APIKey)
|
2024-11-16 19:02:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-11-19 13:42:18 +00:00
|
|
|
middlewares = append(middlewares, eiaapi.WithRequestEditorFn(paramAuth.Intercept))
|
2024-11-16 19:02:46 +00:00
|
|
|
|
|
|
|
// Logging middleware, if logger is given
|
|
|
|
if opts.Logger != nil {
|
|
|
|
logLevel := defaultLogLevel
|
|
|
|
if opts.LogLevel != nil {
|
|
|
|
logLevel = *opts.LogLevel
|
|
|
|
}
|
|
|
|
|
|
|
|
middlewares = append(middlewares,
|
|
|
|
eiaapi.WithRequestEditorFn(newLoggingMiddleware(opts.Logger, logLevel)))
|
|
|
|
}
|
|
|
|
|
2024-11-19 22:00:05 +00:00
|
|
|
client, err := eiaapi.NewClient(baseURL, slices.Clip(middlewares)...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
clientWithResponses, err := eiaapi.NewClientWithResponses(baseURL, slices.Clip(middlewares)...)
|
2024-11-16 19:02:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Client{
|
2024-11-19 13:42:18 +00:00
|
|
|
apiKey: opts.APIKey,
|
|
|
|
ctx: opts.Context,
|
|
|
|
healthCheckTimeout: hcTimeout,
|
2024-11-19 22:00:05 +00:00
|
|
|
Client: client,
|
|
|
|
ClientWithResponses: clientWithResponses,
|
2024-11-16 19:02:46 +00:00
|
|
|
}, nil
|
|
|
|
}
|