diff --git a/cmd/eia-client/internal/util/util_reflect.go b/cmd/eia-client/internal/util/util_reflect.go index ffbf31d..153dd5f 100644 --- a/cmd/eia-client/internal/util/util_reflect.go +++ b/cmd/eia-client/internal/util/util_reflect.go @@ -14,6 +14,21 @@ import ( eiaapi "gitea.libretechconsulting.com/50W/eia-api-go/api" ) +type MethodSubs struct { + TypedParams map[reflect.Type]reflect.Value // Replace field of specific type with value + NameParams map[string]reflect.Value // Replace field with specific name with value + NameContainsParams map[string]reflect.Value // Replace fields with name containing string with value +} + +var defaultMethodSubs = MethodSubs{ + NameContainsParams: map[string]reflect.Value{ + "route": reflect.ValueOf(strconv.Itoa(time.Now().AddDate(-1, 0, 0).Year())), + }, +} + +// func GetFacet(cmd *cobra.Command, route string) (*eiaapi.FacetOptionList, error) { +// } + func GetFacets(cmd *cobra.Command, route string) (*eiaapi.FacetOptionList, error) { client, err := Client(cmd) if err != nil { @@ -29,49 +44,9 @@ func GetFacets(cmd *cobra.Command, route string) (*eiaapi.FacetOptionList, error 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 - if resp, ok = results[0].Interface().(*http.Response); !ok { - return nil, errors.New("no or invalid response received from call") - } - if err := checkCallErr(results[1]); err != nil { - return nil, err - } + args := prepMethodArgs(cmd.Context(), method, GetMethodSubs(cmd)) + // 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) @@ -82,6 +57,21 @@ func GetFacets(cmd *cobra.Command, route string) (*eiaapi.FacetOptionList, error return nil, errors.New("unable to find parser for facet response") } + // Perform the API call + results := method.Call(args) + if len(results) != 2 { + return nil, errors.New("unexpected response from get facet call") + } + + // Prepare *http.Response, error + resp, err := getResponse(results) + if err != nil { + 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") @@ -116,6 +106,55 @@ func GetFacets(cmd *cobra.Command, route string) (*eiaapi.FacetOptionList, error return facetOptions.Response, nil } +// Given an API method, replaces any named fields with the provided value +// from the subs map, otherwise uses built-in logic for common fields +// such as a context.Context. Skips request editor funcs +func prepMethodArgs(ctx context.Context, method reflect.Value, _ MethodSubs) []reflect.Value { + // 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(ctx)) + 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)) + } + + return args +} + +func getResponse(responses []reflect.Value) (*http.Response, error) { + resp, ok := responses[0].Interface().(*http.Response) + if !ok { + return nil, errors.New("no or invalid response received from call") + } + + if err := checkCallErr(responses[1]); err != nil { + return nil, err + } + + return resp, nil +} + func checkCallErr(val reflect.Value) error { var err error var ok bool @@ -128,3 +167,11 @@ func checkCallErr(val reflect.Value) error { return err } + +func GetMethodSubs(cmd *cobra.Command) MethodSubs { + subs := defaultMethodSubs + + subs.TypedParams[reflect.TypeOf(context.Background())] = reflect.ValueOf(cmd.Context()) + + return subs +}