forked from loafle/openapi-generator-original
Rust server html (#1329)
Builds on #1180 by @colelawrence. This addition to the rust-server generator enables the use of text/html responses as plaintext. I've added an html endpoint to the sample to demonstrate that this works (and fixed the problem that that uncovered).
This commit is contained in:
@@ -474,6 +474,10 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
return mimetype.toLowerCase(Locale.ROOT).startsWith("text/plain");
|
||||
}
|
||||
|
||||
boolean isMimetypeHtmlText(String mimetype) {
|
||||
return mimetype.toLowerCase(Locale.ROOT).startsWith("text/html");
|
||||
}
|
||||
|
||||
boolean isMimetypeWwwFormUrlEncoded(String mimetype) {
|
||||
return mimetype.toLowerCase(Locale.ROOT).startsWith("application/x-www-form-urlencoded");
|
||||
}
|
||||
@@ -544,6 +548,8 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
consumesXml = true;
|
||||
} else if (isMimetypePlainText(mimeType)) {
|
||||
consumesPlainText = true;
|
||||
} else if (isMimetypeHtmlText(mimeType)) {
|
||||
consumesPlainText = true;
|
||||
} else if (isMimetypeWwwFormUrlEncoded(mimeType)) {
|
||||
additionalProperties.put("usesUrlEncodedForm", true);
|
||||
}
|
||||
@@ -570,6 +576,8 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
producesXml = true;
|
||||
} else if (isMimetypePlainText(mimeType)) {
|
||||
producesPlainText = true;
|
||||
} else if (isMimetypeHtmlText(mimeType)) {
|
||||
producesPlainText = true;
|
||||
}
|
||||
|
||||
mediaType.put("mediaType", mimeType);
|
||||
@@ -662,7 +670,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig {
|
||||
if (isMimetypeXml(mediaType)) {
|
||||
additionalProperties.put("usesXml", true);
|
||||
consumesXml = true;
|
||||
} else if (isMimetypePlainText(mediaType)) {
|
||||
} else if (isMimetypePlainText(mediaType) || isMimetypeHtmlText(mediaType)) {
|
||||
consumesPlainText = true;
|
||||
} else if (isMimetypeWwwFormUrlEncoded(mediaType)) {
|
||||
additionalProperties.put("usesUrlEncodedForm", true);
|
||||
|
||||
@@ -12,6 +12,22 @@ paths:
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
/html:
|
||||
post:
|
||||
summary: Test HTML handling
|
||||
consumes: [text/html]
|
||||
produces: [text/html]
|
||||
parameters:
|
||||
- in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: Success
|
||||
schema:
|
||||
type: string
|
||||
definitions:
|
||||
additionalPropertiesObject:
|
||||
description: An additionalPropertiesObject
|
||||
|
||||
@@ -56,6 +56,7 @@ To run a client, follow one of the following simple steps:
|
||||
|
||||
```
|
||||
cargo run --example client DummyGet
|
||||
cargo run --example client HtmlPost
|
||||
```
|
||||
|
||||
### HTTPS
|
||||
|
||||
@@ -13,6 +13,22 @@ paths:
|
||||
content: {}
|
||||
description: Success
|
||||
summary: A dummy endpoint to make the spec valid.
|
||||
/html:
|
||||
post:
|
||||
requestBody:
|
||||
content:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
content:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
description: Success
|
||||
summary: Test HTML handling
|
||||
components:
|
||||
schemas:
|
||||
additionalPropertiesObject:
|
||||
|
||||
@@ -19,7 +19,8 @@ use tokio_core::reactor;
|
||||
#[allow(unused_imports)]
|
||||
use rust_server_test::{ApiNoContext, ContextWrapperExt,
|
||||
ApiError,
|
||||
DummyGetResponse
|
||||
DummyGetResponse,
|
||||
HtmlPostResponse
|
||||
};
|
||||
use clap::{App, Arg};
|
||||
|
||||
@@ -29,6 +30,7 @@ fn main() {
|
||||
.help("Sets the operation to run")
|
||||
.possible_values(&[
|
||||
"DummyGet",
|
||||
"HtmlPost",
|
||||
])
|
||||
.required(true)
|
||||
.index(1))
|
||||
@@ -74,6 +76,11 @@ fn main() {
|
||||
println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &Has<XSpanIdString>).get().clone());
|
||||
},
|
||||
|
||||
Some("HtmlPost") => {
|
||||
let result = core.run(client.html_post("body_example".to_string()));
|
||||
println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &Has<XSpanIdString>).get().clone());
|
||||
},
|
||||
|
||||
_ => {
|
||||
panic!("Invalid operation provided")
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ use swagger;
|
||||
use swagger::{Has, XSpanIdString};
|
||||
|
||||
use rust_server_test::{Api, ApiError,
|
||||
DummyGetResponse
|
||||
DummyGetResponse,
|
||||
HtmlPostResponse
|
||||
};
|
||||
use rust_server_test::models;
|
||||
|
||||
@@ -35,4 +36,11 @@ impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{
|
||||
Box::new(futures::failed("Generic failure".into()))
|
||||
}
|
||||
|
||||
/// Test HTML handling
|
||||
fn html_post(&self, body: String, context: &C) -> Box<Future<Item=HtmlPostResponse, Error=ApiError>> {
|
||||
let context = context.clone();
|
||||
println!("html_post(\"{}\") - X-Span-ID: {:?}", body, context.get().0.clone());
|
||||
Box::new(futures::failed("Generic failure".into()))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ use swagger;
|
||||
use swagger::{ApiError, XSpanId, XSpanIdString, Has, AuthData};
|
||||
|
||||
use {Api,
|
||||
DummyGetResponse
|
||||
DummyGetResponse,
|
||||
HtmlPostResponse
|
||||
};
|
||||
use models;
|
||||
|
||||
@@ -302,6 +303,77 @@ impl<F, C> Api<C> for Client<F> where
|
||||
|
||||
}
|
||||
|
||||
fn html_post(&self, param_body: String, context: &C) -> Box<Future<Item=HtmlPostResponse, Error=ApiError>> {
|
||||
|
||||
|
||||
let uri = format!(
|
||||
"{}/html",
|
||||
self.base_path
|
||||
);
|
||||
|
||||
let uri = match Uri::from_str(&uri) {
|
||||
Ok(uri) => uri,
|
||||
Err(err) => return Box::new(futures::done(Err(ApiError(format!("Unable to build URI: {}", err))))),
|
||||
};
|
||||
|
||||
let mut request = hyper::Request::new(hyper::Method::Post, uri);
|
||||
|
||||
let body = param_body;
|
||||
|
||||
|
||||
request.set_body(body.into_bytes());
|
||||
|
||||
|
||||
request.headers_mut().set(ContentType(mimetypes::requests::HTML_POST.clone()));
|
||||
request.headers_mut().set(XSpanId((context as &Has<XSpanIdString>).get().0.clone()));
|
||||
|
||||
|
||||
Box::new(self.client_service.call(request)
|
||||
.map_err(|e| ApiError(format!("No response received: {}", e)))
|
||||
.and_then(|mut response| {
|
||||
match response.status().as_u16() {
|
||||
200 => {
|
||||
let body = response.body();
|
||||
Box::new(
|
||||
body
|
||||
.concat2()
|
||||
.map_err(|e| ApiError(format!("Failed to read response: {}", e)))
|
||||
.and_then(|body| str::from_utf8(&body)
|
||||
.map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))
|
||||
.and_then(|body|
|
||||
|
||||
Ok(body.to_string())
|
||||
|
||||
))
|
||||
.map(move |body|
|
||||
HtmlPostResponse::Success(body)
|
||||
)
|
||||
) as Box<Future<Item=_, Error=_>>
|
||||
},
|
||||
code => {
|
||||
let headers = response.headers().clone();
|
||||
Box::new(response.body()
|
||||
.take(100)
|
||||
.concat2()
|
||||
.then(move |body|
|
||||
future::err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
|
||||
code,
|
||||
headers,
|
||||
match body {
|
||||
Ok(ref body) => match str::from_utf8(body) {
|
||||
Ok(body) => Cow::from(body),
|
||||
Err(e) => Cow::from(format!("<Body was not UTF8: {:?}>", e)),
|
||||
},
|
||||
Err(e) => Cow::from(format!("<Failed to read body: {}>", e)),
|
||||
})))
|
||||
)
|
||||
) as Box<Future<Item=_, Error=_>>
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -45,6 +45,12 @@ pub enum DummyGetResponse {
|
||||
Success ,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum HtmlPostResponse {
|
||||
/// Success
|
||||
Success ( String ) ,
|
||||
}
|
||||
|
||||
|
||||
/// API
|
||||
pub trait Api<C> {
|
||||
@@ -52,6 +58,9 @@ pub trait Api<C> {
|
||||
/// A dummy endpoint to make the spec valid.
|
||||
fn dummy_get(&self, context: &C) -> Box<Future<Item=DummyGetResponse, Error=ApiError>>;
|
||||
|
||||
/// Test HTML handling
|
||||
fn html_post(&self, body: String, context: &C) -> Box<Future<Item=HtmlPostResponse, Error=ApiError>>;
|
||||
|
||||
}
|
||||
|
||||
/// API without a `Context`
|
||||
@@ -60,6 +69,9 @@ pub trait ApiNoContext {
|
||||
/// A dummy endpoint to make the spec valid.
|
||||
fn dummy_get(&self) -> Box<Future<Item=DummyGetResponse, Error=ApiError>>;
|
||||
|
||||
/// Test HTML handling
|
||||
fn html_post(&self, body: String) -> Box<Future<Item=HtmlPostResponse, Error=ApiError>>;
|
||||
|
||||
}
|
||||
|
||||
/// Trait to extend an API to make it easy to bind it to a context.
|
||||
@@ -81,6 +93,11 @@ impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> {
|
||||
self.api().dummy_get(&self.context())
|
||||
}
|
||||
|
||||
/// Test HTML handling
|
||||
fn html_post(&self, body: String) -> Box<Future<Item=HtmlPostResponse, Error=ApiError>> {
|
||||
self.api().html_post(body, &self.context())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
|
||||
@@ -4,10 +4,18 @@ pub mod responses {
|
||||
use hyper::mime::*;
|
||||
|
||||
// The macro is called per-operation to beat the recursion limit
|
||||
/// Create Mime objects for the response content types for HtmlPost
|
||||
lazy_static! {
|
||||
pub static ref HTML_POST_SUCCESS: Mime = "text/html".parse().unwrap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub mod requests {
|
||||
use hyper::mime::*;
|
||||
/// Create Mime objects for the request content types for HtmlPost
|
||||
lazy_static! {
|
||||
pub static ref HTML_POST: Mime = "text/html".parse().unwrap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ use swagger::{ApiError, XSpanId, XSpanIdString, Has, RequestParser};
|
||||
use swagger::auth::Scopes;
|
||||
|
||||
use {Api,
|
||||
DummyGetResponse
|
||||
DummyGetResponse,
|
||||
HtmlPostResponse
|
||||
};
|
||||
#[allow(unused_imports)]
|
||||
use models;
|
||||
@@ -51,10 +52,12 @@ mod paths {
|
||||
|
||||
lazy_static! {
|
||||
pub static ref GLOBAL_REGEX_SET: regex::RegexSet = regex::RegexSet::new(&[
|
||||
r"^/dummy$"
|
||||
r"^/dummy$",
|
||||
r"^/html$"
|
||||
]).unwrap();
|
||||
}
|
||||
pub static ID_DUMMY: usize = 0;
|
||||
pub static ID_HTML: usize = 1;
|
||||
}
|
||||
|
||||
pub struct NewService<T, C> {
|
||||
@@ -166,6 +169,80 @@ where
|
||||
},
|
||||
|
||||
|
||||
// HtmlPost - POST /html
|
||||
&hyper::Method::Post if path.matched(paths::ID_HTML) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Body parameters (note that non-required body parameters will ignore garbage
|
||||
// values, rather than causing a 400 response). Produce warning header and logs for
|
||||
// any unused fields.
|
||||
Box::new(body.concat2()
|
||||
.then(move |result| -> Box<Future<Item=Response, Error=Error>> {
|
||||
match result {
|
||||
Ok(body) => {
|
||||
let param_body: Option<String> = if !body.is_empty() {
|
||||
|
||||
match String::from_utf8(body.to_vec()) {
|
||||
Ok(param_body) => Some(param_body),
|
||||
Err(e) => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't parse body parameter body - not valid UTF-8: {}", e)))),
|
||||
}
|
||||
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let param_body = match param_body {
|
||||
Some(param_body) => param_body,
|
||||
None => return Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body("Missing required body parameter body"))),
|
||||
};
|
||||
|
||||
|
||||
Box::new(api_impl.html_post(param_body, &context)
|
||||
.then(move |result| {
|
||||
let mut response = Response::new();
|
||||
response.headers_mut().set(XSpanId((&context as &Has<XSpanIdString>).get().0.to_string()));
|
||||
|
||||
match result {
|
||||
Ok(rsp) => match rsp {
|
||||
HtmlPostResponse::Success
|
||||
|
||||
(body)
|
||||
|
||||
|
||||
=> {
|
||||
response.set_status(StatusCode::try_from(200).unwrap());
|
||||
|
||||
response.headers_mut().set(ContentType(mimetypes::responses::HTML_POST_SUCCESS.clone()));
|
||||
|
||||
|
||||
response.set_body(body);
|
||||
},
|
||||
},
|
||||
Err(_) => {
|
||||
// Application code returned an error. This should not happen, as the implementation should
|
||||
// return a valid response.
|
||||
response.set_status(StatusCode::InternalServerError);
|
||||
response.set_body("An internal error occurred");
|
||||
},
|
||||
}
|
||||
|
||||
future::ok(response)
|
||||
}
|
||||
))
|
||||
|
||||
|
||||
},
|
||||
Err(e) => Box::new(future::ok(Response::new().with_status(StatusCode::BadRequest).with_body(format!("Couldn't read body parameter body: {}", e)))),
|
||||
}
|
||||
})
|
||||
) as Box<Future<Item=Response, Error=Error>>
|
||||
|
||||
},
|
||||
|
||||
|
||||
_ => Box::new(future::ok(Response::new().with_status(StatusCode::NotFound))) as Box<Future<Item=Response, Error=Error>>,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user