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        };
35        _ = self
36            .organization_state()?
37            .organizations
38            .insert(row.id.clone(), row.clone());
39        Ok(row)
40    }
41    async fn get_organization_by_id(&self, id: &str) -> AuthResult<Option<Organization>> {
42        Ok(self.organization_state()?.organizations.get(id).cloned())
43    }
44    async fn get_organization_by_slug(&self, slug: &str) -> AuthResult<Option<Organization>> {
45        Ok(self
46            .organization_state()?
47            .organizations
48            .values()
49            .find(|row| row.slug == slug)
50            .cloned())
51    }
52    async fn list_organizations_by_ids(&self, ids: &[String]) -> AuthResult<Vec<Organization>> {
53        Ok(self
54            .organization_state()?
55            .organizations
56            .values()
57            .filter(|row| ids.contains(&row.id))
58            .cloned()
59            .collect())
60    }
61    async fn update_organization(
62        &self,
63        id: &str,
64        update: UpdateOrganization,
65    ) -> AuthResult<Organization> {
66        self.update_organization_if_present(id, update)
67            .await?
68            .ok_or_else(|| AuthError::not_found("Organization not found"))
69    }
70    async fn patch_organization_if_present(
71        &self,
72        id: &str,
73        update: UpdateOrganization,
74    ) -> AuthResult<Option<Organization>> {
75        let mut state = self.organization_state()?;
76        let Some(row) = state.organizations.get_mut(id) else {
77            return Ok(None);
78        };
79        // Memory adapter Object.assign accepts an empty patch. No synthesized timestamp.
80        patch(row, update);
81        Ok(Some(row.clone()))
82    }
83    async fn update_organization_if_present(
84        &self,
85        id: &str,
86        update: UpdateOrganization,
87    ) -> AuthResult<Option<Organization>> {
88        let mut state = self.organization_state()?;
89        let Some(row) = state.organizations.get_mut(id) else {
90            return Ok(None);
91        };
92        patch(row, update);
93        Ok(Some(row.clone()))
94    }
95    async fn delete_organization(&self, id: &str) -> AuthResult<()> {
96        let mut state = self.organization_state()?;
97        state.members.retain(|_, row| row.organization_id != id);
98        state.invitations.retain(|_, row| row.organization_id != id);
99        _ = state.organizations.shift_remove(id);
100        Ok(())
101    }
102    async fn list_user_organizations(&self, user_id: &str) -> AuthResult<Vec<Organization>> {
103        let state = self.organization_state()?;
104        Ok(state
105            .members
106            .values()
107            .filter(|row| row.user_id == user_id)
108            .take(self.find_many_limit)
109            .filter_map(|row| state.organizations.get(&row.organization_id).cloned())
110            .collect())
111    }
112}