1use crate::error::OpsModelError;
2use crate::identifiers::BulkOperationId;
3use crate::validation::require_non_empty;
4use coil_auth::Capability;
5use coil_core::{
6 BulkOperationDefinition as ManifestBulkOperationDefinition,
7 BulkOperationKind as ManifestBulkOperationKind,
8 BulkOperationScope as ManifestBulkOperationScope,
9};
10use coil_jobs::RetryPolicy;
11use std::fmt;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum BulkOperationKind {
15 Publish,
16 Unpublish,
17 Reindex,
18 Export,
19 Cancel,
20 CheckIn,
21 Custom,
22}
23
24impl fmt::Display for BulkOperationKind {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 Self::Publish => f.write_str("publish"),
28 Self::Unpublish => f.write_str("unpublish"),
29 Self::Reindex => f.write_str("reindex"),
30 Self::Export => f.write_str("export"),
31 Self::Cancel => f.write_str("cancel"),
32 Self::CheckIn => f.write_str("check_in"),
33 Self::Custom => f.write_str("custom"),
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum BulkOperationScope {
40 Cms,
41 Commerce,
42 Memberships,
43 Events,
44 Media,
45 Search,
46 System,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct BulkOperationDefinition {
51 pub id: BulkOperationId,
52 pub source_module: String,
53 pub title: String,
54 pub description: Option<String>,
55 pub required_capability: Capability,
56 pub kind: BulkOperationKind,
57 pub scope: BulkOperationScope,
58 pub retry_policy: RetryPolicy,
59 pub max_items: Option<usize>,
60 pub requires_idempotency_key: bool,
61}
62
63impl BulkOperationDefinition {
64 pub fn new(
65 id: BulkOperationId,
66 source_module: impl Into<String>,
67 title: impl Into<String>,
68 description: Option<String>,
69 required_capability: Capability,
70 kind: BulkOperationKind,
71 scope: BulkOperationScope,
72 retry_policy: RetryPolicy,
73 max_items: Option<usize>,
74 requires_idempotency_key: bool,
75 ) -> Result<Self, OpsModelError> {
76 let source_module = require_non_empty("bulk_source_module", source_module.into())?;
77 let title = require_non_empty("bulk_title", title.into())?;
78
79 if let Some(max_items) = max_items {
80 if max_items == 0 {
81 return Err(OpsModelError::InvalidBulkOperation {
82 operation_id: id.to_string(),
83 reason: "max_items must be greater than zero".to_string(),
84 });
85 }
86 }
87
88 if retry_policy.is_retrying() && !requires_idempotency_key {
89 return Err(OpsModelError::InvalidBulkOperation {
90 operation_id: id.to_string(),
91 reason: "retrying bulk operations must require an idempotency key".to_string(),
92 });
93 }
94
95 Ok(Self {
96 id,
97 source_module,
98 title,
99 description,
100 required_capability,
101 kind,
102 scope,
103 retry_policy,
104 max_items,
105 requires_idempotency_key,
106 })
107 }
108
109 pub fn allows(&self, capabilities: &[Capability]) -> bool {
110 capabilities.contains(&self.required_capability)
111 }
112
113 pub(crate) fn from_manifest_definition(
114 source_module: &str,
115 definition: &ManifestBulkOperationDefinition,
116 ) -> Result<Self, OpsModelError> {
117 Self::new(
118 BulkOperationId::new(definition.id.clone())?,
119 source_module,
120 definition.title.clone(),
121 definition.description.clone(),
122 definition.required_capability,
123 map_bulk_operation_kind(definition.kind),
124 map_bulk_operation_scope(definition.scope),
125 definition.retry_policy.clone(),
126 definition.max_items,
127 definition.requires_idempotency_key,
128 )
129 }
130}
131
132pub(crate) fn map_bulk_operation_kind(kind: ManifestBulkOperationKind) -> BulkOperationKind {
133 match kind {
134 ManifestBulkOperationKind::Publish => BulkOperationKind::Publish,
135 ManifestBulkOperationKind::Unpublish => BulkOperationKind::Unpublish,
136 ManifestBulkOperationKind::Reindex => BulkOperationKind::Reindex,
137 ManifestBulkOperationKind::Export => BulkOperationKind::Export,
138 ManifestBulkOperationKind::Cancel => BulkOperationKind::Cancel,
139 ManifestBulkOperationKind::CheckIn => BulkOperationKind::CheckIn,
140 ManifestBulkOperationKind::Custom => BulkOperationKind::Custom,
141 }
142}
143
144pub(crate) fn map_bulk_operation_scope(scope: ManifestBulkOperationScope) -> BulkOperationScope {
145 match scope {
146 ManifestBulkOperationScope::Cms => BulkOperationScope::Cms,
147 ManifestBulkOperationScope::Commerce => BulkOperationScope::Commerce,
148 ManifestBulkOperationScope::Memberships => BulkOperationScope::Memberships,
149 ManifestBulkOperationScope::Events => BulkOperationScope::Events,
150 ManifestBulkOperationScope::Media => BulkOperationScope::Media,
151 ManifestBulkOperationScope::Search => BulkOperationScope::Search,
152 ManifestBulkOperationScope::System => BulkOperationScope::System,
153 }
154}