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