Skip to main content

alibi_core/store/stateless/
organizations.rs

1use 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        // Memory adapter Object.assign accepts an empty patch. No synthesized timestamp.
81        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}