alibi_core/store/stateless/
organizations.rs1use crate::store::OrganizationStore;
2use crate::store::stateless::StatelessStore;
3use crate::{AuthError, AuthResult, CreateOrganization, Organization, UpdateOrganization};
4use async_trait::async_trait;
5use chrono::Utc;
6
7fn patch(row: &mut Organization, update: UpdateOrganization) {
8 if let Some(name) = update.name {
9 row.name = name;
10 }
11 if let Some(slug) = update.slug {
12 row.slug = slug;
13 }
14 if let Some(logo) = update.logo {
15 row.logo = logo;
16 }
17 if let Some(metadata) = update.metadata {
18 row.metadata = Some(metadata);
19 }
20}
21
22#[async_trait]
23impl OrganizationStore for StatelessStore {
24 async fn create_organization(&self, data: CreateOrganization) -> AuthResult<Organization> {
25 let now = Utc::now();
26 let row = Organization {
27 additional_fields: std::collections::BTreeMap::default(),
28 id: data.id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
29 name: data.name,
30 slug: data.slug,
31 logo: data.logo,
32 metadata: data.metadata,
33 created_at: now,
34 updated_at: now,
35 };
36 _ = self
37 .organization_state()?
38 .organizations
39 .insert(row.id.clone(), row.clone());
40 Ok(row)
41 }
42 async fn get_organization_by_id(&self, id: &str) -> AuthResult<Option<Organization>> {
43 Ok(self.organization_state()?.organizations.get(id).cloned())
44 }
45 async fn get_organization_by_slug(&self, slug: &str) -> AuthResult<Option<Organization>> {
46 Ok(self
47 .organization_state()?
48 .organizations
49 .values()
50 .find(|row| row.slug == slug)
51 .cloned())
52 }
53 async fn list_organizations_by_ids(&self, ids: &[String]) -> AuthResult<Vec<Organization>> {
54 Ok(self
55 .organization_state()?
56 .organizations
57 .values()
58 .filter(|row| ids.contains(&row.id))
59 .cloned()
60 .collect())
61 }
62 async fn update_organization(
63 &self,
64 id: &str,
65 update: UpdateOrganization,
66 ) -> AuthResult<Organization> {
67 self.update_organization_if_present(id, update)
68 .await?
69 .ok_or_else(|| AuthError::not_found("Organization not found"))
70 }
71 async fn patch_organization_if_present(
72 &self,
73 id: &str,
74 update: UpdateOrganization,
75 ) -> AuthResult<Option<Organization>> {
76 let mut state = self.organization_state()?;
77 let Some(row) = state.organizations.get_mut(id) else {
78 return Ok(None);
79 };
80 patch(row, update);
82 Ok(Some(row.clone()))
83 }
84 async fn update_organization_if_present(
85 &self,
86 id: &str,
87 update: UpdateOrganization,
88 ) -> AuthResult<Option<Organization>> {
89 let mut state = self.organization_state()?;
90 let Some(row) = state.organizations.get_mut(id) else {
91 return Ok(None);
92 };
93 patch(row, update);
94 row.updated_at = Utc::now();
95 Ok(Some(row.clone()))
96 }
97 async fn delete_organization(&self, id: &str) -> AuthResult<()> {
98 let mut state = self.organization_state()?;
99 state.members.retain(|_, row| row.organization_id != id);
100 state.invitations.retain(|_, row| row.organization_id != id);
101 _ = state.organizations.shift_remove(id);
102 Ok(())
103 }
104 async fn list_user_organizations(&self, user_id: &str) -> AuthResult<Vec<Organization>> {
105 let state = self.organization_state()?;
106 Ok(state
107 .members
108 .values()
109 .filter(|row| row.user_id == user_id)
110 .take(self.find_many_limit)
111 .filter_map(|row| state.organizations.get(&row.organization_id).cloned())
112 .collect())
113 }
114}