diff --git a/notifico-core/src/credentials.rs b/notifico-core/src/credentials.rs index 46045ab..91d41e3 100644 --- a/notifico-core/src/credentials.rs +++ b/notifico-core/src/credentials.rs @@ -1,6 +1,7 @@ use crate::error::EngineError; use serde::{Deserialize, Serialize}; use serde_json::Value; +use uuid::Uuid; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Credential { @@ -10,5 +11,6 @@ pub struct Credential { } pub trait Credentials: Send + Sync { - fn get_credential(&self, r#type: &str, name: &str) -> Result; + fn get_credential(&self, project: Uuid, r#type: &str, name: &str) + -> Result; } diff --git a/notifico-core/src/engine.rs b/notifico-core/src/engine.rs index 28f9200..e30a876 100644 --- a/notifico-core/src/engine.rs +++ b/notifico-core/src/engine.rs @@ -7,6 +7,7 @@ use serde_json::{Map, Value}; use std::any::Any; use std::borrow::Cow; use std::collections::HashMap; +use uuid::Uuid; #[derive(Debug, Default, Serialize, Deserialize)] #[serde(transparent)] @@ -14,6 +15,7 @@ pub struct EventContext(pub Map); #[derive(Default, Debug)] pub struct PipelineContext { + pub project_id: Uuid, pub recipient: Option, pub event_context: EventContext, pub plugin_contexts: HashMap, Value>, diff --git a/notifico-core/src/error.rs b/notifico-core/src/error.rs index 1b22e5b..ebb0b37 100644 --- a/notifico-core/src/error.rs +++ b/notifico-core/src/error.rs @@ -1,5 +1,6 @@ use crate::templater::TemplaterError; use std::borrow::Cow; +use uuid::Uuid; #[derive(Debug)] pub enum EngineError { @@ -12,6 +13,7 @@ pub enum EngineError { InvalidContactFormat, RecipientNotSet, TemplateNotSet, + ProjectNotFound(Uuid), } impl From for EngineError { diff --git a/notifico-core/src/pipeline.rs b/notifico-core/src/pipeline.rs index 19412a5..6e21aba 100644 --- a/notifico-core/src/pipeline.rs +++ b/notifico-core/src/pipeline.rs @@ -1,5 +1,7 @@ +use crate::error::EngineError; use serde::{Deserialize, Serialize}; use serde_json::Value; +use uuid::Uuid; #[derive(Serialize, Deserialize, Default, Clone, Debug)] pub struct Pipeline { @@ -28,3 +30,7 @@ impl SerializedStep { Value::Object(self.into_inner()) } } + +pub trait PipelineStorage { + fn get_pipelines(&self, project: Uuid, event_name: &str) -> Result, EngineError>; +} diff --git a/notifico-core/src/recipient.rs b/notifico-core/src/recipient.rs index 9a5e3ee..c16fd19 100644 --- a/notifico-core/src/recipient.rs +++ b/notifico-core/src/recipient.rs @@ -34,3 +34,7 @@ impl Contact { self.0 } } + +pub trait RecipientDirectory { + fn get_recipient(&self, id: Uuid) -> Option; +} diff --git a/notifico-smtp/src/lib.rs b/notifico-smtp/src/lib.rs index b685b97..a331b32 100644 --- a/notifico-smtp/src/lib.rs +++ b/notifico-smtp/src/lib.rs @@ -93,7 +93,7 @@ impl EnginePlugin for EmailPlugin { let smtpcred: SmtpServerCredentials = match cred_selector { CredentialSelector::SmtpName { smtp_name } => self .credentials - .get_credential("smtp_server", &smtp_name)? + .get_credential(context.project_id, "smtp_server", &smtp_name)? .try_into()?, }; diff --git a/notifico-telegram/src/lib.rs b/notifico-telegram/src/lib.rs index 046a70a..58bccca 100644 --- a/notifico-telegram/src/lib.rs +++ b/notifico-telegram/src/lib.rs @@ -87,7 +87,7 @@ impl EnginePlugin for TelegramPlugin { let tgcred: TelegramBotCredentials = match cred_selector { CredentialSelector::BotName { bot_name } => self .credentials - .get_credential("telegram_token", &bot_name)? + .get_credential(context.project_id, "telegram_token", &bot_name)? .try_into()?, }; diff --git a/src/config.rs b/src/config.rs index b0943f4..87d46bf 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,31 +1,62 @@ use notifico_core::credentials::{Credential, Credentials}; use notifico_core::error::EngineError; -use notifico_core::pipeline::Pipeline; +use notifico_core::pipeline::{Pipeline, PipelineStorage}; use notifico_core::recipient::Recipient; use serde::Deserialize; use serde_json::Value; +use std::collections::HashMap; +use std::sync::Arc; +use uuid::Uuid; #[derive(Debug, Deserialize)] pub(crate) struct Config { - pub project: String, + pub projects: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct Project { + #[serde(default = "Uuid::nil")] + pub id: Uuid, pub pipelines: Vec, pub credentials: Vec, pub recipients: Vec, } +#[derive(Default)] pub struct SimpleCredentials { - creds: Vec, + creds: HashMap>, } impl SimpleCredentials { - pub fn new(creds: Vec) -> Self { - SimpleCredentials { creds } + pub fn from_config(config: &Config) -> Self { + let mut slf = Self::default(); + for project in config.projects.iter() { + slf.add_project(project.id, project.credentials.clone()); + } + slf + } + + fn add_project(&mut self, project: Uuid, pipelines: Vec) { + if self.creds.get(&project).is_some() { + panic!("Project already exists: {}", project); + } + + self.creds.insert(project, pipelines); } } impl Credentials for SimpleCredentials { - fn get_credential(&self, r#type: &str, name: &str) -> Result { - for cred in self.creds.iter() { + fn get_credential( + &self, + project: Uuid, + r#type: &str, + name: &str, + ) -> Result { + let Some(creds) = self.creds.get(&project) else { + return Err(EngineError::ProjectNotFound(project)); + }; + + for cred in creds.iter() { if cred.r#type == r#type && cred.name == name { return Ok(cred.value.clone()); } @@ -36,3 +67,51 @@ impl Credentials for SimpleCredentials { )) } } + +#[derive(Default)] +pub struct SimplePipelineStorage { + eventmap: HashMap>>>, +} + +impl SimplePipelineStorage { + pub fn from_config(config: &Config) -> Self { + let mut slf = Self::default(); + for project in config.projects.iter() { + slf.add_project(project.id, project.pipelines.clone()); + } + slf + } + + pub fn add_project(&mut self, project: Uuid, pipelines: Vec) { + if self.eventmap.get(&project).is_some() { + panic!("Project already exists: {}", project); + } + + let mut eventmap = HashMap::new(); + + for pipeline in pipelines.into_iter().map(|p| Arc::new(p)) { + for event in pipeline.events.iter().cloned() { + eventmap + .entry(event) + .or_insert_with(Vec::new) + .push(pipeline.clone()); + } + } + + self.eventmap.insert(project, eventmap); + } +} + +impl PipelineStorage for SimplePipelineStorage { + fn get_pipelines(&self, project: Uuid, event_name: &str) -> Result, EngineError> { + let Some(eventmap) = self.eventmap.get(&project) else { + return Err(EngineError::ProjectNotFound(project)); + }; + + if let Some(pipelines) = eventmap.get(event_name) { + Ok(pipelines.iter().map(|p| p.as_ref().clone()).collect()) + } else { + Ok(vec![]) + } + } +} diff --git a/src/engine/mod.rs b/src/engine.rs similarity index 98% rename from src/engine/mod.rs rename to src/engine.rs index 50b2b27..3ae879a 100644 --- a/src/engine/mod.rs +++ b/src/engine.rs @@ -39,7 +39,7 @@ impl Engine { #[instrument] pub(crate) async fn execute_step( - &mut self, + &self, context: &mut PipelineContext, step: &SerializedStep, ) -> Result<(), EngineError> { diff --git a/src/main.rs b/src/main.rs index e845ded..17378ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ pub mod engine; pub mod recipients; pub mod templater; -use crate::config::{Config, SimpleCredentials}; +use crate::config::{Config, SimpleCredentials, SimplePipelineStorage}; use crate::engine::Engine; use clap::Parser; use figment::{ @@ -11,6 +11,7 @@ use figment::{ Figment, }; use notifico_core::engine::{EventContext, PipelineContext}; +use notifico_core::pipeline::PipelineStorage; use notifico_core::recipient::Recipient; use notifico_smtp::EmailPlugin; use notifico_telegram::TelegramPlugin; @@ -62,36 +63,29 @@ async fn main() { info!("Received context: {:?}", event_context); let templater = Arc::new(TemplaterService::new("http://127.0.0.1:8000")); - let credentials = Arc::new(SimpleCredentials::new(config.credentials.clone())); + let credentials = Arc::new(SimpleCredentials::from_config(&config)); + let pipelines = SimplePipelineStorage::from_config(&config); let mut engine = Engine::new(); engine.add_plugin(TelegramPlugin::new(templater.clone(), credentials.clone())); engine.add_plugin(EmailPlugin::new(templater, credentials)); - let pipelines = { - let mut eventmap = HashMap::new(); - for pipeline in config.pipelines.iter() { - for event in pipeline.events.iter() { - eventmap - .entry(event) - .or_insert_with(Vec::new) - .push(pipeline); - } - } - eventmap - }; + let pipelines = pipelines + .get_pipelines(config.projects[0].id, &args.event) + .unwrap(); let recipinents: HashMap = - HashMap::from_iter(config.recipients.iter().map(|r| (r.id, r))); + HashMap::from_iter(config.projects[0].recipients.iter().map(|r| (r.id, r))); // Pipeline; { let mut context = PipelineContext::default(); + context.project_id = config.projects[0].id; context.recipient = Some(recipinents[&args.recipient].clone()); context.event_context = event_context; - for pipeline in pipelines.get(&args.event).unwrap() { + for pipeline in pipelines { for step in pipeline.steps.iter() { engine.execute_step(&mut context, step).await.unwrap() } diff --git a/src/recipients.rs b/src/recipients.rs index a0fc7ab..ad6fd89 100644 --- a/src/recipients.rs +++ b/src/recipients.rs @@ -1,11 +1,7 @@ -use notifico_core::recipient::Recipient; +use notifico_core::recipient::{Recipient, RecipientDirectory}; use std::collections::HashMap; use uuid::Uuid; -pub trait RecipientDirectory { - fn get_recipient(&self, id: Uuid) -> Option; -} - pub struct MemoryRecipientDirectory { directory: HashMap, }