logging and client, context utils

This commit is contained in:
Ryan McGuire 2024-11-16 14:02:46 -05:00
parent 46fa5ea6bf
commit 9af155dbde
15 changed files with 405 additions and 85 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
bin/*
.env

View File

@ -1,39 +1,23 @@
APP_NAME := eia-api-go
SCHEMA_JSON := schema/eia-api-oapi.json
SCHEMA_DIR := schema
GENERATED_DIR := generated
# Variables
CLIENT_PKG := ./cmd/eia-client
# Default rule
all: build
.PHONY: all generate build install clean
# Retrieve and prepare the schema
schema:
mkdir -p $(SCHEMA_DIR)
# if [ -f $(SCHEMA_JSON) ]; then rm -f $(SCHEMA_JSON); fi
# curl -o $(SCHEMA_DIR)/eia-api-swagger.zip https://www.eia.gov/opendata/eia-api-swagger.zip
# unzip -o $(SCHEMA_DIR)/eia-api-swagger.zip -d $(SCHEMA_DIR)
# Default target
all: generate build
# Generate the Go client using Swagger Codegen
generate: schema
mkdir -p $(GENERATED_DIR)
docker run --rm \
-v ${PWD}:/local \
swaggerapi/swagger-codegen-cli-v3:latest generate \
-i /local/$(SCHEMA_JSON) \
-l go \
-o /local/$(GENERATED_DIR)
# Generate code
generate:
go generate ./...
# Build the application
build:
go build -o $(APP_NAME)
# Build the client command binary
build: generate
go build -o bin/eia-client $(CLIENT_PKG)
# Install the application
install:
go install ./...
# Install the client command binary
install: generate
go install $(CLIENT_PKG)
# Clean generated code
# Clean up generated files and build artifacts
clean:
rm -rf $(GENERATED_DIR)
rm -rf $(SCHEMA_DIR)
.PHONY: all schema generate build install clean
rm -rf bin/eia-client

View File

@ -1,42 +0,0 @@
package list
import (
"fmt"
"os"
"github.com/spf13/cobra"
eiaapi "gitea.libretechconsulting.com/50W/eia-api-go/api"
)
var ListCmd = &cobra.Command{
Use: "list <name>",
Args: cobra.RangeArgs(0, 1),
Short: "List Available EIA Series",
Run: runListCmd,
}
func runListCmd(cmd *cobra.Command, _ []string) {
client, err := eiaapi.NewClient("https://api.eia.gov")
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
resp, err := client.GetV2(cmd.Context())
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
defer resp.Body.Close()
response := make([]byte, 0)
_, err = resp.Body.Read(response)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Printf("%s\n", response)
}

View File

@ -0,0 +1,32 @@
package list
import (
"github.com/spf13/cobra"
"gitea.libretechconsulting.com/50W/eia-api-go/cmd/eia-client/internal/util"
)
var ListCmd = &cobra.Command{
Use: "list <name>",
Args: cobra.RangeArgs(0, 1),
Short: "List Available EIA Series",
PreRun: util.SetClient,
Run: runListCmd,
}
func runListCmd(cmd *cobra.Command, _ []string) {
logger := util.Logger(cmd)
client, err := util.Client(cmd)
if err != nil {
logger.Fatal().Err(err).Send()
}
ctx, cncl := util.RequestCtx(cmd)
defer cncl()
resp, err := client.GetV2(ctx)
if err != nil {
logger.Err(err).Send()
}
logger.Debug().Any("resp", resp).Send()
}

View File

@ -25,15 +25,32 @@ import (
"context"
"os"
"os/signal"
"time"
"gitea.libretechconsulting.com/50W/eia-api-go/cmd/client/cmd/list"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
"gitea.libretechconsulting.com/50W/eia-api-go/cmd/eia-client/cmd/list"
"gitea.libretechconsulting.com/50W/eia-api-go/cmd/eia-client/internal/util"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "eia-client",
Short: "Useful utilities for EIA API v2",
Use: "eia-client",
Short: "Useful utilities for EIA API v2",
PersistentPreRun: PreRun,
}
func PreRun(cmd *cobra.Command, _ []string) {
// Set up logging
console := zerolog.ConsoleWriter{
Out: os.Stdout,
TimeFormat: time.RFC1123,
}
logger := zerolog.New(console).With().
Timestamp().Logger().Level(*util.GetLogLevel(cmd))
util.SetLogger(cmd, &logger)
}
// Execute adds all child commands to the root command and sets flags appropriately.
@ -49,15 +66,21 @@ func Execute() {
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// Enable PreRun for all commands
cobra.EnableTraverseRunHooks = true
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cmd.yaml)")
// Handle command flags
rootCmd.PersistentFlags().StringP(util.FLAG_LOG_LEVEL, "l", util.DefaultLogLevel.String(),
"Log Level, or set "+util.ENV_LOG_LEVEL+" in environment")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
rootCmd.PersistentFlags().StringP(util.FLAG_API_LOG_LEVEL, "L", util.DefAPILogLevel.String(),
"Log Level for EIA API Middleware, or set "+util.ENV_API_LOG_LEVEL+" in environment")
rootCmd.PersistentFlags().StringP(util.FLAG_APIKEY,
"a", "", "API Key, or set "+util.ENV_API+" in environment")
rootCmd.PersistentFlags().DurationP(util.FLAG_TMOUT, "t", util.DefRequestTimeout,
"Request timeout, or set "+util.ENV_TMOUT+" in environment")
// Subcommands
rootCmd.AddCommand(list.ListCmd)

View File

@ -0,0 +1,28 @@
package util
// These constants contain typed values
// for safely setting and retrieving flags
// from the cobra command
const (
FLAG_APIKEY = "apiKey"
ENV_API = "EIA_API_KEY"
FLAG_TMOUT = "timeout"
ENV_TMOUT = "EIA_API_TMOUT"
FLAG_API_LOG_LEVEL = "apiLogLevel"
ENV_API_LOG_LEVEL = "EIA_API_LOG_LEVEL"
FLAG_LOG_LEVEL = "logLevel"
ENV_LOG_LEVEL = "LOG_LEVEL"
)
// Type for context data
type eiaCtxField uint8
// These constants contain fields to safely set
// or get fields from the command context
const (
CTX_EIA_CLIENT eiaCtxField = iota
CTX_EIA_TMOUT
)

View File

@ -0,0 +1,67 @@
package util
import (
"context"
"errors"
"os"
"time"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
"gitea.libretechconsulting.com/50W/eia-api-go/pkg/eia"
)
var (
DefRequestTimeout = 5 * time.Second
DefAPILogLevel = zerolog.DebugLevel
)
func RequestCtx(cmd *cobra.Command) (context.Context, context.CancelFunc) {
return context.WithTimeout(cmd.Context(), GetRequestTmout(cmd))
}
func GetRequestTmout(cmd *cobra.Command) time.Duration {
tmout, ok := cmd.Context().Value(CTX_EIA_TMOUT).(time.Duration)
if !ok || tmout == 0 {
return DefRequestTimeout
}
return tmout
}
func SetRequestTmout(cmd *cobra.Command, tmout time.Duration) {
cmd.SetContext(context.WithValue(cmd.Context(),
CTX_EIA_TMOUT, tmout))
}
func Client(cmd *cobra.Command) (*eia.Client, error) {
client, ok := cmd.Context().Value(CTX_EIA_CLIENT).(*eia.Client)
if !ok {
return nil, errors.New("command context lacks api client")
}
return client, nil
}
func SetClient(cmd *cobra.Command, _ []string) {
client, err := eia.NewClient(&eia.ClientOpts{
Context: cmd.Context(),
APIKey: GetAPIKey(cmd),
Logger: Logger(cmd),
LogLevel: GetAPILogLevel(cmd),
})
if err != nil {
Logger(cmd).Fatal().Err(err).Send()
}
cmd.SetContext(context.WithValue(cmd.Context(),
CTX_EIA_CLIENT, client))
}
func GetAPIKey(cmd *cobra.Command) string {
if key := os.Getenv(ENV_API); key != "" {
return key
}
key, _ := cmd.Flags().GetString(FLAG_APIKEY)
return key
}

View File

@ -0,0 +1,74 @@
package util
import (
"os"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
)
var DefaultLogLevel = zerolog.InfoLevel
func Logger(cmd *cobra.Command) *zerolog.Logger {
return zerolog.Ctx(cmd.Context())
}
func SetLogger(cmd *cobra.Command, logger *zerolog.Logger) {
cmd.SetContext(logger.WithContext(cmd.Context()))
}
func GetLogLevel(cmd *cobra.Command) *zerolog.Level {
var level zerolog.Level
var err error
if envLevel := os.Getenv(ENV_LOG_LEVEL); envLevel != "" {
level, err = zerolog.ParseLevel(envLevel)
if err != nil {
Logger(cmd).Err(err).
Str("envLogLevel", envLevel).
Str("envVar", ENV_LOG_LEVEL).
Str("defaultLevel", DefaultLogLevel.String()).
Msg("Invalid log level in environment, using default")
level = DefaultLogLevel
}
}
if flagLevel, err := cmd.Flags().GetString(FLAG_LOG_LEVEL); err == nil {
level, err = zerolog.ParseLevel(flagLevel)
if err != nil {
Logger(cmd).Err(err).
Str("flagLogLevel", flagLevel).
Str("defaultLevel", DefaultLogLevel.String()).
Msg("Invalid log level in command flag, using default")
level = DefaultLogLevel
}
}
return &level
}
func GetAPILogLevel(cmd *cobra.Command) *zerolog.Level {
var level zerolog.Level
var err error
if envLevel := os.Getenv(ENV_API_LOG_LEVEL); envLevel != "" {
level, err = zerolog.ParseLevel(envLevel)
if err != nil {
Logger(cmd).Err(err).
Str("envLogLevel", envLevel).
Str("envVar", ENV_API_LOG_LEVEL).
Str("defaultLevel", DefAPILogLevel.String()).
Msg("Invalid API log level in environment, using default")
level = DefAPILogLevel
}
}
if flagLevel, err := cmd.Flags().GetString(FLAG_API_LOG_LEVEL); err == nil {
level, err = zerolog.ParseLevel(flagLevel)
if err != nil {
Logger(cmd).Err(err).
Str("flagLogLevel", flagLevel).
Str("defaultLevel", DefAPILogLevel.String()).
Msg("Invalid API log level in command flag, using default")
level = DefAPILogLevel
}
}
return &level
}

View File

@ -21,7 +21,7 @@ THE SOFTWARE.
*/
package main
import "gitea.libretechconsulting.com/50W/eia-api-go/cmd/client/cmd"
import "gitea.libretechconsulting.com/50W/eia-api-go/cmd/eia-client/cmd"
func main() {
cmd.Execute()

5
go.mod
View File

@ -3,8 +3,10 @@ module gitea.libretechconsulting.com/50W/eia-api-go
go 1.23.3
require (
github.com/deepmap/oapi-codegen v1.16.3
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1
github.com/oapi-codegen/runtime v1.1.1
github.com/rs/zerolog v1.33.0
github.com/spf13/cobra v1.8.1
)
@ -19,6 +21,8 @@ require (
github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/speakeasy-api/openapi-overlay v0.9.0 // indirect
@ -26,6 +30,7 @@ require (
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/tools v0.27.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

17
go.sum
View File

@ -5,10 +5,13 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deepmap/oapi-codegen v1.16.3 h1:GT9G86SbQtT1r8ZB+4Cybi9VGdu1P5ieNvNdEoCSbrA=
github.com/deepmap/oapi-codegen v1.16.3/go.mod h1:JD6ErqeX0nYnhdciLc61Konj3NBASREMlkHOgHn8WAM=
github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58=
github.com/dprotaso/go-yit v0.0.0-20240618133044-5a0af90af097 h1:f5nA5Ys8RXqFXtKc0XofVRiuwNTuJzPIwTmbjLz9vj8=
github.com/dprotaso/go-yit v0.0.0-20240618133044-5a0af90af097/go.mod h1:FTAVyH6t+SlS97rv6EXRVuBDLkQqcIe/xQw9f4IFUI4=
@ -24,6 +27,7 @@ github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@ -60,6 +64,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@ -84,10 +94,14 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
@ -143,6 +157,9 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

87
pkg/eia/eia.go Normal file
View File

@ -0,0 +1,87 @@
package eia
import (
"context"
"net/url"
"slices"
"time"
"github.com/deepmap/oapi-codegen/pkg/securityprovider"
"github.com/rs/zerolog"
eiaapi "gitea.libretechconsulting.com/50W/eia-api-go/api"
)
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
*eiaapi.Client
}
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
}
func NewClient(opts *ClientOpts) (*Client, error) {
baseURL := defaultBaseURL
if opts.BaseURL != nil {
baseURL = opts.BaseURL.String()
}
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
basicAuth, err := securityprovider.NewSecurityProviderBearerToken(opts.APIKey)
if err != nil {
return nil, err
}
middlewares = append(middlewares, eiaapi.WithRequestEditorFn(basicAuth.Intercept))
// 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)))
}
client, err := eiaapi.NewClient(baseURL, slices.Clip(middlewares)...)
if err != nil {
return nil, err
}
return &Client{
apiKey: opts.APIKey,
ctx: opts.Context,
healthCheckTimeout: hcTimeout,
Client: client,
}, nil
}

20
pkg/eia/eia_health.go Normal file
View File

@ -0,0 +1,20 @@
package eia
import (
"context"
"fmt"
)
func (c *Client) Ping() error {
ctx, cncl := context.WithTimeout(c.ctx, c.healthCheckTimeout)
defer cncl()
resp, err := c.GetV2(ctx)
if err != nil {
return err
} else if resp.StatusCode != 200 {
return fmt.Errorf("non-200 response [%s] from eia api", resp.Status)
}
return nil
}

24
pkg/eia/eia_logging.go Normal file
View File

@ -0,0 +1,24 @@
package eia
import (
"context"
"net/http"
"time"
"github.com/rs/zerolog"
eiaapi "gitea.libretechconsulting.com/50W/eia-api-go/api"
)
func newLoggingMiddleware(logger *zerolog.Logger, level zerolog.Level) eiaapi.RequestEditorFn {
return func(_ context.Context, req *http.Request) error {
logger.WithLevel(level).
Str("method", req.Method).
Str("host", req.URL.Host).
Str("path", req.URL.Path).
Str("query", req.Form.Encode()).
Time("timestamp", time.Now()).
Send()
return nil
}
}