alibi_core/store/stateless/
transaction.rs1use crate::field_policy::FieldValues;
2use crate::store::stateless::{StatelessSchema, StatelessStore};
3use crate::store::{
4 AccountStore, BoxedTransactionValue, MemberStore, SessionStore, TeamStore, TransactionStore,
5 TransactionWork, UserStore, VerificationStore,
6};
7use crate::types::AddTeamMemberResult;
8use crate::user_validation::PreparedUserCreation;
9use crate::{
10 AccountView, AuthError, AuthResult, AuthTransaction, CreateAccount, CreateMember,
11 CreateSession, CreateUser, Member, SessionView, Team, UpdateAccount, UserView,
12 VerificationView,
13};
14use async_trait::async_trait;
15use chrono::{DateTime, Utc};
16
17fn merge<T: Clone + PartialEq>(
21 live: &mut indexmap::IndexMap<String, T>,
22 base: &indexmap::IndexMap<String, T>,
23 changed: indexmap::IndexMap<String, T>,
24) {
25 live.retain(|id, _| !base.contains_key(id) || changed.contains_key(id));
26 for (id, row) in changed {
27 if base.get(&id) == Some(&row) {
28 continue;
29 }
30 if live.contains_key(&id) || !base.contains_key(&id) {
33 _ = live.insert(id, row);
34 }
35 }
36}
37
38struct OrganizationTransaction<'a> {
39 live: &'a StatelessStore,
40 snapshot: StatelessStore,
41}
42
43#[async_trait]
44impl TransactionStore<StatelessSchema> for StatelessStore {
45 async fn transaction_boxed(
46 &self,
47 work: Box<TransactionWork<StatelessSchema>>,
48 ) -> AuthResult<BoxedTransactionValue> {
49 let base = self.organization_state()?.clone();
50 let tx = OrganizationTransaction {
51 live: self,
52 snapshot: Self {
53 organizations: std::sync::Mutex::new(base.clone()),
54 ..Self::with_find_many_limit(self.find_many_limit)
55 },
56 };
57 let result = work(&tx).await?;
58 let changed = tx
59 .snapshot
60 .organizations
61 .into_inner()
62 .map_err(|_| AuthError::internal("No-database organization state poisoned"))?;
63 let mut live = self.organization_state()?;
64 merge(
65 &mut live.organizations,
66 &base.organizations,
67 changed.organizations,
68 );
69 merge(&mut live.members, &base.members, changed.members);
70 merge(
71 &mut live.invitations,
72 &base.invitations,
73 changed.invitations,
74 );
75 merge(&mut live.teams, &base.teams, changed.teams);
76 merge(
77 &mut live.team_members,
78 &base.team_members,
79 changed.team_members,
80 );
81 merge(&mut live.roles, &base.roles, changed.roles);
82 Ok(result)
83 }
84}
85
86#[async_trait]
87impl AuthTransaction<StatelessSchema> for OrganizationTransaction<'_> {
88 async fn get_team(&self, org: &str, id: &str) -> AuthResult<Option<Team>> {
89 TeamStore::get_team(&self.snapshot, Some(org), id).await
90 }
91 async fn add_team_member(
92 &self,
93 team: &str,
94 user: &str,
95 maximum: Option<f64>,
96 ) -> AuthResult<AddTeamMemberResult> {
97 TeamStore::add_team_member(&self.snapshot, team, user, maximum).await
98 }
99 async fn create_member(&self, data: CreateMember) -> AuthResult<Member> {
100 MemberStore::create_member(&self.snapshot, data).await
101 }
102 async fn prepare_secondary_session_creation(
103 &self,
104 input: CreateSession,
105 persist: bool,
106 ) -> AuthResult<SessionView> {
107 SessionStore::<StatelessSchema>::prepare_secondary_session_creation(
108 self.live, input, persist,
109 )
110 .await
111 }
112 async fn prepare_secondary_session_update(
113 &self,
114 session: SessionView,
115 expires_at: Option<DateTime<Utc>>,
116 fields: FieldValues,
117 ) -> AuthResult<Option<(SessionView, FieldValues)>> {
118 SessionStore::<StatelessSchema>::prepare_secondary_session_update(
119 self.live, session, expires_at, fields,
120 )
121 .await
122 }
123 async fn complete_secondary_session_update(
124 &self,
125 session: SessionView,
126 expires_at: Option<DateTime<Utc>>,
127 fields: FieldValues,
128 persist: bool,
129 ) -> AuthResult<Option<SessionView>> {
130 SessionStore::<StatelessSchema>::complete_secondary_session_update(
131 self.live, session, expires_at, fields, persist,
132 )
133 .await
134 }
135 async fn create_user(&self, data: CreateUser) -> AuthResult<UserView> {
138 UserStore::<StatelessSchema>::create_user(self.live, data).await
139 }
140 async fn create_user_prepared(&self, data: PreparedUserCreation) -> AuthResult<UserView> {
141 UserStore::<StatelessSchema>::create_user_prepared(self.live, data).await
142 }
143 async fn create_account(&self, data: CreateAccount) -> AuthResult<AccountView> {
144 AccountStore::<StatelessSchema>::create_account(self.live, data).await
145 }
146 async fn create_session(&self, data: CreateSession) -> AuthResult<SessionView> {
147 SessionStore::<StatelessSchema>::create_session(self.live, data).await
148 }
149 async fn delete_user_sessions(&self, user_id: &str) -> AuthResult<()> {
150 SessionStore::<StatelessSchema>::delete_user_sessions(self.live, user_id).await
151 }
152 async fn update_account(&self, id: &str, update: UpdateAccount) -> AuthResult<AccountView> {
153 AccountStore::<StatelessSchema>::update_account(self.live, id, update).await
154 }
155 async fn consume_verification_snapshot(
156 &self,
157 identifier: &str,
158 ) -> AuthResult<Option<VerificationView>> {
159 VerificationStore::<StatelessSchema>::consume_verification_snapshot(self.live, identifier)
160 .await
161 }
162}