Skip to main content

sofie_db/children/
through.rs

1use sofie_core::{Error, Result, SqlType, SqlValue, Uuid};
2
3use crate::aggregate::{table, uuid_equals};
4use crate::condition::Kind;
5use crate::{
6    Aggregate, Column, Condition, Context, Dialect, ExpectedRows, Id, Query, Read, Source, Step,
7};
8
9#[must_use]
10pub fn owned_through<A: Aggregate, P: Aggregate>(
11    parent: Column<A, Id<P>>,
12    owner: Uuid,
13) -> Option<Condition<A>> {
14    let owned_parents = P::owned_by(owner)?;
15    Some(crate::in_select(parent, P::ID, owned_parents))
16}
17
18pub(crate) fn ownership_check<A: Aggregate>(id: Uuid, owner: Uuid, dialect: &dyn Dialect) -> Step {
19    let table = table::<A>();
20    let mut params = Vec::new();
21    let mut sql = format!("SELECT 1 FROM {} WHERE ", table.qualified(dialect));
22    match A::owned_by(owner) {
23        Some(condition) => condition.kind.render_into(dialect, &mut params, &mut sql),
24        None => sql.push_str("(1 = 0)"),
25    }
26    sql.push_str(" AND ");
27    sql.push_str(&crate::sql::equals(
28        dialect,
29        "id",
30        SqlValue::Uuid(id),
31        &mut params,
32    ));
33    Step::Statement {
34        sql,
35        params,
36        expect_rows: Some(ExpectedRows::or_not_found(1)),
37    }
38}
39
40/// A child reached under another parent, as in `/orders/{order}/lines/{line}`, is not found.
41pub(crate) fn within_parents<S: Source, C: Context>(mut query: Query<S>, context: &C) -> Query<S> {
42    for field in S::Base::FIELDS {
43        let [column] = field.columns else { continue };
44        if column.ty != SqlType::Uuid || field.name == S::Base::NAME {
45            continue;
46        }
47        if let Ok(parent) = context.bound(field.name) {
48            let column = crate::sql::column_name(field.name, column);
49            query = query.where_(Condition::new(uuid_equals(&column, parent)));
50        }
51    }
52    query
53}
54
55/// A list under a parent the read cannot reach, as in `/wish-lists/{wish_list}/notes` with
56/// another principal's list, is not found: an empty list would say the parent exists.
57pub async fn refuse_parent_beyond_reach<A: Aggregate>(
58    read: &Read,
59    field: &str,
60    parent: Uuid,
61) -> Result<()> {
62    let Some(owner) = read.owner() else {
63        return Ok(());
64    };
65    if A::OWNER != Some(field) {
66        return Ok(());
67    }
68    let Some(Kind::InSelect(owned_parents)) = A::owned_by(owner).map(|owned| owned.kind) else {
69        return Ok(());
70    };
71    let mut params = Vec::new();
72    let mut sql = "SELECT 1 WHERE ".to_owned();
73    owned_parents.render_holding(
74        &SqlValue::Uuid(parent),
75        read.dialect(),
76        &mut params,
77        &mut sql,
78    );
79    let found = read
80        .decoded(&sql, &params, 1, vec![None], |_| Ok(()))
81        .await?;
82    if found.is_empty() {
83        return Err(Error::NotFound);
84    }
85    Ok(())
86}