1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
//! # MCAI Worker SDK
//!
//! This library is an SDK to communicate via message broker with [StepFlow](https://hexdocs.pm/step_flow/readme.html).
//! It's used for every worker as an abstraction.
//! It manage itself requirements, message parsing, direct messaging.
//!
//! ## Worker implementation
//!
//! 1. Create a Rust project
//! 2. Add MCAI Worker SDK as a dependency in Cargo.toml: `mcai_worker_sdk = "^1.0"`
//! 1. Update the main file with the example provided here to implement [MessageEvent](trait.MessageEvent.html) trait,
//! and call the [`start_worker`](fn.start_worker.html) to start the worker itself.
//!
//! ```rust
//! use mcai_worker_sdk::prelude::*;
//! use serde_derive::Deserialize;
//! use schemars::JsonSchema;
//!
//! #[derive(Debug)]
//! struct WorkerNameEvent {}
//!
//! #[derive(Debug, Deserialize, JsonSchema)]
//! struct WorkerParameters {}
//!
//! // For opensource workers
//! default_rust_mcai_worker_description!();
//! // Or uncomment one of these lines for licensed workers
//! // default_rust_mcai_worker_description!(Private);
//! // default_rust_mcai_worker_description!(Commercial);
//! // You can also specify the name of your organisation
//! // default_rust_mcai_worker_description!("My Organisation", Commercial);
//!
//! impl McaiWorker<WorkerParameters, RustMcaiWorkerDescription> for WorkerNameEvent {
//! }
//! static WORKER_NAME_EVENT: WorkerNameEvent = WorkerNameEvent {};
//!
//! // uncomment it to start the worker
//! // fn main() {
//! // mcai_worker_sdk::start_worker(&WORKER_NAME_EVENT);
//! // }
//! ```
//!
//! ## Runtime configuration
//!
//! ### AMQP connection
//!
//! | Variable | Description |
//! |-----------------------------|-------------|
//! | `AMQP_HOSTNAME` | IP or host of AMQP server (default: `localhost`) |
//! | `AMQP_PORT` | AMQP server port (default: `5672`) |
//! | `AMQP_TLS` | enable secure connection using AMQPS (default: `false`, enable with `true` or `1` or `TRUE` or `True`) |
//! | `AMQP_USERNAME` | Username used to connect to AMQP server (default: `guest`) |
//! | `AMQP_PASSWORD` | Password used to connect to AMQP server (default: `guest`) |
//! | `AMQP_VHOST` | AMQP virtual host (default: `/`) |
//! | `AMQP_QUEUE` | AMQP queue name used to receive job orders (default: `job_undefined`) |
//! | `AMQP_SERVER_CONFIGURATION` | RabbitMP configuration. Either standalone or cluster (default: `standalone`) |
//!
//! ### Vault connection
//!
//! | Variable | Description |
//! |--------------------|-------------|
//! | `BACKEND_HOSTNAME` | URL used to connect to backend server (default: `http://127.0.0.1:4000/api`) |
//! | `BACKEND_USERNAME` | Username used to connect to backend server |
//! | `BACKEND_PASSWORD` | Password used to connect to backend server |
//!
//! ## Start worker locally
//!
//! MCAI Worker SDK can be launched locally - without RabbitMQ.
//! It can process some message for different purpose (functional tests, message order examples, etc.).
//!
//! To start worker in this mode, setup the environment variable `SOURCE_ORDERS` with path(s) to json orders.
//! It can take multiple orders, joined with `:` on unix platform, `;` on windows os.
//!
//! ### Examples:
//!
//! ```bash
//! RUST_LOG=info SOURCE_ORDERS=./examples/success_order.json:./examples/error_order.json cargo run --example worker
//! ```
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
#[cfg(feature = "media")]
#[macro_use]
extern crate yaserde_derive;
pub mod config;
mod error;
mod http;
pub mod job;
mod logging;
pub mod mcai_worker;
#[cfg(feature = "media")]
pub mod media;
pub mod message_exchange;
pub mod parameter;
pub mod prelude;
#[cfg(feature = "media")]
mod process_result;
pub mod processor;
mod start_worker;
pub mod worker;
pub mod reexport {
pub use cargo_toml::Package;
pub use mcai_license;
pub use semver::Version;
}
pub use error::{MessageError, Result};
pub use message_exchange::message::publish_job_progression;
pub use parameter::container::ParametersContainer;
use message_exchange::WorkerResponseSender;
use processor::Processor;
use std::sync::{Arc, Mutex};
/// Exposed Channel type
pub type McaiChannel = Arc<Mutex<dyn WorkerResponseSender + Send>>;
#[macro_export]
macro_rules! default_rust_mcai_worker_description {
() => {
default_rust_mcai_worker_description! {
None
}
};
(Commercial) => {
default_rust_mcai_worker_description! {
None, Commercial
}
};
(Private) => {
default_rust_mcai_worker_description! {
None, Private
}
};
($organisation:literal) => {
default_rust_mcai_worker_description! {
Some($organisation.to_string())
}
};
($organisation:expr) => {
default_rust_mcai_worker_description! {
$organisation,
fn get_license(&self) -> mcai_worker_sdk::reexport::mcai_license::McaiWorkerLicense {
McaiWorkerLicense::OpenSource(OpenSourceLicense::new(
self
.package
.license
.as_ref()
.expect("Missing license in Cargo.toml. This field is required with a valid SPDX license for opensource workers.")
)
)
}
}
};
($organisation:literal, Commercial) => {
default_rust_mcai_worker_description! {
Some($organisation.to_string()),
Commercial
}
};
($organisation:literal, Private) => {
default_rust_mcai_worker_description! {
Some($organisation.to_string()),
Private
}
};
($organisation:expr, Commercial) => {
default_rust_mcai_worker_description! {
$organisation,
fn get_license(&self) -> mcai_worker_sdk::reexport::mcai_license::McaiWorkerLicense {
McaiWorkerLicense::Commercial
}
}
};
($organisation:expr, Private) => {
default_rust_mcai_worker_description! {
$organisation,
fn get_license(&self) -> mcai_worker_sdk::reexport::mcai_license::McaiWorkerLicense {
McaiWorkerLicense::Private
}
}
};
($organisation:expr, $($license:tt)*) => {
pub struct RustMcaiWorkerDescription {
pub package: mcai_worker_sdk::reexport::Package,
}
impl mcai_worker_sdk::mcai_worker::McaiWorkerDescription for RustMcaiWorkerDescription {
fn get_name(&self) -> String {
self.package.name.clone()
}
fn get_description(&self) -> String {
self.package.description.clone().unwrap_or_default()
}
fn get_version(&self) -> String {
self.package.version.clone()
}
$($license)*
fn get_authors(&self) -> Vec<String> {
self.package.authors.clone()
}
fn get_homepage(&self) -> Option<String> {
self.package.homepage.clone()
}
fn get_repository(&self) -> Option<String> {
self.package.repository.clone()
}
fn get_organisation(&self) -> Option<String> {
$organisation
}
}
impl Default for RustMcaiWorkerDescription {
fn default() -> Self {
Self {
package: include!(concat!(env!("OUT_DIR"), "/mcai_build.rs")),
}
}
}
};
}
#[test]
fn empty_worker_impl() {
use crate::prelude::*;
#[derive(Debug)]
struct CustomWorker {}
#[derive(JsonSchema, Deserialize)]
struct CustomParameters {}
#[derive(Default)]
struct Description;
impl McaiWorkerDescription for Description {
fn get_name(&self) -> String {
"Custom".to_string()
}
fn get_description(&self) -> String {
"Description".to_string()
}
fn get_version(&self) -> String {
"1.2.3".to_string()
}
fn get_license(&self) -> McaiWorkerLicense {
McaiWorkerLicense::OpenSource(OpenSourceLicense::new("MIT"))
}
}
impl McaiWorker<CustomParameters, Description> for CustomWorker {
fn get_mcai_worker_description(&self) -> Box<Description> {
Box::new(Description)
}
}
let custom_worker = CustomWorker {};
let parameters = CustomParameters {};
let job = job::Job {
job_id: 1234,
parameters: vec![],
};
let job_result = job::JobResult::new(job.job_id);
let result = custom_worker.process(None, parameters, job_result);
assert!(result == Err(MessageError::NotImplemented()));
}