Skip to content

Commit

Permalink
(feature): Go supports multiple files in upload (#3070)
Browse files Browse the repository at this point in the history
  • Loading branch information
amckinney authored Feb 27, 2024
1 parent 63dc9cc commit d8cf2c9
Show file tree
Hide file tree
Showing 38 changed files with 6,965 additions and 1,571 deletions.
Binary file not shown.
5,882 changes: 4,492 additions & 1,390 deletions generators/go/internal/fern/ir.json

Large diffs are not rendered by default.

338 changes: 283 additions & 55 deletions generators/go/internal/fern/ir/types.go

Large diffs are not rendered by default.

95 changes: 75 additions & 20 deletions generators/go/internal/generator/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -1011,28 +1011,50 @@ func (f *fileWriter) WriteClient(
f.P("requestBuffer := bytes.NewBuffer(nil)")
f.P("writer := multipart.NewWriter(requestBuffer)")
for _, fileProperty := range endpoint.FileProperties {
filePropertyInfo, err := filePropertyToInfo(fileProperty)
if err != nil {
return nil, err
}
var (
fileVariable = fileProperty.Key.Name.CamelCase.SafeName
filenameVariable = fileProperty.Key.Name.CamelCase.UnsafeName + "Filename"
filenameValue = fileProperty.Key.Name.CamelCase.UnsafeName + "_filename"
partVariable = fileProperty.Key.Name.CamelCase.UnsafeName + "Part"
fileVariable = filePropertyInfo.Key.Name.CamelCase.SafeName
filenameVariable = filePropertyInfo.Key.Name.CamelCase.UnsafeName + "Filename"
filenameValue = filePropertyInfo.Key.Name.CamelCase.UnsafeName + "_filename"
partVariable = filePropertyInfo.Key.Name.CamelCase.UnsafeName + "Part"
)
if fileProperty.IsOptional {
f.P("if ", fileVariable, " != nil {")
}
f.P(fmt.Sprintf("%s := %q", filenameVariable, filenameValue))
f.P("if named, ok := ", fileVariable, ".(interface{ Name() string }); ok {")
f.P(fmt.Sprintf("%s = named.Name()", filenameVariable))
f.P("}")
f.P(partVariable, `, err := writer.CreateFormFile("`, fileProperty.Key.WireValue, `", `, filenameVariable, ")")
f.P("if err != nil {")
f.P("return ", endpoint.ErrorReturnValues)
f.P("}")
f.P("if _, err := io.Copy(", partVariable, ", ", fileVariable, "); err != nil {")
f.P("return ", endpoint.ErrorReturnValues)
f.P("}")
if fileProperty.IsOptional {
if filePropertyInfo.IsArray {
// We don't care whether the file array is optional or not; the range
// handles that for us.
f.P("for i, f := range ", fileVariable, "{")
f.P(filenameVariable, ` := fmt.Sprintf("`, filenameValue, `_%d", i)`)
f.P("if named, ok := f.(interface{ Name() string }); ok {")
f.P(fmt.Sprintf("%s = named.Name()", filenameVariable))
f.P("}")
f.P(partVariable, `, err := writer.CreateFormFile("`, filePropertyInfo.Key.WireValue, `", `, filenameVariable, ")")
f.P("if err != nil {")
f.P("return ", endpoint.ErrorReturnValues)
f.P("}")
f.P("if _, err := io.Copy(", partVariable, ", f); err != nil {")
f.P("return ", endpoint.ErrorReturnValues)
f.P("}")
f.P("}")
} else {
if filePropertyInfo.IsOptional {
f.P("if ", fileVariable, " != nil {")
}
f.P(fmt.Sprintf("%s := %q", filenameVariable, filenameValue))
f.P("if named, ok := ", fileVariable, ".(interface{ Name() string }); ok {")
f.P(fmt.Sprintf("%s = named.Name()", filenameVariable))
f.P("}")
f.P(partVariable, `, err := writer.CreateFormFile("`, filePropertyInfo.Key.WireValue, `", `, filenameVariable, ")")
f.P("if err != nil {")
f.P("return ", endpoint.ErrorReturnValues)
f.P("}")
f.P("if _, err := io.Copy(", partVariable, ", ", fileVariable, "); err != nil {")
f.P("return ", endpoint.ErrorReturnValues)
f.P("}")
if filePropertyInfo.IsOptional {
f.P("}")
}
}
}

Expand Down Expand Up @@ -1508,6 +1530,29 @@ func generatedClientInstantiation(
}
}

type filePropertyInfo struct {
Key *ir.NameAndWireValue
IsOptional bool
IsArray bool
}

func filePropertyToInfo(fileProperty *ir.FileProperty) (*filePropertyInfo, error) {
switch fileProperty.Type {
case "file":
return &filePropertyInfo{
Key: fileProperty.File.Key,
IsOptional: fileProperty.File.IsOptional,
}, nil
case "fileArray":
return &filePropertyInfo{
Key: fileProperty.FileArray.Key,
IsOptional: fileProperty.FileArray.IsOptional,
IsArray: true,
}, nil
}
return nil, fmt.Errorf("file property %s is not yet supported", fileProperty.Type)
}

// endpoint holds the fields required to generate a client endpoint.
//
// All of the fields are pre-formatted so that they can all be simple
Expand Down Expand Up @@ -1544,6 +1589,7 @@ type endpoint struct {
QueryParameters []*ir.QueryParameter
Headers []*ir.HttpHeader
IdempotencyHeaders []*ir.HttpHeader
FilePropertyInfo *filePropertyInfo
FileProperties []*ir.FileProperty
FileBodyProperties []*ir.InlinedRequestBodyProperty
}
Expand Down Expand Up @@ -1586,12 +1632,20 @@ func (f *fileWriter) endpointFromIR(
var (
fileProperties []*ir.FileProperty
fileBodyProperties []*ir.InlinedRequestBodyProperty
filePropertyInfo *filePropertyInfo
)
if irEndpoint.RequestBody != nil && irEndpoint.RequestBody.FileUpload != nil {
for _, fileUploadProperty := range irEndpoint.RequestBody.FileUpload.Properties {
if fileUploadProperty.File != nil {
parameterName := fileUploadProperty.File.Key.Name.CamelCase.SafeName
filePropertyInfo, err := filePropertyToInfo(fileUploadProperty.File)
if err != nil {
return nil, err
}
parameterName := filePropertyInfo.Key.Name.CamelCase.SafeName
parameterType := "io.Reader"
if filePropertyInfo.IsArray {
parameterType = "[]io.Reader"
}
signatureParameters = append(
signatureParameters,
&signatureParameter{
Expand Down Expand Up @@ -1838,6 +1892,7 @@ func (f *fileWriter) endpointFromIR(
QueryParameters: irEndpoint.QueryParameters,
Headers: irEndpoint.Headers,
IdempotencyHeaders: idempotencyHeaders,
FilePropertyInfo: filePropertyInfo,
FileProperties: fileProperties,
FileBodyProperties: fileBodyProperties,
}, nil
Expand Down
186 changes: 102 additions & 84 deletions generators/go/internal/testdata/sdk/upload/ir.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,29 +213,32 @@
},
{
"type": "file",
"key": {
"name": {
"originalName": "file",
"camelCase": {
"unsafeName": "file",
"safeName": "file"
},
"snakeCase": {
"unsafeName": "file",
"safeName": "file"
},
"screamingSnakeCase": {
"unsafeName": "FILE",
"safeName": "FILE"
"value": {
"type": "file",
"key": {
"name": {
"originalName": "file",
"camelCase": {
"unsafeName": "file",
"safeName": "file"
},
"snakeCase": {
"unsafeName": "file",
"safeName": "file"
},
"screamingSnakeCase": {
"unsafeName": "FILE",
"safeName": "FILE"
},
"pascalCase": {
"unsafeName": "File",
"safeName": "File"
}
},
"pascalCase": {
"unsafeName": "File",
"safeName": "File"
}
"wireValue": "file"
},
"wireValue": "file"
},
"isOptional": false
"isOptional": false
}
}
]
},
Expand Down Expand Up @@ -314,6 +317,7 @@
},
"errors": [],
"examples": [],
"pagination": null,
"availability": null,
"docs": null
},
Expand Down Expand Up @@ -379,29 +383,32 @@
"properties": [
{
"type": "file",
"key": {
"name": {
"originalName": "file",
"camelCase": {
"unsafeName": "file",
"safeName": "file"
},
"snakeCase": {
"unsafeName": "file",
"safeName": "file"
},
"screamingSnakeCase": {
"unsafeName": "FILE",
"safeName": "FILE"
"value": {
"type": "file",
"key": {
"name": {
"originalName": "file",
"camelCase": {
"unsafeName": "file",
"safeName": "file"
},
"snakeCase": {
"unsafeName": "file",
"safeName": "file"
},
"screamingSnakeCase": {
"unsafeName": "FILE",
"safeName": "FILE"
},
"pascalCase": {
"unsafeName": "File",
"safeName": "File"
}
},
"pascalCase": {
"unsafeName": "File",
"safeName": "File"
}
"wireValue": "file"
},
"wireValue": "file"
},
"isOptional": false
"isOptional": false
}
}
]
},
Expand Down Expand Up @@ -480,6 +487,7 @@
},
"errors": [],
"examples": [],
"pagination": null,
"availability": null,
"docs": null
},
Expand Down Expand Up @@ -575,55 +583,61 @@
},
{
"type": "file",
"key": {
"name": {
"originalName": "file",
"camelCase": {
"unsafeName": "file",
"safeName": "file"
},
"snakeCase": {
"unsafeName": "file",
"safeName": "file"
},
"screamingSnakeCase": {
"unsafeName": "FILE",
"safeName": "FILE"
"value": {
"type": "file",
"key": {
"name": {
"originalName": "file",
"camelCase": {
"unsafeName": "file",
"safeName": "file"
},
"snakeCase": {
"unsafeName": "file",
"safeName": "file"
},
"screamingSnakeCase": {
"unsafeName": "FILE",
"safeName": "FILE"
},
"pascalCase": {
"unsafeName": "File",
"safeName": "File"
}
},
"pascalCase": {
"unsafeName": "File",
"safeName": "File"
}
"wireValue": "file"
},
"wireValue": "file"
},
"isOptional": false
"isOptional": false
}
},
{
"type": "file",
"key": {
"name": {
"originalName": "optionalFile",
"camelCase": {
"unsafeName": "optionalFile",
"safeName": "optionalFile"
},
"snakeCase": {
"unsafeName": "optional_file",
"safeName": "optional_file"
},
"screamingSnakeCase": {
"unsafeName": "OPTIONAL_FILE",
"safeName": "OPTIONAL_FILE"
"value": {
"type": "file",
"key": {
"name": {
"originalName": "optionalFile",
"camelCase": {
"unsafeName": "optionalFile",
"safeName": "optionalFile"
},
"snakeCase": {
"unsafeName": "optional_file",
"safeName": "optional_file"
},
"screamingSnakeCase": {
"unsafeName": "OPTIONAL_FILE",
"safeName": "OPTIONAL_FILE"
},
"pascalCase": {
"unsafeName": "OptionalFile",
"safeName": "OptionalFile"
}
},
"pascalCase": {
"unsafeName": "OptionalFile",
"safeName": "OptionalFile"
}
"wireValue": "optionalFile"
},
"wireValue": "optionalFile"
},
"isOptional": true
"isOptional": true
}
}
]
},
Expand Down Expand Up @@ -702,6 +716,7 @@
},
"errors": [],
"examples": [],
"pagination": null,
"availability": null,
"docs": null
}
Expand Down Expand Up @@ -744,6 +759,7 @@
"sharedTypes": []
},
"webhookGroups": {},
"websocketChannels": {},
"subpackages": {
"subpackage_file": {
"name": {
Expand Down Expand Up @@ -814,6 +830,7 @@
"subpackages": [],
"navigationConfig": null,
"webhooks": null,
"websocket": null,
"hasEndpointsInTree": true,
"docs": null
}
Expand All @@ -824,6 +841,7 @@
"packagePath": [],
"file": null
},
"websocket": null,
"service": null,
"types": [],
"errors": [],
Expand Down
Loading

0 comments on commit d8cf2c9

Please sign in to comment.