fix lying swagger spec response type
This commit is contained in:
		
							
								
								
									
										2
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								TODO.md
									
									
									
									
									
								
							@@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## EIA Package
 | 
					## EIA Package
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [ ] Make reflection match a STRING type instead of the underlying type
 | 
					- [x] Make reflection match a STRING type instead of the underlying type
 | 
				
			||||||
- [x] Move cmd/eia-client/internal/util/util_reflect to pkg/eia
 | 
					- [x] Move cmd/eia-client/internal/util/util_reflect to pkg/eia
 | 
				
			||||||
- [x] Figure out how the fuck to support multiple facets in this piece of shit fucking API spec. Seriously, amateur bullshit.
 | 
					- [x] Figure out how the fuck to support multiple facets in this piece of shit fucking API spec. Seriously, amateur bullshit.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -30,33 +30,49 @@ var ParseFunctionsMap = map[string]interface{}{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type FunctionParamInfo struct {
 | 
					type FunctionParamInfo struct {
 | 
				
			||||||
	FunctionName string
 | 
						FunctionName string
 | 
				
			||||||
	Params       []string
 | 
						Params       []FunctionParam
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type FunctionParam struct {
 | 
				
			||||||
 | 
						Name string
 | 
				
			||||||
 | 
						Type string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const tmplFuncParams = `
 | 
					const tmplFuncParams = `
 | 
				
			||||||
// List of functions in eiaapi package
 | 
					// List of functions in eiaapi package
 | 
				
			||||||
// with slice of arg types as strings
 | 
					// with slice of arg types as strings
 | 
				
			||||||
var FunctionParams = map[string][]string{
 | 
					
 | 
				
			||||||
 | 
					type FunctionParam struct {
 | 
				
			||||||
 | 
						Name string
 | 
				
			||||||
 | 
						Type string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var FunctionParams = map[string][]FunctionParam{
 | 
				
			||||||
{{- range .}}
 | 
					{{- range .}}
 | 
				
			||||||
	"{{ .FunctionName }}": {
 | 
						"{{ .FunctionName }}": {
 | 
				
			||||||
	  {{- range .Params }}
 | 
						  {{- range .Params }}
 | 
				
			||||||
	  {{ . | printf "\"%s\"" }},
 | 
						  {
 | 
				
			||||||
 | 
								Name: {{ .Name | printf "\"%s\"" }},
 | 
				
			||||||
 | 
								Type: {{ .Type | printf "\"%s\"" }},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	  {{- end }}
 | 
						  {{- end }}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
{{- end}}
 | 
					{{- end}}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetFuncParamType(funcName string, idx int) string {
 | 
					func GetFuncParamType(funcName string, idx int) *FunctionParam {
 | 
				
			||||||
 | 
						var param FunctionParam
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	funcParams, exists := FunctionParams[funcName]
 | 
						funcParams, exists := FunctionParams[funcName]
 | 
				
			||||||
	if !exists {
 | 
						if !exists {
 | 
				
			||||||
		return ""
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if idx < len(funcParams) {
 | 
						if idx < len(funcParams) {
 | 
				
			||||||
	  return funcParams[idx]
 | 
						  param = funcParams[idx]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ""
 | 
						return ¶m
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -88,7 +104,7 @@ func main() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
					// Load up params for all functions
 | 
										// Load up params for all functions
 | 
				
			||||||
					if funcDecl.Name.IsExported() {
 | 
										if funcDecl.Name.IsExported() {
 | 
				
			||||||
						paramTypes := make([]string, 0)
 | 
											paramTypes := make([]FunctionParam, 0)
 | 
				
			||||||
						if funcDecl.Type.Params != nil {
 | 
											if funcDecl.Type.Params != nil {
 | 
				
			||||||
							for _, param := range funcDecl.Type.Params.List {
 | 
												for _, param := range funcDecl.Type.Params.List {
 | 
				
			||||||
								// Convert the type expression to a string representation
 | 
													// Convert the type expression to a string representation
 | 
				
			||||||
@@ -98,8 +114,11 @@ func main() {
 | 
				
			|||||||
									continue
 | 
														continue
 | 
				
			||||||
								}
 | 
													}
 | 
				
			||||||
								// Append the type once for each name in the parameter
 | 
													// Append the type once for each name in the parameter
 | 
				
			||||||
								for range param.Names {
 | 
													for _, name := range param.Names {
 | 
				
			||||||
									paramTypes = append(paramTypes, typeStr)
 | 
														paramTypes = append(paramTypes, FunctionParam{
 | 
				
			||||||
 | 
															Name: name.Name,
 | 
				
			||||||
 | 
															Type: typeStr,
 | 
				
			||||||
 | 
														})
 | 
				
			||||||
								}
 | 
													}
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,11 +12,15 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func newLoggingMiddleware(logger *zerolog.Logger, level zerolog.Level) eiaapi.RequestEditorFn {
 | 
					func newLoggingMiddleware(logger *zerolog.Logger, level zerolog.Level) eiaapi.RequestEditorFn {
 | 
				
			||||||
	return func(_ context.Context, req *http.Request) error {
 | 
						return func(_ context.Context, req *http.Request) error {
 | 
				
			||||||
 | 
							// Don't log api_key
 | 
				
			||||||
 | 
							params := req.URL.Query()
 | 
				
			||||||
 | 
							delete(params, "api_key")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		logger.WithLevel(level).
 | 
							logger.WithLevel(level).
 | 
				
			||||||
			Str("method", req.Method).
 | 
								Str("method", req.Method).
 | 
				
			||||||
			Str("host", req.URL.Host).
 | 
								Str("host", req.URL.Host).
 | 
				
			||||||
			Str("path", req.URL.Path).
 | 
								Str("path", req.URL.Path).
 | 
				
			||||||
			Str("query", req.Form.Encode()).
 | 
								Str("query", params.Encode()).
 | 
				
			||||||
			Time("timestamp", time.Now()).
 | 
								Time("timestamp", time.Now()).
 | 
				
			||||||
			Send()
 | 
								Send()
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package eia
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
@@ -19,20 +20,25 @@ import (
 | 
				
			|||||||
type MethodSubs struct {
 | 
					type MethodSubs struct {
 | 
				
			||||||
	TypedParams      map[reflect.Type]reflect.Value // Replace field of specific type with value, must be ptr to type
 | 
						TypedParams      map[reflect.Type]reflect.Value // Replace field of specific type with value, must be ptr to type
 | 
				
			||||||
	StrTypedParams   map[string]reflect.Value       // Parameter types by string name from eiaapi mappings
 | 
						StrTypedParams   map[string]reflect.Value       // Parameter types by string name from eiaapi mappings
 | 
				
			||||||
 | 
						StrNamedParams   map[string]reflect.Value       // Parameter names by string name from eiaapi mappings
 | 
				
			||||||
	RequestEditorFns []eiaapi.RequestEditorFn       // Optional request editor functions
 | 
						RequestEditorFns []eiaapi.RequestEditorFn       // Optional request editor functions
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var yearStringVal = reflect.ValueOf(eiaapi.Route1(
 | 
				
			||||||
 | 
						strconv.Itoa(time.Now().AddDate(-1, 0, 0).Year()),
 | 
				
			||||||
 | 
					))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// By default replace all routes with a string year
 | 
					// By default replace all routes with a string year
 | 
				
			||||||
// To be more specific, set NameParams, which will overwrite
 | 
					// To be more specific, set NameParams, which will overwrite
 | 
				
			||||||
// NameContainsParams
 | 
					// NameContainsParams
 | 
				
			||||||
var defaultMethodSubs = MethodSubs{
 | 
					var defaultMethodSubs = MethodSubs{
 | 
				
			||||||
 | 
						StrNamedParams: map[string]reflect.Value{
 | 
				
			||||||
 | 
							"route1": yearStringVal,
 | 
				
			||||||
 | 
							"route2": yearStringVal,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	StrTypedParams: map[string]reflect.Value{
 | 
						StrTypedParams: map[string]reflect.Value{
 | 
				
			||||||
		"Route1": reflect.ValueOf(eiaapi.Route1(
 | 
							"Route1": yearStringVal,
 | 
				
			||||||
			strconv.Itoa(time.Now().AddDate(-1, 0, 0).Year()),
 | 
							"Route2": yearStringVal,
 | 
				
			||||||
		)),
 | 
					 | 
				
			||||||
		"Route2": reflect.ValueOf(eiaapi.Route1(
 | 
					 | 
				
			||||||
			strconv.Itoa(time.Now().AddDate(-1, 0, 0).Year()),
 | 
					 | 
				
			||||||
		)),
 | 
					 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -53,27 +59,37 @@ func (client *Client) GetRoute(ctx context.Context, route string, subs *MethodSu
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	args := prepMethodArgs(method, route, subs)
 | 
						args := prepMethodArgs(method, route, subs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	results := method.Call(args)
 | 
						results := method.Call(args)
 | 
				
			||||||
	resp, err := getResponse(results)
 | 
						resp, err := getResponse(results)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result, err := parseResponse(parser, resp)
 | 
						result, err := ParseResponse(&ParseOpts{
 | 
				
			||||||
 | 
							Parser:   parser,
 | 
				
			||||||
 | 
							Resp:     resp,
 | 
				
			||||||
 | 
							BodyOnly: true,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	desc, ok := result.Interface().(*eiaapi.FinalRouteResponseContainer)
 | 
						// The api doesn't actually return what the swagger spec
 | 
				
			||||||
	if !ok {
 | 
						// tells you it is going to return, so we need to do this
 | 
				
			||||||
		return nil, errors.New("indescribable route returned")
 | 
						// manually for routes
 | 
				
			||||||
 | 
						frr := new(eiaapi.FinalRouteResponse)
 | 
				
			||||||
 | 
						if err = json.Unmarshal(
 | 
				
			||||||
 | 
							result.Interface().([]uint8), frr,
 | 
				
			||||||
 | 
						); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if desc.Response == nil {
 | 
						if frr.Response == nil {
 | 
				
			||||||
		return nil, errors.New("no route response received")
 | 
							return nil, errors.New("no route response received")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return desc.Response.Response, nil
 | 
						return frr.Response, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Given an API route and a facet ID, retrieve information about the facet
 | 
					// Given an API route and a facet ID, retrieve information about the facet
 | 
				
			||||||
@@ -105,7 +121,11 @@ func (client *Client) GetFacet(ctx context.Context, route string, facet string,
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result, err := parseResponse(parser, resp)
 | 
						result, err := ParseResponse(&ParseOpts{
 | 
				
			||||||
 | 
							Parser:   parser,
 | 
				
			||||||
 | 
							Resp:     resp,
 | 
				
			||||||
 | 
							JsonOnly: true,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -154,7 +174,11 @@ func (client *Client) GetFacets(ctx context.Context, route string, subs *MethodS
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	result, err := parseResponse(parser, resp)
 | 
						result, err := ParseResponse(&ParseOpts{
 | 
				
			||||||
 | 
							Parser:   parser,
 | 
				
			||||||
 | 
							Resp:     resp,
 | 
				
			||||||
 | 
							JsonOnly: true,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -229,10 +253,17 @@ func prepMethodArgs(method reflect.Value, name string, subs *MethodSubs) []refle
 | 
				
			|||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Perform type lookups by string match
 | 
							// Perform type lookups by string match on either the name of the
 | 
				
			||||||
		if paramType := eiaapi.GetFuncParamType(name, i); paramType != "" {
 | 
							// parameter, or the type of the parameter, as a string value
 | 
				
			||||||
 | 
							if paramType := eiaapi.GetFuncParamType(name, i); paramType != nil {
 | 
				
			||||||
			for name, val := range subs.StrTypedParams {
 | 
								for name, val := range subs.StrTypedParams {
 | 
				
			||||||
				if paramType == name {
 | 
									if paramType.Type == name {
 | 
				
			||||||
 | 
										args = append(args, val)
 | 
				
			||||||
 | 
										goto next
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								for name, val := range subs.StrNamedParams {
 | 
				
			||||||
 | 
									if paramType.Name == name {
 | 
				
			||||||
					args = append(args, val)
 | 
										args = append(args, val)
 | 
				
			||||||
					goto next
 | 
										goto next
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -263,10 +294,21 @@ func prepMethodArgs(method reflect.Value, name string, subs *MethodSubs) []refle
 | 
				
			|||||||
	return args
 | 
						return args
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseResponse(parser reflect.Value, resp *http.Response) (reflect.Value, error) {
 | 
					// If jsonOnly is set, only the parsed value will be returned in a field called JSON200
 | 
				
			||||||
 | 
					// If there are unmarshaling issues due to code generated from the crappy swagger spec,
 | 
				
			||||||
 | 
					// you may have to manually unmarshal the "Body" field into the correct type. Setting jsonOnly
 | 
				
			||||||
 | 
					// to false will return the entire thing rather than just the JSON200 field.
 | 
				
			||||||
 | 
					type ParseOpts struct {
 | 
				
			||||||
 | 
						Parser   reflect.Value
 | 
				
			||||||
 | 
						Resp     *http.Response
 | 
				
			||||||
 | 
						JsonOnly bool // Used when parser can correctly unmarshal into type
 | 
				
			||||||
 | 
						BodyOnly bool // Used when API response does not contain openapi spec suggested type
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ParseResponse(opts *ParseOpts) (reflect.Value, error) {
 | 
				
			||||||
	var result reflect.Value
 | 
						var result reflect.Value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	results := parser.Call([]reflect.Value{reflect.ValueOf(resp)})
 | 
						results := opts.Parser.Call([]reflect.Value{reflect.ValueOf(opts.Resp)})
 | 
				
			||||||
	if len(results) != 2 {
 | 
						if len(results) != 2 {
 | 
				
			||||||
		return result, errors.New("unexpected response while parsing response")
 | 
							return result, errors.New("unexpected response while parsing response")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -283,9 +325,20 @@ func parseResponse(parser reflect.Value, resp *http.Response) (reflect.Value, er
 | 
				
			|||||||
		return result, fmt.Errorf("unexpected parse result kind %s", result.Kind().String())
 | 
							return result, fmt.Errorf("unexpected parse result kind %s", result.Kind().String())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	field := result.FieldByName("JSON200")
 | 
						if !opts.JsonOnly && !opts.BodyOnly {
 | 
				
			||||||
 | 
							return result, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var field reflect.Value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.JsonOnly {
 | 
				
			||||||
 | 
							field = result.FieldByName("JSON200")
 | 
				
			||||||
 | 
						} else if opts.BodyOnly {
 | 
				
			||||||
 | 
							field = result.FieldByName("Body")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !field.IsValid() {
 | 
						if !field.IsValid() {
 | 
				
			||||||
		return result, errors.New("invalid facet data field in response")
 | 
							return result, errors.New("invalid response container")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return field, nil
 | 
						return field, nil
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user