pipedash_plugin_api/plugin/
defaults.rs1use serde_json::{
2 Map,
3 Value,
4};
5
6use crate::actions::{
7 ActionAvailability,
8 PipelineActionState,
9 RunActionState,
10};
11use crate::metadata::PluginCapabilities;
12use crate::presentation::{
13 DisplayField,
14 PipelineFacts,
15 RunDisplay,
16 RunFacts,
17};
18use crate::types::{
19 PipelineStatus,
20 WorkflowParameter,
21 WorkflowParameterType,
22};
23
24fn flag(metadata: &std::collections::HashMap<String, Value>, key: &str) -> bool {
25 metadata.get(key) == Some(&Value::Bool(true))
26}
27
28fn truthy(value: Option<&Value>) -> bool {
29 match value {
30 None | Some(Value::Null) => false,
31 Some(Value::Bool(value)) => *value,
32 Some(Value::String(value)) => !value.is_empty(),
33 Some(Value::Number(value)) => value.as_f64().is_some_and(|value| value != 0.0),
34 Some(_) => true,
35 }
36}
37
38pub fn pipeline_actions(
39 capabilities: &PluginCapabilities, pipeline: &PipelineFacts<'_>,
40) -> PipelineActionState {
41 PipelineActionState {
42 trigger: ActionAvailability::allowed(capabilities.trigger),
43 cancel: ActionAvailability::allowed(capabilities.cancel),
44 force_cancel: ActionAvailability::allowed(capabilities.force_cancel),
45 enable: ActionAvailability::allowed(flag(pipeline.metadata, "can_enable")),
46 target: namespace_target(pipeline.metadata),
47 ..PipelineActionState::default()
48 }
49}
50
51fn namespace_target(metadata: &std::collections::HashMap<String, Value>) -> Option<DisplayField> {
52 let namespace = metadata.get("namespace").and_then(Value::as_str)?.trim();
53 (!namespace.is_empty()).then(|| DisplayField {
54 label: "Namespace".to_string(),
55 value: namespace.to_string(),
56 })
57}
58
59pub fn run_actions(capabilities: &PluginCapabilities, run: &RunFacts<'_>) -> RunActionState {
60 let active = matches!(
61 run.status,
62 PipelineStatus::Running | PipelineStatus::Pending
63 );
64 let awaiting_review = flag(run.metadata, "awaiting_review");
65
66 RunActionState {
67 cancel: ActionAvailability::allowed(active && capabilities.cancel && !awaiting_review),
68 force_cancel: ActionAvailability::allowed(
69 run.status == PipelineStatus::Running && capabilities.force_cancel,
70 ),
71 rerun: ActionAvailability::allowed(!active && capabilities.trigger),
72 rerun_failed: ActionAvailability::allowed(
73 !active && flag(run.metadata, "can_rerun_failed_jobs"),
74 ),
75 review: ActionAvailability::allowed(awaiting_review),
76 watch: active,
77 }
78}
79
80pub fn run_display(run: &RunFacts<'_>) -> RunDisplay {
81 let context = [("Branch", run.branch), ("Actor", run.actor)]
82 .into_iter()
83 .filter_map(|(label, value)| {
84 value
85 .filter(|value| !value.is_empty())
86 .map(|value| DisplayField {
87 label: label.to_string(),
88 value: value.to_string(),
89 })
90 })
91 .collect();
92
93 RunDisplay {
94 title: format!("#{}", run.run_number),
95 context,
96 started_label: "Started".to_string(),
97 }
98}
99
100pub fn trigger_values(
101 previous: Option<&RunFacts<'_>>, parameters: &[WorkflowParameter],
102) -> Map<String, Value> {
103 let mut inputs = previous
104 .and_then(|run| run.inputs)
105 .and_then(Value::as_object)
106 .cloned()
107 .unwrap_or_default();
108
109 if let Some(branch) = previous
110 .and_then(|run| run.branch)
111 .filter(|branch| !branch.is_empty())
112 {
113 if !truthy(inputs.get("branch")) && !truthy(inputs.get("ref")) {
114 inputs.insert("ref".to_string(), Value::String(branch.to_string()));
115 }
116 }
117
118 let mut values = Map::new();
119 for parameter in parameters {
120 if let Some(replayed) = inputs.get(¶meter.name) {
121 values.insert(parameter.name.clone(), replayed.clone());
122 } else if let Some(default) = default_value(¶meter.param_type) {
123 values.insert(parameter.name.clone(), default);
124 }
125 }
126 values
127}
128
129fn default_value(parameter_type: &WorkflowParameterType) -> Option<Value> {
130 match parameter_type {
131 WorkflowParameterType::String { default }
132 | WorkflowParameterType::Choice { default, .. } => default
133 .as_deref()
134 .filter(|default| !default.trim().is_empty())
135 .map(Value::from),
136 WorkflowParameterType::Boolean { default } => Some(Value::Bool(*default)),
137 WorkflowParameterType::Number { default } => default.map(Value::from),
138 }
139}