-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1798 from ballerina-platform/util-fix
Fix util issue with multipart/formdata
- Loading branch information
Showing
6 changed files
with
323 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
...pi-cli/src/test/resources/generators/client/ballerina/multipart_formdata_array_client.bal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import ballerina/http; | ||
import ballerina/mime; | ||
|
||
# API to handle multipart form-data requests. | ||
public isolated client class Client { | ||
final http:Client clientEp; | ||
# Gets invoked to initialize the `connector`. | ||
# | ||
# + config - The configurations to be used when initializing the `connector` | ||
# + serviceUrl - URL of the target service | ||
# + return - An error if connector initialization failed | ||
public isolated function init(string serviceUrl, ConnectionConfig config = {}) returns error? { | ||
http:ClientConfiguration httpClientConfig = {httpVersion: config.httpVersion, timeout: config.timeout, forwarded: config.forwarded, poolConfig: config.poolConfig, compression: config.compression, circuitBreaker: config.circuitBreaker, retryConfig: config.retryConfig, validation: config.validation}; | ||
do { | ||
if config.http1Settings is ClientHttp1Settings { | ||
ClientHttp1Settings settings = check config.http1Settings.ensureType(ClientHttp1Settings); | ||
httpClientConfig.http1Settings = {...settings}; | ||
} | ||
if config.http2Settings is http:ClientHttp2Settings { | ||
httpClientConfig.http2Settings = check config.http2Settings.ensureType(http:ClientHttp2Settings); | ||
} | ||
if config.cache is http:CacheConfig { | ||
httpClientConfig.cache = check config.cache.ensureType(http:CacheConfig); | ||
} | ||
if config.responseLimits is http:ResponseLimitConfigs { | ||
httpClientConfig.responseLimits = check config.responseLimits.ensureType(http:ResponseLimitConfigs); | ||
} | ||
if config.secureSocket is http:ClientSecureSocket { | ||
httpClientConfig.secureSocket = check config.secureSocket.ensureType(http:ClientSecureSocket); | ||
} | ||
if config.proxy is http:ProxyConfig { | ||
httpClientConfig.proxy = check config.proxy.ensureType(http:ProxyConfig); | ||
} | ||
} | ||
http:Client httpEp = check new (serviceUrl, httpClientConfig); | ||
self.clientEp = httpEp; | ||
return; | ||
} | ||
|
||
# Upload a file with metadata and tags | ||
# | ||
# + headers - Headers to be sent with the request | ||
# + return - File uploaded successfully. | ||
resource isolated function post upload(upload_body payload, map<string|string[]> headers = {}) returns inline_response_200|error { | ||
string resourcePath = string `/upload`; | ||
http:Request request = new; | ||
map<Encoding> encodingMap = {"file": {contentType: "application/octet-stream"}, "metadata": {contentType: "application/json"}, "tags": {contentType: "text/plain"}}; | ||
mime:Entity[] bodyParts = check createBodyParts(payload, encodingMap); | ||
request.setBodyParts(bodyParts); | ||
return self.clientEp->post(resourcePath, request, headers); | ||
} | ||
} |
117 changes: 117 additions & 0 deletions
117
...api-cli/src/test/resources/generators/client/ballerina/multipart_formdata_array_utils.bal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import ballerina/url; | ||
import ballerina/mime; | ||
|
||
type SimpleBasicType string|boolean|int|float|decimal; | ||
|
||
# Represents encoding mechanism details. | ||
type Encoding record { | ||
# Defines how multiple values are delimited | ||
string style = FORM; | ||
# Specifies whether arrays and objects should generate as separate fields | ||
boolean explode = true; | ||
# Specifies the custom content type | ||
string contentType?; | ||
# Specifies the custom headers | ||
map<any> headers?; | ||
}; | ||
|
||
enum EncodingStyle { | ||
DEEPOBJECT, FORM, SPACEDELIMITED, PIPEDELIMITED | ||
} | ||
|
||
# Get Encoded URI for a given value. | ||
# | ||
# + value - Value to be encoded | ||
# + return - Encoded string | ||
isolated function getEncodedUri(anydata value) returns string { | ||
string|error encoded = url:encode(value.toString(), "UTF8"); | ||
if encoded is string { | ||
return encoded; | ||
} else { | ||
return value.toString(); | ||
} | ||
} | ||
|
||
isolated function createBodyParts(record {|anydata...;|} anyRecord, map<Encoding> encodingMap = {}) | ||
returns mime:Entity[]|error { | ||
mime:Entity[] entities = []; | ||
foreach [string, anydata] [key, value] in anyRecord.entries() { | ||
Encoding encodingData = encodingMap.hasKey(key) ? encodingMap.get(key) : {}; | ||
string contentDisposition = string `form-data; name=${key};`; | ||
if value is record {byte[] fileContent; string fileName;} { | ||
string fileContentDisposition = string `${contentDisposition} filename=${value.fileName}`; | ||
mime:Entity entity = check constructEntity(fileContentDisposition, encodingData, | ||
value.fileContent); | ||
entities.push(entity); | ||
} else if value is byte[] { | ||
mime:Entity entity = check constructEntity(contentDisposition, encodingData, value); | ||
entities.push(entity); | ||
} else if value is SimpleBasicType { | ||
mime:Entity entity = check constructEntity(contentDisposition, encodingData, | ||
value.toString()); | ||
entities.push(entity); | ||
} else if value is SimpleBasicType[] { | ||
if encodingData.explode { | ||
foreach SimpleBasicType member in value { | ||
mime:Entity entity = check constructEntity(contentDisposition, encodingData, | ||
member.toString()); | ||
entities.push(entity); | ||
} | ||
} else { | ||
string[] valueStrArray = from SimpleBasicType val in value | ||
select val.toString(); | ||
mime:Entity entity = check constructEntity(contentDisposition, encodingData, | ||
string:'join(",", ...valueStrArray)); | ||
entities.push(entity); | ||
} | ||
} else if value is record {} { | ||
mime:Entity entity = check constructEntity(contentDisposition, encodingData, | ||
value.toString()); | ||
entities.push(entity); | ||
} else if value is record {}[] { | ||
if encodingData.explode { | ||
foreach record {} member in value { | ||
mime:Entity entity = check constructEntity(contentDisposition, encodingData, | ||
member.toString()); | ||
entities.push(entity); | ||
} | ||
} else { | ||
string[] valueStrArray = from record {} val in value | ||
select val.toJsonString(); | ||
mime:Entity entity = check constructEntity(contentDisposition, encodingData, | ||
string:'join(",", ...valueStrArray)); | ||
entities.push(entity); | ||
} | ||
} | ||
} | ||
return entities; | ||
} | ||
|
||
isolated function constructEntity(string contentDisposition, Encoding encoding, | ||
string|byte[]|record {} data) returns mime:Entity|error { | ||
mime:Entity entity = new mime:Entity(); | ||
entity.setContentDisposition(mime:getContentDispositionObject(contentDisposition)); | ||
if data is byte[] { | ||
entity.setByteArray(data); | ||
} else if data is string { | ||
entity.setText(data); | ||
} else { | ||
entity.setJson(data.toJson()); | ||
} | ||
check populateEncodingInfo(entity, encoding); | ||
return entity; | ||
} | ||
|
||
isolated function populateEncodingInfo(mime:Entity entity, Encoding encoding) returns error? { | ||
if encoding?.contentType is string { | ||
check entity.setContentType(encoding?.contentType.toString()); | ||
} | ||
map<any>? headers = encoding?.headers; | ||
if headers is map<any> { | ||
foreach var [headerName, headerValue] in headers.entries() { | ||
if headerValue is SimpleBasicType { | ||
entity.setHeader(headerName, headerValue.toString()); | ||
} | ||
} | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
openapi-cli/src/test/resources/generators/client/utils/swagger/multipart_formdata_array.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
openapi: 3.0.3 | ||
info: | ||
title: Multipart Form-Data API | ||
description: API to handle multipart form-data requests. | ||
version: "1.0.0" | ||
paths: | ||
/upload: | ||
post: | ||
summary: Upload a file with metadata and tags | ||
description: Accepts a file upload along with metadata and tags as a multipart form-data request. | ||
operationId: uploadFile | ||
requestBody: | ||
required: true | ||
content: | ||
multipart/form-data: | ||
schema: | ||
type: object | ||
properties: | ||
file: | ||
type: string | ||
format: binary | ||
description: The file to be uploaded. | ||
metadata: | ||
type: object | ||
additionalProperties: true | ||
description: Additional metadata associated with the file. | ||
tags: | ||
type: array | ||
items: | ||
type: string | ||
description: List of tags associated with the file. | ||
required: | ||
- file | ||
encoding: | ||
file: | ||
contentType: application/octet-stream | ||
style: form | ||
metadata: | ||
contentType: application/json | ||
style: form | ||
tags: | ||
contentType: text/plain | ||
style: form | ||
explode: true | ||
responses: | ||
"200": | ||
description: File uploaded successfully. | ||
content: | ||
application/json: | ||
schema: | ||
type: object | ||
properties: | ||
message: | ||
type: string | ||
example: File uploaded successfully. | ||
file: | ||
type: string | ||
description: The name of the uploaded file. | ||
tags: | ||
type: array | ||
items: | ||
type: string | ||
description: List of tags associated with the uploaded file. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters