Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ quote = "1.0.40"
rand = "0.9.2"
regex = "1.11.2"
regress = "0.10.4"
reqwest = { version = "0.12.4", default-features = false, features = ["json", "stream"] }
reqwest = { version = "0.12.4", default-features = false, features = [
"json",
"stream",
"multipart",
] }
rustfmt-wrapper = "0.2.1"
schemars = { version = "0.8.22", features = ["chrono", "uuid1"] }
semver = "1.0.27"
Expand All @@ -66,3 +70,6 @@ uuid = { version = "1.18.1", features = ["serde", "v4"] }
#serde_tokenstream = { path = "../serde_tokenstream" }
#typify = { path = "../typify/typify" }
#rustfmt-wrapper = { path = "../rustfmt-wrapper" }

[workspace.lints.rust]
unused_crate_dependencies = "forbid"
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ You'll need to add the following to `Cargo.toml`:
[dependencies]
futures = "0.3"
progenitor = { git = "https://github.com/oxidecomputer/progenitor" }
reqwest = { version = "0.12", features = ["json", "stream"] }
reqwest = { version = "0.12", features = ["json", "stream", "multipart"] }
serde = { version = "1.0", features = ["derive"] }
```

Expand Down Expand Up @@ -133,7 +133,7 @@ You'll need to add the following to `Cargo.toml`:
[dependencies]
futures = "0.3"
progenitor-client = { git = "https://github.com/oxidecomputer/progenitor" }
reqwest = { version = "0.12", features = ["json", "stream"] }
reqwest = { version = "0.12", features = ["json", "stream", "multipart"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

Expand Down Expand Up @@ -175,6 +175,7 @@ cargo progenitor -i sample_openapi/keeper.json -o keeper -n keeper -v 0.1.0
```

... or within the repo:

```
cargo run --bin cargo-progenitor -- progenitor -i sample_openapi/keeper.json -o keeper -n keeper -v 0.1.0
```
Expand All @@ -200,7 +201,7 @@ bytes = "1.9"
chrono = { version = "0.4", default-features=false, features = ["serde"] }
futures-core = "0.3"
progenitor-client = "0.9.1"
reqwest = { version = "0.12", default-features=false, features = ["json", "stream"] }
reqwest = { version = "0.12", default-features=false, features = ["json", "stream", "multipart"] }
serde = { version = "1.0", features = ["derive"] }
serde_urlencoded = "0.7"
```
Expand Down Expand Up @@ -378,7 +379,7 @@ Currently, the generated code doesn't deal with request headers. To add default

```rust
let baseurl = std::env::var("API_URL").expect("$API_URL not set");

let access_token = std::env::var("API_ACCESS_TOKEN").expect("$API_ACCESS_TOKEN not set");
let authorization_header = format!("Bearer {}", access_token);

Expand All @@ -396,4 +397,4 @@ Currently, the generated code doesn't deal with request headers. To add default
.unwrap();

