Expand generated code, add routes list
This commit is contained in:
@ -1,14 +1,187 @@
|
||||
package eia
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
eiaapi "gitea.libretechconsulting.com/50W/eia-api-go/api"
|
||||
)
|
||||
|
||||
func GetRoutes() []string {
|
||||
// For reflected API requests, dynamically replace certain
|
||||
// parameters by type
|
||||
type MethodSubs struct {
|
||||
TypedParams map[reflect.Type]reflect.Value // Replace field of specific type with value, must be ptr to type
|
||||
RequestEditorFns []eiaapi.RequestEditorFn // Optional request editor functions
|
||||
}
|
||||
|
||||
// By default replace all routes with a string year
|
||||
// To be more specific, set NameParams, which will overwrite
|
||||
// NameContainsParams
|
||||
var defaultMethodSubs = MethodSubs{
|
||||
TypedParams: map[reflect.Type]reflect.Value{
|
||||
reflect.TypeOf((*eiaapi.Route1)(nil)): reflect.ValueOf(
|
||||
strconv.Itoa(time.Now().AddDate(-1, 0, 0).Year())),
|
||||
},
|
||||
}
|
||||
|
||||
// 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) {
|
||||
eiaClient := reflect.ValueOf(client)
|
||||
|
||||
// Get the method for describing this facet
|
||||
methodName := fmt.Sprintf("%sFacetId", route)
|
||||
method := eiaClient.MethodByName(methodName)
|
||||
if !method.IsValid() {
|
||||
return nil, fmt.Errorf("method %s not found", methodName)
|
||||
}
|
||||
|
||||
// Populate facet param anywhere in func call
|
||||
subs.TypedParams[reflect.TypeOf((*eiaapi.FacetId)(nil))] = reflect.ValueOf(facet)
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
results := method.Call(args)
|
||||
resp, err := getResponse(results)
|
||||
if err != nil {
|
||||
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 {
|
||||
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)
|
||||
if !ok {
|
||||
return nil, errors.New("response does not contain facet details")
|
||||
}
|
||||
|
||||
if facetDetails == nil {
|
||||
return nil, errors.New("no facet details found for facet")
|
||||
}
|
||||
|
||||
return facetDetails.Response, nil
|
||||
}
|
||||
|
||||
// Return a list of facets given a named route
|
||||
func (client *Client) GetFacets(ctx context.Context, route string, subs *MethodSubs) (*eiaapi.FacetOptionList, error) {
|
||||
eiaClient := reflect.ValueOf(client)
|
||||
|
||||
// Get the method by name
|
||||
method := eiaClient.MethodByName(route)
|
||||
if !method.IsValid() {
|
||||
return nil, fmt.Errorf("method %q not found", route)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Prepare default substitutions with a provided context
|
||||
func DefaultMethodSubs(ctx context.Context) *MethodSubs {
|
||||
subs := defaultMethodSubs
|
||||
|
||||
subs.TypedParams[reflect.TypeOf((*context.Context)(nil)).Elem()] = reflect.ValueOf(ctx)
|
||||
|
||||
return &subs
|
||||
}
|
||||
|
||||
// Return a list of API routes with a given list of
|
||||
// function suffixes
|
||||
func GetRoutes(suffixes ...string) []string {
|
||||
eiaClientInterface := new(eiaapi.ClientInterface)
|
||||
t := reflect.TypeOf(eiaClientInterface).Elem()
|
||||
|
||||
@ -16,10 +189,103 @@ func GetRoutes() []string {
|
||||
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
method := t.Method(i)
|
||||
if strings.HasSuffix(method.Name, "Facet") {
|
||||
if len(suffixes) < 1 {
|
||||
routes = append(routes, method.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, suffix := range suffixes {
|
||||
if strings.HasSuffix(method.Name, suffix) {
|
||||
routes = append(routes, method.Name)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return slices.Clip(routes)
|
||||
}
|
||||
|
||||
// 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(method reflect.Value, name string, subs *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)
|
||||
|
||||
// Supply provided request editor Fn args if this function
|
||||
// is varidic and if we're in the lest arg
|
||||
if methodType.IsVariadic() && i == methodType.NumIn()-1 {
|
||||
for _, reFn := range subs.RequestEditorFns {
|
||||
args = append(args, reflect.ValueOf(reFn))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Retrieve arg from generated arg mapping due to use of
|
||||
// type aliases (e.g. type Route1 = string) rather than use of
|
||||
// discrete types (e.g. type Route1 string) in generated code
|
||||
funcArgs, ok := eiaapi.FunctionParams[name]
|
||||
if ok {
|
||||
if i > len(funcArgs) {
|
||||
// WARN: This indicates a generator issues
|
||||
} else {
|
||||
argType = funcArgs[i]
|
||||
}
|
||||
} else {
|
||||
// WARN: This indicates a generator issues
|
||||
}
|
||||
|
||||
// Run through type substitutions
|
||||
for t, v := range subs.TypedParams {
|
||||
if argType.Kind() == reflect.Ptr && argType.Elem() == t {
|
||||
args = append(args, v)
|
||||
goto next
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to less-specific types
|
||||
for t, v := range subs.TypedParams {
|
||||
if argType == t {
|
||||
args = append(args, v)
|
||||
goto next
|
||||
}
|
||||
}
|
||||
|
||||
// Zero value of other stuff
|
||||
args = append(args, reflect.Zero(argType))
|
||||
next:
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
Reference in New Issue
Block a user