Upload file error `undefined: localVarFile`
Closed this issue · 1 comments
grokify commented
A undefined: localVarFile
error is encountered when a required formData
file
is present in the request:
Compile-time Errors
../../client/api_call_handling_settings.go:646:5: undefined: localVarFile
../../client/api_call_handling_settings.go:647:28: undefined: localVarFile
../../client/api_call_handling_settings.go:649:22: undefined: localVarFile
../../client/api_call_handling_settings.go:650:3: undefined: localVarFile
../../client/api_glip.go:82:5: undefined: localVarFile
../../client/api_glip.go:83:28: undefined: localVarFile
../../client/api_glip.go:85:22: undefined: localVarFile
../../client/api_glip.go:86:3: undefined: localVarFile
Example Spec
-
name: "attachment"
in: "formData"
type: "file"
required: true
description: "The file to upload"
Example Generated Code
Here is some example generated code. The issue is that:
attachment *os.File
is present in the function definition. butattachment
is never used.localVarFile
is never instantiated in the generate code
type CreatePromptsOpts struct {
Name optional.String
}
func (a *CallHandlingSettingsApiService) CreatePrompts(ctx context.Context, accountId string, attachment *os.File, localVarOptionals *CreatePromptsOpts) (PromptInfo, *http.Response, error) {
var (
localVarHttpMethod = strings.ToUpper("Post")
localVarPostBody interface{}
localVarFileName string
localVarFileBytes []byte
localVarReturnValue PromptInfo
)
// create path and map variables
localVarPath := a.client.cfg.BasePath + "/restapi/v1.0/account/{accountId}/ivr-prompts"
localVarPath = strings.Replace(localVarPath, "{"+"accountId"+"}", fmt.Sprintf("%v", accountId), -1)
localVarHeaderParams := make(map[string]string)
localVarQueryParams := url.Values{}
localVarFormParams := url.Values{}
// to determine the Content-Type header
localVarHttpContentTypes := []string{"multipart/form-data"}
// set Content-Type header
localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
if localVarHttpContentType != "" {
localVarHeaderParams["Content-Type"] = localVarHttpContentType
}
// to determine the Accept header
localVarHttpHeaderAccepts := []string{"application/json"}
// set Accept header
localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
if localVarHttpHeaderAccept != "" {
localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
}
if localVarFile != nil {
fbs, _ := ioutil.ReadAll(localVarFile)
localVarFileBytes = fbs
localVarFileName = localVarFile.Name()
localVarFile.Close()
}
if localVarOptionals != nil && localVarOptionals.Name.IsSet() {
localVarFormParams.Add("name", parameterToString(localVarOptionals.Name.Value(), ""))
}
r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
if err != nil {
return localVarReturnValue, nil, err
}
localVarHttpResponse, err := a.client.callAPI(r)
if err != nil || localVarHttpResponse == nil {
return localVarReturnValue, localVarHttpResponse, err
}
localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
localVarHttpResponse.Body.Close()
if err != nil {
return localVarReturnValue, localVarHttpResponse, err
}
if localVarHttpResponse.StatusCode < 300 {
// If we succeed, return the data, otherwise pass on to decode error.
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type"))
if err == nil {
return localVarReturnValue, localVarHttpResponse, err
}
}
if localVarHttpResponse.StatusCode >= 300 {
newErr := GenericOpenAPIError{
body: localVarBody,
error: localVarHttpResponse.Status,
}
if localVarHttpResponse.StatusCode == 0 {
var v PromptInfo
err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"))
if err != nil {
newErr.error = err.Error()
return localVarReturnValue, localVarHttpResponse, newErr
}
newErr.model = v
return localVarReturnValue, localVarHttpResponse, newErr
}
return localVarReturnValue, localVarHttpResponse, newErr
}
return localVarReturnValue, localVarHttpResponse, nil
}
Explanation of Error
- For an optional
file
, theapi.mustache
code will instantiatevar localVarFile {{dataType}}
which turns out to bevar localVarFile *os.File
- There is no such instantiation when the parameter is required (
{{#required}}
).
Here's the api.mustache
template excerpt:
{{#isFile}}
{{^required}}
var localVarFile {{dataType}}
if localVarOptionals != nil && localVarOptionals.{{vendorExtensions.x-exportParamName}}.IsSet() {
localVarFileOk := false
localVarFile, localVarFileOk = localVarOptionals.{{vendorExtensions.x-exportParamName}}.Value().({{dataType}})
if !localVarFileOk {
return {{#returnType}}localVarReturnValue, {{/returnType}}nil, reportError("{{paramName}} should be {{dataType}}")
}
}
{{/required}}
if localVarFile != nil {
fbs, _ := ioutil.ReadAll(localVarFile)
localVarFileBytes = fbs
localVarFileName = localVarFile.Name()
localVarFile.Close()
}
Desired Code
The variable should be instantiated and set to the parameter passed in the function call:
localVarFile := attachment
Proposed Fix
- Process required file params before optional file params similar to method function.
- Loop through all
{{allParams}}
like in function definition in the event there's more than one required param - Identify the parameter that is both
{{#required}}
and{{#isFile}}
- Write
localVarFile := {{paramName}}
. The data type will be implicit here and set to the same{{{dataType}}}
used in the function definition, which is*os.File
.
Here is the diff:
{{#isFile}}
+{{#required}}
+ localVarFile := {{paramName}}
+{{/required}}
Known Limitations
This approach can only handle a single file, but it seems like the Go generator can only handle a single file in general since APIClient.prepareRequest()
takes a single pair of localVarFileName
and localVarFileBytes
. A future enhancement could support multiple files.