eio_okta_client/
client.rs1use crate::MapInto;
2use crate::Options;
3use eio_okta_api::authorization::SSWS;
4use eio_okta_api::traits::Endpoint;
5use eio_okta_api::traits::IntoRequest;
6use eio_okta_api::traits::Pagination;
7use eio_okta_api::traits::Response;
8use eio_okta_api::traits::Service;
9use headers::Authorization;
10use headers::HeaderMapExt;
11use http::Request;
12use http::Uri;
13
14impl From<Options> for Client<crate::OktaService> {
15 fn from(options: Options) -> Self {
16 let Options {
17 agent,
18 authorization,
19 auto_paginate,
20 service,
21 } = options;
22
23 Self {
24 agent: agent.into(),
25 authorization,
26 auto_paginate,
27 service,
28 }
29 }
30}
31
32pub struct Client<S>
33where
34 S: Service,
35{
36 pub agent: ureq::Agent,
37 pub authorization: Authorization<SSWS>,
38 pub auto_paginate: bool,
39 pub service: S,
40}
41
42use crate::Error;
43
44impl<S> Client<S>
45where
46 S: Service,
47{
48 pub fn uri<T>(&self, endpoint: &T) -> Result<Uri, Error>
49 where
50 T: Endpoint,
51 {
52 let base = self.service.uri()?;
53 match base.authority_str() {
54 Some(authority) => Uri::builder().authority(authority),
55 None => Uri::builder(),
56 }
57 .scheme(base.scheme_str())
58 .path_and_query(endpoint.uri()?.as_str())
59 .build()
60 .map_into()
61 }
62
63 pub fn request<T>(&self, endpoint: T) -> Result<Request<<T as IntoRequest>::Body>, Error>
64 where
65 T: Endpoint,
66 {
67 let uri = self.uri(&endpoint)?;
68 let mut request = Request::builder().version(T::VERSION).method(T::METHOD).uri(uri);
69
70 if let Some(headers) = request.headers_mut() {
71 headers.typed_insert(self.authorization.clone());
72 headers.typed_insert(endpoint.accept());
73 headers.typed_insert(endpoint.content_type());
74 }
75
76 let body = endpoint.as_ref().clone();
77 request.body(body).map_into()
78 }
79
80 pub fn callable<T>(&self, endpoint: T) -> Result<(ureq::Request, <T as IntoRequest>::Body), Error>
81 where
82 T: Endpoint,
83 {
84 let request = self.request(endpoint)?;
85 let (parts, body) = request.into_parts();
86 let request = ureq::Request::from(parts);
87 Ok((request, body))
88 }
89
90 pub fn call<T>(&self, endpoint: T) -> Result<<T as Response>::Body, Error>
91 where
92 T: Endpoint,
93 {
94 let (request, _body) = self.callable(endpoint)?;
95 let response = request.call()?;
96 let body = if response.status() == 204 {
97 serde_json::from_str("null")?
98 } else {
99 response.into_json()?
100 };
101
102 Ok(body)
103 }
104
105 pub fn paginate<T>(&self, endpoint: T) -> Result<Vec<<T as Pagination>::Item>, Error>
106 where
107 T: Endpoint + Pagination,
108 {
109 let (original_request, _body) = self.callable(endpoint)?;
110 let mut request = original_request.clone();
111 let mut items = Vec::new();
112
113 let mut done = false;
114
115 while !done {
116 eprintln!("request: {}", request.url());
117 let response = request.clone().call()?;
118
119 match response
120 .all(T::HEADER.as_str())
121 .into_iter()
122 .flat_map(parse_link_header::parse_with_rel)
123 .filter_map(|mut rel| rel.remove(T::REL))
124 .find_map(|mut link| link.queries.remove(T::QUERY))
125 {
126 Some(value) => request = original_request.clone().query(T::QUERY, &value),
127 None => done = true,
128 }
129
130 let batch = response.into_json::<Vec<_>>()?;
131 items.extend_from_slice(&batch);
132 }
133
134 Ok(items)
135 }
136
137 pub fn autopaginate_json<T>(&self, endpoint: T, pretty: bool) -> Result<String, Error>
138 where
139 T: Endpoint + Pagination,
140 {
141 let value = if self.auto_paginate {
142 self.paginate(endpoint).map(serde_json::to_value)?
143 } else {
144 self.call(endpoint).map(serde_json::to_value)?
145 }?;
146
147 let json = if pretty {
148 serde_json::to_string_pretty(&value)?
149 } else {
150 serde_json::to_string(&value)?
151 };
152
153 Ok(json)
154 }
155
156 pub fn json<T>(&self, endpoint: T, pretty: bool) -> Result<String, Error>
157 where
158 T: Endpoint,
159 {
160 let value = self.call(endpoint).map(serde_json::to_value)??;
161
162 let json = if pretty {
163 serde_json::to_string_pretty(&value)?
164 } else {
165 serde_json::to_string(&value)?
166 };
167
168 Ok(json)
169 }
170}
171
172