From f16189c240f32a4dc71190de53794dff29310964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= Date: Thu, 8 Feb 2024 13:13:15 +0100 Subject: [PATCH] Update swagger generated API crates (#590) ## Type of change ``` - [ ] Bug fix - [ ] New feature development - [x] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [ ] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective Update swagger generator to version 7.2.0, pull in the new templates and generate the new API crates. I've also updated the build instructions to use .NET 8, as is required in the latest server code. The version of the server code used to generate this is https://github.com/bitwarden/server/commit/fc1d7c70592cfbc65e61c047bcc138850d91a55b I've had to make some small modifications to the server code to get it working, ~~and I'll link the PR here shortly.~~ https://github.com/bitwarden/server/pull/3760 To make this easier to review, I've split this into three commits: - b42f189924a770aa127b88478876548e8beb3879: Update the swagger and dotnet versions, and pull in the new templates as-is. Note that this won't generate working code. - 9727d462924f9e533c0e3608ff0ba8aaeb0a2124: Patched the provided templates to make it work with our code. This is in a separate commit to make it easier to see what we've changed and hopefully make it easier in the future to update these templates. The changes are: - Ignore warnings by default - Use Uuids instead of strings - Use serde_repr to support int enums - Disabled default features for reqwest (to remove default tls) - Removing double optionals (This could be reverted once we're better at marking everything as required in the server) - ~~f9b63606477d0f0f987b8534ef6e6755c76003a2: New autogenerated swagger code, no manually edited files here.~~ Moved to a separate PR (#593) --- README.md | 4 +- openapitools.json | 2 +- support/openapi-template/Cargo.mustache | 55 ++++++++--- support/openapi-template/git_push.sh.mustache | 0 support/openapi-template/hyper/api.mustache | 16 ++-- support/openapi-template/model.mustache | 2 +- support/openapi-template/request.rs | 93 +++++++++---------- support/openapi-template/reqwest/api.mustache | 29 +++++- .../openapi-template/reqwest/api_mod.mustache | 56 ++++++++++- .../reqwest/configuration.mustache | 6 +- 10 files changed, 175 insertions(+), 88 deletions(-) mode change 100644 => 100755 support/openapi-template/git_push.sh.mustache diff --git a/README.md b/README.md index 2de94b09c..dbf4e4f2c 100644 --- a/README.md +++ b/README.md @@ -57,10 +57,10 @@ The first step is to generate the swagger documents from the server repository. ```bash # src/Api -dotnet swagger tofile --output ../../api.json ./bin/Debug/net6.0/Api.dll internal +dotnet swagger tofile --output ../../api.json ./bin/Debug/net8.0/Api.dll internal # src/Identity -ASPNETCORE_ENVIRONMENT=development dotnet swagger tofile --output ../../identity.json ./bin/Debug/net6.0/Identity.dll v1 +ASPNETCORE_ENVIRONMENT=development dotnet swagger tofile --output ../../identity.json ./bin/Debug/net8.0/Identity.dll v1 ``` ### OpenApi Generator diff --git a/openapitools.json b/openapitools.json index a3883a34f..e73b97583 100644 --- a/openapitools.json +++ b/openapitools.json @@ -2,6 +2,6 @@ "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", "spaces": 2, "generator-cli": { - "version": "6.5.0" + "version": "7.2.0" } } diff --git a/support/openapi-template/Cargo.mustache b/support/openapi-template/Cargo.mustache index 3b89aa079..6417d15cd 100644 --- a/support/openapi-template/Cargo.mustache +++ b/support/openapi-template/Cargo.mustache @@ -1,42 +1,71 @@ [package] name = "{{{packageName}}}" version = "{{#lambdaVersion}}{{{packageVersion}}}{{/lambdaVersion}}" +{{#infoEmail}} +authors = ["{{{.}}}"] +{{/infoEmail}} +{{^infoEmail}} authors = ["OpenAPI Generator team and contributors"] +{{/infoEmail}} +{{#appDescription}} +description = "{{{.}}}" +{{/appDescription}} +{{#licenseInfo}} +license = "{{.}}" +{{/licenseInfo}} +{{^licenseInfo}} +# Override this license by providing a License Object in the OpenAPI. +license = "Unlicense" +{{/licenseInfo}} edition = "2018" +{{#publishRustRegistry}} +publish = ["{{.}}"] +{{/publishRustRegistry}} +{{#repositoryUrl}} +repository = "{{.}}" +{{/repositoryUrl}} +{{#documentationUrl}} +documentation = "{{.}}" +{{/documentationUrl}} +{{#homePageUrl}} +homepage = "{{.}} +{{/homePageUrl}} [dependencies] serde = "^1.0" serde_derive = "^1.0" +{{#serdeWith}} +serde_with = "^2.0" +{{/serdeWith}} serde_json = "^1.0" serde_repr = "^0.1" url = "^2.2" -uuid = { version = "^1.0", features = ["serde"] } +uuid = { version = "^1.0", features = ["serde", "v4"] } {{#hyper}} hyper = { version = "~0.14", features = ["full"] } hyper-tls = "~0.5" http = "~0.2" -serde_yaml = "0.7" base64 = "~0.7.0" futures = "^0.3" {{/hyper}} +{{#withAWSV4Signature}} +aws-sigv4 = "0.3.0" +http = "0.2.5" +secrecy = "0.8.0" +{{/withAWSV4Signature}} {{#reqwest}} {{^supportAsync}} -reqwest = "~0.9" +[dependencies.reqwest] +version = "^0.11" +features = ["json", "blocking", "multipart"] {{/supportAsync}} {{#supportAsync}} +{{#supportMiddleware}} +reqwest-middleware = "0.2.0" +{{/supportMiddleware}} [dependencies.reqwest] version = "^0.11" features = ["json", "multipart"] default-features = false {{/supportAsync}} {{/reqwest}} -{{#withAWSV4Signature}} -aws-sigv4 = "0.3.0" -http = "0.2.5" -secrecy = "0.8.0" -{{/withAWSV4Signature}} - -[dev-dependencies] -{{#hyper}} -tokio-core = "*" -{{/hyper}} diff --git a/support/openapi-template/git_push.sh.mustache b/support/openapi-template/git_push.sh.mustache old mode 100644 new mode 100755 diff --git a/support/openapi-template/hyper/api.mustache b/support/openapi-template/hyper/api.mustache index 03ea90ad0..dffab3ce8 100644 --- a/support/openapi-template/hyper/api.mustache +++ b/support/openapi-template/hyper/api.mustache @@ -28,7 +28,7 @@ impl {{{classname}}}Client pub trait {{{classname}}} { {{#operations}} {{#operation}} - fn {{{operationId}}}(&self, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}&str{{/isString}}{{#isUuid}}uuid::Uuid{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}crate::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) -> Pin>>>; + fn {{{operationId}}}(&self, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}{{^isUuid}}&str{{/isUuid}}{{/isString}}{{#isUuid}}&str{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}crate::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) -> Pin>>>; {{/operation}} {{/operations}} } @@ -38,7 +38,7 @@ impl{{{classname}}} for {{{classname}}}Clien {{#operations}} {{#operation}} #[allow(unused_mut)] - fn {{{operationId}}}(&self, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}&str{{/isString}}{{#isUuid}}uuid::Uuid{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}crate::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) -> Pin>>> { + fn {{{operationId}}}(&self, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}{{^isUuid}}&str{{/isUuid}}{{/isString}}{{#isUuid}}&str{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}crate::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) -> Pin>>> { let mut req = __internal_request::Request::new(hyper::Method::{{{httpMethod.toUpperCase}}}, "{{{path}}}".to_string()) {{#hasAuthMethods}} {{#authMethods}} @@ -49,9 +49,9 @@ impl{{{classname}}} for {{{classname}}}Clien param_name: "{{{keyParamName}}}".to_owned(), })) {{/isApiKey}} - {{#isBasic}} + {{#isBasicBasic}} .with_auth(__internal_request::Auth::Basic) - {{/isBasic}} + {{/isBasicBasic}} {{#isOAuth}} .with_auth(__internal_request::Auth::Oauth) {{/isOAuth}} @@ -66,7 +66,7 @@ impl{{{classname}}} for {{{classname}}}Clien {{#isNullable}} match {{{paramName}}} { Some(param_value) => { req = req.with_query_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, - None => { req = req.with_query_param("{{{baseName}}}".to_string(), String::new()); }, + None => { req = req.with_query_param("{{{baseName}}}".to_string(), "".to_string()); }, } {{/isNullable}} {{/required}} @@ -85,7 +85,7 @@ impl{{{classname}}} for {{{classname}}}Clien {{#isNullable}} match {{{paramName}}} { Some(param_value) => { req = req.with_path_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, - None => { req = req.with_path_param("{{{baseName}}}".to_string(), String::new()); }, + None => { req = req.with_path_param("{{{baseName}}}".to_string(), "".to_string()); }, } {{/isNullable}} {{/required}} @@ -104,7 +104,7 @@ impl{{{classname}}} for {{{classname}}}Clien {{#isNullable}} match {{{paramName}}} { Some(param_value) => { req = req.with_header_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, - None => { req = req.with_header_param("{{{baseName}}}".to_string(), String::new()); }, + None => { req = req.with_header_param("{{{baseName}}}".to_string(), "".to_string()); }, } {{/isNullable}} {{/required}} @@ -143,7 +143,7 @@ impl{{{classname}}} for {{{classname}}}Clien {{#isNullable}} match {{{paramName}}} { Some(param_value) => { req = req.with_form_param("{{{baseName}}}".to_string(), param_value{{#isArray}}.join(","){{/isArray}}.to_string()); }, - None => { req = req.with_form_param("{{{baseName}}}".to_string(), String::new()); }, + None => { req = req.with_form_param("{{{baseName}}}".to_string(), "".to_string()); }, } {{/isNullable}} {{/required}} diff --git a/support/openapi-template/model.mustache b/support/openapi-template/model.mustache index 233c88010..4a22118a8 100644 --- a/support/openapi-template/model.mustache +++ b/support/openapi-template/model.mustache @@ -100,7 +100,7 @@ pub enum {{{classname}}} { {{!-- for non-enum schemas --}} {{^isEnum}} {{^discriminator}} -#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct {{{classname}}} { {{#vars}} {{#description}} diff --git a/support/openapi-template/request.rs b/support/openapi-template/request.rs index 6e534abaa..81497706a 100644 --- a/support/openapi-template/request.rs +++ b/support/openapi-template/request.rs @@ -2,10 +2,10 @@ use std::collections::HashMap; use std::pin::Pin; use futures; -use futures::future::*; use futures::Future; +use futures::future::*; use hyper; -use hyper::header::{HeaderValue, AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, USER_AGENT}; +use hyper::header::{AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, HeaderValue, USER_AGENT}; use serde; use serde_json; @@ -50,6 +50,7 @@ pub(crate) struct Request { serialized_body: Option, } +#[allow(dead_code)] impl Request { pub fn new(method: hyper::Method, path: String) -> Self { Request { @@ -75,16 +76,19 @@ impl Request { self } + #[allow(unused)] pub fn with_query_param(mut self, basename: String, param: String) -> Self { self.query_params.insert(basename, param); self } + #[allow(unused)] pub fn with_path_param(mut self, basename: String, param: String) -> Self { self.path_params.insert(basename, param); self } + #[allow(unused)] pub fn with_form_param(mut self, basename: String, param: String) -> Self { self.form_params.insert(basename, param); self @@ -103,13 +107,13 @@ impl Request { pub fn execute<'a, C, U>( self, conf: &configuration::Configuration, - ) -> Pin> + 'a>> - where - C: hyper::client::connect::Connect + Clone + std::marker::Send + Sync, - U: Sized + std::marker::Send + 'a, - for<'de> U: serde::Deserialize<'de>, + ) -> Pin> + 'a>> + where + C: hyper::client::connect::Connect + Clone + std::marker::Send + Sync, + U: Sized + std::marker::Send + 'a, + for<'de> U: serde::Deserialize<'de>, { - let mut query_string = ::url::form_urlencoded::Serializer::new(String::new()); + let mut query_string = ::url::form_urlencoded::Serializer::new("".to_owned()); let mut path = self.path; for (k, v) in self.path_params { @@ -133,7 +137,9 @@ impl Request { Ok(u) => u, }; - let mut req_builder = hyper::Request::builder().uri(uri).method(self.method); + let mut req_builder = hyper::Request::builder() + .uri(uri) + .method(self.method); // Detect the authorization type if it hasn't been set. let auth = self.auth.unwrap_or_else(|| @@ -180,13 +186,10 @@ impl Request { } if let Some(ref user_agent) = conf.user_agent { - req_builder = req_builder.header( - USER_AGENT, - match HeaderValue::from_str(user_agent) { - Ok(header_value) => header_value, - Err(e) => return Box::pin(futures::future::err(super::Error::Header(e))), - }, - ); + req_builder = req_builder.header(USER_AGENT, match HeaderValue::from_str(user_agent) { + Ok(header_value) => header_value, + Err(e) => return Box::pin(futures::future::err(super::Error::Header(e))) + }); } for (k, v) in self.header_params { @@ -195,11 +198,8 @@ impl Request { let req_headers = req_builder.headers_mut().unwrap(); let request_result = if self.form_params.len() > 0 { - req_headers.insert( - CONTENT_TYPE, - HeaderValue::from_static("application/ x-www-form-urlencoded"), - ); - let mut enc = ::url::form_urlencoded::Serializer::new(String::new()); + req_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/x-www-form-urlencoded")); + let mut enc = ::url::form_urlencoded::Serializer::new("".to_owned()); for (k, v) in self.form_params { enc.append_pair(&k, &v); } @@ -213,37 +213,30 @@ impl Request { }; let request = match request_result { Ok(request) => request, - Err(e) => return Box::pin(futures::future::err(Error::from(e))), + Err(e) => return Box::pin(futures::future::err(Error::from(e))) }; let no_return_type = self.no_return_type; - Box::pin( - conf.client - .request(request) - .map_err(Error::from) - .and_then(move |response| { - let status = response.status(); - if !status.is_success() { - futures::future::err::(Error::from(( - status, - response.into_body(), - ))) - .boxed() - } else if no_return_type { - // This is a hack; if there's no_ret_type, U is (), but serde_json gives an - // error when deserializing "" into (), so deserialize 'null' into it - // instead. - // An alternate option would be to require U: Default, and then return - // U::default() here instead since () implements that, but then we'd - // need to impl default for all models. - futures::future::ok::(serde_json::Value::Null).boxed() - } else { - hyper::body::to_bytes(response.into_body()) - .map(|bytes| serde_json::from_slice(&bytes.unwrap())) - .map_err(Error::from) - .boxed() - } - }), - ) + Box::pin(conf.client + .request(request) + .map_err(|e| Error::from(e)) + .and_then(move |response| { + let status = response.status(); + if !status.is_success() { + futures::future::err::(Error::from((status, response.into_body()))).boxed() + } else if no_return_type { + // This is a hack; if there's no_ret_type, U is (), but serde_json gives an + // error when deserializing "" into (), so deserialize 'null' into it + // instead. + // An alternate option would be to require U: Default, and then return + // U::default() here instead since () implements that, but then we'd + // need to impl default for all models. + futures::future::ok::(serde_json::from_str("null").expect("serde null value")).boxed() + } else { + hyper::body::to_bytes(response.into_body()) + .map(|bytes| serde_json::from_slice(&bytes.unwrap())) + .map_err(|e| Error::from(e)).boxed() + } + })) } } diff --git a/support/openapi-template/reqwest/api.mustache b/support/openapi-template/reqwest/api.mustache index b1a2ec245..8128244a2 100644 --- a/support/openapi-template/reqwest/api.mustache +++ b/support/openapi-template/reqwest/api.mustache @@ -11,13 +11,13 @@ use super::{Error, configuration}; {{#allParams}} {{#-first}} /// struct for passing parameters to the method [`{{operationId}}`] -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct {{{operationIdCamelCase}}}Params { {{/-first}} {{#description}} /// {{{.}}} {{/description}} - pub {{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}{{#isArray}}Vec<{{/isArray}}String{{#isArray}}>{{/isArray}}{{/isString}}{{#isUuid}}{{#isArray}}Vec<{{/isArray}}uuid::Uuid{{#isArray}}>{{/isArray}}{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}crate::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}},{{/-last}} + pub {{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{^isUuid}}{{#isString}}{{#isArray}}Vec<{{/isArray}}String{{#isArray}}>{{/isArray}}{{/isString}}{{/isUuid}}{{#isUuid}}{{#isArray}}Vec<{{/isArray}}uuid::Uuid{{#isArray}}>{{/isArray}}{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}{{#isBodyParam}}crate::models::{{/isBodyParam}}{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{^-last}},{{/-last}} {{#-last}} } @@ -103,12 +103,27 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: {{#required}} {{#isArray}} local_var_req_builder = match "{{collectionFormat}}" { - "multi" => local_var_req_builder.query(&{{{paramName}}}.into_iter().map(|p| ("{{{baseName}}}".to_owned(), p)).collect::>()), + "multi" => local_var_req_builder.query(&{{{paramName}}}.into_iter().map(|p| ("{{{baseName}}}".to_owned(), p.to_string())).collect::>()), _ => local_var_req_builder.query(&[("{{{baseName}}}", &{{{paramName}}}.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), }; {{/isArray}} {{^isArray}} + {{^isNullable}} local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &{{{paramName}}}.to_string())]); + {{/isNullable}} + {{#isNullable}} + {{#isDeepObject}} + if let Some(ref local_var_str) = {{{paramName}}} { + let params = crate::apis::parse_deep_object("{{{baseName}}}", local_var_str); + local_var_req_builder = local_var_req_builder.query(¶ms); + }; + {{/isDeepObject}} + {{^isDeepObject}} + if let Some(ref local_var_str) = {{{paramName}}} { + local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &local_var_str.to_string())]); + }; + {{/isDeepObject}} + {{/isNullable}} {{/isArray}} {{/required}} {{^required}} @@ -120,7 +135,13 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: }; {{/isArray}} {{^isArray}} + {{#isDeepObject}} + let params = crate::apis::parse_deep_object("{{{baseName}}}", local_var_str); + local_var_req_builder = local_var_req_builder.query(¶ms); + {{/isDeepObject}} + {{^isDeepObject}} local_var_req_builder = local_var_req_builder.query(&[("{{{baseName}}}", &local_var_str.to_string())]); + {{/isDeepObject}} {{/isArray}} } {{/required}} @@ -223,7 +244,7 @@ pub {{#supportAsync}}async {{/supportAsync}}fn {{{operationId}}}(configuration: {{/hasAuthMethods}} {{#isMultipart}} {{#hasFormParams}} - let mut local_var_form = reqwest::multipart::Form::new(); + let mut local_var_form = reqwest{{^supportAsync}}::blocking{{/supportAsync}}::multipart::Form::new(); {{#formParams}} {{#isFile}} {{^supportAsync}} diff --git a/support/openapi-template/reqwest/api_mod.mustache b/support/openapi-template/reqwest/api_mod.mustache index 628ec898a..347f33379 100644 --- a/support/openapi-template/reqwest/api_mod.mustache +++ b/support/openapi-template/reqwest/api_mod.mustache @@ -14,6 +14,9 @@ pub struct ResponseContent { #[derive(Debug)] pub enum Error { Reqwest(reqwest::Error), + {{#supportMiddleware}} + ReqwestMiddleware(reqwest_middleware::Error), + {{/supportMiddleware}} Serde(serde_json::Error), Io(std::io::Error), ResponseError(ResponseContent), @@ -26,12 +29,15 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let (module, e) = match self { Error::Reqwest(e) => ("reqwest", e.to_string()), + {{#supportMiddleware}} + Error::ReqwestMiddleware(e) => ("reqwest-middleware", e.to_string()), + {{/supportMiddleware}} Error::Serde(e) => ("serde", e.to_string()), Error::Io(e) => ("IO", e.to_string()), Error::ResponseError(e) => ("response", format!("status code {}", e.status)), - {{#withAWSV4Signature}} + {{#withAWSV4Signature}} Error::AWSV4SignatureError(e) => ("aws v4 signature", e.to_string()), - {{/withAWSV4Signature}} + {{/withAWSV4Signature}} }; write!(f, "error in {}: {}", module, e) } @@ -41,12 +47,15 @@ impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(match self { Error::Reqwest(e) => e, + {{#supportMiddleware}} + Error::ReqwestMiddleware(e) => e, + {{/supportMiddleware}} Error::Serde(e) => e, Error::Io(e) => e, Error::ResponseError(_) => return None, - {{#withAWSV4Signature}} - Error::AWSV4SignatureError(_) => return None, - {{/withAWSV4Signature}} + {{#withAWSV4Signature}} + Error::AWSV4SignatureError(_) => return None, + {{/withAWSV4Signature}} }) } } @@ -57,6 +66,14 @@ impl From for Error { } } +{{#supportMiddleware}} +impl From for Error { + fn from(e: reqwest_middleware::Error) -> Self { + Error::ReqwestMiddleware(e) + } +} + +{{/supportMiddleware}} impl From for Error { fn from(e: serde_json::Error) -> Self { Error::Serde(e) @@ -73,6 +90,35 @@ pub fn urlencode>(s: T) -> String { ::url::form_urlencoded::byte_serialize(s.as_ref().as_bytes()).collect() } +pub fn parse_deep_object(prefix: &str, value: &serde_json::Value) -> Vec<(String, String)> { + if let serde_json::Value::Object(object) = value { + let mut params = vec![]; + + for (key, value) in object { + match value { + serde_json::Value::Object(_) => params.append(&mut parse_deep_object( + &format!("{}[{}]", prefix, key), + value, + )), + serde_json::Value::Array(array) => { + for (i, value) in array.iter().enumerate() { + params.append(&mut parse_deep_object( + &format!("{}[{}][{}]", prefix, key, i), + value, + )); + } + }, + serde_json::Value::String(s) => params.push((format!("{}[{}]", prefix, key), s.clone())), + _ => params.push((format!("{}[{}]", prefix, key), value.to_string())), + } + } + + return params; + } + + unimplemented!("Only objects are supported with style=deepObject") +} + {{#apiInfo}} {{#apis}} pub mod {{{classFilename}}}; diff --git a/support/openapi-template/reqwest/configuration.mustache b/support/openapi-template/reqwest/configuration.mustache index cbc21644e..0de2884a8 100644 --- a/support/openapi-template/reqwest/configuration.mustache +++ b/support/openapi-template/reqwest/configuration.mustache @@ -1,7 +1,5 @@ {{>partial_header}} -use reqwest; - {{#withAWSV4Signature}} use std::time::SystemTime; use aws_sigv4::http_request::{sign, SigningSettings, SigningParams, SignableRequest}; @@ -13,7 +11,7 @@ use secrecy::{SecretString, ExposeSecret}; pub struct Configuration { pub base_path: String, pub user_agent: Option, - pub client: reqwest::Client, + pub client: {{#supportMiddleware}}reqwest_middleware::ClientWithMiddleware{{/supportMiddleware}}{{^supportMiddleware}}reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client{{/supportMiddleware}}, pub basic_auth: Option, pub oauth_access_token: Option, pub bearer_access_token: Option, @@ -82,7 +80,7 @@ impl Default for Configuration { Configuration { base_path: "{{{basePath}}}".to_owned(), user_agent: {{#httpUserAgent}}Some("{{{.}}}".to_owned()){{/httpUserAgent}}{{^httpUserAgent}}Some("OpenAPI-Generator/{{{version}}}/rust".to_owned()){{/httpUserAgent}}, - client: reqwest::Client::new(), + client: {{#supportMiddleware}}reqwest_middleware::ClientBuilder::new(reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::new()).build(){{/supportMiddleware}}{{^supportMiddleware}}reqwest{{^supportAsync}}::blocking{{/supportAsync}}::Client::new(){{/supportMiddleware}}, basic_auth: None, oauth_access_token: None, bearer_access_token: None,