comdirect_rest_api/
session.rs1use std::future::Future;
2use std::sync::Arc;
3use std::time::Duration;
4
5use crate::application::session_runtime::{
6 RefreshTokenCallback, SessionRuntime, SessionRuntimeConfig, StateChangeCallback,
7};
8use crate::auth::{LoginChallenge, TanAction};
9use crate::error::Result;
10use crate::types::SessionStateType;
11
12const DEFAULT_BASE_URL: &str = "https://api.comdirect.de";
13
14#[derive(Debug, Clone)]
37pub struct SessionConfig {
38 client_id: String,
39 client_secret: String,
40 username: String,
41 password: String,
42 base_url: String,
43 request_timeout: Duration,
44 refresh_buffer: Duration,
45}
46
47impl SessionConfig {
48 pub fn new(
50 client_id: impl Into<String>,
51 client_secret: impl Into<String>,
52 username: impl Into<String>,
53 password: impl Into<String>,
54 ) -> Self {
55 Self {
56 client_id: client_id.into(),
57 client_secret: client_secret.into(),
58 username: username.into(),
59 password: password.into(),
60 base_url: DEFAULT_BASE_URL.to_string(),
61 request_timeout: Duration::from_secs(30),
62 refresh_buffer: Duration::from_secs(60),
63 }
64 }
65
66 pub fn with_base_url(mut self, base_url: impl Into<String>) -> Self {
68 self.base_url = base_url.into();
69 self
70 }
71
72 pub fn with_request_timeout(mut self, request_timeout: Duration) -> Self {
74 self.request_timeout = request_timeout;
75 self
76 }
77
78 pub fn with_refresh_buffer(mut self, refresh_buffer: Duration) -> Self {
80 self.refresh_buffer = refresh_buffer;
81 self
82 }
83}
84
85#[derive(Clone)]
111pub struct Session {
112 username: String,
113 runtime: Arc<SessionRuntime>,
114}
115
116impl Session {
117 pub fn new(
119 client_id: impl Into<String>,
120 client_secret: impl Into<String>,
121 username: impl Into<String>,
122 password: impl Into<String>,
123 ) -> Result<Self> {
124 let config = SessionConfig::new(client_id, client_secret, username, password);
125 Self::from_config(config)
126 }
127
128 pub fn from_config(config: SessionConfig) -> Result<Self> {
130 let username = config.username.clone();
131 let runtime = Arc::new(SessionRuntime::new(SessionRuntimeConfig {
132 base_url: config.base_url,
133 client_id: config.client_id,
134 client_secret: config.client_secret,
135 username: config.username,
136 password: config.password,
137 request_timeout: config.request_timeout,
138 refresh_buffer_secs: config.refresh_buffer.as_secs(),
139 })?);
140
141 Ok(Self { username, runtime })
142 }
143
144 pub fn accounts(&self) -> crate::accounts::AccountsApi {
146 crate::accounts::AccountsApi::new(self.clone())
147 }
148
149 pub fn brokerage(&self) -> crate::brokerage::BrokerageApi {
151 crate::brokerage::BrokerageApi::new(self.clone())
152 }
153
154 pub async fn set_state_change_callback<F>(&self, callback: F)
156 where
157 F: Fn(SessionStateType, SessionStateType) + Send + Sync + 'static,
158 {
159 self.runtime
160 .set_state_change_callback(Some(Arc::new(callback) as Arc<StateChangeCallback>))
161 .await;
162 }
163
164 pub async fn set_refresh_token_callback<F>(&self, callback: F)
166 where
167 F: Fn(String) + Send + Sync + 'static,
168 {
169 self.runtime
170 .set_refresh_token_callback(Some(Arc::new(callback) as Arc<RefreshTokenCallback>))
171 .await;
172 }
173
174 pub async fn state(&self) -> SessionStateType {
176 self.runtime.state().await
177 }
178
179 pub async fn try_restore(&self, refresh_token: &str) -> Result<()> {
184 self.runtime.login_try(refresh_token).await
185 }
186
187 pub async fn login<F, Fut>(&self, callback: F) -> Result<()>
193 where
194 F: Fn(LoginChallenge) -> Fut + Send + Sync,
195 Fut: Future<Output = TanAction> + Send,
196 {
197 self.runtime.login(callback).await
198 }
199
200 pub async fn shutdown(&self) {
205 self.runtime.shutdown().await;
206 }
207
208 #[cfg(feature = "web")]
209 pub async fn login_web(self: Arc<Self>, router: axum::Router) -> axum::Router {
211 crate::web::mount_session_login_route(router, "/comdirect", self.username.clone(), self)
212 .await
213 }
214
215 pub(crate) async fn get_authorized_resource(
216 &self,
217 operation: &'static str,
218 api_path: &str,
219 ) -> Result<String> {
220 self.runtime
221 .get_authorized_resource(operation, api_path)
222 .await
223 }
224}