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::insert_asset;
10use crate::storage::heap::store::insert_asset_encoding;
11use crate::storage::stable::{get_assets, get_content_chunks};
12use crate::strategies::{CdnHeapStrategy, CdnStableStrategy, CdnWorkflowStrategy};
13use hex::encode;
14use junobuild_storage::types::store::AssetEncoding;
15
16pub fn commit_proposal(
17 cdn_heap: &impl CdnHeapStrategy,
18 cdn_stable: &impl CdnStableStrategy,
19 cdn_workflow: &impl CdnWorkflowStrategy,
20 proposition: &CommitProposal,
21) -> Result<(), CommitProposalError> {
22 let proposal = get_proposal(cdn_stable, &proposition.proposal_id).ok_or_else(|| {
23 CommitProposalError::ProposalNotFound(format!(
24 "{} ({})",
25 JUNO_CDN_PROPOSALS_ERROR_CANNOT_COMMIT, proposition.proposal_id
26 ))
27 })?;
28
29 match secure_commit_proposal(cdn_heap, cdn_stable, cdn_workflow, proposition, &proposal) {
30 Ok(_) => {
31 let executed_proposal = Proposal::execute(&proposal);
32 insert_proposal(cdn_stable, &proposition.proposal_id, &executed_proposal);
33 Ok(())
34 }
35 Err(e @ CommitProposalError::CommitAssetsIssue(_))
36 | Err(e @ CommitProposalError::PostCommitAssetsIssue(_)) => {
37 let failed_proposal = Proposal::fail(&proposal);
38 insert_proposal(cdn_stable, &proposition.proposal_id, &failed_proposal);
39 Err(e)
40 }
41 Err(e) => Err(e),
42 }
43}
44
45fn secure_commit_proposal(
46 cdn_heap: &impl CdnHeapStrategy,
47 cdn_stable: &impl CdnStableStrategy,
48 cdn_workflow: &impl CdnWorkflowStrategy,
49 commit_proposal: &CommitProposal,
50 proposal: &Proposal,
51) -> Result<(), CommitProposalError> {
52 if proposal.status != ProposalStatus::Open {
53 return Err(CommitProposalError::ProposalNotOpen(format!(
54 "{} ({:?})",
55 JUNO_CDN_PROPOSALS_ERROR_CANNOT_COMMIT_INVALID_STATUS, proposal.status
56 )));
57 }
58
59 match &proposal.sha256 {
60 Some(sha256) if sha256 == &commit_proposal.sha256 => (),
61 _ => {
62 return Err(CommitProposalError::InvalidSha256(format!(
63 "{} ({})",
64 JUNO_CDN_PROPOSALS_ERROR_INVALID_HASH,
65 encode(commit_proposal.sha256)
66 )));
67 }
68 }
69
70 assert_known_proposal_type(proposal).map_err(CommitProposalError::InvalidType)?;
71
72 let accepted_proposal = Proposal::accept(proposal);
74 insert_proposal(cdn_stable, &commit_proposal.proposal_id, &accepted_proposal);
75
76 cdn_workflow.pre_commit_assets(proposal);
77
78 copy_committed_assets(cdn_heap, cdn_stable, &commit_proposal.proposal_id)
79 .map_err(CommitProposalError::CommitAssetsIssue)?;
80
81 cdn_workflow
82 .post_commit_assets(proposal)
83 .map_err(CommitProposalError::PostCommitAssetsIssue)?;
84
85 Ok(())
86}
87
88fn copy_committed_assets(
89 cdn_heap: &impl CdnHeapStrategy,
90 cdn_stable: &impl CdnStableStrategy,
91 proposal_id: &ProposalId,
92) -> Result<(), String> {
93 let assets = get_assets(cdn_stable, proposal_id);
95
96 if assets.is_empty() {
97 return Err(format!(
98 "{} ({})",
99 JUNO_CDN_PROPOSALS_ERROR_EMPTY_ASSETS, proposal_id
100 ));
101 }
102
103 for (key, asset) in assets {
104 insert_asset(cdn_heap, &key.full_path, &asset);
105
106 for (encoding_type, encoding) in asset.encodings {
107 let mut content_chunks = Vec::new();
108
109 for (i, _) in encoding.content_chunks.iter().enumerate() {
110 let chunks = get_content_chunks(cdn_stable, &encoding, i).ok_or_else(|| {
111 format!(
112 "{} ({} - {}).",
113 JUNO_CDN_PROPOSALS_ERROR_NOT_CONTENT_CHUNKS_AT_INDEX, encoding_type, i
114 )
115 })?;
116
117 content_chunks.push(chunks);
118 }
119
120 if content_chunks.is_empty() {
121 return Err(format!(
122 "{} ({})",
123 JUNO_CDN_PROPOSALS_ERROR_EMPTY_CONTENT_CHUNKS, encoding_type
124 ));
125 }
126
127 let encoding_with_content = AssetEncoding {
128 content_chunks,
129 ..encoding
130 };
131
132 insert_asset_encoding(
133 cdn_heap,
134 &key.full_path,
135 &encoding_type,
136 &encoding_with_content,
137 )?;
138 }
139 }
140
141 Ok(())
142}