package util import ( "context" "errors" "fmt" "net/http" "reflect" "strconv" "time" "github.com/spf13/cobra" 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 { 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) } 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) } parser := reflect.ValueOf(parserFunc) if !parser.IsValid() { 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") } if err := checkCallErr(results[1]); 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) if !ok { return nil, errors.New("response does not contain facet options") } if facetOptions == nil { return nil, errors.New("no facet options found for facet request") } 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 if val.IsValid() && !val.IsNil() { if err, ok = val.Interface().(error); !ok { return errors.New("unexpected call response") } } return err } func GetMethodSubs(cmd *cobra.Command) MethodSubs { subs := defaultMethodSubs subs.TypedParams[reflect.TypeOf(context.Background())] = reflect.ValueOf(cmd.Context()) return subs }