cognite/api/request_builder/
builder.rs1use reqwest::header::{HeaderName, HeaderValue, ACCEPT, CONTENT_TYPE, USER_AGENT};
2
3use prost::Message;
4use serde::de::DeserializeOwned;
5use serde::Serialize;
6
7use crate::ApiClient;
8use crate::CondSend;
9use crate::CondSync;
10use crate::Error;
11use crate::SkipAuthentication;
12use reqwest::{IntoUrl, Response};
13
14use crate::Result;
15
16use super::{
17 JsonResponseHandler, NoResponseHandler, ProtoResponseHandler, RawResponseHandler,
18 ResponseHandler,
19};
20
21pub struct RequestBuilder<'a, T = ()> {
23 inner: reqwest_middleware::RequestBuilder,
24 client: &'a ApiClient,
25 output: T,
26}
27
28impl<'a> RequestBuilder<'a, ()> {
29 pub fn post(client: &'a ApiClient, url: impl IntoUrl) -> RequestBuilder<'a, ()> {
36 RequestBuilder {
37 inner: client.client().post(url),
38 client,
39 output: (),
40 }
41 }
42
43 pub fn get(client: &'a ApiClient, url: impl IntoUrl) -> RequestBuilder<'a, ()> {
50 RequestBuilder {
51 inner: client.client().get(url),
52 client,
53 output: (),
54 }
55 }
56
57 pub fn delete(client: &'a ApiClient, url: impl IntoUrl) -> RequestBuilder<'a, ()> {
64 RequestBuilder {
65 inner: client.client().delete(url),
66 client,
67 output: (),
68 }
69 }
70
71 pub fn put(client: &'a ApiClient, url: impl IntoUrl) -> RequestBuilder<'a, ()> {
78 RequestBuilder {
79 inner: client.client().put(url),
80 client,
81 output: (),
82 }
83 }
84}
85
86impl<'a, T> RequestBuilder<'a, T> {
87 pub fn header<K, V>(mut self, key: K, value: V) -> Self
94 where
95 HeaderName: TryFrom<K>,
96 <HeaderName as TryFrom<K>>::Error: Into<http::Error>,
97 HeaderValue: TryFrom<V>,
98 <HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
99 {
100 self.inner = self.inner.header(key, value);
101 self
102 }
103
104 pub fn query<Q: Serialize + ?Sized>(mut self, query: &Q) -> Self {
106 self.inner = self.inner.query(query);
107 self
108 }
109
110 pub fn json<B: Serialize + ?Sized>(mut self, body: &B) -> Result<Self> {
112 self.inner = self.inner.header(
113 CONTENT_TYPE,
114 const { HeaderValue::from_static("application/json") },
115 );
116 Ok(self.body(serde_json::to_vec(body)?))
117 }
118
119 pub fn body(mut self, body: impl Into<reqwest::Body>) -> Self {
121 self.inner = self.inner.body(body);
122 self
123 }
124
125 pub fn protobuf<B: Message>(mut self, body: &B) -> Self {
127 self.inner = self.inner.header(
128 CONTENT_TYPE,
129 const { HeaderValue::from_static("application/protobuf") },
130 );
131 self.body(body.encode_to_vec())
132 }
133
134 pub fn omit_auth_headers(mut self) -> Self {
138 self.inner = self.inner.with_extension(SkipAuthentication);
139 self
140 }
141
142 pub fn with_inner<
144 R: FnOnce(reqwest_middleware::RequestBuilder) -> reqwest_middleware::RequestBuilder,
145 >(
146 mut self,
147 m: R,
148 ) -> Self {
149 self.inner = m(self.inner);
150 self
151 }
152}
153
154impl<'a> RequestBuilder<'a, ()> {
155 pub fn accept_json<T: DeserializeOwned>(self) -> RequestBuilder<'a, JsonResponseHandler<T>> {
157 self.accept(JsonResponseHandler::new())
158 }
159
160 pub fn accept_protobuf<T: Message + Default + CondSend + CondSync>(
162 self,
163 ) -> RequestBuilder<'a, ProtoResponseHandler<T>> {
164 self.accept(ProtoResponseHandler::new())
165 }
166
167 pub fn accept_nothing(self) -> RequestBuilder<'a, NoResponseHandler> {
169 self.accept(NoResponseHandler)
170 }
171
172 pub fn accept_raw(self) -> RequestBuilder<'a, RawResponseHandler> {
174 self.accept(RawResponseHandler)
175 }
176
177 pub fn accept<T: ResponseHandler>(self, handler: T) -> RequestBuilder<'a, T> {
179 RequestBuilder {
180 inner: self
181 .inner
182 .header(ACCEPT, const { HeaderValue::from_static(T::ACCEPT_HEADER) }),
183 client: self.client,
184 output: handler,
185 }
186 }
187}
188
189async fn handle_error(response: Response) -> Error {
190 let request_id = response
191 .headers()
192 .get("x-request-id")
193 .and_then(|x| x.to_str().ok())
194 .map(|x| x.to_string());
195
196 let status = response.status();
197
198 match &response.text().await {
199 Ok(s) => match serde_json::from_str(s) {
200 Ok(error_message) => Error::new_from_cdf(status, error_message, request_id),
201 Err(e) => Error::new_without_json(status, format!("{e}. Raw: {s}"), request_id),
202 },
203 Err(e) => Error::new_without_json(status, e.to_string(), request_id),
204 }
205}
206
207const SDK_USER_AGENT: &str = concat!("CogniteSdkRust/", env!("CARGO_PKG_VERSION"));
208const SDK_VERSION: &str = concat!("rust-sdk-v", env!("CARGO_PKG_VERSION"));
209
210impl<T: ResponseHandler> RequestBuilder<'_, T> {
211 pub async fn send(mut self) -> Result<T::Output> {
214 self.inner.extensions().insert(self.client.client().clone());
215
216 self.inner = self
217 .inner
218 .header(
219 USER_AGENT,
220 const { HeaderValue::from_static(SDK_USER_AGENT) },
221 )
222 .header("x-cdp-sdk", const { HeaderValue::from_static(SDK_VERSION) })
223 .header(
224 "x-cdp-app",
225 HeaderValue::from_str(self.client.app_name()).expect("Invalid app name"),
226 );
227 if let Some(cdf_version) = self.client.api_version() {
228 self.inner = self.inner.header(
229 "cdf-version",
230 HeaderValue::from_str(cdf_version).expect("Invalid CDF version"),
231 );
232 }
233
234 match self.inner.send().await {
235 Ok(response) => {
236 if response.status().is_success() {
237 self.output.handle_response(response).await
238 } else {
239 Err(handle_error(response).await)
240 }
241 }
242 Err(e) => Err(e.into()),
243 }
244 }
245}