Skip to content

Commit d229de5

Browse files
author
lif
committed
Generate a Client method for Dropshot websocket channels
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 0.11.12 or newer for HTTP Upgrade support, as well as base64 and rand if any endpoints are websocket channels: ``` [dependencies] reqwest = { version = "0.11.12" features = ["json", "stream"] } base64 = "0.13" rand = "0.8" ``` reqwest 0.11.12 just dropped Add output test for propolis-server
1 parent bffee9c commit d229de5

19 files changed

+5523
-14
lines changed

CHANGELOG.adoc

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ https://github.com/oxidecomputer/progenitor/compare/v0.1.1\...v0.2.0[Full list o
2424
* Derive `Debug` for `Client` and builders for the various operations (#145)
2525
* Builders for `struct` types (#171)
2626
* Add a prelude that include the `Client` and any extension traits (#176)
27+
* Add support for upgrading connections, which requires a version bump to reqwest. (#183)
2728

2829
== 0.1.1 (released 2022-05-13)
2930

Cargo.lock

+43-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+9-2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ Similarly if there is a `format` field set to `uuid`:
5454
+uuid = { version = "1.0.0", features = ["serde", "v4"] }
5555
```
5656

57+
And if there are any websocket channel endpoints:
58+
```diff
59+
[dependencies]
60+
+base64 = "0.13"
61+
+rand = "0.8"
62+
```
63+
5764
The macro has some additional fancy options to control the generated code:
5865

5966
```rust
@@ -116,7 +123,7 @@ You'll need to add add the following to `Cargo.toml`:
116123
+serde_json = "1.0"
117124
```
118125

119-
(`chrono` and `uuid` as above)
126+
(`chrono`, `uuid`, `base64`, and `rand` as above)
120127

121128
Note that `progenitor` is used by `build.rs`, but the generated code required
122129
`progenitor-client`.
@@ -290,4 +297,4 @@ let result = client
290297
```
291298

292299
Consumers do not need to specify parameters and struct properties that are not
293-
required or for which the API specifies defaults. Neat!
300+
required or for which the API specifies defaults. Neat!

example-build/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ edition = "2021"
77
[dependencies]
88
chrono = { version = "0.4", features = ["serde"] }
99
progenitor-client = { path = "../progenitor-client" }
10-
reqwest = { version = "0.11", features = ["json", "stream"] }
10+
reqwest = { version = "0.11.12", features = ["json", "stream"] }
11+
base64 = "0.13"
12+
rand = "0.8"
1113
serde = { version = "1.0", features = ["derive"] }
1214
uuid = { version = "1.0", features = ["serde", "v4"] }
1315

example-macro/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ edition = "2021"
77
[dependencies]
88
chrono = { version = "0.4", features = ["serde"] }
99
progenitor = { path = "../progenitor" }
10-
reqwest = { version = "0.11", features = ["json", "stream"] }
10+
reqwest = { version = "0.11.12", features = ["json", "stream"] }
1111
schemars = { version = "0.8.10", features = ["uuid1"] }
1212
serde = { version = "1.0", features = ["derive"] }
1313
uuid = { version = "1.0", features = ["serde", "v4"] }

progenitor-client/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ description = "An OpenAPI client generator - client support"
1010
bytes = "1.2.1"
1111
futures-core = "0.3.24"
1212
percent-encoding = "2.2"
13-
reqwest = { version = "0.11", default-features = false, features = ["json", "stream"] }
13+
reqwest = { version = "0.11.12", default-features = false, features = ["json", "stream"] }
1414
serde = "1.0"
1515
serde_json = "1.0"
1616
serde_urlencoded = "0.7.1"

progenitor-client/src/progenitor_client.rs

+24
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,30 @@ impl<T: DeserializeOwned> ResponseValue<T> {
7676
}
7777
}
7878

79+
impl ResponseValue<reqwest::Upgraded> {
80+
#[doc(hidden)]
81+
pub async fn upgrade<E: std::fmt::Debug>(
82+
response: reqwest::Response,
83+
) -> Result<Self, Error<E>> {
84+
let status = response.status();
85+
let headers = response.headers().clone();
86+
if status == reqwest::StatusCode::SWITCHING_PROTOCOLS {
87+
let inner = response
88+
.upgrade()
89+
.await
90+
.map_err(Error::InvalidResponsePayload)?;
91+
92+
Ok(Self {
93+
inner,
94+
status,
95+
headers,
96+
})
97+
} else {
98+
Err(Error::UnexpectedResponse(response))
99+
}
100+
}
101+
}
102+
79103
impl ResponseValue<ByteStream> {
80104
#[doc(hidden)]
81105
pub fn stream(response: reqwest::Response) -> Self {

progenitor-impl/src/lib.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ pub enum Error {
2626
UnexpectedFormat(String),
2727
#[error("invalid operation path {0}")]
2828
InvalidPath(String),
29+
#[error("invalid dropshot extension use: {0}")]
30+
InvalidExtension(String),
2931
#[error("internal error {0}")]
3032
InternalError(String),
3133
}
@@ -36,6 +38,7 @@ pub struct Generator {
3638
type_space: TypeSpace,
3739
settings: GenerationSettings,
3840
uses_futures: bool,
41+
uses_websockets: bool,
3942
}
4043

4144
#[derive(Default, Clone)]
@@ -116,6 +119,7 @@ impl Default for Generator {
116119
),
117120
settings: Default::default(),
118121
uses_futures: Default::default(),
122+
uses_websockets: Default::default(),
119123
}
120124
}
121125
}
@@ -133,6 +137,7 @@ impl Generator {
133137
type_space: TypeSpace::new(&type_settings),
134138
settings: settings.clone(),
135139
uses_futures: false,
140+
uses_websockets: false,
136141
}
137142
}
138143

@@ -361,6 +366,8 @@ impl Generator {
361366
RequestBuilderExt,
362367
ResponseValue,
363368
};
369+
#[allow(unused_imports)]
370+
use std::convert::TryInto;
364371

365372
#(#builder_struct)*
366373

@@ -426,7 +433,7 @@ impl Generator {
426433
"bytes = \"1.1\"",
427434
"futures-core = \"0.3\"",
428435
"percent-encoding = \"2.1\"",
429-
"reqwest = { version = \"0.11\", features = [\"json\", \"stream\"] }",
436+
"reqwest = { version = \"0.11.12\", features = [\"json\", \"stream\"] }",
430437
"serde = { version = \"1.0\", features = [\"derive\"] }",
431438
"serde_urlencoded = \"0.7\"",
432439
];
@@ -444,6 +451,10 @@ impl Generator {
444451
if self.uses_futures {
445452
deps.push("futures = \"0.3\"")
446453
}
454+
if self.uses_websockets {
455+
deps.push("base64 = \"0.13\"");
456+
deps.push("rand = \"0.8\"");
457+
}
447458
if self.type_space.uses_serde_json() {
448459
deps.push("serde_json = \"1.0\"")
449460
}

0 commit comments

Comments
 (0)