bios-basic 0.2.0

An embeddable message queue system
Documentation
use async_trait::async_trait;
use tardis::{
    basic::{dto::TardisContext, result::TardisResult},
    db::sea_orm::{sea_query::*, EntityName, Set},
    TardisFunsInst,
};

use crate::{
    rbum::{
        domain::{rbum_cert, rbum_domain, rbum_item, rbum_kind},
        dto::{
            rbum_cert_dto::{RbumCertAddReq, RbumCertModifyReq},
            rbum_filer_dto::{RbumBasicFilterReq, RbumCertFilterReq, RbumItemRelFilterReq, RbumRelFilterReq},
            rbum_item_dto::{RbumItemKernelAddReq, RbumItemKernelModifyReq},
            rbum_rel_agg_dto::{RbumRelAggAddReq, RbumRelAttrAggAddReq, RbumRelEnvAggAddReq},
            rbum_rel_dto::{RbumRelAddReq, RbumRelSimpleFindReq},
        },
        rbum_enumeration::{RbumCertRelKind, RbumCertStatusKind, RbumRelFromKind, RbumScopeLevelKind},
        serv::{
            rbum_cert_serv::RbumCertServ, rbum_crud_serv::RbumCrudOperation, rbum_domain_serv::RbumDomainServ, rbum_item_serv::RbumItemCrudOperation, rbum_rel_serv::RbumRelServ,
        },
    },
    spi::{
        domain::spi_bs,
        dto::spi_bs_dto::{SpiBsAddReq, SpiBsCertResp, SpiBsDetailResp, SpiBsFilterReq, SpiBsModifyReq, SpiBsSummaryResp},
        spi_constants::{SPI_CERT_KIND, SPI_IDENT_REL_TAG},
    },
};

pub struct SpiBsServ;

