Skip to content

Commit a9961a0

Browse files
bjgillwing328
authored andcommitted
[rust-server] Drop file support (OpenAPITools#547)
* [rust-server] drop 'file' support In swagger v2, we had 'binary', 'byte', and 'file'. OpenAPI v3 only has the former two. This commit drops the old 'file' handling. This has the side-effect of removing a half-complete implementation of form parameter handling. This removes the ability to send files as streams, so will make life harder for those wishing to send large files without running out of memory. * Remove all remaining uses of `hasFile`
1 parent af3ca29 commit a9961a0

File tree

17 files changed

+239
-789
lines changed

17 files changed

+239
-789
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ public RustServerCodegen() {
164164
typeMapping.put("date", "chrono::DateTime<chrono::Utc>");
165165
typeMapping.put("DateTime", "chrono::DateTime<chrono::Utc>");
166166
typeMapping.put("password", "String");
167-
typeMapping.put("File", "Box<Stream<Item=Vec<u8>, Error=Error> + Send>");
168-
typeMapping.put("file", "Box<Stream<Item=Vec<u8>, Error=Error> + Send>");
167+
typeMapping.put("File", "swagger::ByteArray");
168+
typeMapping.put("file", "swagger::ByteArray");
169169
typeMapping.put("array", "Vec");
170170
typeMapping.put("map", "HashMap");
171171

@@ -710,8 +710,6 @@ public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> o
710710
}
711711
header.nameInCamelCase = toModelName(header.baseName);
712712
}
713-
714-
additionalProperties.put("apiHasFile", true);
715713
}
716714

