Implement reflection and list facets
This commit is contained in:
parent
cba9c76a83
commit
b97b24d394
9
Makefile
9
Makefile
@ -1,5 +1,7 @@
|
|||||||
# Variables
|
# Variables
|
||||||
CLIENT_PKG := ./cmd/eia-client
|
CLIENT_PKG := ./cmd/eia-client
|
||||||
|
CLIENT_GEN_FILE := ./api/eiaapi.gen.go
|
||||||
|
GO_FORMATTER := gofumpt
|
||||||
|
|
||||||
.PHONY: all generate build install clean
|
.PHONY: all generate build install clean
|
||||||
|
|
||||||
@ -9,6 +11,11 @@ all: generate build
|
|||||||
# Generate code
|
# Generate code
|
||||||
generate:
|
generate:
|
||||||
go generate ./...
|
go generate ./...
|
||||||
|
# Fix errors in generated code
|
||||||
|
sed -E -i '' 's/Total[[:space:]]+\*int/Total *string/g' $(CLIENT_GEN_FILE)
|
||||||
|
sed -E -i '' 's/Command[[:space:]]+\*\[\]string/Command *string/g' $(CLIENT_GEN_FILE)
|
||||||
|
# Pretty it up
|
||||||
|
$(GO_FORMATTER) -w $(CLIENT_GEN_FILE)
|
||||||
|
|
||||||
# Build the client command binary
|
# Build the client command binary
|
||||||
build: generate
|
build: generate
|
||||||
@ -16,7 +23,7 @@ build: generate
|
|||||||
|
|
||||||
# Install the client command binary
|
# Install the client command binary
|
||||||
install: generate
|
install: generate
|
||||||
go install $(CLIENT_PKG)
|
go install -v $(CLIENT_PKG)
|
||||||
|
|
||||||
# Clean up generated files and build artifacts
|
# Clean up generated files and build artifacts
|
||||||
clean:
|
clean:
|
||||||
|
13
cmd/eia-client/cmd/list/list.go
Normal file
13
cmd/eia-client/cmd/list/list.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package list
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
var ListCmd = &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Aliases: []string{"ls", "l"},
|
||||||
|
Short: "Commands for listing metadata",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ListCmd.AddCommand(ListFacetsCmd)
|
||||||
|
}
|
21
cmd/eia-client/cmd/list/list_facets.go
Normal file
21
cmd/eia-client/cmd/list/list_facets.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package list
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"gitea.libretechconsulting.com/50W/eia-api-go/cmd/eia-client/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ListFacetsCmd = &cobra.Command{
|
||||||
|
Use: "facets route",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Short: "List facets for given API route",
|
||||||
|
ValidArgsFunction: util.CompleteRoutes,
|
||||||
|
PreRun: util.SetClient,
|
||||||
|
Run: RunListFacetsCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunListFacetsCmd(cmd *cobra.Command, args []string) {
|
||||||
|
resp, err := util.GetFacets(cmd, args[0])
|
||||||
|
util.Logger(cmd).Info().Any("resp", resp).Err(err).Send()
|
||||||
|
}
|
@ -1,80 +0,0 @@
|
|||||||
package list
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"k8s.io/utils/ptr"
|
|
||||||
|
|
||||||
eiaapi "gitea.libretechconsulting.com/50W/eia-api-go/api"
|
|
||||||
"gitea.libretechconsulting.com/50W/eia-api-go/cmd/eia-client/internal/util"
|
|
||||||
"gitea.libretechconsulting.com/50W/eia-api-go/pkg/eia"
|
|
||||||
)
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Facets
|
|
||||||
ctx, cncl := util.RequestCtx(cmd)
|
|
||||||
defer cncl()
|
|
||||||
facets, err := client.GetV2AeoRoute1FacetWithResponse(ctx, "2023")
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal().Err(err).Send()
|
|
||||||
}
|
|
||||||
logger.Debug().Any("facets", facets.JSON200).Send()
|
|
||||||
|
|
||||||
// Facet
|
|
||||||
for _, option := range *facets.JSON200.Response.FacetOptions {
|
|
||||||
ctx, cncl = util.RequestCtx(cmd)
|
|
||||||
defer cncl()
|
|
||||||
facet, err := client.GetV2AeoRoute1FacetFacetIdWithResponse(ctx, "2023", option)
|
|
||||||
if err != nil {
|
|
||||||
logger.Err(err).Send()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.Debug().Str("facet", option).Any("options", facet.JSON200.Response.Facets).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cncl = util.RequestCtx(cmd)
|
|
||||||
defer cncl()
|
|
||||||
resp, err := client.GetV2AeoRoute1DataWithResponse(ctx, "2023", &eiaapi.GetV2AeoRoute1DataParams{
|
|
||||||
Start: ptr.To("2023"),
|
|
||||||
End: ptr.To("2023"),
|
|
||||||
Facets: eia.NewFacets(
|
|
||||||
&eia.Facet{
|
|
||||||
Name: "regionId",
|
|
||||||
Data: "5-4",
|
|
||||||
},
|
|
||||||
&eia.Facet{
|
|
||||||
Name: "seriesId",
|
|
||||||
Data: "gen_NA_elep_NA_nuc_NA_mcc_blnkwh",
|
|
||||||
},
|
|
||||||
&eia.Facet{
|
|
||||||
Name: "seriesId",
|
|
||||||
Data: "cap_NA_elep_NA_nup_NA_mcc_gw",
|
|
||||||
},
|
|
||||||
&eia.Facet{
|
|
||||||
Name: "scenario",
|
|
||||||
Data: "highmacro",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal().Err(err).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
// body, err := io.ReadAll(resp.Body)
|
|
||||||
// defer resp.Body.Close()
|
|
||||||
logger.Debug().Any("body", resp.JSON200).Send()
|
|
||||||
}
|
|
@ -25,6 +25,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
@ -52,6 +53,10 @@ func PreRun(cmd *cobra.Command, _ []string) {
|
|||||||
|
|
||||||
util.SetLogger(cmd, &logger)
|
util.SetLogger(cmd, &logger)
|
||||||
|
|
||||||
|
if strings.Contains(cmd.CommandPath(), "comple") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
util.Logger(cmd).Debug().
|
util.Logger(cmd).Debug().
|
||||||
Str("logLevel", util.Logger(cmd).GetLevel().String()).
|
Str("logLevel", util.Logger(cmd).GetLevel().String()).
|
||||||
Msg("logging configured")
|
Msg("logging configured")
|
||||||
|
23
cmd/eia-client/internal/util/util_completion.go
Normal file
23
cmd/eia-client/internal/util/util_completion.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"gitea.libretechconsulting.com/50W/eia-api-go/pkg/eia"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CompleteRoutes(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
|
routes := eia.GetRoutes()
|
||||||
|
compRoutes := make([]string, 0, len(routes))
|
||||||
|
|
||||||
|
for _, r := range routes {
|
||||||
|
if strings.HasPrefix(r, toComplete) {
|
||||||
|
compRoutes = append(compRoutes, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return slices.Clip(compRoutes), cobra.ShellCompDirectiveNoFileComp
|
||||||
|
}
|
92
cmd/eia-client/internal/util/util_reflect.go
Normal file
92
cmd/eia-client/internal/util/util_reflect.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
eiaapi "gitea.libretechconsulting.com/50W/eia-api-go/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetFacets(cmd *cobra.Command, route string) (any, error) {
|
||||||
|
client, err := Client(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the reflect.Value of the target object
|
||||||
|
targetValue := reflect.ValueOf(client)
|
||||||
|
|
||||||
|
// Get the method by name
|
||||||
|
method := targetValue.MethodByName(route)
|
||||||
|
if !method.IsValid() {
|
||||||
|
return nil, fmt.Errorf("method %q not found", route)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a slice of reflect.Value for the method's arguments
|
||||||
|
methodType := method.Type()
|
||||||
|
args := make([]reflect.Value, 0, methodType.NumIn())
|
||||||
|
|
||||||
|
// Populate arguments with zero values for their respective types
|
||||||
|
for i := 0; i < methodType.NumIn(); i++ {
|
||||||
|
argType := methodType.In(i)
|
||||||
|
|
||||||
|
// Don't supply request editor Fn args
|
||||||
|
if methodType.IsVariadic() && i == methodType.NumIn()-1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if argType == reflect.TypeOf((*context.Context)(nil)).Elem() {
|
||||||
|
args = append(args, reflect.ValueOf(cmd.Context()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to last year for Route1
|
||||||
|
if argType == reflect.TypeOf(eiaapi.Route1("1999")) {
|
||||||
|
lastYear := time.Now().AddDate(-1, 0, 0).Year()
|
||||||
|
args = append(args, reflect.ValueOf(eiaapi.Route1(strconv.Itoa(lastYear))))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero value of other stuff
|
||||||
|
args = append(args, reflect.Zero(argType))
|
||||||
|
}
|
||||||
|
|
||||||
|
results := method.Call(args)
|
||||||
|
if len(results) != 2 {
|
||||||
|
return nil, errors.New("unexpected response from get facet call")
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
var ok bool
|
||||||
|
err = nil
|
||||||
|
|
||||||
|
if resp, ok = results[0].Interface().(*http.Response); !ok {
|
||||||
|
return nil, errors.New("no or invalid response received from call")
|
||||||
|
}
|
||||||
|
|
||||||
|
if results[1].IsValid() && !results[1].IsNil() {
|
||||||
|
if err, ok = results[1].Interface().(error); !ok {
|
||||||
|
return nil, errors.New("unexpected call response")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parser := reflect.ValueOf(eiaapi).MethodByName(fmt.Sprintf("Parse%sResponse", route))
|
||||||
|
if !parser.IsValid() {
|
||||||
|
return nil, errors.New("unable to find parser for facet response")
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.Call([]reflect.Value{reflect.ValueOf(resp)})
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
@ -51,7 +51,14 @@ type Facet struct {
|
|||||||
func NewFacets(facets ...*Facet) *eiaapi.Facets {
|
func NewFacets(facets ...*Facet) *eiaapi.Facets {
|
||||||
newFacets := make(map[string]interface{}, len(facets))
|
newFacets := make(map[string]interface{}, len(facets))
|
||||||
for _, f := range facets {
|
for _, f := range facets {
|
||||||
newFacets[fmt.Sprintf("facets[%s][]", f.Name)] = f.Data
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &newFacets
|
return &newFacets
|
||||||
}
|
}
|
||||||
|
25
pkg/eia/eia_reflection.go
Normal file
25
pkg/eia/eia_reflection.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package eia
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
eiaapi "gitea.libretechconsulting.com/50W/eia-api-go/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetRoutes() []string {
|
||||||
|
eiaClientInterface := new(eiaapi.ClientInterface)
|
||||||
|
t := reflect.TypeOf(eiaClientInterface).Elem()
|
||||||
|
|
||||||
|
routes := make([]string, 0, t.NumMethod())
|
||||||
|
|
||||||
|
for i := 0; i < t.NumMethod(); i++ {
|
||||||
|
method := t.Method(i)
|
||||||
|
if strings.HasSuffix(method.Name, "Facet") {
|
||||||
|
routes = append(routes, method.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return slices.Clip(routes)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user