#[async_trait]
impl RbumItemCrudOperation<spi_bs::ActiveModel, SpiBsAddReq, SpiBsModifyReq, SpiBsSummaryResp, SpiBsSummaryResp, SpiBsFilterReq> for SpiBsServ {
    fn get_ext_table_name() -> &'static str {
        spi_bs::Entity.table_name()
    }

    fn get_rbum_kind_id() -> Option<String> {
        None
    }

    fn get_rbum_domain_id() -> Option<String> {
        None
    }

    async fn package_item_add(add_req: &SpiBsAddReq, funs: &TardisFunsInst, _: &TardisContext) -> TardisResult<RbumItemKernelAddReq> {
        let domain_id = RbumDomainServ::get_rbum_domain_id_by_code(funs.module_code(), funs)
            .await?
            .ok_or_else(|| funs.err().not_found(&Self::get_obj_name(), "add_bs", "not found domain", "404-rbum-*-obj-not-exist"))?;
        Ok(RbumItemKernelAddReq {
            name: add_req.name.clone(),
            rel_rbum_kind_id: Some(add_req.kind_id.to_string()),
            rel_rbum_domain_id: Some(domain_id),
            disabled: add_req.disabled,
            // SPI backend service is a global scope, and the permission is determined by its own binding relationship
            //
            // SPI后端服务都为全局作用域,通过自身的绑定关系判定权限
            scope_level: Some(RbumScopeLevelKind::Root),
            ..Default::default()
        })
    }

    async fn package_ext_add(id: &str, add_req: &SpiBsAddReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<spi_bs::ActiveModel> {
        Ok(spi_bs::ActiveModel {
            id: Set(id.to_string()),
            private: Set(add_req.private),
            ..Default::default()
        })
    }

    async fn after_add_item(id: &str, add_req: &mut SpiBsAddReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
        // Add certificate information
        // 添加认证信息
        RbumCertServ::add_rbum(
            &mut RbumCertAddReq {
                ak: add_req.ak.clone(),
                sk: Some(add_req.sk.clone()),
                sk_invisible: Some(false),
                kind: Some(SPI_CERT_KIND.to_string()),
                supplier: Some(id.to_string()),
                conn_uri: Some(add_req.conn_uri.clone()),
                ext: Some(add_req.ext.clone()),
                rel_rbum_kind: RbumCertRelKind::Item,
                rel_rbum_id: id.to_string(),
                status: RbumCertStatusKind::Enabled,
                vcode: None,
                rel_rbum_cert_conf_id: None,
                start_time: None,
                end_time: None,
                is_outside: false,
                ignore_check_sk: false,
            },
            funs,
            ctx,
        )
        .await?;
        Ok(())
    }

    async fn package_item_modify(_: &str, modify_req: &SpiBsModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<RbumItemKernelModifyReq>> {
        if modify_req.name.is_none() && modify_req.disabled.is_none() {
            return Ok(None);
        }
        Ok(Some(RbumItemKernelModifyReq {
            code: None,
            name: modify_req.name.clone(),
            scope_level: None,
            disabled: modify_req.disabled,
        }))
    }

    async fn package_ext_modify(id: &str, modify_req: &SpiBsModifyReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<Option<spi_bs::ActiveModel>> {
        if modify_req.private.is_none() {
            return Ok(None);
        }
        let mut spi_bs = spi_bs::ActiveModel {
            id: Set(id.to_string()),
            ..Default::default()
        };
        if let Some(private) = modify_req.private {
            spi_bs.private = Set(private);
        }
        Ok(Some(spi_bs))
    }

    async fn after_modify_item(id: &str, modify_req: &mut SpiBsModifyReq, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
        if let Some(cert) = RbumCertServ::find_one_rbum(
            &RbumCertFilterReq {
                kind: Some(SPI_CERT_KIND.to_string()),
                suppliers: Some(vec![id.to_string()]),
                rel_rbum_kind: Some(RbumCertRelKind::Item),
                rel_rbum_id: Some(id.to_string()),
                ..Default::default()
            },
            funs,
            ctx,
        )
        .await?
        {
            RbumCertServ::modify_rbum(
                &cert.id,
                &mut RbumCertModifyReq {
                    ak: modify_req.ak.clone(),
                    sk: modify_req.sk.clone(),
                    ignore_check_sk: false,
                    conn_uri: modify_req.conn_uri.clone(),
                    ext: modify_req.ext.clone(),
                    status: None,
                    start_time: None,
                    end_time: None,
                    sk_invisible: None,
                },
                funs,
                ctx,
            )
            .await?;
        }
        Ok(())
    }

    async fn package_ext_query(query: &mut SelectStatement, _: bool, filter: &SpiBsFilterReq, _: &TardisFunsInst, _: &TardisContext) -> TardisResult<()> {
        query
            .column((spi_bs::Entity, spi_bs::Column::Private))
            .expr_as(Expr::col((rbum_kind::Entity, rbum_kind::Column::Id)), Alias::new("kind_id"))
            .expr_as(Expr::col((rbum_kind::Entity, rbum_kind::Column::Code)), Alias::new("kind_code"))
            .expr_as(Expr::col((rbum_kind::Entity, rbum_kind::Column::Name)), Alias::new("kind_name"))
            .column((rbum_cert::Entity, rbum_cert::Column::ConnUri))
            .column((rbum_cert::Entity, rbum_cert::Column::Ak))
            .column((rbum_cert::Entity, rbum_cert::Column::Sk))
            .column((rbum_cert::Entity, rbum_cert::Column::SkInvisible))
            .column((rbum_cert::Entity, rbum_cert::Column::Ext))
            .left_join(
                rbum_kind::Entity,
                Expr::col((rbum_kind::Entity, rbum_kind::Column::Id)).equals((rbum_item::Entity, rbum_item::Column::RelRbumKindId)),
            )
            .left_join(
                rbum_cert::Entity,
                Condition::all()
                    .add(Expr::col((rbum_cert::Entity, rbum_cert::Column::Kind)).eq(SPI_CERT_KIND))
                    .add(Expr::col((rbum_cert::Entity, rbum_cert::Column::RelRbumKind)).eq(RbumCertRelKind::Item.to_int()))
                    .add(Expr::col((rbum_cert::Entity, rbum_cert::Column::Supplier)).equals((spi_bs::Entity, spi_bs::Column::Id)))
                    .add(Expr::col((rbum_cert::Entity, rbum_cert::Column::RelRbumId)).equals((spi_bs::Entity, spi_bs::Column::Id))),
            );
        if let Some(private) = filter.private {
            query.and_where(Expr::col((spi_bs::Entity, spi_bs::Column::Private)).eq(private));
        }
        if let Some(kind_code) = &filter.kind_code {
            query.and_where(Expr::col((rbum_kind::Entity, rbum_kind::Column::Code)).eq(kind_code.to_string()));
        }
        if let Some(kind_codes) = &filter.kind_codes {
            if kind_codes.len() == 1 {
                query.and_where(Expr::col((rbum_kind::Entity, rbum_kind::Column::Code)).eq(kind_codes.first().expect("ignore").to_string()));
            } else if !kind_codes.is_empty() {
                query.and_where(Expr::col((rbum_kind::Entity, rbum_kind::Column::Code)).is_in(kind_codes));
            }
        }
        if let Some(kind_id) = &filter.kind_id {
            query.and_where(Expr::col((rbum_kind::Entity, rbum_kind::Column::Id)).eq(kind_id.to_string()));
        }
        if let Some(domain_code) = &filter.domain_code {
            query.left_join(
                rbum_domain::Entity,
                Expr::col((rbum_domain::Entity, rbum_domain::Column::Id)).equals((rbum_item::Entity, rbum_item::Column::RelRbumDomainId)),
            );
            query.and_where(Expr::col((rbum_domain::Entity, rbum_domain::Column::Code)).eq(domain_code.to_string()));
        }
        Ok(())
    }
}

impl SpiBsServ {
    pub async fn get_bs(id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SpiBsDetailResp> {
        let bs = Self::peek_item(id, &SpiBsFilterReq::default(), funs, ctx).await?;
        let app_tenant_ids = RbumRelServ::find_rbums(
            &RbumRelFilterReq {
                tag: Some(SPI_IDENT_REL_TAG.to_string()),
                from_rbum_kind: Some(RbumRelFromKind::Item),
                from_rbum_id: Some(id.to_string()),
                ..Default::default()
            },
            None,
            None,
            funs,
            ctx,
        )
        .await?
        .into_iter()
        .map(|rel| rel.to_rbum_item_id)
        .collect::<Vec<String>>();
        Ok(SpiBsDetailResp {
            id: bs.id,
            name: bs.name,
            kind_id: bs.kind_id,
            kind_code: bs.kind_code,
            kind_name: bs.kind_name,
            conn_uri: bs.conn_uri,
            ak: bs.ak,
            sk: bs.sk,
            ext: bs.ext,
            private: bs.private,
            disabled: bs.disabled,
            create_time: bs.create_time,
            update_time: bs.update_time,
            rel_app_tenant_ids: app_tenant_ids,
        })
    }

    pub async fn add_rel(bs_id: &str, app_tenant_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
        Self::add_rel_agg(bs_id, app_tenant_id, None, None, funs, ctx).await
    }

    pub async fn add_rel_agg(
        bs_id: &str,
        app_tenant_id: &str,
        attrs: Option<Vec<RbumRelAttrAggAddReq>>,
        envs: Option<Vec<RbumRelEnvAggAddReq>>,
        funs: &TardisFunsInst,
        ctx: &TardisContext,
    ) -> TardisResult<()> {
        if !RbumRelServ::check_simple_rel(
            &RbumRelSimpleFindReq {
                tag: Some(SPI_IDENT_REL_TAG.to_string()),
                from_rbum_kind: Some(RbumRelFromKind::Item),
                from_rbum_id: Some(bs_id.to_string()),
                to_rbum_item_id: Some(app_tenant_id.to_string()),
                ..Default::default()
            },
            funs,
            ctx,
        )
        .await?
        {
            let attrs = attrs.unwrap_or_default();
            let envs = envs.unwrap_or_default();
            RbumRelServ::add_rel(
                &mut RbumRelAggAddReq {
                    rel: RbumRelAddReq {
                        tag: SPI_IDENT_REL_TAG.to_string(),
                        note: None,
                        from_rbum_kind: RbumRelFromKind::Item,
                        from_rbum_id: bs_id.to_string(),
                        to_rbum_item_id: app_tenant_id.to_string(),
                        to_own_paths: ctx.own_paths.to_string(),
                        to_is_outside: true,
                        ext: None,
                    },
                    attrs,
                    envs,
                },
                funs,
                ctx,
            )
            .await?;
        }
        Ok(())
    }

    pub async fn delete_rel(bs_id: &str, app_tenant_id: &str, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<()> {
        let ids = RbumRelServ::find_id_rbums(
            &RbumRelFilterReq {
                tag: Some(SPI_IDENT_REL_TAG.to_string()),
                from_rbum_kind: Some(RbumRelFromKind::Item),
                from_rbum_id: Some(bs_id.to_string()),
                to_rbum_item_id: Some(app_tenant_id.to_string()),
                ..Default::default()
            },
            None,
            None,
            funs,
            ctx,
        )
        .await?;
        for id in ids {
            RbumRelServ::delete_rel_with_ext(&id, funs, ctx).await?;
        }
        Ok(())
    }

    pub async fn get_bs_by_rel(app_tenant_id: &str, kind_code: Option<String>, funs: &TardisFunsInst, ctx: &TardisContext) -> TardisResult<SpiBsCertResp> {
        let bs = Self::find_one_item(
            &SpiBsFilterReq {
                basic: RbumBasicFilterReq {
                    enabled: Some(true),
                    ..Default::default()
                },
                rel: Some(RbumItemRelFilterReq {
                    rel_by_from: true,
                    tag: Some(SPI_IDENT_REL_TAG.to_string()),
                    from_rbum_kind: Some(RbumRelFromKind::Item),
                    rel_item_id: Some(app_tenant_id.to_string()),
                    ..Default::default()
                }),
                kind_code,
                domain_code: Some(funs.module_code().to_string()),
                ..Default::default()
            },
            funs,
            ctx,
        )
        .await?
        .ok_or_else(|| funs.err().not_found(&Self::get_obj_name(), "get_bs_by_rel", "not found backend service", "404-spi-bs-not-exist"))?;
        Ok(SpiBsCertResp {
            kind_code: bs.kind_code,
            conn_uri: bs.conn_uri,
            ak: bs.ak,
            sk: bs.sk,
            ext: bs.ext,
            private: bs.private,
        })
    }
}