Skip to content

Commit

Permalink
Rust server html (#1329)
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
bjgill authored Nov 5, 2018
1 parent 303b469 commit 30bfebf
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,10 @@ boolean isMimetypePlainText(String mimetype) {
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");
}
Expand Down Expand Up @@ -544,6 +548,8 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
consumesXml = true;
} else if (isMimetypePlainText(mimeType)) {
consumesPlainText = true;
} else if (isMimetypeHtmlText(mimeType)) {
consumesPlainText = true;
} else if (isMimetypeWwwFormUrlEncoded(mimeType)) {
additionalProperties.put("usesUrlEncodedForm", true);
}
Expand All @@ -570,6 +576,8 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
producesXml = true;
} else if (isMimetypePlainText(mimeType)) {
producesPlainText = true;
} else if (isMimetypeHtmlText(mimeType)) {
producesPlainText = true;
}

mediaType.put("mediaType", mimeType);
Expand Down Expand Up @@ -662,7 +670,7 @@ public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> o
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -29,6 +30,7 @@ fn main() {
.help("Sets the operation to run")
.possible_values(&[
"DummyGet",
"HtmlPost",
])
.required(true)
.index(1))
Expand Down Expand Up @@ -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")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use swagger;
use swagger::{Has, XSpanIdString};

use rust_server_test::{Api, ApiError,
DummyGetResponse
DummyGetResponse,
HtmlPostResponse
};
use rust_server_test::models;

Expand All @@ -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()))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ use swagger;
use swagger::{ApiError, XSpanId, XSpanIdString, Has, AuthData};

use {Api,
DummyGetResponse
DummyGetResponse,
HtmlPostResponse
};
use models;

Expand Down Expand Up @@ -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)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,22 @@ pub enum DummyGetResponse {
Success ,
}

#[derive(Debug, PartialEq)]
pub enum HtmlPostResponse {
/// Success
Success ( String ) ,
}


/// API
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`
Expand All @@ -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.
Expand All @@ -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")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

}
Loading

0 comments on commit 30bfebf

Please sign in to comment.