junobuild_satellite/storage/
handlers.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use crate::controllers::store::get_controllers;
use crate::storage::state::{get_asset, get_config, get_rule, insert_asset, insert_asset_encoding};
use ic_cdk::id;
use junobuild_collections::assert_stores::assert_permission;
use junobuild_collections::types::rules::Rule;
use junobuild_shared::types::core::Blob;
use junobuild_shared::types::state::Controllers;
use junobuild_storage::constants::ASSET_ENCODING_NO_COMPRESSION;
use junobuild_storage::http::types::HeaderField;
use junobuild_storage::msg::SET_NOT_ALLOWED;
use junobuild_storage::runtime::update_certified_asset as update_runtime_certified_asset;
use junobuild_storage::types::store::{Asset, AssetKey};
use junobuild_storage::utils::{create_empty_asset, map_content_encoding};

/// Handles the setting of an asset within the store. This function performs
/// various checks and operations to ensure the asset can be set and updated
/// correctly.
///
/// # Parameters
/// - `key`: A reference to the `AssetKey` representing the unique identifier
///   for the asset within the collection.
/// - `content`: A reference to the `String` containing the asset content to be
///   stored.
/// - `headers`: A slice of `HeaderField` representing any additional headers
///   associated with the asset.
///
/// # Returns
/// - `Result<(), String>`: Returns `Ok(())` if the asset is successfully set.
///   Returns an `Err(String)` with an error message if the operation fails.
///
/// # Errors
/// - Returns an error if the asset cannot be retrieved from the storage.
/// - Returns an error if the permission check fails when the asset is
///   identified as existing and private.
///
/// # Important Note
/// The content is set for the identity encoding, meaning there is no
/// compression applied.
pub fn set_asset_handler(
    key: &AssetKey,
    content: &Blob,
    headers: &[HeaderField],
) -> Result<(), String> {
    let rule = get_rule(&key.collection)?;

    let existing_asset = get_asset(&key.collection, &key.full_path, &rule);

    if let Some(ref existing_asset) = existing_asset {
        let controllers: Controllers = get_controllers();
        // The handler is used in Serverless Functions therefore the caller is itself.
        // This allows to assert for permission. Useful for collection set as "Private".
        let caller = id();

        if !assert_permission(&rule.write, existing_asset.key.owner, caller, &controllers) {
            return Err(SET_NOT_ALLOWED.to_string());
        }
    }

    set_asset_handler_impl(key, &existing_asset, content, headers, &rule)
}

fn set_asset_handler_impl(
    key: &AssetKey,
    existing_asset: &Option<Asset>,
    content: &Blob,
    headers: &[HeaderField],
    rule: &Rule,
) -> Result<(), String> {
    let mut asset = create_empty_asset(headers, existing_asset.clone(), key.clone());

    let encoding = map_content_encoding(content);

    insert_asset_encoding(
        &key.full_path,
        ASSET_ENCODING_NO_COMPRESSION,
        &encoding,
        &mut asset,
        rule,
    );

    insert_asset(&key.collection, &key.full_path, &asset, rule);

    let config = get_config();

    update_runtime_certified_asset(&asset, &config);

    Ok(())
}