use crate::cache_file::ManagedReadBudget;
use crate::{
cache::CacheRefreshAttemptStatus,
snapshot_cache::{
SnapshotRefreshAttempt, SnapshotRefreshProgress, read_snapshot_refresh_attempt_strict,
validate_snapshot_refresh_attempt,
},
sns::report::{
SNS_CACHE_COMPONENT, SnsHostError, SnsRefreshAttemptStatus,
cache_paths::sns_governance_from_cache_path, cache_storage::SnsCacheMetadata,
neurons_cache::SnsRefreshContext,
},
};
use std::path::Path;
pub(in crate::sns::report) type SnsRefreshAttempt = SnapshotRefreshAttempt<SnsCacheMetadata>;
pub(in crate::sns::report) fn write_starting_sns_refresh_attempt(
context: SnsRefreshContext<'_>,
) -> Result<(), SnsHostError> {
write_sns_refresh_attempt_status(
context,
CacheRefreshAttemptStatus::Running,
SnapshotRefreshProgress::default(),
None,
)
}
pub(in crate::sns::report) fn write_running_sns_refresh_page(
context: SnsRefreshContext<'_>,
page_count: u32,
row_count: usize,
last_cursor: Option<String>,
) -> Result<(), SnsHostError> {
write_sns_refresh_attempt_status(
context,
CacheRefreshAttemptStatus::Running,
SnapshotRefreshProgress::new(page_count, row_count, last_cursor),
None,
)
}
pub(in crate::sns::report) fn write_complete_sns_refresh_attempt(
context: SnsRefreshContext<'_>,
progress: SnapshotRefreshProgress,
) -> Result<(), SnsHostError> {
write_sns_refresh_attempt_status(context, CacheRefreshAttemptStatus::Complete, progress, None)
}
pub(in crate::sns::report) fn write_failed_sns_refresh_attempt(
context: SnsRefreshContext<'_>,
error: &SnsHostError,
) {
let latest = read_sns_refresh_attempt(
&context.request.cache_root,
&context.paths.refresh_attempt_path,
&context.request.target.network,
None,
)
.ok()
.flatten();
let progress = latest.map_or_else(
SnapshotRefreshProgress::default,
SnapshotRefreshProgress::from,
);
let _ = write_sns_refresh_attempt_status(
context,
CacheRefreshAttemptStatus::Failed,
progress,
Some(error.to_string()),
);
}
fn write_sns_refresh_attempt_status(
context: SnsRefreshContext<'_>,
status: CacheRefreshAttemptStatus,
progress: SnapshotRefreshProgress,
last_error: Option<String>,
) -> Result<(), SnsHostError> {
SnsRefreshAttempt::new(
&context.request.target.network,
&context.request.target.source_endpoint,
context.fetch_request.fetched_at.clone(),
SnsCacheMetadata {
governance_canister_id: context.governance_canister_id.to_string(),
},
context.request.page_size,
)
.write(
&context.request.cache_root,
&context.paths.refresh_attempt_path,
SNS_CACHE_COMPONENT,
status,
progress,
last_error,
)
.map_err(SnsHostError::from)
}
fn validate_sns_refresh_attempt(
path: &Path,
expected_network: &str,
attempt: &SnapshotRefreshAttempt<SnsCacheMetadata>,
) -> Result<(), SnsHostError> {
let invalid = |reason| SnsHostError::InvalidRefreshAttempt {
path: path.to_path_buf(),
reason,
};
validate_snapshot_refresh_attempt(attempt, expected_network).map_err(invalid)?;
let expected_governance = sns_governance_from_cache_path(path)
.and_then(|name| name.to_str())
.ok_or_else(|| {
invalid("attempt path does not contain a Governance identity".to_string())
})?;
if attempt.metadata.governance_canister_id != expected_governance {
return Err(invalid(format!(
"governance_canister_id is {}, expected {expected_governance}",
attempt.metadata.governance_canister_id
)));
}
super::target::parse_governance_canister_input(expected_governance)
.map_err(|error| invalid(error.to_string()))?;
Ok(())
}
fn read_sns_refresh_attempt(
cache_root: &Path,
path: &Path,
expected_network: &str,
budget: Option<&mut ManagedReadBudget>,
) -> Result<Option<SnsRefreshAttempt>, SnsHostError> {
read_snapshot_refresh_attempt_strict::<SnsRefreshAttempt>(cache_root, path, budget)
.map_err(|error| {
error.into_error(SNS_CACHE_COMPONENT, SnsHostError::from, |path, reason| {
SnsHostError::InvalidRefreshAttempt { path, reason }
})
})?
.map(|attempt| {
validate_sns_refresh_attempt(path, expected_network, &attempt)?;
Ok(attempt)
})
.transpose()
}
pub(in crate::sns::report) fn read_sns_refresh_attempt_status_strict(
cache_root: &Path,
path: &Path,
expected_network: &str,
budget: Option<&mut ManagedReadBudget>,
) -> Result<Option<SnsRefreshAttemptStatus>, SnsHostError> {
Ok(
read_sns_refresh_attempt(cache_root, path, expected_network, budget)?
.map(SnsRefreshAttemptStatus::from_validated),
)
}