junobuild_cdn/proposals/workflows/
commit.rs1use crate::proposals::errors::{
2 JUNO_CDN_PROPOSALS_ERROR_CANNOT_COMMIT, JUNO_CDN_PROPOSALS_ERROR_CANNOT_COMMIT_INVALID_STATUS,
3 JUNO_CDN_PROPOSALS_ERROR_EMPTY_ASSETS, JUNO_CDN_PROPOSALS_ERROR_EMPTY_CONTENT_CHUNKS,
4 JUNO_CDN_PROPOSALS_ERROR_INVALID_HASH, JUNO_CDN_PROPOSALS_ERROR_NOT_CONTENT_CHUNKS_AT_INDEX,
5};
6use crate::proposals::workflows::assert::assert_known_proposal_type;
7use crate::proposals::{get_proposal, insert_proposal};
8use crate::proposals::{CommitProposal, CommitProposalError, Proposal, ProposalId, ProposalStatus};
9use crate::storage::heap::get_rule;
10use crate::storage::stable::{get_assets, get_content_chunks};
11use crate::strategies::{
12 CdnCommitAssetsStrategy, CdnHeapStrategy, CdnStableStrategy, CdnWorkflowStrategy,
13};
14use hex::encode;
15use junobuild_collections::types::core::CollectionKey;
16use junobuild_collections::types::rules::Rule;
17use junobuild_storage::types::store::AssetEncoding;
18use std::collections::HashMap;
19
20pub fn commit_proposal(
21 cdn_heap: &impl CdnHeapStrategy,
22 cdn_commit_assets: &impl CdnCommitAssetsStrategy,
23 cdn_stable: &impl CdnStableStrategy,
24 cdn_workflow: &impl CdnWorkflowStrategy,
25 proposition: &CommitProposal,
26) -> Result<(), CommitProposalError> {
27 let proposal = get_proposal(cdn_stable, &proposition.proposal_id).ok_or_else(|| {
28 CommitProposalError::ProposalNotFound(format!(
29 "{} ({})",
30 JUNO_CDN_PROPOSALS_ERROR_CANNOT_COMMIT, proposition.proposal_id
31 ))
32 })?;
33
34 match secure_commit_proposal(
35 cdn_heap,
36 cdn_commit_assets,
37 cdn_stable,
38 cdn_workflow,
39 proposition,
40 &proposal,
41 ) {
42 Ok(_) => {
43 let executed_proposal = Proposal::execute(&proposal);
44 insert_proposal(cdn_stable, &proposition.proposal_id, &executed_proposal);
45 Ok(())
46 }
47 Err(e @ CommitProposalError::CommitAssetsIssue(_))
48 | Err(e @ CommitProposalError::PreCommitAssetsIssue(_))
49 | Err(e @ CommitProposalError::PostCommitAssetsIssue(_)) => {
50 let failed_proposal = Proposal::fail(&proposal);
51 insert_proposal(cdn_stable, &proposition.proposal_id, &failed_proposal);
52 Err(e)
53 }
54 Err(e) => Err(e),
55 }
56}
57
58fn secure_commit_proposal(
59 cdn_heap: &impl CdnHeapStrategy,
60 cdn_commit_assets: &impl CdnCommitAssetsStrategy,
61 cdn_stable: &impl CdnStableStrategy,
62 cdn_workflow: &impl CdnWorkflowStrategy,
63 commit_proposal: &CommitProposal,
64 proposal: &Proposal,
65) -> Result<(), CommitProposalError> {
66 if proposal.status != ProposalStatus::Open {
67 return Err(CommitProposalError::ProposalNotOpen(format!(
68 "{} ({:?})",
69 JUNO_CDN_PROPOSALS_ERROR_CANNOT_COMMIT_INVALID_STATUS, proposal.status
70 )));
71 }
72
73 match &proposal.sha256 {
74 Some(sha256) if sha256 == &commit_proposal.sha256 => (),
75 _ => {
76 return Err(CommitProposalError::InvalidSha256(format!(
77 "{} ({})",
78 JUNO_CDN_PROPOSALS_ERROR_INVALID_HASH,
79 encode(commit_proposal.sha256)
80 )));
81 }
82 }
83
84 assert_known_proposal_type(proposal).map_err(CommitProposalError::InvalidType)?;
85
86 let accepted_proposal = Proposal::accept(proposal);
88 insert_proposal(cdn_stable, &commit_proposal.proposal_id, &accepted_proposal);
89
90 cdn_workflow
91 .pre_commit_assets(proposal)
92 .map_err(CommitProposalError::PreCommitAssetsIssue)?;
93
94 copy_committed_assets(
95 cdn_heap,
96 cdn_commit_assets,
97 cdn_stable,
98 &commit_proposal.proposal_id,
99 )
100 .map_err(CommitProposalError::CommitAssetsIssue)?;
101
102 cdn_workflow
103 .post_commit_assets(proposal)
104 .map_err(CommitProposalError::PostCommitAssetsIssue)?;
105
106 Ok(())
107}
108
109fn copy_committed_assets(
110 cdn_heap: &impl CdnHeapStrategy,
111 cdn_commit_assets: &impl CdnCommitAssetsStrategy,
112 cdn_stable: &impl CdnStableStrategy,
113 proposal_id: &ProposalId,
114) -> Result<(), String> {
115 let assets = get_assets(cdn_stable, proposal_id);
117
118 if assets.is_empty() {
119 return Err(format!(
120 "{JUNO_CDN_PROPOSALS_ERROR_EMPTY_ASSETS} ({proposal_id})"
121 ));
122 }
123
124 let mut rule_cache: HashMap<CollectionKey, Rule> = HashMap::new();
127
128 for (key, mut asset) in assets {
129 let rule = rule_cache
130 .entry(key.collection.clone())
131 .or_insert_with(|| get_rule(cdn_heap, &key.collection).unwrap())
132 .clone();
133
134 let encodings = std::mem::take(&mut asset.encodings);
135
136 for (encoding_type, encoding) in encodings {
137 let mut content_chunks = Vec::new();
138
139 for (i, _) in encoding.content_chunks.iter().enumerate() {
140 let chunks = get_content_chunks(cdn_stable, &encoding, i).ok_or_else(|| {
141 format!(
142 "{JUNO_CDN_PROPOSALS_ERROR_NOT_CONTENT_CHUNKS_AT_INDEX} ({encoding_type} - {i})."
143 )
144 })?;
145
146 content_chunks.push(chunks);
147 }
148
149 if content_chunks.is_empty() {
150 return Err(format!(
151 "{JUNO_CDN_PROPOSALS_ERROR_EMPTY_CONTENT_CHUNKS} ({encoding_type})"
152 ));
153 }
154
155 let encoding_with_content = AssetEncoding {
156 content_chunks,
157 ..encoding
158 };
159
160 cdn_commit_assets.insert_asset_encoding(
161 &key.full_path,
162 &encoding_type,
163 &encoding_with_content,
164 &mut asset,
165 &rule,
166 );
167
168 cdn_commit_assets.insert_asset(&key.collection, &key.full_path, &asset, &rule);
169 }
170 }
171
172 Ok(())
173}