717715
return objs;
@@ -1069,22 +1067,11 @@ private boolean paramHasXmlNamespace(CodegenParameter param, Map<String, Schema>
10691067
private void processParam(CodegenParameter param, CodegenOperation op) {
10701068
String example = null;
10711069

1072-
if (param.isFile) {
1073-
param.vendorExtensions.put("formatString", "{:?}");
1074-
op.vendorExtensions.put("hasFile", true);
1075-
additionalProperties.put("apiHasFile", true);
1076-
example = "Box::new(stream::once(Ok(b\"hello\".to_vec()))) as Box<Stream<Item=_, Error=_> + Send>";
1077-
} else if (param.isString) {
1078-
if (param.dataFormat != null && param.dataFormat.equals("byte")) {
1079-
param.vendorExtensions.put("formatString", "\\\"{:?}\\\"");
1080-
example = "swagger::ByteArray(\"" + ((param.example != null) ? param.example : "") + "\".to_string().into_bytes())";
1081-
} else {
1082-
param.vendorExtensions.put("formatString", "\\\"{}\\\"");
1083-
example = "\"" + ((param.example != null) ? param.example : "") + "\".to_string()";
1084-
}
1070+
if (param.isString) {
1071+
param.vendorExtensions.put("formatString", "\\\"{}\\\"");
1072+
example = "\"" + ((param.example != null) ? param.example : "") + "\".to_string()";
10851073
} else if (param.isPrimitiveType) {
1086-
if ((param.isByteArray) ||
1087-
(param.isBinary)) {
1074+
if ((param.isByteArray) || (param.isBinary)) {
10881075
// Binary primitive types don't implement `Display`.
10891076
param.vendorExtensions.put("formatString", "{:?}");
10901077
example = "swagger::ByteArray(Vec::from(\"" + ((param.example != null) ? param.example : "") + "\"))";
@@ -1119,12 +1106,7 @@ private void processParam(CodegenParameter param, CodegenOperation op) {
11191106
} else {
11201107
// Not required, so override the format string and example
11211108
param.vendorExtensions.put("formatString", "{:?}");
1122-
if (param.isFile) {
1123-
// Optional file types are wrapped in a future
1124-
param.vendorExtensions.put("example", (example != null) ? "Box::new(future::ok(Some(" + example + "))) as Box<Future<Item=_, Error=_> + Send>" : "None");
1125-
} else {
1126-
param.vendorExtensions.put("example", (example != null) ? "Some(" + example + ")" : "None");
1127-
}
1109+
param.vendorExtensions.put("example", (example != null) ? "Some(" + example + ")" : "None");
11281110
}
11291111
}
11301112
}

modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ license = "Unlicense"
99

1010
[features]
1111
default = ["client", "server"]
12-
client = ["serde_json", {{#usesUrlEncodedForm}}"serde_urlencoded", {{/usesUrlEncodedForm}} {{#usesXml}}"serde-xml-rs", {{/usesXml}}"serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio-core", "url", "uuid"{{#apiHasFile}}, "multipart"{{/apiHasFile}}]
13-
server = ["serde_json", {{#usesXml}}"serde-xml-rs", {{/usesXml}}"serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio-core", "tokio-proto", "tokio-tls", "regex", "percent-encoding", "url", "uuid"{{#apiHasFile}}, "multipart"{{/apiHasFile}}]
12+
client = ["serde_json", {{#usesUrlEncodedForm}}"serde_urlencoded", {{/usesUrlEncodedForm}} {{#usesXml}}"serde-xml-rs", {{/usesXml}}"serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio-core", "url", "uuid"]
13+
server = ["serde_json", {{#usesXml}}"serde-xml-rs", {{/usesXml}}"serde_ignored", "hyper", "hyper-tls", "native-tls", "openssl", "tokio-core", "tokio-proto", "tokio-tls", "regex", "percent-encoding", "url", "uuid"]
1414

1515
[dependencies]
1616
# Required by example server.

modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache

Lines changed: 5 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ extern crate openssl;
66
extern crate mime;
77
extern crate chrono;
88
extern crate url;
9-
{{#apiHasFile}}extern crate multipart;{{/apiHasFile}}
109
{{#usesUrlEncodedForm}}extern crate serde_urlencoded;{{/usesUrlEncodedForm}}
1110

1211
{{#apiUsesUuid}}use uuid;{{/apiUsesUuid}}
13-
{{#apiHasFile}}use self::multipart::client::lazy::Multipart;{{/apiHasFile}}
1412
use hyper;
1513
use hyper::header::{Headers, ContentType};
1614
use hyper::Uri;
@@ -239,7 +237,7 @@ impl<F, C> Api<C> for Client<F> where
239237
F: Future<Item=hyper::Response, Error=hyper::Error> + 'static,
240238
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<AuthData>>{{/hasAuthMethods}}{
241239
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
242-
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
240+
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{paramName}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
243241
{{#queryParams}}{{#-first}}
244242
// Query parameters
245243
{{/-first}}{{#required}} let query_{{paramName}} = format!("{{baseName}}={{=<% %>=}}{<% paramName %>}<%={{ }}=%>&", {{paramName}}=param_{{paramName}}{{#isListContainer}}.join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}});
@@ -259,47 +257,13 @@ impl<F, C> Api<C> for Client<F> where
259257

260258
let mut request = hyper::Request::new(hyper::Method::{{#vendorExtensions}}{{HttpMethod}}{{/vendorExtensions}}, uri);
261259

262-
{{#vendorExtensions}}{{#hasFile}} // Form data body
263-
let mut multipart = Multipart::new();
264-
265-
// Helper function to convert a Stream into a String. The String can then be used to build the HTTP body.
266-
fn convert_stream_to_string(stream: Box<Stream<Item=Vec<u8>, Error=Error> + Send>) -> Result<String, ApiError> {
267-
268-
stream.concat2()
269-
.wait()
270-
.map_err(|e| ApiError(format!("Unable to collect stream: {}", e)))
271-
.and_then(|body| String::from_utf8(body)
272-
.map_err(|e| ApiError(format!("Failed to convert utf8 stream to String: {}", e))))
273-
}{{/hasFile}}{{/vendorExtensions}}{{#formParams}}{{#isFile}}
274-
275-
{{^required}} if let Ok(Some(param_{{paramName}})) = param_{{paramName}}.wait() { {{/required}}
276-
{{^required}} {{/required}} match convert_stream_to_string(param_{{paramName}}) {
277-
{{^required}} {{/required}} Ok(param_{{paramName}}) => {
278-
// Add file to multipart form.
279-
multipart.add_text("{{paramName}}", param_{{paramName}});
280-
},
281-
{{^required}} {{/required}} Err(err) => return Box::new(futures::done(Err(err))),
282-
{{^required}} {{/required}} }
283-
{{^required}}}{{/required}}{{/isFile}}{{/formParams}}{{#vendorExtensions}}{{#hasFile}}
284-
285-
let mut fields = match multipart.prepare() {
286-
Ok(fields) => fields,
287-
Err(err) => return Box::new(futures::done(Err(ApiError(format!("Unable to build request: {}", err))))),
288-
};
289-
290-
let mut body_string = String::new();
291-
let body = fields.to_body().read_to_string(&mut body_string);
292-
let boundary = fields.boundary();
293-
let multipart_header = match mime::Mime::from_str(&format!("multipart/form-data;boundary={}", boundary)) {
294-
Ok(multipart_header) => multipart_header,
295-
Err(err) => return Box::new(futures::done(Err(ApiError(format!("Unable to build multipart header: {:?}", err))))),
296-
};{{/hasFile}}{{^hasFile}}{{#formParams}}{{#-first}} let params = &[{{/-first}}
260+
{{#vendorExtensions}}{{#formParams}}{{#-first}} let params = &[{{/-first}}
297261
("{{baseName}}", {{#vendorExtensions}}{{#required}}Some({{#isString}}param_{{paramName}}{{/isString}}{{^isString}}format!("{:?}", param_{{paramName}}){{/isString}}){{/required}}{{^required}}{{#isString}}param_{{paramName}}{{/isString}}{{^isString}}param_{{paramName}}.map(|param| format!("{:?}", param)){{/isString}}{{/required}}),{{/vendorExtensions}}{{#-last}}
298262
];
299263
let body = serde_urlencoded::to_string(params).expect("impossible to fail to serialize");
300264

301265
request.headers_mut().set(ContentType(mimetypes::requests::{{#vendorExtensions}}{{uppercase_operation_id}}{{/vendorExtensions}}.clone()));
302-
request.set_body(body.into_bytes());{{/-last}}{{/formParams}}{{/hasFile}}{{/vendorExtensions}}{{#bodyParam}}{{#-first}}
266+
request.set_body(body.into_bytes());{{/-last}}{{/formParams}}{{/vendorExtensions}}{{#bodyParam}}{{#-first}}
303267
// Body parameter
304268
{{/-first}}{{#vendorExtensions}}{{#required}}{{#consumesPlainText}} let body = param_{{paramName}};{{/consumesPlainText}}{{#consumesXml}}
305269
{{^has_namespace}} let body = serde_xml_rs::to_string(&param_{{paramName}}).expect("impossible to fail to serialize");{{/has_namespace}}{{#has_namespace}}
@@ -340,11 +304,6 @@ impl<F, C> Api<C> for Client<F> where
340304
{{/required}}{{/isMapContainer}}{{#isMapContainer}} let param_{{paramName}}: Option<{{{dataType}}}> = None;
341305
{{/isMapContainer}}{{/headerParams}}
342306

343-
{{#vendorExtensions}}{{#hasFile}}
344-
request.headers_mut().set(ContentType(multipart_header));
345-
request.set_body(body_string.into_bytes());
346-
{{/hasFile}}{{/vendorExtensions}}
347-
348307
Box::new(self.client_service.call(request)
349308
.map_err(|e| ApiError(format!("No response received: {}", e)))
350309
.and_then(|mut response| {
@@ -357,13 +316,9 @@ impl<F, C> Api<C> for Client<F> where
357316
None => return Box::new(future::err(ApiError(String::from("Required response header {{baseName}} for response {{code}} was not found.")))) as Box<Future<Item=_, Error=_>>,
358317
};
359318
{{/headers}}
360-
{{^isFile}}let body = response.body();{{/isFile}}{{#isFile}}let body = Box::new(response.body()
361-
.map(|chunk| chunk.to_vec())
362-
.map_err(|_|
363-
Error::new(ErrorKind::Other, "Received error reading response.")
364-
));{{/isFile}}
319+
let body = response.body();
365320
Box::new(
366-
{{#dataType}}{{^isFile}}
321+
{{#dataType}}
367322
body
368323
.concat2()
369324
.map_err(|e| ApiError(format!("Failed to read response: {}", e)))
@@ -383,9 +338,6 @@ impl<F, C> Api<C> for Client<F> where
383338
{{/producesPlainText}}{{/vendorExtensions}}
384339
))
385340
.map(move |body|
386-
{{/isFile}}{{#isFile}}
387-
future::ok(
388-
{{/isFile}}
389341
{{operationId}}Response::{{#vendorExtensions}}{{x-responseId}}{{/vendorExtensions}}{{^headers}}(body){{/headers}}{{#headers}}{{#-first}}{ body: body, {{/-first}}{{name}}: response_{{name}}{{^-last}}, {{/-last}}{{#-last}} }{{/-last}}{{/headers}}
390342
)
391343
{{/dataType}}{{^dataType}}

modules/openapi-generator/src/main/resources/rust-server/example-server_server.mustache

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
use futures::{self, Future};
66
use chrono;
7-
{{#apiHasFile}}use futures::Stream;{{/apiHasFile}}
87
use std::collections::HashMap;
9-
{{#apiHasFile}}use std::io::Error;{{/apiHasFile}}
108
use std::marker::PhantomData;
119
{{#apiUsesUuid}}use uuid;{{/apiUsesUuid}}
1210
use swagger;
@@ -31,10 +29,9 @@ impl<C> Server<C> {
3129
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{
3230
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
3331
{{#summary}} /// {{{summary}}}{{/summary}}
34-
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
32+
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
3533
let context = context.clone();
36-
println!("{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^isFile}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/isFile}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}{{^isFile}}, {{paramName}}{{/isFile}}{{/allParams}}, context.get().0.clone());{{#allParams}}{{#isFile}}
37-
let _ = {{paramName}}; //Suppresses unused param warning{{/isFile}}{{/allParams}}
34+
println!("{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}, {{paramName}}{{/allParams}}, context.get().0.clone());{{#allParams}}{{/allParams}}
3835
Box::new(futures::failed("Generic failure".into()))
3936
}
4037
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}

modules/openapi-generator/src/main/resources/rust-server/lib.mustache

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ pub enum {{operationId}}Response {
5151
pub trait Api<C> {
5252
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
5353
{{#summary}} /// {{{summary}}}{{/summary}}
54-
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>>;
54+
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>>;
5555
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
5656
}
5757

5858
/// API without a `Context`
5959
pub trait ApiNoContext {
6060
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
6161
{{#summary}} /// {{{summary}}}{{/summary}}
62-
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}) -> Box<Future<Item={{operationId}}Response, Error=ApiError>>;
62+
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}) -> Box<Future<Item={{operationId}}Response, Error=ApiError>>;
6363
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
6464
}
6565

@@ -78,7 +78,7 @@ impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T {
7878
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> {
7979
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
8080
{{#summary}} /// {{{summary}}}{{/summary}}
81-
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
81+
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{/required}}{{/allParams}}) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
8282
self.api().{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{paramName}}, {{/allParams}}&self.context())
8383
}
8484
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}

modules/openapi-generator/src/main/resources/rust-server/mimetypes.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ pub mod requests {
1717
lazy_static! {
1818
pub static ref {{#vendorExtensions}}{{uppercase_operation_id}}{{/vendorExtensions}}: Mime = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/json{{/consumes}}".parse().unwrap();
1919
}
20-
{{/bodyParam}}{{^bodyParam}}{{#vendorExtensions}}{{#formParams}}{{#-first}}{{^hasFile}} /// Create Mime objects for the request content types for {{operationId}}
20+
{{/bodyParam}}{{^bodyParam}}{{#vendorExtensions}}{{#formParams}}{{#-first}} /// Create Mime objects for the request content types for {{operationId}}
2121
lazy_static! {
2222
pub static ref {{#vendorExtensions}}{{uppercase_operation_id}}{{/vendorExtensions}}: Mime = "{{#consumes}}{{#-first}}{{{mediaType}}}{{/-first}}{{/consumes}}{{^consumes}}application/x-www-form-urlencoded{{/consumes}}".parse().unwrap();
2323
}
24-
{{/hasFile}}{{/-first}}{{/formParams}}{{/vendorExtensions}}{{/bodyParam}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
24+
{{/-first}}{{/formParams}}{{/vendorExtensions}}{{/bodyParam}}{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
2525
}

0 commit comments

Comments
 (0)