airlab-lib 0.1.3

airlab backend
Documentation
use crate::ctx::Ctx;
use crate::model::ModelManager;
use crate::model::Result;
use crate::model::base::{self, DbBmc};
use modql::field::Fields;
use modql::filter::{FilterNodes, ListOptions, OpValsInt64};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;

impl PanelElementBmc {
    #[must_use]
    pub fn get_create_sql(drop_table: bool) -> String {
        let table = Self::TABLE;
        format!(
            r##"{}
create table if not exists "{table}" (
  id serial primary key,
  panel_id integer NOT NULL,
  conjugate_id integer NOT NULL,
  dilution_type smallint DEFAULT 0 NOT NULL,
  concentration real
);
ALTER TABLE ONLY panel_element
  ADD CONSTRAINT "UQ_panel_element_panel_id_and_conjugate_id" UNIQUE (panel_id, conjugate_id);
CREATE INDEX "IDX_panel_element_conjugate_id" ON panel_element USING btree (conjugate_id);
CREATE INDEX "IDX_panel_element_panel_id" ON panel_element USING btree (panel_id);
        "##,
            if drop_table {
                format!("drop table if exists {table};")
            } else {
                String::new()
            }
        )
    }
}

#[derive(Debug, Clone, Fields, FromRow, Serialize, Deserialize, Default)]
pub struct PanelElement {
    pub id: i32,

    #[serde(rename = "panelId")]
    pub panel_id: i32,
    #[serde(rename = "conjugateId")]
    pub conjugate_id: i32,
    #[serde(rename = "dilutionType")]
    pub dilution_type: i16,
    pub concentration: Option<f32>,
}

#[derive(Fields, Deserialize, Clone, Debug)]
pub struct PanelElementForCreate {
    #[serde(rename = "panelId")]
    pub panel_id: i32,
    #[serde(rename = "conjugateId")]
    pub conjugate_id: i32,
    #[serde(rename = "dilutionType")]
    pub dilution_type: i32,
    pub concentration: Option<f32>,
}

#[derive(Fields, Default, Deserialize, Debug)]
pub struct PanelElementForUpdate {
    #[serde(rename = "dilutionType")]
    pub dilution_type: i32,
    pub concentration: Option<f32>,
}

#[derive(FilterNodes, Deserialize, Default, Debug)]
pub struct PanelElementFilter {
    id: Option<OpValsInt64>,

    panel_id: Option<OpValsInt64>,
    conjugate_id: Option<OpValsInt64>,
}

pub struct PanelElementBmc;

impl DbBmc for PanelElementBmc {
    const TABLE: &'static str = "panel_element";
}

impl PanelElementBmc {
    pub async fn create(
        ctx: &Ctx,
        mm: &ModelManager,
        panel_element_c: PanelElementForCreate,
    ) -> Result<i32> {
        base::create::<Self, _>(ctx, mm, panel_element_c).await
    }
    pub async fn create_full(
        ctx: &Ctx,
        mm: &ModelManager,
        panel_element_c: PanelElement,
    ) -> Result<i32> {
        base::create::<Self, _>(ctx, mm, panel_element_c).await
    }

