alibi-seaorm 0.11.0

SeaORM integration for Alibi
Documentation
use super::entities::member;
use super::entities::organization_role::{self, Column, Entity};
use super::{SeaOrmStore, map_db_err};
use crate::schema::AuthSchema;
use alibi_core::error::{AuthError, AuthResult};
use alibi_core::store::OrganizationRoleStore;
use alibi_core::types::{
    CreateOrganizationRole, OrganizationRole, OrganizationRoleSelector, UpdateOrganizationRole,
};
use async_trait::async_trait;
use chrono::Utc;
use sea_orm::sea_query::Expr;
use sea_orm::{
    ActiveModelTrait, ColumnTrait, Condition, EntityTrait, PaginatorTrait, QueryFilter,
    QuerySelect, Set,
};
use uuid::Uuid;

#[async_trait]
impl<S: AuthSchema> OrganizationRoleStore for SeaOrmStore<S> {
    async fn create_organization_role(
        &self,
        data: CreateOrganizationRole,
    ) -> AuthResult<OrganizationRole> {
        organization_role::ActiveModel {
            id: Set(self
                .generated_id(&self.db, "organizationRole", "organizationRole", "id")
                .await?
                .unwrap_or_else(|| Uuid::new_v4().to_string())),
            organization_id: Set(data.organization_id),
            role: Set(data.role),
            permission: Set(serde_json::to_string(&data.permission)?),
            created_at: Set(Utc::now()),
            updated_at: Set(None),
        }
        .insert(self.scoped_connection())
        .await
        .map_err(map_db_err)?
        .try_into()
    }
    async fn get_organization_role(
        &self,
        organization_id: &str,
        selector: &OrganizationRoleSelector,
    ) -> AuthResult<Option<OrganizationRole>> {
        scoped(organization_id, selector)
            .one(self.scoped_connection())
            .await
            .map_err(map_db_err)?
            .map(TryInto::try_into)
            .transpose()
    }
    async fn list_organization_roles(
        &self,
        organization_id: &str,
    ) -> AuthResult<Vec<OrganizationRole>> {
        Entity::find()
            .filter(Column::OrganizationId.eq(organization_id))
            .limit(self.config().advanced.database.default_find_many_limit as u64)
            .all(self.scoped_connection())
            .await
            .map_err(map_db_err)?
            .into_iter()
            .map(TryInto::try_into)
            .collect()
    }
    async fn count_organization_roles(&self, organization_id: &str) -> AuthResult<usize> {
        let count = Entity::find()
            .filter(Column::OrganizationId.eq(organization_id))
            .count(self.scoped_connection())
            .await
            .map_err(map_db_err)?;
        usize::try_from(count)
            .map_err(|_error| AuthError::internal("Organization role count overflow"))
    }
    async fn has_organization_role_members(
        &self,
        organization_id: &str,
        role: &str,
    ) -> AuthResult<bool> {
        let members = member::Entity::find()
            .filter(member::Column::OrganizationId.eq(organization_id))
            .filter(member::Column::Role.contains(role))
            .limit(self.config().advanced.database.default_find_many_limit as u64)
            .all(self.scoped_connection())
            .await
            .map_err(map_db_err)?;
        Ok(members.iter().any(|member| {
            member
                .role
                .split(',')
                .map(str::trim)
                .any(|name| name == role)
        }))
    }
    async fn update_organization_role(
        &self,
        organization_id: &str,
        selector: &OrganizationRoleSelector,
        update: UpdateOrganizationRole,
    ) -> AuthResult<OrganizationRole> {
        let mut row = scoped(organization_id, selector)
            .one(self.scoped_connection())
            .await
            .map_err(map_db_err)?
            .ok_or_else(|| AuthError::bad_request("Role not found"))?;
        let updated_at = Utc::now();
        let mut query = Entity::update_many()
            .filter(scope(organization_id, selector))
            .col_expr(Column::UpdatedAt, Expr::value(Some(updated_at)));
        if let Some(role) = update.role {
            query = query.col_expr(Column::Role, Expr::value(role.clone()));
            row.role = role;
        }
        if let Some(permission) = update.permission {
            let permission = serde_json::to_string(&permission)?;
            query = query.col_expr(Column::Permission, Expr::value(permission.clone()));
            row.permission = permission;
        }
        _ = query
            .exec(self.scoped_connection())
            .await
            .map_err(map_db_err)?;
        row.updated_at = Some(updated_at);
        row.try_into()
    }
    async fn delete_organization_role(
        &self,
        organization_id: &str,
        selector: &OrganizationRoleSelector,
    ) -> AuthResult<bool> {
        Entity::delete_many()
            .filter(scope(organization_id, selector))
            .exec(self.scoped_connection())
            .await
            .map(|r| r.rows_affected > 0)
            .map_err(map_db_err)
    }
}

fn scope(organization_id: &str, selector: &OrganizationRoleSelector) -> Condition {
    let predicate = Condition::all().add(Column::OrganizationId.eq(organization_id));
    match selector {
        OrganizationRoleSelector::Id(id) => predicate.add(Column::Id.eq(id)),
        OrganizationRoleSelector::Name(name) => predicate.add(Column::Role.eq(name)),
    }
}

fn scoped(organization_id: &str, selector: &OrganizationRoleSelector) -> sea_orm::Select<Entity> {
    Entity::find().filter(scope(organization_id, selector))
}