use tracing::debug;
use tracing::info;
use crate::io::remote::Remote;
use crate::io::storage::Storage;
use crate::manifest::Manifest;
use crate::paths::DomainPaths;
use crate::uri::ManifestUri;
use crate::uri::S3Uri;
use crate::Res;
async fn fetch_jsonl(remote: &impl Remote, manifest_uri: &ManifestUri) -> Res<Manifest> {
let s3_uri: S3Uri = manifest_uri.clone().into();
let contents = remote
.get_object_stream(&manifest_uri.origin, &s3_uri)
.await?;
Manifest::from_reader(contents.body.into_async_read()).await
}
pub async fn cache_remote_manifest(
paths: &DomainPaths,
storage: &(impl Storage + Sync),
remote: &impl Remote,
manifest_uri: &ManifestUri,
) -> Res<Manifest> {
info!("⏳ Caching remote manifest: {}", manifest_uri.display());
let cache_dir = paths.manifest_cache_dir(&manifest_uri.bucket);
let cache_path = cache_dir.join(&manifest_uri.hash);
let manifest_path = cache_path.clone();
if !storage.exists(&cache_path).await {
debug!("🔍 Manifest does not exist in cache, fetching from remote");
debug!(
"⏳ Fetching JSONL manifest {} from remote…",
manifest_uri.display()
);
let manifest = fetch_jsonl(remote, manifest_uri).await?;
debug!("✔️ Fetched JSONL manifest");
let jsonl_content = manifest.to_jsonlines();
storage
.write_file(&cache_path, jsonl_content.as_bytes())
.await?;
debug!("✔️ JSONL manifest written to {}", cache_path.display());
} else {
debug!("✔️ Manifest exists already in {}", cache_path.display());
}
info!("✔️ Manifest {} was written …", manifest_uri.display());
let manifest = Manifest::from_path(storage, &manifest_path).await?;
info!("✔️ … and, Successfully cached:\n{:?}", manifest.header);
Ok(manifest)
}
pub async fn browse_remote_manifest(
paths: &DomainPaths,
storage: &(impl Storage + Sync),
remote: &impl Remote,
manifest_uri: &ManifestUri,
) -> Res<Manifest> {
cache_remote_manifest(paths, storage, remote, manifest_uri).await
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
use std::str::FromStr;
use test_log::test;
use crate::fixtures;
use crate::io::remote::mocks::MockRemote;
use crate::io::storage::mocks::MockStorage;
#[test(tokio::test)]
async fn test_if_cached() -> Res {
let paths = DomainPaths::default();
let manifest_uri = ManifestUri {
bucket: "a".to_string(),
namespace: ("f", "b").into(),
hash: fixtures::manifest::JSONL_HASH.to_string(),
origin: None,
};
let cache_path = paths.manifest_cache(&manifest_uri.bucket, &manifest_uri.hash);
let jsonl = std::fs::read(fixtures::manifest::jsonl()?)?;
let storage = MockStorage::default();
storage.write_file(&cache_path, &jsonl).await?;
let remote = MockRemote::default();
let cached_manifest =
cache_remote_manifest(&paths, &storage, &remote, &manifest_uri).await?;
assert_eq!(
cached_manifest.header.user_meta,
Some(serde_json::Value::Null)
);
Ok(())
}
#[test(tokio::test)]
async fn test_if_cached_random_file() -> Res {
let paths = DomainPaths::default();
let manifest = ManifestUri {
bucket: "a".to_string(),
namespace: ("f", "b").into(),
hash: "invalid_hash".to_string(),
origin: None,
};
let cache_path = paths.manifest_cache(&manifest.bucket, &manifest.hash);
let storage = MockStorage::default();
storage.write_file(cache_path, &Vec::new()).await?;
let remote = MockRemote::default();
let cached_manifest = cache_remote_manifest(&paths, &storage, &remote, &manifest).await;
assert!(cached_manifest.is_err());
Ok(())
}
#[test(tokio::test)]
async fn test_caching_jsonl() -> Res {
let paths = DomainPaths::default();
let manifest = ManifestUri {
bucket: "a".to_string(),
namespace: ("f", "b").into(),
hash: fixtures::manifest::JSONL_HASH.to_string(),
origin: None,
};
let jsonl = std::fs::read(fixtures::manifest::jsonl()?)?;
let remote = MockRemote::default();
let remote_uri = S3Uri::from_str(&format!(
"s3://{}/.quilt/packages/{}",
manifest.bucket, manifest.hash
))?;
remote
.put_object(&manifest.origin, &remote_uri, jsonl.clone())
.await?;
let storage = MockStorage::default();
paths
.scaffold_for_caching(&storage, &manifest.bucket)
.await?;
let cached_manifest = cache_remote_manifest(&paths, &storage, &remote, &manifest).await?;
let cache_path = PathBuf::from(format!(
".quilt/packages/{}/{}",
manifest.bucket, manifest.hash
));
assert!(storage.exists(cache_path).await);
assert!(cached_manifest
.get_record(&PathBuf::from("README.md"))
.is_some());
Ok(())
}
}