use crate::{
common::{
Icon, Identifier, NotificationHook, NotificationWithoutCondition, Parameter, ParameterType,
Right, StartParameter, Step,
},
SchemaVersion,
};
use mcai_graph::LinkType;
use schemars::JsonSchema;
use semver::Version;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
#[serde(tag = "schema_version")]
pub enum WorkflowDefinition {
#[serde(rename = "1.8")]
Version1_8(Version1_8),
#[serde(rename = "1.9")]
Version1_9(Version1_9),
#[serde(rename = "1.10")]
Version1_10(Version1_10),
#[serde(rename = "1.11")]
Version1_11(Version1_11),
}
impl WorkflowDefinition {
pub fn new(identifier: &str, label: &str) -> Self {
WorkflowDefinition::Version1_11(Version1_11 {
common: Common {
icon: Icon { icon: None },
identifier: Identifier {
content: identifier.to_string(),
},
label: label.to_string(),
is_live: false,
tags: vec![],
version_major: 0,
version_minor: 0,
version_micro: 1,
},
notifications: NotificationHooks {
notification_hooks: vec![],
},
parameters: Parameters { parameters: vec![] },
steps: Steps { steps: vec![] },
start_parameters: StartParameters {
start_parameters: vec![],
},
})
}
pub fn identifier(&self) -> &str {
&self.get_common().identifier.content
}
pub fn label(&self) -> &str {
&self.get_common().label
}
pub fn version(&self) -> Version {
let common = self.get_common();
Version::new(
common.version_major as u64,
common.version_minor as u64,
common.version_micro as u64,
)
}
pub fn schema_version(&self) -> SchemaVersion {
match self {
WorkflowDefinition::Version1_8(_) => SchemaVersion::_1_8,
WorkflowDefinition::Version1_9(_) => SchemaVersion::_1_9,
WorkflowDefinition::Version1_10(_) => SchemaVersion::_1_10,
WorkflowDefinition::Version1_11(_) => SchemaVersion::_1_11,
}
}
pub fn is_live(&self) -> bool {
self.get_common().is_live
}
pub fn toggle_is_live(&mut self) {
self.get_mut_common().is_live = !self.get_common().is_live;
}
pub fn tags(&self) -> &Vec<String> {
&self.get_common().tags
}
pub fn steps(&self) -> &Vec<Step> {
match self {
WorkflowDefinition::Version1_8(workflow) => &workflow.steps.steps,
WorkflowDefinition::Version1_9(workflow) => &workflow.steps.steps,
WorkflowDefinition::Version1_10(workflow) => &workflow.steps.steps,
WorkflowDefinition::Version1_11(workflow) => &workflow.steps.steps,
}
}
pub fn get_mut_steps(&mut self) -> &mut Vec<Step> {
match self {
WorkflowDefinition::Version1_8(workflow) => &mut workflow.steps.steps,
WorkflowDefinition::Version1_9(workflow) => &mut workflow.steps.steps,
WorkflowDefinition::Version1_10(workflow) => &mut workflow.steps.steps,
WorkflowDefinition::Version1_11(workflow) => &mut workflow.steps.steps,
}
}
pub fn get_mut_step(&mut self, step_id: u32) -> Option<&mut Step> {
self
.get_mut_steps()
.iter_mut()
.find(|step| step.id == step_id)
}
fn get_common(&self) -> &Common {
match self {
WorkflowDefinition::Version1_8(workflow) => &workflow.common,
WorkflowDefinition::Version1_9(workflow) => &workflow.common,
WorkflowDefinition::Version1_10(workflow) => &workflow.common,
WorkflowDefinition::Version1_11(workflow) => &workflow.common,
}
}
fn get_mut_common(&mut self) -> &mut Common {
match self {
WorkflowDefinition::Version1_8(workflow) => &mut workflow.common,
WorkflowDefinition::Version1_9(workflow) => &mut workflow.common,
WorkflowDefinition::Version1_10(workflow) => &mut workflow.common,
WorkflowDefinition::Version1_11(workflow) => &mut workflow.common,
}
}
pub fn set_parameter_on_step(
&mut self,
step_id: u32,
field_name: &str,
new_value: ParameterType,
is_parameter: bool,
) -> bool {
if let Some(step) = self.get_mut_step(step_id) {
if is_parameter {
step.set_parameter(field_name, new_value)
} else {
let new_value = new_value.get_string().unwrap_or_default();
match field_name {
"name" => step.name = new_value,
"label" => step.label = new_value,
"icon" => {
if let Ok(icon) = Icon::new(Some(new_value)) {
step.icon = icon;
return true;
} else {
return false;
}
}
_ => {}
}
true
}
} else {
false
}
}
pub fn update_step(&mut self, step: Step) {
if let Some(current_step) = self.get_mut_step(step.id) {
*current_step = step;
};
}
pub fn remove_step(&mut self, step_id: u32) {
let steps = self.get_mut_steps();
steps.retain(|step| step.id != step_id);
steps.iter_mut().for_each(|step| {
step.parent_ids.retain(|parent_id| *parent_id != step_id);
step
.required_to_start
.retain(|required| *required != step_id);
});
}
pub fn connect_steps(&mut self, child_step_id: u32, parent_step_id: u32, link_type: LinkType) {
if let Some(child_step) = self.get_mut_step(child_step_id) {
match link_type {
LinkType::Parentage => child_step.add_parent(&parent_step_id),
LinkType::Requirement => child_step.add_required(&parent_step_id),
}
}
}
pub fn disconnect_steps(&mut self, child_step_id: u32, parent_step_id: u32, link_type: LinkType) {
if let Some(step) = self.get_mut_step(child_step_id) {
let parents = match link_type {
LinkType::Parentage => &mut step.parent_ids,
LinkType::Requirement => &mut step.required_to_start,
};
parents.retain(|id| *id != parent_step_id);
}
}
pub fn set_workflow_property(&mut self, field_name: &str, value: ParameterType) -> bool {
if field_name == "identifier" {
self.get_mut_common().identifier.content = value.get_string().unwrap_or_default();
return true;
}
if field_name == "label" {
self.get_mut_common().label = value.get_string().unwrap_or_default();
return true;
}
if field_name == "tags" {
self.get_mut_common().tags = value.get_string_array().unwrap_or_default();
return true;
}
false
}
pub fn get_start_parameters(&self) -> &Vec<StartParameter> {
match self {
WorkflowDefinition::Version1_8(workflow) => &workflow.start_parameters.start_parameters,
WorkflowDefinition::Version1_9(workflow) => &workflow.start_parameters.start_parameters,
WorkflowDefinition::Version1_10(workflow) => &workflow.start_parameters.start_parameters,
WorkflowDefinition::Version1_11(workflow) => &workflow.start_parameters.start_parameters,
}
}
pub fn get_mut_start_parameters(&mut self) -> &mut Vec<StartParameter> {
match self {
WorkflowDefinition::Version1_8(workflow) => &mut workflow.start_parameters.start_parameters,
WorkflowDefinition::Version1_9(workflow) => &mut workflow.start_parameters.start_parameters,
WorkflowDefinition::Version1_10(workflow) => &mut workflow.start_parameters.start_parameters,
WorkflowDefinition::Version1_11(workflow) => &mut workflow.start_parameters.start_parameters,
}
}
pub fn get_notification_hooks(&self) -> Option<&Vec<NotificationHook>> {
match self {
WorkflowDefinition::Version1_8(_workflow) => None,
WorkflowDefinition::Version1_9(_workflow) => None,
WorkflowDefinition::Version1_10(_workflow) => None,
WorkflowDefinition::Version1_11(workflow) => Some(&workflow.notifications.notification_hooks),
}
}
pub fn get_mut_notification_hooks(&mut self) -> Option<&mut Vec<NotificationHook>> {
match self {
WorkflowDefinition::Version1_8(_workflow) => None,
WorkflowDefinition::Version1_9(_workflow) => None,
WorkflowDefinition::Version1_10(_workflow) => None,
WorkflowDefinition::Version1_11(workflow) => {
Some(&mut workflow.notifications.notification_hooks)
}
}
}
}
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Version1_8 {
#[serde(flatten)]
pub common: Common,
#[serde(flatten)]
pub parameters: Parameters,
#[serde(flatten)]
pub rights: Rights,
#[serde(flatten)]
pub steps: Steps,
#[serde(flatten)]
pub start_parameters: StartParameters,
}
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Version1_9 {
#[serde(flatten)]
pub common: Common,
#[serde(flatten)]
pub parameters: Parameters,
#[serde(flatten)]
pub steps: Steps,
#[serde(flatten)]
pub start_parameters: StartParameters,
}
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Version1_10 {
#[serde(flatten)]
pub common: Common,
#[serde(flatten)]
pub notifications: NotificationsWithoutCondition,
#[serde(flatten)]
pub parameters: Parameters,
#[serde(flatten)]
pub steps: Steps,
#[serde(flatten)]
pub start_parameters: StartParameters,
}
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Version1_11 {
#[serde(flatten)]
pub common: Common,
#[serde(flatten)]
pub notifications: NotificationHooks,
#[serde(flatten)]
pub parameters: Parameters,
#[serde(flatten)]
pub steps: Steps,
#[serde(flatten)]
pub start_parameters: StartParameters,
}
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
pub struct Common {
pub icon: Icon,
pub identifier: Identifier,
pub label: String,
#[serde(default, skip_serializing_if = "crate::is_false")]
pub is_live: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
pub version_major: u32,
pub version_minor: u32,
pub version_micro: u32,
}
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
pub struct NotificationsWithoutCondition {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub notification_hooks: Vec<NotificationWithoutCondition>,
}
#[derive(Clone, Debug, Default, Deserialize, JsonSchema, PartialEq, Serialize)]
pub struct NotificationHooks {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub notification_hooks: Vec<NotificationHook>,
}
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
pub struct Rights {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub rights: Vec<Right>,
}
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
pub struct Parameters {
pub parameters: Vec<Parameter>,
}
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
pub struct StartParameters {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub start_parameters: Vec<StartParameter>,
}
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
pub struct Steps {
pub steps: Vec<Step>,
}
#[cfg(test)]
mod tests {
use super::WorkflowDefinition;
#[test]
fn load_version_1_8() {
let str_workflow_definition =
include_str!("../tests/resources/workflow_definitions/1.8/simple.json");
let _: WorkflowDefinition = serde_json::from_str(str_workflow_definition).unwrap();
let str_workflow_definition =
include_str!("../tests/resources/workflow_definitions/1.8/transfer.json");
let _: WorkflowDefinition = serde_json::from_str(str_workflow_definition).unwrap();
}
#[test]
fn load_version_1_9() {
let str_workflow_definition =
include_str!("../tests/resources/workflow_definitions/1.9/objet_detection.json");
let _: WorkflowDefinition = serde_json::from_str(str_workflow_definition).unwrap();
}
#[test]
fn load_version_1_10() {
let str_workflow_definition =
include_str!("../tests/resources/workflow_definitions/1.10/simple.json");
let _: WorkflowDefinition = serde_json::from_str(str_workflow_definition).unwrap();
}
}