Skip to content

Commit

Permalink
feat(ic-asset-certification): add function to delete all assets
Browse files Browse the repository at this point in the history
  • Loading branch information
nathanosdev committed Nov 18, 2024
1 parent ac8f90b commit fee7b9d
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check-commit-messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ jobs:
pip install -U Commitizen
- name: Check Commit Messages
run: cz check --rev-range c8ecbc19b8c4a482e55907d37554d66f2f2f9a8f..HEAD
run: cz check --rev-range ac8f90b7c45e13e240d6c01a43d191b99f1aec4a..HEAD
10 changes: 10 additions & 0 deletions packages/ic-asset-certification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,16 @@ use ic_cdk::api::set_certified_data;
set_certified_data(&asset_router.root_hash());
```

It's also possible to delete all assets and their certification in one go:

```rust
# use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRouter, AssetRedirectKind, AssetEncoding};

# let mut asset_router = AssetRouter::default();

asset_router.delete_all_assets();
```

## Querying assets

The `AssetRouter` has two functions to retrieve an `AssetMap` containing assets.
Expand Down
40 changes: 36 additions & 4 deletions packages/ic-asset-certification/src/asset_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ impl<'content> AssetRouter<'content> {
///
/// If no configuration matches an individual asset, the asset will be
/// served and certified as-is, without headers.
///
/// After performing this operation, the canister's certified variable will need to be updated
/// with the new [root hash](AssetRouter::root_hash) of the tree.
pub fn certify_assets<'path>(
&mut self,
assets: impl IntoIterator<Item = Asset<'content, 'path>>,
Expand Down Expand Up @@ -320,6 +323,9 @@ impl<'content> AssetRouter<'content> {
/// Depending on the configuration provided to the [certify_assets](AssetRouter::certify_assets) function,
/// multiple responses may be generated for the same asset. To ensure that all generated responses are deleted,
/// this function accepts the same configuration.
///
/// After performing this operation, the canister's certified variable will need to be updated
/// with the new [root hash](AssetRouter::root_hash) of the tree.
pub fn delete_assets<'path>(
&mut self,
assets: impl IntoIterator<Item = Asset<'content, 'path>>,
Expand Down Expand Up @@ -368,6 +374,16 @@ impl<'content> AssetRouter<'content> {
Ok(())
}

/// Deletes all assets from the router, including any certification for those assets.
///
/// After performing this operation, the canister's certified variable will need to be updated
/// with the new [root hash](AssetRouter::root_hash) of the tree.
pub fn delete_all_assets(&mut self) {
self.responses.clear();
self.fallback_responses.clear();
self.tree.borrow_mut().clear();
}

/// Returns the root hash of the underlying
/// [HttpCertificationTree](ic_http_certification::HttpCertificationTree).
pub fn root_hash(&self) -> Hash {
Expand Down Expand Up @@ -2689,6 +2705,25 @@ mod tests {
));
}

#[rstest]
fn test_delete_all_assets() {
let mut asset_router = asset_router();
asset_router.delete_all_assets();

assert_matches!(
asset_router.serve_asset(
&data_certificate(),
&HttpRequest::get("/index.html").build(),
),
Err(AssetCertificationError::NoAssetMatchingRequestUrl {
request_url,
}) if request_url == "/index.html"
);

let assets: Vec<_> = asset_router.get_assets().iter().collect();
assert!(assets.len() == 0);
}

#[rstest]
fn test_asset_map(mut asset_router: AssetRouter) {
let index_html_response = asset_router.get_assets().get("/index.html", None, None);
Expand Down Expand Up @@ -2993,8 +3028,6 @@ mod tests {
let assets: Vec<_> = asset_router.get_assets().iter().collect();
assert!(assets.len() == 3);

println!("{:#?}", assets);

let first_chunk_body = &full_body[0..ASSET_CHUNK_SIZE];
let expected_first_chunk_response = build_206_response(
first_chunk_body.to_vec(),
Expand All @@ -3012,7 +3045,6 @@ mod tests {
],
);

println!("expected first chunk: {:#?}", expected_first_chunk_response);
assert!(assets.contains(&(
(&format!("/{}", TWO_CHUNKS_ASSET_NAME), None, Some(0)),
&expected_first_chunk_response
Expand Down Expand Up @@ -3694,7 +3726,7 @@ mod tests {
}

#[fixture]
fn asset_router() -> AssetRouter<'static> {
fn asset_router<'a>() -> AssetRouter<'a> {
let mut asset_router = AssetRouter::default();

let assets = vec![
Expand Down
10 changes: 10 additions & 0 deletions packages/ic-asset-certification/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,16 @@
//! set_certified_data(&asset_router.root_hash());
//! ```
//!
//! It's also possible to delete all assets and their certification in one go:
//!
//! ```rust
//! # use ic_asset_certification::{Asset, AssetConfig, AssetFallbackConfig, AssetRouter, AssetRedirectKind, AssetEncoding};
//!
//! # let mut asset_router = AssetRouter::default();
//!
//! asset_router.delete_all_assets();
//! ```
//!
//! ## Querying assets
//!
//! The [AssetRouter] has two functions to retrieve an [AssetMap] containing assets.
Expand Down
10 changes: 7 additions & 3 deletions packages/ic-certification/src/nested_rb_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl<K: NestedTreeKeyRequirements, V: NestedTreeValueRequirements> NestedTree<K,
if let Some(key) = path.first() {
match self {
NestedTree::Leaf(_) => {
*self = NestedTree::default();
self.clear();
self.insert(path, value);
}
NestedTree::Nested(tree) => {
Expand All @@ -110,7 +110,7 @@ impl<K: NestedTreeKeyRequirements, V: NestedTreeValueRequirements> NestedTree<K,
}
}
} else {
*self = NestedTree::Leaf(value);
self.clear();
}
}

Expand All @@ -137,10 +137,14 @@ impl<K: NestedTreeKeyRequirements, V: NestedTreeValueRequirements> NestedTree<K,
}
}
} else {
*self = NestedTree::default();
self.clear();
}
}

pub fn clear(&mut self) {
*self = NestedTree::default();
}

pub fn witness(&self, path: &[K]) -> HashTree {
if let Some(key) = path.first() {
match self {
Expand Down
7 changes: 7 additions & 0 deletions packages/ic-http-certification/src/tree/certification_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ impl HttpCertificationTree {
self.tree.delete(&tree_path);
}

/// Clears the tree of all [HttpCertificationTreeEntry].
/// After performing this operation, the canister's certified variable will need to be updated
/// with the new [root hash](HttpCertificationTree::root_hash) of the tree.
pub fn clear(&mut self) {
self.tree.clear();
}

/// Returns a pruned [HashTree] that will prove the presence of a given [HttpCertificationTreeEntry]
/// in the full [HttpCertificationTree], without needing to return the full tree.
///
Expand Down

0 comments on commit fee7b9d

Please sign in to comment.