diff --git a/cmd/eia-client/cmd/get/get_route.go b/cmd/eia-client/cmd/get/get_route.go index 751363e..40e7e9e 100644 --- a/cmd/eia-client/cmd/get/get_route.go +++ b/cmd/eia-client/cmd/get/get_route.go @@ -1,20 +1,26 @@ package get import ( + "slices" "strings" + "github.com/k0kubun/pp/v3" "github.com/spf13/cobra" "gitea.libretechconsulting.com/50W/eia-api-go/cmd/eia-client/internal/util" "gitea.libretechconsulting.com/50W/eia-api-go/pkg/eia" ) -var filteredSuffixes = []string{ - "Data", - "Facet", - "FacetId", - "Body", -} +var ( + filteredSuffixes = []string{ + "Facet", + "FacetId", + "Body", + } + filteredPrefixes = []string{ + "EIAA", + } +) var GetRouteCmd = &cobra.Command{ Use: "route", @@ -28,24 +34,46 @@ var GetRouteCmd = &cobra.Command{ func RunGetRouteCmd(cmd *cobra.Command, args []string) { logger := util.Logger(cmd) logger.Info().Str("route", args[0]).Msg("getting route description") + + route, err := util.GetRoute(cmd, args[0]) + if err != nil { + logger.Fatal().Err(err).Send() + } + + pp.Println(route) } func completeRouteRoutes(cmd *cobra.Command, args []string, toComplete string) ( []string, cobra.ShellCompDirective, ) { - routes := eia.GetRoutes() + routes := eia.GetRoutes("Data") routeRoutes := make([]string, 0, len(routes)) for _, r := range routes { + var rName string for _, suffix := range filteredSuffixes { if strings.HasSuffix(r, suffix) { goto next } } - routeRoutes = append(routeRoutes, r) + for _, prefix := range filteredPrefixes { + if strings.HasPrefix(r, prefix) { + goto next + } + } + rName, _ = strings.CutSuffix(r, "Data") + routeRoutes = append(routeRoutes, rName) next: } - // TODO: Filter for toComplete - - return routeRoutes, cobra.ShellCompDirectiveNoFileComp + filteredRoutes := make([]string, 0, len(routeRoutes)) + if toComplete != "" { + for _, route := range routeRoutes { + if strings.HasPrefix(route, toComplete) { + filteredRoutes = append(filteredRoutes, route) + } + } + } else { + filteredRoutes = routeRoutes + } + return slices.Clip(filteredRoutes), cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/eia-client/internal/util/util_reflect.go b/cmd/eia-client/internal/util/util_reflect.go index fa31965..d94d129 100644 --- a/cmd/eia-client/internal/util/util_reflect.go +++ b/cmd/eia-client/internal/util/util_reflect.go @@ -7,6 +7,15 @@ import ( "gitea.libretechconsulting.com/50W/eia-api-go/pkg/eia" ) +func GetRoute(cmd *cobra.Command, route string) (*eiaapi.FinalRoute, error) { + client, err := Client(cmd) + if err != nil { + return nil, err + } + + return client.GetRoute(cmd.Context(), route, eia.DefaultMethodSubs(cmd.Context())) +} + func GetFacet(cmd *cobra.Command, route string, facet string) (*eiaapi.FacetDetails, error) { client, err := Client(cmd) if err != nil { diff --git a/pkg/eia/eia_reflection.go b/pkg/eia/eia_reflection.go index d2e89cf..15e3c8b 100644 --- a/pkg/eia/eia_reflection.go +++ b/pkg/eia/eia_reflection.go @@ -36,8 +36,50 @@ var defaultMethodSubs = MethodSubs{ }, } +func (client *Client) GetRoute(ctx context.Context, route string, subs *MethodSubs) ( + *eiaapi.FinalRoute, error, +) { + eiaClient := reflect.ValueOf(client) + + // Get the method for describing this facet + method := eiaClient.MethodByName(route) + if !method.IsValid() { + return nil, fmt.Errorf("method %s not found", route) + } + + parser, err := getParser(route) + if err != nil { + return nil, err + } + + args := prepMethodArgs(method, route, subs) + results := method.Call(args) + resp, err := getResponse(results) + if err != nil { + return nil, err + } + + result, err := parseResponse(parser, resp) + if err != nil { + return nil, err + } + + desc, ok := result.Interface().(*eiaapi.FinalRouteResponseContainer) + if !ok { + return nil, errors.New("indescribable route returned") + } + + if desc.Response == nil { + return nil, errors.New("no route response received") + } + + return desc.Response.Response, nil +} + // Given an API route and a facet ID, retrieve information about the facet -func (client *Client) GetFacet(ctx context.Context, route string, facet string, subs *MethodSubs) (*eiaapi.FacetDetails, error) { +func (client *Client) GetFacet(ctx context.Context, route string, facet string, subs *MethodSubs) ( + *eiaapi.FacetDetails, error, +) { eiaClient := reflect.ValueOf(client) // Get the method for describing this facet @@ -52,14 +94,9 @@ func (client *Client) GetFacet(ctx context.Context, route string, facet string, args := prepMethodArgs(method, methodName, subs) // Prepare a parser func for our facet response - parserFunc, exists := eiaapi.ParseFunctionsMap[fmt.Sprintf("Parse%sResponse", methodName)] - if !exists { - return nil, fmt.Errorf("parser func for %s not found", route) - } - - parser := reflect.ValueOf(parserFunc) - if !parser.IsValid() { - return nil, errors.New("unable to find parser for facet response") + parser, err := getParser(methodName) + if err != nil { + return nil, err } results := method.Call(args) @@ -68,29 +105,12 @@ func (client *Client) GetFacet(ctx context.Context, route string, facet string, return nil, err } - results = parser.Call([]reflect.Value{reflect.ValueOf(resp)}) - if len(results) != 2 { - return nil, errors.New("unexpected response while parsing facet response") - } - if err := checkCallErr(results[1]); err != nil { + result, err := parseResponse(parser, resp) + if err != nil { return nil, err } - result := results[0] - if result.Kind() == reflect.Ptr { - result = result.Elem() - } - - if result.Kind() != reflect.Struct { - return nil, fmt.Errorf("unexpected parse result kind %s", result.Kind().String()) - } - - field := result.FieldByName("JSON200") - if !field.IsValid() { - return nil, errors.New("invalid facet data field in response") - } - - facetDetails, ok := field.Interface().(*eiaapi.FacetDetailsContainer) + facetDetails, ok := result.Interface().(*eiaapi.FacetDetailsContainer) if !ok { return nil, errors.New("response does not contain facet details") } @@ -103,7 +123,9 @@ func (client *Client) GetFacet(ctx context.Context, route string, facet string, } // Return a list of facets given a named route -func (client *Client) GetFacets(ctx context.Context, route string, subs *MethodSubs) (*eiaapi.FacetOptionList, error) { +func (client *Client) GetFacets(ctx context.Context, route string, subs *MethodSubs) ( + *eiaapi.FacetOptionList, error, +) { eiaClient := reflect.ValueOf(client) // Get the method by name @@ -115,14 +137,9 @@ func (client *Client) GetFacets(ctx context.Context, route string, subs *MethodS args := prepMethodArgs(method, route, subs) // Prepare a parser func for our facet response - parserFunc, exists := eiaapi.ParseFunctionsMap[fmt.Sprintf("Parse%sResponse", route)] - if !exists { - return nil, fmt.Errorf("parser func for %s not found", route) - } - - parser := reflect.ValueOf(parserFunc) - if !parser.IsValid() { - return nil, errors.New("unable to find parser for facet response") + parser, err := getParser(route) + if err != nil { + return nil, err } // Perform the API call @@ -137,32 +154,12 @@ func (client *Client) GetFacets(ctx context.Context, route string, subs *MethodS return nil, err } - // Call the parser with our response, then extract the JSON200 response, - // and return the expected FacetOptiionsList from the container - - results = parser.Call([]reflect.Value{reflect.ValueOf(resp)}) - if len(results) != 2 { - return nil, errors.New("unexpected response while parsing facet response") - } - if err := checkCallErr(results[1]); err != nil { + result, err := parseResponse(parser, resp) + if err != nil { return nil, err } - result := results[0] - if result.Kind() == reflect.Ptr { - result = result.Elem() - } - - if result.Kind() != reflect.Struct { - return nil, fmt.Errorf("unexpected parse result kind %s", result.Kind().String()) - } - - field := result.FieldByName("JSON200") - if !field.IsValid() { - return nil, errors.New("invalid facet data field in response") - } - - facetOptions, ok := field.Interface().(*eiaapi.FacetOptionListContainer) + facetOptions, ok := result.Interface().(*eiaapi.FacetOptionListContainer) if !ok { return nil, errors.New("response does not contain facet options") } @@ -266,6 +263,34 @@ func prepMethodArgs(method reflect.Value, name string, subs *MethodSubs) []refle return args } +func parseResponse(parser reflect.Value, resp *http.Response) (reflect.Value, error) { + var result reflect.Value + + results := parser.Call([]reflect.Value{reflect.ValueOf(resp)}) + if len(results) != 2 { + return result, errors.New("unexpected response while parsing response") + } + if err := checkCallErr(results[1]); err != nil { + return result, err + } + + result = results[0] + if result.Kind() == reflect.Ptr { + result = result.Elem() + } + + if result.Kind() != reflect.Struct { + return result, fmt.Errorf("unexpected parse result kind %s", result.Kind().String()) + } + + field := result.FieldByName("JSON200") + if !field.IsValid() { + return result, errors.New("invalid facet data field in response") + } + + return field, nil +} + func getResponse(responses []reflect.Value) (*http.Response, error) { resp, ok := responses[0].Interface().(*http.Response) if !ok { @@ -279,6 +304,20 @@ func getResponse(responses []reflect.Value) (*http.Response, error) { return resp, nil } +func getParser(forMethod string) (reflect.Value, error) { + parserFunc, exists := eiaapi.ParseFunctionsMap[fmt.Sprintf("Parse%sResponse", forMethod)] + if !exists { + return reflect.Value{}, fmt.Errorf("parser func for %s not found", forMethod) + } + + parser := reflect.ValueOf(parserFunc) + if !parser.IsValid() { + return reflect.Value{}, errors.New("unable to find parser for facet response") + } + + return parser, nil +} + func checkCallErr(val reflect.Value) error { var err error var ok bool