Skip to main content

headless_lms_server/domain/credit_registration_phases/
config_validation.rs

1//! The `config-validation` phase: the daily pass over every Suotar-enabled module's configuration.
2//!
3//! Database-only. Everything Suotar could say about a configuration has already been recorded by
4//! the phases that call it — a listing answered `courseCodeNotFound`, a product token that never
5//! resolved — so this reads their traces rather than spending a call of its own.
6
7use headless_lms_models::course_module_suotar_configurations::{
8    get_config_facts_for_enabled_modules, record_config_check,
9};
10use headless_lms_models::credit_registration_phase_state::PhaseRunOutcome;
11use headless_lms_models::library::credit_registration::config_validation::check_module_config;
12
13use super::{PhaseContext, PhaseScope};
14
15pub async fn run(ctx: &PhaseContext<'_>, scope: &PhaseScope) -> anyhow::Result<PhaseRunOutcome> {
16    let mut conn = ctx.pool.acquire().await?;
17    let modules = get_config_facts_for_enabled_modules(&mut conn, scope.course_id).await?;
18    let mut with_problems = 0;
19    for module in &modules {
20        let check = check_module_config(module);
21        if check.message.is_some() {
22            with_problems += 1;
23        }
24        record_config_check(&mut conn, module.course_module_id, &check).await?;
25    }
26    if with_problems > 0 {
27        info!("{with_problems} Suotar-enabled course modules have configuration problems.");
28    }
29
30    Ok(PhaseRunOutcome {
31        items_processed: i32::try_from(modules.len()).unwrap_or(i32::MAX),
32        // A misconfigured module is a finding, not a failed iteration: the phase did its job.
33        items_failed: 0,
34        error: None,
35    })
36}