|
| 1 | +//! A person browses a target's publications only with view on every current node, like its jobs. |
| 2 | +
|
| 3 | +use std::collections::BTreeMap; |
| 4 | + |
| 5 | +use chrono::{DateTime, Utc}; |
| 6 | +use uuid::Uuid; |
| 7 | + |
| 8 | +use crate::authorization::main::authorize_listing::authorize_listing; |
| 9 | +use crate::authorization::main::person_assigns_roles::person_assigns_roles; |
| 10 | +use crate::credentials::models::{AuditContext, DefaultRole}; |
| 11 | +use crate::snapshots::models::{PublishedChange, PublishedSnapshot}; |
| 12 | +use crate::store::errors::ControlError; |
| 13 | +use crate::store::main::{begin::begin, commit::commit, decode_row::decode_row, query::query}; |
| 14 | +use crate::store::models::{ControlStore, Parameter, TransactionMode}; |
| 15 | +use crate::store::types::Row; |
| 16 | + |
| 17 | +/// Newest first, at most 100 publications; changed roles are named only for people who assign roles. |
| 18 | +pub async fn list_person_snapshots( |
| 19 | + store: &ControlStore, |
| 20 | + (context, credentials): (&AuditContext, Uuid), |
| 21 | + ((location_id, target), limit): ((Uuid, &str), u32), |
| 22 | + default_role: DefaultRole, |
| 23 | +) -> Result<Vec<PublishedSnapshot>, ControlError> { |
| 24 | + let (transaction, _) = authorize_listing( |
| 25 | + begin(store, TransactionMode::Write).await?, |
| 26 | + (context, credentials), |
| 27 | + (location_id, target), |
| 28 | + default_role, |
| 29 | + ) |
| 30 | + .await?; |
| 31 | + let scope = vec![ |
| 32 | + Parameter::Id(location_id), |
| 33 | + Parameter::Text(target.into()), |
| 34 | + Parameter::Integer(i64::from(limit.min(100))), |
| 35 | + ]; |
| 36 | + let (transaction, published) = query(transaction, |
| 37 | + "SELECT snapshot_id, generation, git_revision, CASE WHEN git_dirty THEN 1 ELSE 0 END AS git_dirty, git_author, environment_hash, sqb_version, published_at \ |
| 38 | + FROM sqb_snapshots WHERE location_id=$1 AND target=$2 ORDER BY generation DESC LIMIT $3", |
| 39 | + scope.clone()).await?; |
| 40 | + let (transaction, changed) = query(transaction, |
| 41 | + "SELECT c.snapshot_id, c.change_kind, c.subject_key FROM sqb_snapshot_changes c \ |
| 42 | + WHERE c.snapshot_id IN (SELECT s.snapshot_id FROM sqb_snapshots s WHERE s.location_id=$1 AND s.target=$2 \ |
| 43 | + ORDER BY s.generation DESC LIMIT $3) ORDER BY c.snapshot_id, c.change_seq", |
| 44 | + scope).await?; |
| 45 | + let (transaction, names_roles) = person_assigns_roles(transaction, credentials).await?; |
| 46 | + commit(transaction).await?; |
| 47 | + let mut changes: BTreeMap<Uuid, Vec<PublishedChange>> = BTreeMap::new(); |
| 48 | + for row in changed { |
| 49 | + let snapshot_id = id_of(&row)?; |
| 50 | + let mut change: PublishedChange = decode_row(row)?; |
| 51 | + if !names_roles && change.subject_key.starts_with("role:") { |
| 52 | + change.subject_key = "role:".into(); |
| 53 | + } |
| 54 | + changes.entry(snapshot_id).or_default().push(change); |
| 55 | + } |
| 56 | + published |
| 57 | + .into_iter() |
| 58 | + .map(|row| { |
| 59 | + let mut snapshot = snapshot_of(row)?; |
| 60 | + snapshot.changes = changes.remove(&snapshot.snapshot_id).unwrap_or_default(); |
| 61 | + Ok(snapshot) |
| 62 | + }) |
| 63 | + .collect() |
| 64 | +} |
| 65 | + |
| 66 | +fn snapshot_of(row: Row) -> Result<PublishedSnapshot, ControlError> { |
| 67 | + let snapshot_id = id_of(&row)?; |
| 68 | + let text = |key: &str| row[key].as_str().map(String::from); |
| 69 | + let published_at: DateTime<Utc> = serde_json::from_value(row["published_at"].clone()) |
| 70 | + .map_err(|error| ControlError::Invalid(format!("stored publication time: {error}")))?; |
| 71 | + Ok(PublishedSnapshot { |
| 72 | + snapshot_id, |
| 73 | + generation: row["generation"].as_i64().unwrap_or_default(), |
| 74 | + git_revision: text("git_revision"), |
| 75 | + git_dirty: row["git_dirty"].as_i64() == Some(1), |
| 76 | + git_author: text("git_author"), |
| 77 | + environment_hash: text("environment_hash").unwrap_or_default(), |
| 78 | + sqb_version: text("sqb_version").unwrap_or_default(), |
| 79 | + published_at, |
| 80 | + changes: Vec::new(), |
| 81 | + }) |
| 82 | +} |
| 83 | + |
| 84 | +fn id_of(row: &Row) -> Result<Uuid, ControlError> { |
| 85 | + serde_json::from_value(row["snapshot_id"].clone()) |
| 86 | + .map_err(|error| ControlError::Invalid(format!("stored snapshot id: {error}"))) |
| 87 | +} |
0 commit comments