let client = Client::new_with_client(baseurl, client_with_custom_defaults);
```
```
2 changes: 1 addition & 1 deletion cargo-progenitor/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ pub fn dependencies(builder: Generator, include_client: bool) -> Vec<String> {
let mut deps = vec![
format!("bytes = \"{}\"", DEPENDENCIES.bytes),
format!("futures-core = \"{}\"", DEPENDENCIES.futures),
format!("reqwest = {{ version = \"{}\", default-features=false, features = [\"json\", \"stream\"] }}", DEPENDENCIES.reqwest),
format!("reqwest = {{ version = \"{}\", default-features=false, features = [\"json\", \"stream\", \"multipart\"] }}", DEPENDENCIES.reqwest),
format!("serde = {{ version = \"{}\", features = [\"derive\"] }}", DEPENDENCIES.serde),
format!("serde_urlencoded = \"{}\"", DEPENDENCIES.serde_urlencoded),
];
Expand Down
2 changes: 1 addition & 1 deletion example-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
progenitor-client = { path = "../progenitor-client" }
reqwest = { version = "0.12.4", features = ["json", "stream"] }
reqwest = { version = "0.12.4", features = ["json", "stream", "multipart"] }
base64 = "0.22"
rand = "0.9"
serde = { version = "1.0", features = ["derive"] }
Expand Down
2 changes: 1 addition & 1 deletion example-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
progenitor = { path = "../progenitor" }
reqwest = { version = "0.12.4", features = ["json", "stream"] }
reqwest = { version = "0.12.4", features = ["json", "stream", "multipart"] }
schemars = { version = "0.8.22", features = ["uuid1"] }
serde = { version = "1.0", features = ["derive"] }
uuid = { version = "1.18", features = ["serde", "v4"] }
38 changes: 34 additions & 4 deletions progenitor-client/src/progenitor_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::ops::{Deref, DerefMut};

use bytes::Bytes;
use futures_core::Stream;
use reqwest::RequestBuilder;
use reqwest::{multipart::Part, RequestBuilder};
use serde::{de::DeserializeOwned, ser::SerializeStruct, Serialize};

#[cfg(not(target_arch = "wasm32"))]
Expand Down Expand Up @@ -527,8 +527,16 @@ pub fn encode_path(pc: &str) -> String {
}

#[doc(hidden)]
pub trait RequestBuilderExt<E> {
pub trait RequestBuilderExt<E>
where
Self: Sized,
{
fn form_urlencoded<T: Serialize + ?Sized>(self, body: &T) -> Result<RequestBuilder, Error<E>>;

fn form_from_raw<S: AsRef<str>, T: AsRef<[u8]>, I: Sized + IntoIterator<Item = (S, T)>>(
self,
iter: I,
) -> Result<Self, Error<E>>;
}

impl<E> RequestBuilderExt<E> for RequestBuilder {
Expand All @@ -539,10 +547,32 @@ impl<E> RequestBuilderExt<E> for RequestBuilder {
reqwest::header::HeaderValue::from_static("application/x-www-form-urlencoded"),
)
.body(
serde_urlencoded::to_string(body)
.map_err(|_| Error::InvalidRequest("failed to serialize body".to_string()))?,
serde_urlencoded::to_string(body).map_err(|e| {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spurious, consider moving to another commit.

Error::InvalidRequest(format!("failed to serialize body: {e:?}"))
})?,
))
}

fn form_from_raw<S: AsRef<str>, T: AsRef<[u8]>, I: Sized + IntoIterator<Item = (S, T)>>(
self,
iter: I,
) -> Result<Self, Error<E>> {
use reqwest::multipart::Form;

let mut form = Form::new();
for (name, value) in iter {
form = form.part(
name.as_ref().to_owned(),
Part::stream(Vec::from(value.as_ref())),
);
}
Ok(self
.header(
reqwest::header::CONTENT_TYPE,
reqwest::header::HeaderValue::from_static("multipart/form-data"),
)
.multipart(form))
}
}

#[doc(hidden)]
Expand Down
3 changes: 2 additions & 1 deletion progenitor-impl/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,8 @@ impl Generator {
// are currently...
OperationParameterType::RawBody => None,

OperationParameterType::Type(body_type_id) => Some(body_type_id),
OperationParameterType::Type(body_type_id)
| OperationParameterType::Form(body_type_id) => Some(body_type_id),
});

if let Some(body_type_id) = maybe_body_type_id {
Expand Down
6 changes: 3 additions & 3 deletions progenitor-impl/src/httpmock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ impl Generator {
description: _,
}| {
let arg_type_name = match typ {
OperationParameterType::Type(arg_type_id) => self
OperationParameterType::Type(arg_type_id)
| OperationParameterType::Form(arg_type_id) => self
.type_space
.get_type(arg_type_id)
.unwrap()
Expand Down Expand Up @@ -243,11 +244,10 @@ impl Generator {
},
),
OperationParameterKind::Body(body_content_type) => match typ {
OperationParameterType::Type(_) => (
OperationParameterType::Type(_) | OperationParameterType::Form(_) => (
true,
quote! {
Self(self.0.json_body_obj(value))

},
),
OperationParameterType::RawBody => match body_content_type {
Expand Down
Loading