From a196296e57daa681ce8c07f7807c34c7edac09cb Mon Sep 17 00:00:00 2001 From: Richard Whitehouse Date: Sat, 29 Feb 2020 21:54:11 +0000 Subject: [PATCH] [Rust Server] Support RFC 7386 (#5454) * [Rust Server] Support RFC 7386 Support application/merge-patch+json as defined by RFC 7386 - https://tools.ietf.org/html/rfc7386 Handle exactly the same as application/json. * [Rust Server] Add test for RFC 7386 * Update samples --- .../codegen/languages/RustServerCodegen.java | 4 + .../resources/3_0/rust-server/openapi-v3.yaml | 10 ++- .../rust-server/output/openapi-v3/README.md | 2 + .../output/openapi-v3/api/openapi.yaml | 9 ++ .../output/openapi-v3/docs/default_api.md | 23 +++++ .../output/openapi-v3/examples/client.rs | 10 +++ .../openapi-v3/examples/server_lib/mod.rs | 8 ++ .../output/openapi-v3/src/client/mod.rs | 84 +++++++++++++++++++ .../rust-server/output/openapi-v3/src/lib.rs | 18 ++++ .../output/openapi-v3/src/mimetypes.rs | 3 + .../output/openapi-v3/src/server/mod.rs | 79 ++++++++++++++--- 11 files changed, 237 insertions(+), 13 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java index fcfd8f496e7..eb6fb74231e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java @@ -72,6 +72,9 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { private static final String plainTextMimeType = "text/plain"; private static final String jsonMimeType = "application/json"; + // RFC 7386 support + private static final String mergePatchJsonMimeType = "application/merge-patch+json"; + // RFC 7807 Support private static final String problemJsonMimeType = "application/problem+json"; private static final String problemXmlMimeType = "application/problem+xml"; @@ -537,6 +540,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { private boolean isMimetypeJson(String mimetype) { return mimetype.toLowerCase(Locale.ROOT).startsWith(jsonMimeType) || + mimetype.toLowerCase(Locale.ROOT).startsWith(mergePatchJsonMimeType) || mimetype.toLowerCase(Locale.ROOT).startsWith(problemJsonMimeType); } diff --git a/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml b/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml index 5e5d9fa093b..388cef51e5e 100644 --- a/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml @@ -267,7 +267,15 @@ paths: application/problem+xml: schema: $ref: '#/components/schemas/ObjectWithArrayOfObjects' - + /merge-patch-json: + get: + responses: + 200: + description: merge-patch+json-encoded response + content: + application/merge-patch+json: + schema: + $ref: "#/components/schemas/anotherXmlObject" components: securitySchemes: authScheme: diff --git a/samples/server/petstore/rust-server/output/openapi-v3/README.md b/samples/server/petstore/rust-server/output/openapi-v3/README.md index 24d3cb6dd45..043498fe88e 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/README.md +++ b/samples/server/petstore/rust-server/output/openapi-v3/README.md @@ -62,6 +62,7 @@ To run a client, follow one of the following simple steps: ``` cargo run --example client MandatoryRequestHeaderGet +cargo run --example client MergePatchJsonGet cargo run --example client MultigetGet cargo run --example client MultipleAuthSchemeGet cargo run --example client ParamgetGet @@ -110,6 +111,7 @@ All URIs are relative to *http://localhost* Method | HTTP request | Description ------------- | ------------- | ------------- [****](docs/default_api.md#) | **GET** /mandatory-request-header | +[****](docs/default_api.md#) | **GET** /merge-patch-json | [****](docs/default_api.md#) | **GET** /multiget | Get some stuff. [****](docs/default_api.md#) | **GET** /multiple_auth_scheme | [****](docs/default_api.md#) | **GET** /paramget | Get some stuff with parameters. diff --git a/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml b/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml index a8cdc74cb38..e34d003fdd8 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml +++ b/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml @@ -262,6 +262,15 @@ paths: schema: $ref: '#/components/schemas/ObjectWithArrayOfObjects' description: NotAcceptable + /merge-patch-json: + get: + responses: + "200": + content: + application/merge-patch+json: + schema: + $ref: '#/components/schemas/anotherXmlObject' + description: merge-patch+json-encoded response components: schemas: EnumWithStarObject: diff --git a/samples/server/petstore/rust-server/output/openapi-v3/docs/default_api.md b/samples/server/petstore/rust-server/output/openapi-v3/docs/default_api.md index 0b12e71671c..5be97b22d5f 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/docs/default_api.md +++ b/samples/server/petstore/rust-server/output/openapi-v3/docs/default_api.md @@ -5,6 +5,7 @@ All URIs are relative to *http://localhost* Method | HTTP request | Description ------------- | ------------- | ------------- ****](default_api.md#) | **GET** /mandatory-request-header | +****](default_api.md#) | **GET** /merge-patch-json | ****](default_api.md#) | **GET** /multiget | Get some stuff. ****](default_api.md#) | **GET** /multiple_auth_scheme | ****](default_api.md#) | **GET** /paramget | Get some stuff with parameters. @@ -46,6 +47,28 @@ No authorization required [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) +# **** +> models::AnotherXmlObject () + + +### Required Parameters +This endpoint does not need any parameter. + +### Return type + +[**models::AnotherXmlObject**](anotherXmlObject.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/merge-patch+json, + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + # **** > models::AnotherXmlObject () Get some stuff. diff --git a/samples/server/petstore/rust-server/output/openapi-v3/examples/client.rs b/samples/server/petstore/rust-server/output/openapi-v3/examples/client.rs index 8c7f98fe71a..1476397cbb0 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/examples/client.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/examples/client.rs @@ -15,6 +15,7 @@ use futures::{Future, future, Stream, stream}; use openapi_v3::{Api, ApiNoContext, Client, ContextWrapperExt, ApiError, MandatoryRequestHeaderGetResponse, + MergePatchJsonGetResponse, MultigetGetResponse, MultipleAuthSchemeGetResponse, ParamgetGetResponse, @@ -40,6 +41,8 @@ fn main() { "MandatoryRequestHeaderGet", + "MergePatchJsonGet", + "MultigetGet", "MultipleAuthSchemeGet", @@ -120,6 +123,13 @@ fn main() { println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has).get().clone()); }, + Some("MergePatchJsonGet") => { + let mut rt = tokio::runtime::Runtime::new().unwrap(); + let result = rt.block_on(client.merge_patch_json_get( + )); + println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &dyn Has).get().clone()); + }, + Some("MultigetGet") => { let mut rt = tokio::runtime::Runtime::new().unwrap(); let result = rt.block_on(client.multiget_get( diff --git a/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/mod.rs b/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/mod.rs index a3e7550cd73..e58ca3581eb 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/mod.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/examples/server_lib/mod.rs @@ -18,6 +18,7 @@ use swagger::{Has, XSpanIdString}; use openapi_v3::{Api, ApiError, MandatoryRequestHeaderGetResponse, + MergePatchJsonGetResponse, MultigetGetResponse, MultipleAuthSchemeGetResponse, ParamgetGetResponse, @@ -55,6 +56,13 @@ impl Api for Server where C: Has{ Box::new(futures::failed("Generic failure".into())) } + + fn merge_patch_json_get(&self, context: &C) -> Box + Send> { + let context = context.clone(); + println!("merge_patch_json_get() - X-Span-ID: {:?}", context.get().0.clone()); + Box::new(futures::failed("Generic failure".into())) + } + /// Get some stuff. fn multiget_get(&self, context: &C) -> Box + Send> { let context = context.clone(); diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs index e8600bb0a80..5337f48ee9d 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs @@ -30,6 +30,7 @@ use serde_xml_rs; use {Api, MandatoryRequestHeaderGetResponse, + MergePatchJsonGetResponse, MultigetGetResponse, MultipleAuthSchemeGetResponse, ParamgetGetResponse, @@ -286,6 +287,89 @@ impl Api for Client where } + fn merge_patch_json_get(&self, context: &C) -> Box + Send> { + let mut uri = format!( + "{}/merge-patch-json", + self.base_path + ); + + // Query parameters + let mut query_string = url::form_urlencoded::Serializer::new("".to_owned()); + let query_string_str = query_string.finish(); + if !query_string_str.is_empty() { + uri += "?"; + uri += &query_string_str; + } + + let uri = match Uri::from_str(&uri) { + Ok(uri) => uri, + Err(err) => return Box::new(future::err(ApiError(format!("Unable to build URI: {}", err)))), + }; + + let mut request = match hyper::Request::builder() + .method("GET") + .uri(uri) + .body(Body::empty()) { + Ok(req) => req, + Err(e) => return Box::new(future::err(ApiError(format!("Unable to create request: {}", e)))) + }; + + + let header = HeaderValue::from_str((context as &dyn Has).get().0.clone().to_string().as_str()); + request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header { + Ok(h) => h, + Err(e) => return Box::new(future::err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))) + }); + + + Box::new(self.client_service.request(request) + .map_err(|e| ApiError(format!("No response received: {}", e))) + .and_then(|mut response| { + match response.status().as_u16() { + 200 => { + let body = response.into_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| + serde_json::from_str::(body) + .map_err(|e| e.into()) + ) + ) + .map(move |body| { + MergePatchJsonGetResponse::Merge + (body) + }) + ) as Box + Send> + }, + code => { + let headers = response.headers().clone(); + Box::new(response.into_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!("", e)), + }, + Err(e) => Cow::from(format!("", e)), + }))) + ) + ) as Box + Send> + } + } + })) + + } + fn multiget_get(&self, context: &C) -> Box + Send> { let mut uri = format!( "{}/multiget", diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/lib.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/lib.rs index 8e5da4aafc2..c4dc13af19b 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/lib.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/lib.rs @@ -80,6 +80,13 @@ pub enum MandatoryRequestHeaderGetResponse { Success } +#[derive(Debug, PartialEq)] +pub enum MergePatchJsonGetResponse { + /// merge-patch+json-encoded response + Merge + ( models::AnotherXmlObject ) +} + #[derive(Debug, PartialEq)] pub enum MultigetGetResponse { /// JSON rsp @@ -234,6 +241,9 @@ pub trait Api { fn mandatory_request_header_get(&self, x_header: String, context: &C) -> Box + Send>; + + fn merge_patch_json_get(&self, context: &C) -> Box + Send>; + /// Get some stuff. fn multiget_get(&self, context: &C) -> Box + Send>; @@ -284,6 +294,9 @@ pub trait ApiNoContext { fn mandatory_request_header_get(&self, x_header: String) -> Box + Send>; + + fn merge_patch_json_get(&self) -> Box + Send>; + /// Get some stuff. fn multiget_get(&self) -> Box + Send>; @@ -347,6 +360,11 @@ impl<'a, T: Api, C> ApiNoContext for ContextWrapper<'a, T, C> { self.api().mandatory_request_header_get(x_header, &self.context()) } + + fn merge_patch_json_get(&self) -> Box + Send> { + self.api().merge_patch_json_get(&self.context()) + } + /// Get some stuff. fn multiget_get(&self) -> Box + Send> { self.api().multiget_get(&self.context()) diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/mimetypes.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/mimetypes.rs index f2d0008dd41..3d95a136cf2 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/mimetypes.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/mimetypes.rs @@ -3,6 +3,9 @@ pub mod responses { + /// Create &str objects for the response content types for MergePatchJsonGet + pub static MERGE_PATCH_JSON_GET_MERGE: &str = "application/merge-patch+json"; + /// Create &str objects for the response content types for MultigetGet pub static MULTIGET_GET_JSON_RSP: &str = "application/json"; diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs index 406abcd1126..f5b6e9ea490 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs @@ -22,6 +22,7 @@ pub use swagger::auth::Authorization; use {Api, MandatoryRequestHeaderGetResponse, + MergePatchJsonGetResponse, MultigetGetResponse, MultipleAuthSchemeGetResponse, ParamgetGetResponse, @@ -50,6 +51,7 @@ mod paths { lazy_static! { pub static ref GLOBAL_REGEX_SET: regex::RegexSet = regex::RegexSet::new(vec![ r"^/mandatory-request-header$", + r"^/merge-patch-json$", r"^/multiget$", r"^/multiple_auth_scheme$", r"^/paramget$", @@ -66,18 +68,19 @@ mod paths { .expect("Unable to create global regex set"); } pub static ID_MANDATORY_REQUEST_HEADER: usize = 0; - pub static ID_MULTIGET: usize = 1; - pub static ID_MULTIPLE_AUTH_SCHEME: usize = 2; - pub static ID_PARAMGET: usize = 3; - pub static ID_READONLY_AUTH_SCHEME: usize = 4; - pub static ID_REQUIRED_OCTET_STREAM: usize = 5; - pub static ID_RESPONSES_WITH_HEADERS: usize = 6; - pub static ID_RFC7807: usize = 7; - pub static ID_UNTYPED_PROPERTY: usize = 8; - pub static ID_UUID: usize = 9; - pub static ID_XML: usize = 10; - pub static ID_XML_EXTRA: usize = 11; - pub static ID_XML_OTHER: usize = 12; + pub static ID_MERGE_PATCH_JSON: usize = 1; + pub static ID_MULTIGET: usize = 2; + pub static ID_MULTIPLE_AUTH_SCHEME: usize = 3; + pub static ID_PARAMGET: usize = 4; + pub static ID_READONLY_AUTH_SCHEME: usize = 5; + pub static ID_REQUIRED_OCTET_STREAM: usize = 6; + pub static ID_RESPONSES_WITH_HEADERS: usize = 7; + pub static ID_RFC7807: usize = 8; + pub static ID_UNTYPED_PROPERTY: usize = 9; + pub static ID_UUID: usize = 10; + pub static ID_XML: usize = 11; + pub static ID_XML_EXTRA: usize = 12; + pub static ID_XML_OTHER: usize = 13; } pub struct MakeService { @@ -208,6 +211,55 @@ where }) as Self::Future }, + // MergePatchJsonGet - GET /merge-patch-json + &hyper::Method::GET if path.matched(paths::ID_MERGE_PATCH_JSON) => { + Box::new({ + {{ + Box::new( + api_impl.merge_patch_json_get( + &context + ).then(move |result| { + let mut response = Response::new(Body::empty()); + response.headers_mut().insert( + HeaderName::from_static("x-span-id"), + HeaderValue::from_str((&context as &dyn Has).get().0.clone().to_string().as_str()) + .expect("Unable to create X-Span-ID header value")); + + + match result { + Ok(rsp) => match rsp { + MergePatchJsonGetResponse::Merge + + (body) + + + => { + *response.status_mut() = StatusCode::from_u16(200).expect("Unable to turn 200 into a StatusCode"); + + response.headers_mut().insert( + CONTENT_TYPE, + HeaderValue::from_str(mimetypes::responses::MERGE_PATCH_JSON_GET_MERGE) + .expect("Unable to create Content-Type header for MERGE_PATCH_JSON_GET_MERGE")); + + let body = serde_json::to_string(&body).expect("impossible to fail to serialize"); + *response.body_mut() = Body::from(body); + }, + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; + *response.body_mut() = Body::from("An internal error occurred"); + }, + } + + future::ok(response) + } + )) + }} + }) as Self::Future + }, + // MultigetGet - GET /multiget &hyper::Method::GET if path.matched(paths::ID_MULTIGET) => { Box::new({ @@ -1329,6 +1381,9 @@ impl RequestParser for ApiRequestParser { // MandatoryRequestHeaderGet - GET /mandatory-request-header &hyper::Method::GET if path.matched(paths::ID_MANDATORY_REQUEST_HEADER) => Ok("MandatoryRequestHeaderGet"), + // MergePatchJsonGet - GET /merge-patch-json + &hyper::Method::GET if path.matched(paths::ID_MERGE_PATCH_JSON) => Ok("MergePatchJsonGet"), + // MultigetGet - GET /multiget &hyper::Method::GET if path.matched(paths::ID_MULTIGET) => Ok("MultigetGet"),