use mako_engine::{
error::WorkflowError,
ids::DeadlineId,
outbox::PendingOutbox,
types::{MaLo, MarktpartnerCode, MessageRef, Pruefidentifikator},
workflow::{CommandPayload, EventPayload, Workflow, WorkflowOutput},
};
pub const WORKFLOW_NAME: &str = "gpke-konfiguration-aenderung";
pub const ORDERS_ANFRAGE_NB_PIDS: &[u32] = &[17120, 17122, 17123, 17133];
pub const ORDERS_ANFRAGE_MSB_PIDS: &[u32] = &[17128, 17129, 17130, 17131];
pub const ORDERS_NB_MSB_PIDS: &[u32] = &[17121];
pub const ORDERS_ANFRAGE_PIDS: &[u32] = &[
17120, 17121, 17122, 17123, 17128, 17129, 17130, 17131, 17133,
];
pub const ORDRSP_NB_LF_PIDS: &[u32] = &[19120, 19121, 19123, 19124, 19127, 19133];
pub const ORDRSP_MSB_LF_PIDS: &[u32] = &[19130, 19131, 19132];
pub const ORDRSP_PIDS: &[u32] = &[
19120, 19121, 19123, 19124, 19127, 19130, 19131, 19132, 19133,
];
pub const IFTSTA_PIDS: &[u32] = &[21_043, 21_044];
pub const ANTWORT_WINDOW_LABEL: &str = "gpke-konfiguration-aenderung-antwort";
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum KonfigurationAenderungEvent {
AnfrageGesendet {
orders_pid: Pruefidentifikator,
recipient: MarktpartnerCode,
malo: MaLo,
message_ref: MessageRef,
},
AntwortErhalten {
ordrsp_pid: Pruefidentifikator,
accepted: bool,
reason: Option<String>,
message_ref: MessageRef,
},
ZwischenstandErhalten {
ordrsp_pid: Pruefidentifikator,
message_ref: MessageRef,
},
DeadlineExpired {
deadline_id: DeadlineId,
label: Box<str>,
},
}
impl EventPayload for KonfigurationAenderungEvent {
fn event_type(&self) -> &'static str {
match self {
Self::AnfrageGesendet { .. } => "KonfigAenderungAnfrageGesendet",
Self::AntwortErhalten { .. } => "KonfigAenderungAntwortErhalten",
Self::ZwischenstandErhalten { .. } => "KonfigAenderungZwischenstandErhalten",
Self::DeadlineExpired { .. } => "KonfigAenderungDeadlineExpired",
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AnfrageData {
pub orders_pid: Pruefidentifikator,
pub recipient: MarktpartnerCode,
pub malo: MaLo,
pub message_ref: MessageRef,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(tag = "status", content = "data")]
pub enum KonfigurationAenderungState {
New,
AnfrageGesendet(AnfrageData),
Beantwortet {
anfrage: AnfrageData,
ordrsp_pid: Pruefidentifikator,
accepted: bool,
},
DeadlineExpired {
anfrage: AnfrageData,
},
}
impl Default for KonfigurationAenderungState {
fn default() -> Self {
Self::New
}
}
impl KonfigurationAenderungState {
#[must_use]
pub fn label(&self) -> &'static str {
match self {
Self::New => "New",
Self::AnfrageGesendet(_) => "AnfrageGesendet",
Self::Beantwortet { .. } => "Beantwortet",
Self::DeadlineExpired { .. } => "DeadlineExpired",
}
}
#[must_use]
pub fn is_terminal(&self) -> bool {
matches!(
self,
Self::Beantwortet { .. } | Self::DeadlineExpired { .. }
)
}
}
#[derive(Clone)]
pub enum KonfigurationAenderungCommand {
SendAnfrage {
orders_pid: Pruefidentifikator,
recipient: MarktpartnerCode,
malo: MaLo,
message_ref: MessageRef,
payload: serde_json::Value,
},
ReceiveOrdrsp {
ordrsp_pid: Pruefidentifikator,
accepted: bool,
reason: Option<String>,
message_ref: MessageRef,
},
TimeoutExpired {
deadline_id: DeadlineId,
label: Box<str>,
},
}
impl CommandPayload for KonfigurationAenderungCommand {}
pub struct GpkeKonfigurationAenderungWorkflow;
const REJECTION_PIDS: &[u32] = &[19123, 19130];
const ZWISCHENSTAND_PIDS: &[u32] = &[19133];
impl Workflow for GpkeKonfigurationAenderungWorkflow {
type State = KonfigurationAenderungState;
type Event = KonfigurationAenderungEvent;
type Command = KonfigurationAenderungCommand;
fn on_deadline(
deadline: &mako_engine::deadline::Deadline,
state: &Self::State,
) -> Option<Self::Command> {
match (deadline.label(), state) {
(ANTWORT_WINDOW_LABEL, KonfigurationAenderungState::AnfrageGesendet(_)) => {
Some(KonfigurationAenderungCommand::TimeoutExpired {
deadline_id: deadline.deadline_id(),
label: deadline.label().into(),
})
}
_ => None,
}
}
fn apply(state: Self::State, event: &Self::Event) -> Self::State {
match event {
KonfigurationAenderungEvent::AnfrageGesendet {
orders_pid,
recipient,
malo,
message_ref,
} => KonfigurationAenderungState::AnfrageGesendet(AnfrageData {
orders_pid: *orders_pid,
recipient: recipient.clone(),
malo: malo.clone(),
message_ref: message_ref.clone(),
}),
KonfigurationAenderungEvent::ZwischenstandErhalten { .. } => state, KonfigurationAenderungEvent::AntwortErhalten {
ordrsp_pid,
accepted,
..
} => match state {
KonfigurationAenderungState::AnfrageGesendet(anfrage) => {
KonfigurationAenderungState::Beantwortet {
anfrage,
ordrsp_pid: *ordrsp_pid,
accepted: *accepted,
}
}
other => other,
},
KonfigurationAenderungEvent::DeadlineExpired { .. } => match state {
KonfigurationAenderungState::AnfrageGesendet(anfrage) => {
KonfigurationAenderungState::DeadlineExpired { anfrage }
}
other => other,
},
}
}
fn handle(
state: &Self::State,
command: Self::Command,
) -> Result<WorkflowOutput<Self::Event>, WorkflowError> {
match command {
KonfigurationAenderungCommand::SendAnfrage {
orders_pid,
recipient,
malo,
message_ref,
payload,
} => {
if !matches!(state, KonfigurationAenderungState::New) {
return Err(WorkflowError::invalid_state("New", state.label()));
}
if !ORDERS_ANFRAGE_PIDS.contains(&orders_pid.as_u32()) {
return Err(WorkflowError::rejected(format!(
"not a valid GPKE Konfigurationsänderung ORDERS PID: {orders_pid}",
)));
}
let event = KonfigurationAenderungEvent::AnfrageGesendet {
orders_pid,
recipient: recipient.clone(),
malo: malo.clone(),
message_ref: message_ref.clone(),
};
let outbox = vec![PendingOutbox::new(
"ORDERS",
recipient.as_str(),
serde_json::json!({
"pid": orders_pid.as_u32(),
"malo": malo.as_str(),
"orders_ref": message_ref.as_str(),
"payload": payload,
}),
)];
Ok(WorkflowOutput::with_outbox(vec![event], outbox))
}
KonfigurationAenderungCommand::ReceiveOrdrsp {
ordrsp_pid,
accepted,
reason,
message_ref,
} => {
if state.is_terminal() {
return Ok(WorkflowOutput::events(vec![]));
}
if !matches!(state, KonfigurationAenderungState::AnfrageGesendet(_)) {
return Err(WorkflowError::invalid_state(
"AnfrageGesendet",
state.label(),
));
}
if !ORDRSP_PIDS.contains(&ordrsp_pid.as_u32()) {
return Err(WorkflowError::rejected(format!(
"not a valid GPKE Konfigurationsänderung ORDRSP PID: {ordrsp_pid}",
)));
}
if ZWISCHENSTAND_PIDS.contains(&ordrsp_pid.as_u32()) {
return Ok(vec![KonfigurationAenderungEvent::ZwischenstandErhalten {
ordrsp_pid,
message_ref,
}]
.into());
}
let is_accepted = accepted && !REJECTION_PIDS.contains(&ordrsp_pid.as_u32());
Ok(vec![KonfigurationAenderungEvent::AntwortErhalten {
ordrsp_pid,
accepted: is_accepted,
reason,
message_ref,
}]
.into())
}
KonfigurationAenderungCommand::TimeoutExpired { deadline_id, label } => {
if state.is_terminal() {
return Ok(WorkflowOutput::events(vec![]));
}
Ok(
vec![KonfigurationAenderungEvent::DeadlineExpired { deadline_id, label }]
.into(),
)
}
}
}
}