Implement reflection and list facets
This commit is contained in:
		
							
								
								
									
										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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user