    pub async fn get(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<PanelElement> {
        base::get::<Self, _>(ctx, mm, id).await
    }

    pub async fn list(
        ctx: &Ctx,
        mm: &ModelManager,
        filters: Option<Vec<PanelElementFilter>>,
        list_options: Option<ListOptions>,
    ) -> Result<Vec<PanelElement>> {
        base::list::<Self, _, _>(ctx, mm, filters, list_options).await
    }

    pub async fn update(
        ctx: &Ctx,
        mm: &ModelManager,
        id: i32,
        panel_element_u: PanelElementForUpdate,
    ) -> Result<()> {
        base::update::<Self, _>(ctx, mm, id, panel_element_u).await
    }

    pub async fn delete(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<()> {
        base::delete::<Self>(ctx, mm, id).await
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::_dev_utils;
    use crate::model::Error;
    use anyhow::Result;
    use serde_json::json;

    #[ignore]
    #[tokio::test]
    async fn test_panel_element_create_ok() -> Result<()> {
        let mm = ModelManager::new().await?;
        let ctx = Ctx::root_ctx();
        let _fx_name = "test_create_ok name";

        let panel_element_c = PanelElementForCreate {
            panel_id: 1815,
            conjugate_id: 4292,
            dilution_type: 1,
            concentration: None,
        };
        let id = PanelElementBmc::create(&ctx, &mm, panel_element_c).await?;

        let panel_element = PanelElementBmc::get(&ctx, &mm, id).await?;
        assert_eq!(panel_element.id, 1);

        PanelElementBmc::delete(&ctx, &mm, id).await?;

        Ok(())
    }

    #[ignore]
    #[tokio::test]
    async fn test_panel_element_get_err_not_found() -> Result<()> {
        let mm = ModelManager::new().await?;
        let ctx = Ctx::root_ctx();
        let fx_id = 100;

        let res = PanelElementBmc::get(&ctx, &mm, fx_id).await;

        assert!(
            matches!(
                res,
                Err(Error::EntityNotFound {
                    entity: "panel_element",
                    id: 100
                })
            ),
            "EntityNotFound not matching"
        );

        Ok(())
    }

    #[ignore]
    #[tokio::test]
    async fn test_panel_element_list_all_ok() -> Result<()> {
        let mm = ModelManager::new().await?;
        let ctx = Ctx::root_ctx();
        let tname = "test_panel_element_list_all_ok";
        let seeds = _dev_utils::get_panel_element_seed(tname);
        _dev_utils::seed_panel_elements(&ctx, &mm, &seeds).await?;

        let panel_elements = PanelElementBmc::list(&ctx, &mm, None, None).await?;

        let panel_elements: Vec<PanelElement> =
            panel_elements.into_iter().filter(|t| t.id == 1).collect();
        assert_eq!(panel_elements.len(), 4, "number of seeded panel_elements.");

        if false {
            for panel_element in panel_elements.iter() {
                PanelElementBmc::delete(&ctx, &mm, panel_element.id).await?;
            }
        }

        Ok(())
    }

    #[ignore]
    #[tokio::test]
    async fn test_panel_element_list_by_filter_ok() -> Result<()> {
        let mm = ModelManager::new().await?;
        let ctx = Ctx::root_ctx();
        let tname = "test_panel_element_list_all_ok";
        let seeds = _dev_utils::get_panel_element_seed(tname);
        _dev_utils::seed_panel_elements(&ctx, &mm, &seeds).await?;

        let filters: Vec<PanelElementFilter> = serde_json::from_value(json!([
            {
                "name": {
                    "$endsWith": ".a",
                    "$containsAny": ["01", "02"]
                }
            },
            {
                "name": {"$contains": "03"}
            }
        ]))?;
        let list_options = serde_json::from_value(json!({
            "order_bys": "!id"
        }))?;
        let panel_elements =
            PanelElementBmc::list(&ctx, &mm, Some(filters), Some(list_options)).await?;

        assert_eq!(panel_elements.len(), 3);
        assert!(panel_elements[0].id == 3);

        if false {
            let panel_elements = PanelElementBmc::list(
                &ctx,
                &mm,
                Some(serde_json::from_value(json!([{
                    "name": {"$startsWith": "test_list_by_filter_ok"}
                }]))?),
                None,
            )
            .await?;
            assert_eq!(panel_elements.len(), 5);
            for panel_element in panel_elements.iter() {
                PanelElementBmc::delete(&ctx, &mm, panel_element.id).await?;
            }
        }

        Ok(())
    }

    #[ignore]
    #[tokio::test]
    async fn test_panel_element_update_ok() -> Result<()> {
        let mm = ModelManager::new().await?;
        let ctx = Ctx::root_ctx();
        let tname = "test_panel_element_list_all_ok";
        let seeds = _dev_utils::get_panel_element_seed(tname);
        let fx_panel_element = _dev_utils::seed_panel_elements(&ctx, &mm, &seeds)
            .await?
            .remove(0);

        PanelElementBmc::update(
            &ctx,
            &mm,
            fx_panel_element.id,
            PanelElementForUpdate {
                ..Default::default()
            },
        )
        .await?;

        let panel_element = PanelElementBmc::get(&ctx, &mm, fx_panel_element.id).await?;
        assert_eq!(panel_element.id, 1);

        Ok(())
    }

    #[ignore]
    #[tokio::test]
    async fn test_panel_element_delete_err_not_found() -> Result<()> {
        let mm = ModelManager::new().await?;
        let ctx = Ctx::root_ctx();
        let fx_id = 100;

        let res = PanelElementBmc::delete(&ctx, &mm, fx_id).await;

        assert!(
            matches!(
                res,
                Err(Error::EntityNotFound {
                    entity: "panel_element",
                    id: 100
                })
            ),
            "EntityNotFound not matching"
        );

        Ok(())
    }
}