Skip to main content

orchestrator_config/config/
dynamic_items.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// WP03: Action to generate new task items dynamically from a pipeline variable.
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6pub struct GenerateItemsAction {
7    /// Pipeline variable name containing the JSON data.
8    pub from_var: String,
9    /// JSON path to the array of candidates within the variable value.
10    pub json_path: String,
11    /// How to map each array element to a task item.
12    pub mapping: DynamicItemMapping,
13    /// Whether to replace existing items (default: false, meaning append).
14    #[serde(default)]
15    pub replace: bool,
16}
17
18/// Mapping from a JSON array element to task item fields.
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
20pub struct DynamicItemMapping {
21    /// JSON path to the item ID field within each element.
22    pub item_id: String,
23    /// Optional JSON path to a label field.
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub label: Option<String>,
26    /// Per-item variables: key → JSON path within element.
27    #[serde(default)]
28    pub vars: HashMap<String, String>,
29}
30
31/// A dynamically generated task item, ready for DB insertion.
32#[derive(Debug, Clone)]
33pub struct NewDynamicItem {
34    /// Identifier assigned to the new task item.
35    pub item_id: String,
36    /// Optional display label for the item.
37    pub label: Option<String>,
38    /// Per-item variables extracted from the source payload.
39    pub vars: HashMap<String, String>,
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn test_generate_items_action_minimal() {
48        let json = r#"{
49            "from_var": "candidates",
50            "json_path": "$.items",
51            "mapping": {"item_id": "$.id"}
52        }"#;
53        let action: GenerateItemsAction =
54            serde_json::from_str(json).expect("deserialize generate items");
55        assert_eq!(action.from_var, "candidates");
56        assert!(!action.replace);
57        assert!(action.mapping.label.is_none());
58        assert!(action.mapping.vars.is_empty());
59    }
60
61    #[test]
62    fn test_generate_items_action_full() {
63        let json = r#"{
64            "from_var": "candidates",
65            "json_path": "$.items",
66            "mapping": {
67                "item_id": "$.id",
68                "label": "$.name",
69                "vars": {
70                    "approach": "$.approach",
71                    "config": "$.config_path"
72                }
73            },
74            "replace": true
75        }"#;
76        let action: GenerateItemsAction =
77            serde_json::from_str(json).expect("deserialize full generate items");
78        assert!(action.replace);
79        assert_eq!(action.mapping.label, Some("$.name".to_string()));
80        assert_eq!(action.mapping.vars.len(), 2);
81    }
82}