Skip to content

Commit cebe76d

Browse files
analogrelayheaths
authored andcommitted
Replace Model trait with Format trait (applied to Response instead) (#2559)
* start building Format/DeserializeWith trait * remove Model and replace with Format type parameter * fix issues in azure_core, azure_identity, and azure_data_cosmos * fix tests in hand-written crates * updates to generated crates * make send_format more clearly a short-cut method * fix async_trait on wasm32 * fix tyop * initial pr feedback * rename DefaultFormat to JsonFormat * paramerize pipeline on format * undo all generated code changes * revert keyvault generated changes * some updates to copilot instructions * split Response into Response and RawResponse * remove extraneous helpers, let's focus on simple APIs and be ok with boilerplate * api doc updates * remove some extraneous copilot instructions * refactor cosmos for new Response types * fix typespec client examples * fix issues in non-generated clients * fix issues in generated clients * a few other hand-written client fixes * update changelog * pr feedback * refmt * fix doctest * fix more doctests * add `F` parameter to `Pager`
1 parent cfaa6c5 commit cebe76d

File tree

79 files changed

+923
-1572
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+923
-1572
lines changed

.github/copilot-instructions.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ Use these instructions for test generation as well.
1616
- Field and function names are snake_case.
1717
- Parameter names are snake_case.
1818
- Crate and module names are snake_case.
19+
- Keep `use` directives at the top of the module in which they are used, and avoid placing them inside functions or blocks unless absolutely necessary.
20+
- Prefer using `crate` in `use` directives to refer to types anywhere in the current crate instead of using it's name, or relative paths like `super` or `self`.
21+
- Prefer merging new `use` directives into existing ones rather than creating new `use` blocks.
1922
- Prioritize safety, efficiency, and correctness.
2023
- Respect Rust's ownership and borrowing rules.
2124
- Use short, descriptive names for fields, functions, parameters, and variables.
@@ -29,6 +32,7 @@ Use these instructions for test generation as well.
2932
- Use `clippy` to validate that generated code does not contain lint errors.
3033
- If you have trouble generating safe, efficient, maintainable, and lint-free code, insert a `TODO` comment describing what should happen.
3134
- All imported types, constants, functions, modules, and macros should be imported explicitly. Never import `*`.
35+
- Do not modify generated code, found in `generated` subdirectories. These files are generated by external tools and should not be edited manually.
3236

3337
## Test Generation
3438

@@ -38,3 +42,4 @@ Use these instructions for test generation as well.
3842
- The `tests` module should be conditioned on `#[cfg(test)]`.
3943
- The `tests` module should always import APIs from `super`.
4044
- Do not begin test function names with "test" unless necessary to disambiguate from the function being tested.
45+
- Test functions do not need to be public.

sdk/core/azure_core/benches/benchmarks.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use azure_core::http::{
2-
headers::Headers, response::Response, HttpClient, Method, Request, StatusCode, Url,
2+
headers::Headers, HttpClient, Method, RawResponse, Request, StatusCode, Url,
33
};
44
use azure_core_test::http::MockHttpClient;
55
use criterion::{black_box, criterion_group, criterion_main, Criterion};
@@ -30,7 +30,14 @@ fn http_transport_test(c: &mut Criterion) {
3030

3131
// client to be used in the benchmark
3232
let mock_client = Arc::new(MockHttpClient::new(move |_| {
33-
async move { Ok(Response::from_bytes(StatusCode::Ok, Headers::new(), vec![])) }.boxed()
33+
async move {
34+
Ok(RawResponse::from_bytes(
35+
StatusCode::Ok,
36+
Headers::new(),
37+
vec![],
38+
))
39+
}
40+
.boxed()
3441
})) as Arc<dyn HttpClient>;
3542

3643
// requests to be used in the benchmark

sdk/core/azure_core/src/http/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ pub use options::*;
1717
pub use pager::*;
1818
pub use pipeline::*;
1919
pub use request::{Body, Request, RequestContent};
20-
pub use response::{Model, Response};
20+
pub use response::{RawResponse, Response};
2121

2222
pub use typespec_client_core::http::response;
2323
pub use typespec_client_core::http::{
24-
new_http_client, AppendToUrlQuery, Context, HttpClient, Method, StatusCode, Url,
24+
new_http_client, AppendToUrlQuery, Context, Format, HttpClient, JsonFormat, Method, StatusCode,
25+
Url,
2526
};
27+
28+
#[cfg(feature = "xml")]
29+
pub use typespec_client_core::http::XmlFormat;

sdk/core/azure_core/src/http/pager.rs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::http::{headers::HeaderName, response::Response};
55
use futures::{stream::unfold, Stream};
66
use std::{future::Future, pin::Pin};
77
use typespec::Error;
8+
use typespec_client_core::http::JsonFormat;
89

910
/// The result of fetching a single page from a [`Pager`], whether the `Pager` should continue or is complete.
1011
#[derive(Debug)]
@@ -15,12 +16,12 @@ pub enum PagerResult<T, C> {
1516
Complete { response: T },
1617
}
1718

18-
impl<T> PagerResult<Response<T>, String> {
19+
impl<T, F> PagerResult<Response<T, F>, String> {
1920
/// Creates a [`PagerResult<T, C>`] from the provided response, extracting the continuation value from the provided header.
2021
///
2122
/// If the provided response has a header with the matching name, this returns [`PagerResult::Continue`], using the value from the header as the continuation.
2223
/// If the provided response does not have a header with the matching name, this returns [`PagerResult::Complete`].
23-
pub fn from_response_header(response: Response<T>, header_name: &HeaderName) -> Self {
24+
pub fn from_response_header(response: Response<T, F>, header_name: &HeaderName) -> Self {
2425
match response.headers().get_optional_string(header_name) {
2526
Some(continuation) => PagerResult::Continue {
2627
response,
@@ -34,7 +35,7 @@ impl<T> PagerResult<Response<T>, String> {
3435
/// Represents a paginated stream of results generated through HTTP requests to a service.
3536
///
3637
/// Specifically, this is a [`PageStream`] that yields [`Response<T>`] values.
37-
pub type Pager<T> = PageStream<Response<T>>;
38+
pub type Pager<T, F = JsonFormat> = PageStream<Response<T, F>>;
3839

3940
/// Represents a paginated stream of results from a service.
4041
#[pin_project::pin_project]
@@ -78,7 +79,8 @@ impl<T> PageStream<T> {
7879
/// }
7980
/// let resp: Response<MyModel> = pipeline
8081
/// .send(&Context::new(), &mut req)
81-
/// .await?;
82+
/// .await?
83+
/// .into();
8284
/// Ok(PagerResult::from_response_header(resp, &HeaderName::from_static("x-next-continuation")))
8385
/// }
8486
/// });
@@ -162,59 +164,60 @@ enum State<T> {
162164
mod tests {
163165
use std::collections::HashMap;
164166

165-
use crate::http::Model;
166167
use futures::StreamExt;
167168
use serde::Deserialize;
168169

169170
use crate::http::{
170171
headers::{HeaderName, HeaderValue},
171-
Pager, PagerResult, Response, StatusCode,
172+
Pager, PagerResult, RawResponse, StatusCode,
172173
};
173174

174175
#[tokio::test]
175176
pub async fn standard_pagination() {
176-
#[derive(Model, Deserialize, Debug, PartialEq, Eq)]
177-
#[typespec(crate = "crate")]
177+
#[derive(Deserialize, Debug, PartialEq, Eq)]
178178
struct Page {
179179
pub page: usize,
180180
}
181181

182182
let pager: Pager<Page> = Pager::from_callback(|continuation| async move {
183183
match continuation {
184184
None => Ok(PagerResult::Continue {
185-
response: Response::from_bytes(
185+
response: RawResponse::from_bytes(
186186
StatusCode::Ok,
187187
HashMap::from([(
188188
HeaderName::from_static("x-test-header"),
189189
HeaderValue::from_static("page-1"),
190190
)])
191191
.into(),
192192
r#"{"page":1}"#,
193-
),
193+
)
194+
.into(),
194195
continuation: "1",
195196
}),
196197
Some("1") => Ok(PagerResult::Continue {
197-
response: Response::from_bytes(
198+
response: RawResponse::from_bytes(
198199
StatusCode::Ok,
199200
HashMap::from([(
200201
HeaderName::from_static("x-test-header"),
201202
HeaderValue::from_static("page-2"),
202203
)])
203204
.into(),
204205
r#"{"page":2}"#,
205-
),
206+
)
207+
.into(),
206208
continuation: "2",
207209
}),
208210
Some("2") => Ok(PagerResult::Complete {
209-
response: Response::from_bytes(
211+
response: RawResponse::from_bytes(
210212
StatusCode::Ok,
211213
HashMap::from([(
212214
HeaderName::from_static("x-test-header"),
213215
HeaderValue::from_static("page-3"),
214216
)])
215217
.into(),
216218
r#"{"page":3}"#,
217-
),
219+
)
220+
.into(),
218221
}),
219222
_ => {
220223
panic!("Unexpected continuation value")
@@ -245,24 +248,24 @@ mod tests {
245248

246249
#[tokio::test]
247250
pub async fn error_stops_pagination() {
248-
#[derive(Model, Deserialize, Debug, PartialEq, Eq)]
249-
#[typespec(crate = "crate")]
251+
#[derive(Deserialize, Debug, PartialEq, Eq)]
250252
struct Page {
251253
pub page: usize,
252254
}
253255

254256
let pager: Pager<Page> = Pager::from_callback(|continuation| async move {
255257
match continuation {
256258
None => Ok(PagerResult::Continue {
257-
response: Response::from_bytes(
259+
response: RawResponse::from_bytes(
258260
StatusCode::Ok,
259261
HashMap::from([(
260262
HeaderName::from_static("x-test-header"),
261263
HeaderValue::from_static("page-1"),
262264
)])
263265
.into(),
264266
r#"{"page":1}"#,
265-
),
267+
)
268+
.into(),
266269
continuation: "1",
267270
}),
268271
Some("1") => Err(typespec::Error::message(

sdk/core/azure_core/src/http/pipeline.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,14 @@ mod tests {
9090
headers::{self, HeaderName, Headers},
9191
policies::Policy,
9292
request::options::ClientRequestId,
93-
Context, Method, Request, Response, StatusCode, TransportOptions,
93+
Context, Method, Request, StatusCode, TransportOptions,
9494
},
9595
Bytes,
9696
};
9797
use azure_core_test::http::MockHttpClient;
9898
use futures::FutureExt as _;
9999
use std::sync::Arc;
100-
use typespec_client_core::http::ClientOptions;
100+
use typespec_client_core::http::{ClientOptions, RawResponse};
101101

102102
#[tokio::test]
103103
async fn pipeline_with_custom_client_request_id_policy() {
@@ -121,7 +121,7 @@ mod tests {
121121
"Custom header value should match the client request ID"
122122
);
123123

124-
Ok(Response::from_bytes(
124+
Ok(RawResponse::from_bytes(
125125
StatusCode::Ok,
126126
Headers::new(),
127127
Bytes::new(),
@@ -153,7 +153,7 @@ mod tests {
153153

154154
// Act
155155
pipeline
156-
.send::<()>(&ctx, &mut request)
156+
.send(&ctx, &mut request)
157157
.await
158158
.expect("Pipeline execution failed");
159159
}
@@ -178,7 +178,7 @@ mod tests {
178178
"Default header value should match the client request ID"
179179
);
180180

181-
Ok(Response::from_bytes(
181+
Ok(RawResponse::from_bytes(
182182
StatusCode::Ok,
183183
Headers::new(),
184184
Bytes::new(),
@@ -206,7 +206,7 @@ mod tests {
206206

207207
// Act
208208
pipeline
209-
.send::<()>(&ctx, &mut request)
209+
.send(&ctx, &mut request)
210210
.await
211211
.expect("Pipeline execution failed");
212212
}

sdk/core/azure_core/src/http/policies/bearer_token_policy.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ mod tests {
125125
http::{
126126
headers::{Headers, AUTHORIZATION},
127127
policies::Policy,
128-
Request, Response, StatusCode,
128+
Request, StatusCode,
129129
},
130130
Bytes, Result,
131131
};
@@ -138,7 +138,9 @@ mod tests {
138138
};
139139
use std::time::Duration;
140140
use time::OffsetDateTime;
141-
use typespec_client_core::http::{policies::TransportPolicy, Method, TransportOptions};
141+
use typespec_client_core::http::{
142+
policies::TransportPolicy, Method, RawResponse, TransportOptions,
143+
};
142144

143145
#[derive(Debug, Clone)]
144146
struct MockCredential {
@@ -217,7 +219,7 @@ mod tests {
217219

218220
assert_eq!(format!("Bearer {}", expected.token.secret()), authz);
219221

220-
Ok(Response::from_bytes(
222+
Ok(RawResponse::from_bytes(
221223
StatusCode::Ok,
222224
Headers::new(),
223225
Bytes::new(),

sdk/core/azure_core/src/http/policies/client_request_id.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ mod tests {
7878
use azure_core_test::http::MockHttpClient;
7979
use futures::FutureExt;
8080
use std::sync::Arc;
81-
use typespec_client_core::http::{policies::TransportPolicy, Response, TransportOptions};
81+
use typespec_client_core::http::{policies::TransportPolicy, RawResponse, TransportOptions};
8282

8383
#[tokio::test]
8484
async fn header_already_present() {
@@ -100,7 +100,7 @@ mod tests {
100100
"Header value should not change"
101101
);
102102

103-
Ok(Response::from_bytes(
103+
Ok(RawResponse::from_bytes(
104104
StatusCode::Ok,
105105
headers::Headers::new(),
106106
Bytes::new(),
@@ -133,7 +133,7 @@ mod tests {
133133
.expect("Header should be present");
134134
assert!(!header_value.is_empty(), "Header value should be generated");
135135

136-
Ok(Response::from_bytes(
136+
Ok(RawResponse::from_bytes(
137137
StatusCode::Ok,
138138
headers::Headers::new(),
139139
Bytes::new(),
@@ -174,7 +174,7 @@ mod tests {
174174
"Custom header value should not change"
175175
);
176176

177-
Ok(Response::from_bytes(
177+
Ok(RawResponse::from_bytes(
178178
StatusCode::Ok,
179179
headers::Headers::new(),
180180
Bytes::new(),
@@ -214,7 +214,7 @@ mod tests {
214214
"Header value should match the client request ID from the context"
215215
);
216216

217-
Ok(Response::from_bytes(
217+
Ok(RawResponse::from_bytes(
218218
StatusCode::Ok,
219219
headers::Headers::new(),
220220
Bytes::new(),

sdk/core/azure_core_test/src/http/clients.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33

44
use async_trait::async_trait;
55
use azure_core::{
6-
http::{request::Request, response::Response, HttpClient},
6+
http::{request::Request, HttpClient, RawResponse},
77
Result,
88
};
99
use futures::{future::BoxFuture, lock::Mutex};
1010
use std::fmt;
1111

12-
/// An [`HttpClient`] from which you can assert [`Request`]s and return mock [`Response`]s.
12+
/// An [`HttpClient`] from which you can assert [`Request`]s and return mock [`RawResponse`]s.
1313
///
1414
/// # Examples
1515
///
1616
/// ```
1717
/// use azure_core::{
18-
/// http::{headers::Headers, ClientOptions, Response, StatusCode, TransportOptions},
18+
/// http::{headers::Headers, ClientOptions, RawResponse, StatusCode, TransportOptions},
1919
/// Bytes,
2020
/// };
2121
/// use azure_core_test::http::MockHttpClient;
@@ -28,7 +28,7 @@ use std::fmt;
2828
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
2929
/// let mock_client = Arc::new(MockHttpClient::new(|req| async {
3030
/// assert_eq!(req.url().host_str(), Some("my-vault.vault.azure.net"));
31-
/// Ok(Response::from_bytes(
31+
/// Ok(RawResponse::from_bytes(
3232
/// StatusCode::Ok,
3333
/// Headers::new(),
3434
/// Bytes::from_static(br#"{"value":"secret"}"#),
@@ -54,7 +54,7 @@ pub struct MockHttpClient<C>(Mutex<C>);
5454

5555
impl<C> MockHttpClient<C>
5656
where
57-
C: FnMut(&Request) -> BoxFuture<'_, Result<Response>> + Send + Sync,
57+
C: FnMut(&Request) -> BoxFuture<'_, Result<RawResponse>> + Send + Sync,
5858
{
5959
/// Creates a new `MockHttpClient` using a capture.
6060
///
@@ -75,9 +75,9 @@ impl<C> fmt::Debug for MockHttpClient<C> {
7575
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
7676
impl<C> HttpClient for MockHttpClient<C>
7777
where
78-
C: FnMut(&Request) -> BoxFuture<'_, Result<Response>> + Send + Sync,
78+
C: FnMut(&Request) -> BoxFuture<'_, Result<RawResponse>> + Send + Sync,
7979
{
80-
async fn execute_request(&self, req: &Request) -> Result<Response> {
80+
async fn execute_request(&self, req: &Request) -> Result<RawResponse> {
8181
let mut client = self.0.lock().await;
8282
(client)(req).await
8383
}
@@ -109,7 +109,11 @@ mod tests {
109109
*count += 1;
110110
}
111111

112-
Ok(Response::from_bytes(StatusCode::Ok, Headers::new(), vec![]))
112+
Ok(RawResponse::from_bytes(
113+
StatusCode::Ok,
114+
Headers::new(),
115+
vec![],
116+
))
113117
}
114118
.boxed()
115119
})) as Arc<dyn HttpClient>;

0 commit comments

Comments
 (0)