Skip to content

Commit

Permalink
Generate a Client method for Dropshot websocket channels
Browse files Browse the repository at this point in the history
Generated methods return `ResponseValue<reqwest::Upgrade`, which may be
passed to a websocket protocol implementation such as
`tokio_tungstenite::WebSocketStream::from_raw_stream(rv.into_inner(), ...)`
for the purpose of implementing against the raw websocket connection, but
may later be extended as a generic to allow higher-level channel message
definitions.

Per the changelog, consumers will need to depend on reqwest from git until
such time upstream cuts a new crates.io release:
```
reqwest = {
    git = "https://github.com/seanmonstar/reqwest",
    rev = "6ceb23958c8b6d7f1d8ee093f0ad73184d133d40",
    features = ["json", "stream"]
}
```
  • Loading branch information
lif committed Sep 13, 2022
1 parent 6e69bb4 commit 183ef35
Show file tree
Hide file tree
Showing 24 changed files with 1,085 additions and 52 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Expand Up @@ -20,6 +20,7 @@ https://github.com/oxidecomputer/progenitor/compare/v0.1.1\...HEAD[Full list of
* Derive `Debug` for `Client` and builders for the various operations (#145)
* Builders for `struct` types (#171)
* Add a prelude that include the `Client` and any extension traits (#176)
* Add support for upgrading connections, which requires a version bump to reqwest. (#183)

== 0.1.1 (released 2022-05-13)

Expand Down
83 changes: 60 additions & 23 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion example-build/Cargo.toml
Expand Up @@ -7,7 +7,8 @@ edition = "2021"
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
progenitor-client = { path = "../progenitor-client" }
reqwest = { version = "0.11", features = ["json", "stream"] }
reqwest = { git = "https://github.com/seanmonstar/reqwest", rev = "6ceb23958c8b6d7f1d8ee093f0ad73184d133d40", features = ["json", "stream"] }
#reqwest = { version = "0.11", features = ["json", "stream"] }
serde = { version = "1.0", features = ["derive"] }
uuid = { version = "1.0", features = ["serde", "v4"] }

Expand Down
3 changes: 2 additions & 1 deletion example-macro/Cargo.toml
Expand Up @@ -7,7 +7,8 @@ edition = "2021"
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
progenitor = { path = "../progenitor" }
reqwest = { version = "0.11", features = ["json", "stream"] }
reqwest = { git = "https://github.com/seanmonstar/reqwest", rev = "6ceb23958c8b6d7f1d8ee093f0ad73184d133d40", features = ["json", "stream"] }
#reqwest = { version = "0.11", features = ["json", "stream"] }
schemars = { version = "0.8.10", features = ["uuid1"] }
serde = { version = "1.0", features = ["derive"] }
uuid = { version = "1.0", features = ["serde", "v4"] }
5 changes: 4 additions & 1 deletion progenitor-client/Cargo.toml
Expand Up @@ -10,7 +10,10 @@ description = "An OpenAPI client generator - client support"
bytes = "1.2.1"
futures-core = "0.3.24"
percent-encoding = "2.1"
reqwest = { version = "0.11", default-features = false, features = ["json", "stream"] }
reqwest = { git = "https://github.com/seanmonstar/reqwest", rev = "6ceb23958c8b6d7f1d8ee093f0ad73184d133d40", features = ["json", "stream"] }
#reqwest = { version = "0.11", default-features = false, features = ["json", "stream"] }
serde = "1.0"
serde_json = "1.0"
serde_urlencoded = "0.7.1"
base64 = "0.13"
rand = "0.8"
29 changes: 29 additions & 0 deletions progenitor-client/src/progenitor_client.rs
Expand Up @@ -76,6 +76,30 @@ impl<T: DeserializeOwned> ResponseValue<T> {
}
}

impl ResponseValue<reqwest::Upgraded> {
#[doc(hidden)]
pub async fn upgrade<E: std::fmt::Debug>(
response: reqwest::Response,
) -> Result<Self, Error<E>> {
let status = response.status();
let headers = response.headers().clone();
if status == reqwest::StatusCode::SWITCHING_PROTOCOLS {
let inner = response
.upgrade()
.await
.map_err(Error::InvalidResponsePayload)?;

Ok(Self {
inner,
status,
headers,
})
} else {
Err(Error::UnexpectedResponse(response))
}
}
}

impl ResponseValue<ByteStream> {
#[doc(hidden)]
pub fn stream(response: reqwest::Response) -> Self {
Expand Down Expand Up @@ -377,3 +401,8 @@ impl<E> RequestBuilderExt<E> for RequestBuilder {
})?))
}
}

#[doc(hidden)]
pub fn generate_websocket_key() -> String {
base64::encode(rand::random::<[u8; 16]>())
}
13 changes: 12 additions & 1 deletion progenitor-impl/src/lib.rs
Expand Up @@ -26,6 +26,8 @@ pub enum Error {
UnexpectedFormat(String),
#[error("invalid operation path {0}")]
InvalidPath(String),
#[error("invalid dropshot extension use: {0}")]
InvalidExtension(String),
#[error("internal error {0}")]
InternalError(String),
}
Expand Down Expand Up @@ -213,7 +215,12 @@ impl Generator {
let file = quote! {
// Re-export ResponseValue and Error since those are used by the
// public interface of Client.
pub use progenitor_client::{ByteStream, Error, ResponseValue};
pub use progenitor_client::{
ByteStream,
Error,
ResponseValue,
generate_websocket_key
};
#[allow(unused_imports)]
use progenitor_client::{encode_path, RequestBuilderExt};

Expand Down Expand Up @@ -319,6 +326,7 @@ impl Generator {
#[allow(unused_imports)]
use super::{
encode_path,
generate_websocket_key,
ByteStream,
Error,
RequestBuilderExt,
Expand Down Expand Up @@ -358,11 +366,14 @@ impl Generator {
#[allow(unused_imports)]
use super::{
encode_path,
generate_websocket_key,
ByteStream,
Error,
RequestBuilderExt,
ResponseValue,
};
#[allow(unused_imports)]
use std::convert::TryInto;

#(#builder_struct)*

Expand Down

0 comments on commit 183ef35

Please sign in to comment.