Skip to main content

coil_ops/bulk/
definition.rs

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}