add support for application/x-www-form-urlencoded bodies (#109)
* add support for application/x-www-form-urlencoded bodies * self review * alphabetizing * remove commented code * update changelog
This commit is contained in:
parent
4dbad20942
commit
03e2cfad3c
|
@ -16,6 +16,7 @@
|
||||||
https://github.com/oxidecomputer/progenitor/compare/v0.1.1\...HEAD[Full list of commits]
|
https://github.com/oxidecomputer/progenitor/compare/v0.1.1\...HEAD[Full list of commits]
|
||||||
|
|
||||||
* Add support for a builder-style generation in addition to the positional style (#86)
|
* Add support for a builder-style generation in addition to the positional style (#86)
|
||||||
|
* Add support for body parameters with application/x-www-form-urlencoded media type (#109)
|
||||||
|
|
||||||
== 0.1.1 (released 2022-05-13)
|
== 0.1.1 (released 2022-05-13)
|
||||||
|
|
||||||
|
|
|
@ -975,6 +975,7 @@ dependencies = [
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -13,3 +13,4 @@ percent-encoding = "2.1"
|
||||||
reqwest = { version = "0.11", default-features = false, features = ["json", "stream"] }
|
reqwest = { version = "0.11", default-features = false, features = ["json", "stream"] }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
serde_urlencoded = "0.7.1"
|
||||||
|
|
|
@ -11,7 +11,8 @@ use std::{
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use serde::de::DeserializeOwned;
|
use reqwest::RequestBuilder;
|
||||||
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
||||||
/// Represents an untyped byte stream for both success and error responses.
|
/// Represents an untyped byte stream for both success and error responses.
|
||||||
pub type ByteStream =
|
pub type ByteStream =
|
||||||
|
@ -140,7 +141,7 @@ pub enum Error<E = ()> {
|
||||||
/// The request did not conform to API requirements.
|
/// The request did not conform to API requirements.
|
||||||
InvalidRequest(String),
|
InvalidRequest(String),
|
||||||
|
|
||||||
/// A server error either with the data, or with the connection.
|
/// A server error either due to the data, or with the connection.
|
||||||
CommunicationError(reqwest::Error),
|
CommunicationError(reqwest::Error),
|
||||||
|
|
||||||
/// A documented, expected error response.
|
/// A documented, expected error response.
|
||||||
|
@ -247,3 +248,29 @@ const PATH_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
|
||||||
pub fn encode_path(pc: &str) -> String {
|
pub fn encode_path(pc: &str) -> String {
|
||||||
percent_encoding::utf8_percent_encode(pc, PATH_SET).to_string()
|
percent_encoding::utf8_percent_encode(pc, PATH_SET).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub trait RequestBuilderExt<E> {
|
||||||
|
fn form_urlencoded<T: Serialize + ?Sized>(
|
||||||
|
self,
|
||||||
|
body: &T,
|
||||||
|
) -> Result<RequestBuilder, Error<E>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> RequestBuilderExt<E> for RequestBuilder {
|
||||||
|
fn form_urlencoded<T: Serialize + ?Sized>(
|
||||||
|
self,
|
||||||
|
body: &T,
|
||||||
|
) -> Result<Self, Error<E>> {
|
||||||
|
Ok(self
|
||||||
|
.header(
|
||||||
|
reqwest::header::CONTENT_TYPE,
|
||||||
|
reqwest::header::HeaderValue::from_static(
|
||||||
|
"application/x-www-form-urlencoded",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.body(serde_json::to_vec(&body).map_err(|_| {
|
||||||
|
Error::InvalidRequest("failed to serialize body".to_string())
|
||||||
|
})?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub enum Error {
|
||||||
UnexpectedFormat(String),
|
UnexpectedFormat(String),
|
||||||
#[error("invalid operation path")]
|
#[error("invalid operation path")]
|
||||||
InvalidPath(String),
|
InvalidPath(String),
|
||||||
#[error("invalid operation path")]
|
#[error("internal error")]
|
||||||
InternalError(String),
|
InternalError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ impl Generator {
|
||||||
// public interface of Client.
|
// public interface of Client.
|
||||||
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use progenitor_client::encode_path;
|
use progenitor_client::{encode_path, RequestBuilderExt};
|
||||||
|
|
||||||
pub mod types {
|
pub mod types {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -254,7 +254,6 @@ impl Generator {
|
||||||
pub fn client(&self) -> &reqwest::Client {
|
pub fn client(&self) -> &reqwest::Client {
|
||||||
&self.client
|
&self.client
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#operation_code
|
#operation_code
|
||||||
|
@ -301,9 +300,13 @@ impl Generator {
|
||||||
pub mod builder {
|
pub mod builder {
|
||||||
use super::types;
|
use super::types;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use super::{ByteStream, Error, ResponseValue};
|
use super::{
|
||||||
#[allow(unused_imports)]
|
encode_path,
|
||||||
use super::encode_path;
|
ByteStream,
|
||||||
|
Error,
|
||||||
|
RequestBuilderExt,
|
||||||
|
ResponseValue,
|
||||||
|
};
|
||||||
|
|
||||||
#(#builder_struct)*
|
#(#builder_struct)*
|
||||||
}
|
}
|
||||||
|
@ -329,9 +332,13 @@ impl Generator {
|
||||||
pub mod builder {
|
pub mod builder {
|
||||||
use super::types;
|
use super::types;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use super::{ByteStream, Error, ResponseValue};
|
use super::{
|
||||||
#[allow(unused_imports)]
|
encode_path,
|
||||||
use super::encode_path;
|
ByteStream,
|
||||||
|
Error,
|
||||||
|
RequestBuilderExt,
|
||||||
|
ResponseValue,
|
||||||
|
};
|
||||||
|
|
||||||
#(#builder_struct)*
|
#(#builder_struct)*
|
||||||
}
|
}
|
||||||
|
@ -391,8 +398,9 @@ impl Generator {
|
||||||
"bytes = \"1.1.0\"",
|
"bytes = \"1.1.0\"",
|
||||||
"futures-core = \"0.3.21\"",
|
"futures-core = \"0.3.21\"",
|
||||||
"percent-encoding = \"2.1\"",
|
"percent-encoding = \"2.1\"",
|
||||||
"serde = { version = \"1.0\", features = [\"derive\"] }",
|
|
||||||
"reqwest = { version = \"0.11\", features = [\"json\", \"stream\"] }",
|
"reqwest = { version = \"0.11\", features = [\"json\", \"stream\"] }",
|
||||||
|
"serde = { version = \"1.0\", features = [\"derive\"] }",
|
||||||
|
"serde_urlencoded = 0.7",
|
||||||
];
|
];
|
||||||
if self.type_space.uses_uuid() {
|
if self.type_space.uses_uuid() {
|
||||||
deps.push(
|
deps.push(
|
||||||
|
|
|
@ -27,7 +27,6 @@ pub(crate) struct OperationMethod {
|
||||||
summary: Option<String>,
|
summary: Option<String>,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
params: Vec<OperationParameter>,
|
params: Vec<OperationParameter>,
|
||||||
raw_body_param: bool,
|
|
||||||
responses: Vec<OperationResponse>,
|
responses: Vec<OperationResponse>,
|
||||||
dropshot_paginated: Option<DropshotPagination>,
|
dropshot_paginated: Option<DropshotPagination>,
|
||||||
}
|
}
|
||||||
|
@ -91,12 +90,6 @@ struct DropshotPagination {
|
||||||
item: TypeId,
|
item: TypeId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
enum OperationParameterKind {
|
|
||||||
Path,
|
|
||||||
Query(bool),
|
|
||||||
Body,
|
|
||||||
}
|
|
||||||
struct OperationParameter {
|
struct OperationParameter {
|
||||||
/// Sanitized parameter name.
|
/// Sanitized parameter name.
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -112,6 +105,37 @@ enum OperationParameterType {
|
||||||
Type(TypeId),
|
Type(TypeId),
|
||||||
RawBody,
|
RawBody,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
enum OperationParameterKind {
|
||||||
|
Path,
|
||||||
|
Query(bool),
|
||||||
|
Body(BodyContentType),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
enum BodyContentType {
|
||||||
|
OctetStream,
|
||||||
|
Json,
|
||||||
|
FormUrlencoded,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for BodyContentType {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
match s {
|
||||||
|
"application/octet-stream" => Ok(Self::OctetStream),
|
||||||
|
"application/json" => Ok(Self::Json),
|
||||||
|
"application/x-www-form-urlencoded" => Ok(Self::FormUrlencoded),
|
||||||
|
_ => Err(Error::UnexpectedFormat(format!(
|
||||||
|
"unexpected content type: {}",
|
||||||
|
s
|
||||||
|
))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct OperationResponse {
|
struct OperationResponse {
|
||||||
status_code: OperationResponseStatus,
|
status_code: OperationResponseStatus,
|
||||||
|
@ -313,44 +337,11 @@ impl Generator {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
let mut raw_body_param = false;
|
|
||||||
if let Some(b) = &operation.request_body {
|
|
||||||
let b = b.item(components)?;
|
|
||||||
let typ = if b.is_binary(components)? {
|
|
||||||
raw_body_param = true;
|
|
||||||
OperationParameterType::RawBody
|
|
||||||
} else {
|
|
||||||
let mt = b.content_json()?;
|
|
||||||
if !mt.encoding.is_empty() {
|
|
||||||
todo!("media type encoding not empty: {:#?}", mt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(s) = &mt.schema {
|
if let Some(body_param) = self.get_body_param(operation, components)? {
|
||||||
let schema = s.to_schema();
|
params.push(body_param);
|
||||||
let name = sanitize(
|
|
||||||
&format!(
|
|
||||||
"{}-body",
|
|
||||||
operation.operation_id.as_ref().unwrap(),
|
|
||||||
),
|
|
||||||
Case::Pascal,
|
|
||||||
);
|
|
||||||
let typ = self
|
|
||||||
.type_space
|
|
||||||
.add_type_with_name(&schema, Some(name))?;
|
|
||||||
OperationParameterType::Type(typ)
|
|
||||||
} else {
|
|
||||||
todo!("media type encoding, no schema: {:#?}", mt);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
params.push(OperationParameter {
|
|
||||||
name: "body".to_string(),
|
|
||||||
api_name: "body".to_string(),
|
|
||||||
description: b.description.clone(),
|
|
||||||
typ,
|
|
||||||
kind: OperationParameterKind::Body,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let tmp = crate::template::parse(path)?;
|
let tmp = crate::template::parse(path)?;
|
||||||
let names = tmp.names();
|
let names = tmp.names();
|
||||||
|
|
||||||
|
@ -472,7 +463,6 @@ impl Generator {
|
||||||
.clone()
|
.clone()
|
||||||
.filter(|s| !s.is_empty()),
|
.filter(|s| !s.is_empty()),
|
||||||
params,
|
params,
|
||||||
raw_body_param,
|
|
||||||
responses,
|
responses,
|
||||||
dropshot_paginated,
|
dropshot_paginated,
|
||||||
})
|
})
|
||||||
|
@ -506,7 +496,12 @@ impl Generator {
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let bounds = if method.raw_body_param {
|
let raw_body_param = method
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.any(|param| param.typ == OperationParameterType::RawBody);
|
||||||
|
|
||||||
|
let bounds = if raw_body_param {
|
||||||
quote! { <'a, B: Into<reqwest::Body> > }
|
quote! { <'a, B: Into<reqwest::Body> > }
|
||||||
} else {
|
} else {
|
||||||
quote! { <'a> }
|
quote! { <'a> }
|
||||||
|
@ -724,19 +719,44 @@ impl Generator {
|
||||||
.collect();
|
.collect();
|
||||||
let url_path = method.path.compile(url_renames, client.clone());
|
let url_path = method.path.compile(url_renames, client.clone());
|
||||||
|
|
||||||
// Generate code to handle the body...
|
// Generate code to handle the body param.
|
||||||
let body_func =
|
let body_func = method.params.iter().filter_map(|param| {
|
||||||
method.params.iter().filter_map(|param| match ¶m.kind {
|
match (¶m.kind, ¶m.typ) {
|
||||||
OperationParameterKind::Body => match ¶m.typ {
|
(
|
||||||
OperationParameterType::Type(_) => {
|
OperationParameterKind::Body(BodyContentType::OctetStream),
|
||||||
Some(quote! { .json(&body) })
|
OperationParameterType::RawBody,
|
||||||
}
|
) => Some(quote! {
|
||||||
OperationParameterType::RawBody => {
|
// Set the content type (this is handled by helper
|
||||||
Some(quote! { .body(body) })
|
// functions for other MIME types).
|
||||||
}
|
.header(
|
||||||
},
|
reqwest::header::CONTENT_TYPE,
|
||||||
|
reqwest::header::HeaderValue::from_static("application/octet-stream"),
|
||||||
|
)
|
||||||
|
.body(body)
|
||||||
|
}),
|
||||||
|
(
|
||||||
|
OperationParameterKind::Body(BodyContentType::Json),
|
||||||
|
OperationParameterType::Type(_),
|
||||||
|
) => Some(quote! {
|
||||||
|
// Serialization errors are deferred.
|
||||||
|
.json(&body)
|
||||||
|
}),
|
||||||
|
(
|
||||||
|
OperationParameterKind::Body(
|
||||||
|
BodyContentType::FormUrlencoded,
|
||||||
|
),
|
||||||
|
OperationParameterType::Type(_),
|
||||||
|
) => Some(quote! {
|
||||||
|
// This uses progenitor_client::RequestBuilderExt which
|
||||||
|
// returns an error in the case of a serialization failure.
|
||||||
|
.form_urlencoded(&body)?
|
||||||
|
}),
|
||||||
|
(OperationParameterKind::Body(_), _) => {
|
||||||
|
unreachable!("invalid body kind/type combination")
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
}
|
||||||
|
});
|
||||||
// ... and there can be at most one body.
|
// ... and there can be at most one body.
|
||||||
assert!(body_func.clone().count() <= 1);
|
assert!(body_func.clone().count() <= 1);
|
||||||
|
|
||||||
|
@ -1277,7 +1297,7 @@ impl Generator {
|
||||||
.filter_map(|param| match param.kind {
|
.filter_map(|param| match param.kind {
|
||||||
OperationParameterKind::Path
|
OperationParameterKind::Path
|
||||||
| OperationParameterKind::Query(true)
|
| OperationParameterKind::Query(true)
|
||||||
| OperationParameterKind::Body => {
|
| OperationParameterKind::Body(_) => {
|
||||||
Some(format_ident!("{}", param.name))
|
Some(format_ident!("{}", param.name))
|
||||||
}
|
}
|
||||||
OperationParameterKind::Query(false) => None,
|
OperationParameterKind::Query(false) => None,
|
||||||
|
@ -1655,6 +1675,100 @@ impl Generator {
|
||||||
|
|
||||||
impl_body
|
impl_body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_body_param(
|
||||||
|
&mut self,
|
||||||
|
operation: &openapiv3::Operation,
|
||||||
|
components: &Option<Components>,
|
||||||
|
) -> Result<Option<OperationParameter>> {
|
||||||
|
let body = match &operation.request_body {
|
||||||
|
Some(body) => body.item(components)?,
|
||||||
|
None => return Ok(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (content_str, media_type) =
|
||||||
|
match (body.content.first(), body.content.len()) {
|
||||||
|
(None, _) => return Ok(None),
|
||||||
|
(Some(first), 1) => first,
|
||||||
|
(_, n) => todo!("more media types than expected: {}", n),
|
||||||
|
};
|
||||||
|
|
||||||
|
let schema = media_type.schema.as_ref().ok_or_else(|| {
|
||||||
|
Error::UnexpectedFormat(
|
||||||
|
"No schema specified for request body".to_string(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let content_type = BodyContentType::from_str(content_str)?;
|
||||||
|
|
||||||
|
let typ = match content_type {
|
||||||
|
BodyContentType::OctetStream => {
|
||||||
|
// For an octet stream, we expect a simple, specific schema:
|
||||||
|
// "schema": {
|
||||||
|
// "type": "string",
|
||||||
|
// "format": "binary"
|
||||||
|
// }
|
||||||
|
match schema.item(components)? {
|
||||||
|
openapiv3::Schema {
|
||||||
|
schema_data:
|
||||||
|
openapiv3::SchemaData {
|
||||||
|
nullable: false,
|
||||||
|
discriminator: None,
|
||||||
|
default: None,
|
||||||
|
// Other fields that describe or document the
|
||||||
|
// schema are fine.
|
||||||
|
..
|
||||||
|
},
|
||||||
|
schema_kind:
|
||||||
|
openapiv3::SchemaKind::Type(openapiv3::Type::String(
|
||||||
|
openapiv3::StringType {
|
||||||
|
format:
|
||||||
|
openapiv3::VariantOrUnknownOrEmpty::Item(
|
||||||
|
openapiv3::StringFormat::Binary,
|
||||||
|
),
|
||||||
|
pattern: None,
|
||||||
|
enumeration,
|
||||||
|
min_length: None,
|
||||||
|
max_length: None,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
} if enumeration.is_empty() => Ok(()),
|
||||||
|
_ => Err(Error::UnexpectedFormat(format!(
|
||||||
|
"invalid schema for application/octet-stream: {:?}",
|
||||||
|
schema
|
||||||
|
))),
|
||||||
|
}?;
|
||||||
|
OperationParameterType::RawBody
|
||||||
|
}
|
||||||
|
BodyContentType::Json | BodyContentType::FormUrlencoded => {
|
||||||
|
// TODO it would be legal to have the encoding field set for
|
||||||
|
// application/x-www-form-urlencoded content, but I'm not sure
|
||||||
|
// how to interpret the values.
|
||||||
|
if !media_type.encoding.is_empty() {
|
||||||
|
todo!("media type encoding not empty: {:#?}", media_type);
|
||||||
|
}
|
||||||
|
let name = sanitize(
|
||||||
|
&format!(
|
||||||
|
"{}-body",
|
||||||
|
operation.operation_id.as_ref().unwrap(),
|
||||||
|
),
|
||||||
|
Case::Pascal,
|
||||||
|
);
|
||||||
|
let typ = self
|
||||||
|
.type_space
|
||||||
|
.add_type_with_name(&schema.to_schema(), Some(name))?;
|
||||||
|
OperationParameterType::Type(typ)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(OperationParameter {
|
||||||
|
name: "body".to_string(),
|
||||||
|
api_name: "body".to_string(),
|
||||||
|
description: body.description.clone(),
|
||||||
|
typ,
|
||||||
|
kind: OperationParameterKind::Body(content_type),
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_doc_comment(method: &OperationMethod) -> String {
|
fn make_doc_comment(method: &OperationMethod) -> String {
|
||||||
|
@ -1810,13 +1924,13 @@ fn sort_params(raw_params: &mut [OperationParameter], names: &[String]) {
|
||||||
) => Ordering::Less,
|
) => Ordering::Less,
|
||||||
(
|
(
|
||||||
OperationParameterKind::Path,
|
OperationParameterKind::Path,
|
||||||
OperationParameterKind::Body,
|
OperationParameterKind::Body(_),
|
||||||
) => Ordering::Less,
|
) => Ordering::Less,
|
||||||
|
|
||||||
// Query params are in lexicographic order.
|
// Query params are in lexicographic order.
|
||||||
(
|
(
|
||||||
OperationParameterKind::Query(_),
|
OperationParameterKind::Query(_),
|
||||||
OperationParameterKind::Body,
|
OperationParameterKind::Body(_),
|
||||||
) => Ordering::Less,
|
) => Ordering::Less,
|
||||||
(
|
(
|
||||||
OperationParameterKind::Query(_),
|
OperationParameterKind::Query(_),
|
||||||
|
@ -1829,16 +1943,16 @@ fn sort_params(raw_params: &mut [OperationParameter], names: &[String]) {
|
||||||
|
|
||||||
// Body params are last and should be singular.
|
// Body params are last and should be singular.
|
||||||
(
|
(
|
||||||
OperationParameterKind::Body,
|
OperationParameterKind::Body(_),
|
||||||
OperationParameterKind::Path,
|
OperationParameterKind::Path,
|
||||||
) => Ordering::Greater,
|
) => Ordering::Greater,
|
||||||
(
|
(
|
||||||
OperationParameterKind::Body,
|
OperationParameterKind::Body(_),
|
||||||
OperationParameterKind::Query(_),
|
OperationParameterKind::Query(_),
|
||||||
) => Ordering::Greater,
|
) => Ordering::Greater,
|
||||||
(
|
(
|
||||||
OperationParameterKind::Body,
|
OperationParameterKind::Body(_),
|
||||||
OperationParameterKind::Body,
|
OperationParameterKind::Body(_),
|
||||||
) => {
|
) => {
|
||||||
panic!("should only be one body")
|
panic!("should only be one body")
|
||||||
}
|
}
|
||||||
|
@ -1861,90 +1975,3 @@ impl ParameterDataExt for openapiv3::ParameterData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO do I want/need this?
|
|
||||||
trait ExtractJsonMediaType {
|
|
||||||
fn is_binary(&self, components: &Option<Components>) -> Result<bool>;
|
|
||||||
fn content_json(&self) -> Result<openapiv3::MediaType>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtractJsonMediaType for openapiv3::RequestBody {
|
|
||||||
fn content_json(&self) -> Result<openapiv3::MediaType> {
|
|
||||||
if self.content.len() != 1 {
|
|
||||||
todo!("expected one content entry, found {}", self.content.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(mt) = self.content.get("application/json") {
|
|
||||||
Ok(mt.clone())
|
|
||||||
} else {
|
|
||||||
todo!(
|
|
||||||
"could not find application/json, only found {}",
|
|
||||||
self.content.keys().next().unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_binary(&self, components: &Option<Components>) -> Result<bool> {
|
|
||||||
if self.content.is_empty() {
|
|
||||||
/*
|
|
||||||
* XXX If there are no content types, I guess it is not binary?
|
|
||||||
*/
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.content.len() != 1 {
|
|
||||||
todo!("expected one content entry, found {}", self.content.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(mt) = self.content.get("application/octet-stream") {
|
|
||||||
if !mt.encoding.is_empty() {
|
|
||||||
todo!("XXX encoding");
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(s) = &mt.schema {
|
|
||||||
use openapiv3::{
|
|
||||||
SchemaKind, StringFormat, Type,
|
|
||||||
VariantOrUnknownOrEmpty::Item,
|
|
||||||
};
|
|
||||||
|
|
||||||
let s = s.item(components)?;
|
|
||||||
if s.schema_data.nullable {
|
|
||||||
todo!("XXX nullable binary?");
|
|
||||||
}
|
|
||||||
if s.schema_data.default.is_some() {
|
|
||||||
todo!("XXX default binary?");
|
|
||||||
}
|
|
||||||
if s.schema_data.discriminator.is_some() {
|
|
||||||
todo!("XXX binary discriminator?");
|
|
||||||
}
|
|
||||||
match &s.schema_kind {
|
|
||||||
SchemaKind::Type(Type::String(st)) => {
|
|
||||||
if st.min_length.is_some() || st.max_length.is_some() {
|
|
||||||
todo!("binary min/max length");
|
|
||||||
}
|
|
||||||
if !matches!(st.format, Item(StringFormat::Binary)) {
|
|
||||||
todo!(
|
|
||||||
"expected binary format string, got {:?}",
|
|
||||||
st.format
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if st.pattern.is_some() {
|
|
||||||
todo!("XXX pattern");
|
|
||||||
}
|
|
||||||
if !st.enumeration.is_empty() {
|
|
||||||
todo!("XXX enumeration");
|
|
||||||
}
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
x => {
|
|
||||||
todo!("XXX schemakind type {:?}", x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
todo!("binary thing had no schema?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use progenitor_client::encode_path;
|
use progenitor_client::{encode_path, RequestBuilderExt};
|
||||||
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
||||||
pub mod types {
|
pub mod types {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -365,11 +365,9 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod builder {
|
pub mod builder {
|
||||||
#[allow(unused_imports)]
|
|
||||||
use super::encode_path;
|
|
||||||
use super::types;
|
use super::types;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use super::{ByteStream, Error, ResponseValue};
|
use super::{encode_path, ByteStream, Error, RequestBuilderExt, ResponseValue};
|
||||||
///Builder for [`Client::control_hold`]
|
///Builder for [`Client::control_hold`]
|
||||||
///
|
///
|
||||||
///[`Client::control_hold`]: super::Client::control_hold
|
///[`Client::control_hold`]: super::Client::control_hold
|
||||||
|
@ -994,7 +992,15 @@ pub mod builder {
|
||||||
client.baseurl,
|
client.baseurl,
|
||||||
encode_path(&task.to_string()),
|
encode_path(&task.to_string()),
|
||||||
);
|
);
|
||||||
let request = client.client.post(url).body(body).build()?;
|
let request = client
|
||||||
|
.client
|
||||||
|
.post(url)
|
||||||
|
.header(
|
||||||
|
reqwest::header::CONTENT_TYPE,
|
||||||
|
reqwest::header::HeaderValue::from_static("application/octet-stream"),
|
||||||
|
)
|
||||||
|
.body(body)
|
||||||
|
.build()?;
|
||||||
let result = client.client.execute(request).await;
|
let result = client.client.execute(request).await;
|
||||||
let response = result?;
|
let response = result?;
|
||||||
match response.status().as_u16() {
|
match response.status().as_u16() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use progenitor_client::encode_path;
|
use progenitor_client::{encode_path, RequestBuilderExt};
|
||||||
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
||||||
pub mod types {
|
pub mod types {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -365,11 +365,9 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod builder {
|
pub mod builder {
|
||||||
#[allow(unused_imports)]
|
|
||||||
use super::encode_path;
|
|
||||||
use super::types;
|
use super::types;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use super::{ByteStream, Error, ResponseValue};
|
use super::{encode_path, ByteStream, Error, RequestBuilderExt, ResponseValue};
|
||||||
///Builder for [`Client::control_hold`]
|
///Builder for [`Client::control_hold`]
|
||||||
///
|
///
|
||||||
///[`Client::control_hold`]: super::Client::control_hold
|
///[`Client::control_hold`]: super::Client::control_hold
|
||||||
|
@ -994,7 +992,15 @@ pub mod builder {
|
||||||
client.baseurl,
|
client.baseurl,
|
||||||
encode_path(&task.to_string()),
|
encode_path(&task.to_string()),
|
||||||
);
|
);
|
||||||
let request = client.client.post(url).body(body).build()?;
|
let request = client
|
||||||
|
.client
|
||||||
|
.post(url)
|
||||||
|
.header(
|
||||||
|
reqwest::header::CONTENT_TYPE,
|
||||||
|
reqwest::header::HeaderValue::from_static("application/octet-stream"),
|
||||||
|
)
|
||||||
|
.body(body)
|
||||||
|
.build()?;
|
||||||
let result = client.client.execute(request).await;
|
let result = client.client.execute(request).await;
|
||||||
let response = result?;
|
let response = result?;
|
||||||
match response.status().as_u16() {
|
match response.status().as_u16() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use progenitor_client::encode_path;
|
use progenitor_client::{encode_path, RequestBuilderExt};
|
||||||
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
||||||
pub mod types {
|
pub mod types {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -388,7 +388,15 @@ impl Client {
|
||||||
self.baseurl,
|
self.baseurl,
|
||||||
encode_path(&task.to_string()),
|
encode_path(&task.to_string()),
|
||||||
);
|
);
|
||||||
let request = self.client.post(url).body(body).build()?;
|
let request = self
|
||||||
|
.client
|
||||||
|
.post(url)
|
||||||
|
.header(
|
||||||
|
reqwest::header::CONTENT_TYPE,
|
||||||
|
reqwest::header::HeaderValue::from_static("application/octet-stream"),
|
||||||
|
)
|
||||||
|
.body(body)
|
||||||
|
.build()?;
|
||||||
let result = self.client.execute(request).await;
|
let result = self.client.execute(request).await;
|
||||||
let response = result?;
|
let response = result?;
|
||||||
match response.status().as_u16() {
|
match response.status().as_u16() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use progenitor_client::encode_path;
|
use progenitor_client::{encode_path, RequestBuilderExt};
|
||||||
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
||||||
pub mod types {
|
pub mod types {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -173,11 +173,9 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod builder {
|
pub mod builder {
|
||||||
#[allow(unused_imports)]
|
|
||||||
use super::encode_path;
|
|
||||||
use super::types;
|
use super::types;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use super::{ByteStream, Error, ResponseValue};
|
use super::{encode_path, ByteStream, Error, RequestBuilderExt, ResponseValue};
|
||||||
///Builder for [`Client::enrol`]
|
///Builder for [`Client::enrol`]
|
||||||
///
|
///
|
||||||
///[`Client::enrol`]: super::Client::enrol
|
///[`Client::enrol`]: super::Client::enrol
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use progenitor_client::encode_path;
|
use progenitor_client::{encode_path, RequestBuilderExt};
|
||||||
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
||||||
pub mod types {
|
pub mod types {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -173,11 +173,9 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod builder {
|
pub mod builder {
|
||||||
#[allow(unused_imports)]
|
|
||||||
use super::encode_path;
|
|
||||||
use super::types;
|
use super::types;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use super::{ByteStream, Error, ResponseValue};
|
use super::{encode_path, ByteStream, Error, RequestBuilderExt, ResponseValue};
|
||||||
///Builder for [`Client::enrol`]
|
///Builder for [`Client::enrol`]
|
||||||
///
|
///
|
||||||
///[`Client::enrol`]: super::Client::enrol
|
///[`Client::enrol`]: super::Client::enrol
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use progenitor_client::encode_path;
|
use progenitor_client::{encode_path, RequestBuilderExt};
|
||||||
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
||||||
pub mod types {
|
pub mod types {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use progenitor_client::encode_path;
|
use progenitor_client::{encode_path, RequestBuilderExt};
|
||||||
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
||||||
pub mod types {
|
pub mod types {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use progenitor_client::encode_path;
|
use progenitor_client::{encode_path, RequestBuilderExt};
|
||||||
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
||||||
pub mod types {
|
pub mod types {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use progenitor_client::encode_path;
|
use progenitor_client::{encode_path, RequestBuilderExt};
|
||||||
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
pub use progenitor_client::{ByteStream, Error, ResponseValue};
|
||||||
pub mod types {
|
pub mod types {
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
|
@ -85,7 +85,6 @@ fn main() -> Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
let api = load_api(&args.input)?;
|
let api = load_api(&args.input)?;
|
||||||
|
|
||||||
//let mut builder = Generator::default();
|
|
||||||
let mut builder = Generator::new(
|
let mut builder = Generator::new(
|
||||||
GenerationSettings::default()
|
GenerationSettings::default()
|
||||||
.with_interface(args.interface.into())
|
.with_interface(args.interface.into())
|
||||||
|
|
|
@ -15,7 +15,7 @@ mod positional {
|
||||||
let org = types::Name("org".to_string());
|
let org = types::Name("org".to_string());
|
||||||
let project = types::Name("project".to_string());
|
let project = types::Name("project".to_string());
|
||||||
let instance = types::Name("instance".to_string());
|
let instance = types::Name("instance".to_string());
|
||||||
let stream = client.instance_disks_get_stream(
|
let stream = client.instance_disk_list_stream(
|
||||||
&org, &project, &instance, None, None,
|
&org, &project, &instance, None, None,
|
||||||
);
|
);
|
||||||
let _ = stream.collect::<Vec<_>>();
|
let _ = stream.collect::<Vec<_>>();
|
||||||
|
@ -39,7 +39,7 @@ mod builder_untagged {
|
||||||
pub fn _ignore() {
|
pub fn _ignore() {
|
||||||
let client = Client::new("");
|
let client = Client::new("");
|
||||||
let stream = client
|
let stream = client
|
||||||
.instance_disks_get()
|
.instance_disk_list()
|
||||||
.organization_name(types::Name("org".to_string()))
|
.organization_name(types::Name("org".to_string()))
|
||||||
.project_name(types::Name("project".to_string()))
|
.project_name(types::Name("project".to_string()))
|
||||||
.instance_name(types::Name("instance".to_string()))
|
.instance_name(types::Name("instance".to_string()))
|
||||||
|
@ -64,7 +64,7 @@ mod builder_tagged {
|
||||||
fn _ignore() {
|
fn _ignore() {
|
||||||
let client = Client::new("");
|
let client = Client::new("");
|
||||||
let stream = client
|
let stream = client
|
||||||
.instance_disks_get()
|
.instance_disk_list()
|
||||||
.organization_name(types::Name("org".to_string()))
|
.organization_name(types::Name("org".to_string()))
|
||||||
.project_name(types::Name("project".to_string()))
|
.project_name(types::Name("project".to_string()))
|
||||||
.instance_name(types::Name("instance".to_string()))
|
.instance_name(types::Name("instance".to_string()))
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue