use serde::Serialize;
use serde_json::Value;
use crate::connector::ConnectorType;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum FieldKind {
String,
Number,
Bool,
Object,
Array,
Any,
}
impl FieldKind {
pub fn as_str(self) -> &'static str {
match self {
FieldKind::String => "string",
FieldKind::Number => "number",
FieldKind::Bool => "bool",
FieldKind::Object => "object",
FieldKind::Array => "array",
FieldKind::Any => "any",
}
}
pub(super) fn matches(self, v: &Value) -> bool {
match self {
FieldKind::String => v.is_string(),
FieldKind::Number => v.is_number(),
FieldKind::Bool => v.is_boolean(),
FieldKind::Object => v.is_object(),
FieldKind::Array => v.is_array(),
FieldKind::Any => true,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct FieldSchema {
pub name: &'static str,
pub description: &'static str,
pub kind: FieldKind,
pub required: bool,
pub resolvable: bool,
pub secret_at: &'static [&'static str],
pub template_at: &'static [&'static str],
pub alias: Option<&'static str>,
}
impl FieldSchema {
pub const DEFAULT: Self = FieldSchema {
name: "",
description: "",
kind: FieldKind::Any,
required: false,
resolvable: false,
secret_at: &[],
template_at: &[],
alias: None,
};
}
pub type StaticValidator =
fn(&serde_json::Map<String, Value>) -> Vec<(&'static str, &'static str, String)>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub enum WriteShape {
OutputPath { default_root: Option<&'static str> },
Target,
Mappings,
Nothing,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum RetrySafety {
Pure,
Read,
IdempotentWrite,
UnsafeWrite,
DependsOn { input: &'static str },
}
impl RetrySafety {
pub fn as_str(self) -> &'static str {
match self {
Self::Pure => "pure",
Self::Read => "read",
Self::IdempotentWrite => "idempotent_write",
Self::UnsafeWrite => "unsafe_write",
Self::DependsOn { .. } => "depends_on",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ConnectorRule {
pub types: &'static [ConnectorType],
pub requires_mongo_database: bool,
}
impl ConnectorRule {
const fn of(types: &'static [ConnectorType]) -> Option<Self> {
Some(Self {
types,
requires_mongo_database: false,
})
}
const fn mongo(types: &'static [ConnectorType]) -> Option<Self> {
Some(Self {
types,
requires_mongo_database: true,
})
}
}
#[derive(Debug, Clone, Serialize)]
pub struct FunctionSchema {
pub name: &'static str,
pub description: &'static str,
pub category: &'static str,
pub input_fields: &'static [FieldSchema],
pub writes: WriteShape,
pub retry_safety: RetrySafety,
#[serde(skip)]
pub connector: Option<ConnectorRule>,
pub deny_unknown: bool,
#[serde(skip)]
pub validate_static: Option<StaticValidator>,
}
use super::cache_read::CACHE_READ_FIELDS;
use super::cache_write::CACHE_WRITE_FIELDS;
use super::channel_call::CHANNEL_CALL_FIELDS;
use super::crypto::CRYPTO_FIELDS;
use super::data_query::DATA_QUERY_FIELDS;
use super::data_write::DATA_WRITE_FIELDS;
use super::db_read::DB_READ_FIELDS;
use super::db_write::DB_WRITE_FIELDS;
use super::http_call::HTTP_CALL_FIELDS;
use super::jwt_sign::JWT_SIGN_FIELDS;
use super::jwt_verify::JWT_VERIFY_FIELDS;
use super::model_infer::MODEL_INFER_FIELDS;
use super::mongo_aggregate::MONGO_AGGREGATE_FIELDS;
use super::mongo_read::MONGO_READ_FIELDS;
use super::mongo_write::MONGO_WRITE_FIELDS;
use super::publish_kafka::PUBLISH_KAFKA_FIELDS;
use super::send_email::SEND_EMAIL_FIELDS;
use super::storage_head::STORAGE_HEAD_FIELDS;
use super::storage_presign::STORAGE_PRESIGN_FIELDS;
const REGISTRY: &[FunctionSchema] = &[
FunctionSchema {
name: "cache_read",
description: "Read a value from a cache connector (Redis or in-memory).",
category: "connector",
input_fields: CACHE_READ_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::Read,
connector: ConnectorRule::of(&[ConnectorType::Cache]),
deny_unknown: false,
validate_static: None,
},
FunctionSchema {
name: "cache_write",
description: "Write a value to a cache connector.",
category: "connector",
input_fields: CACHE_WRITE_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::IdempotentWrite,
connector: ConnectorRule::of(&[ConnectorType::Cache]),
deny_unknown: false,
validate_static: None,
},
FunctionSchema {
name: "db_read",
description: "Execute a SELECT against a SQL connector.",
category: "connector",
input_fields: DB_READ_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::Read,
connector: ConnectorRule::of(&[ConnectorType::Db]),
deny_unknown: false,
validate_static: None,
},
FunctionSchema {
name: "db_write",
description: "Execute INSERT/UPDATE/DELETE against a SQL connector.",
category: "connector",
input_fields: DB_WRITE_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::DependsOn { input: "sql" },
connector: ConnectorRule::of(&[ConnectorType::Db]),
deny_unknown: false,
validate_static: None,
},
FunctionSchema {
name: "data_query",
description: "Run a backend-neutral query (filter + envelope) against a SQL, MongoDB, or Elasticsearch connector.",
category: "connector",
input_fields: DATA_QUERY_FIELDS,
writes: WriteShape::OutputPath {
default_root: Some("data"),
},
retry_safety: RetrySafety::Read,
connector: ConnectorRule::mongo(&[ConnectorType::Db, ConnectorType::Es]),
deny_unknown: false,
validate_static: None,
},
FunctionSchema {
name: "data_write",
description: "Run a backend-neutral mutation (insert/update/delete/upsert) against a SQL, MongoDB, or Elasticsearch connector.",
category: "connector",
input_fields: DATA_WRITE_FIELDS,
writes: WriteShape::OutputPath {
default_root: Some("data"),
},
retry_safety: RetrySafety::DependsOn { input: "op" },
connector: ConnectorRule::mongo(&[ConnectorType::Db, ConnectorType::Es]),
deny_unknown: false,
validate_static: None,
},
FunctionSchema {
name: "mongo_read",
description: "Run find() against a MongoDB connector, with optional projection/sort/limit/skip.",
category: "connector",
input_fields: MONGO_READ_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::Read,
connector: ConnectorRule::mongo(&[ConnectorType::Db]),
deny_unknown: false,
validate_static: None,
},
FunctionSchema {
name: "mongo_write",
description: "Write documents to a MongoDB connector: insert/update/replace/delete, nested documents as extended JSON.",
category: "connector",
input_fields: MONGO_WRITE_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::DependsOn { input: "op" },
connector: ConnectorRule::mongo(&[ConnectorType::Db]),
deny_unknown: true,
validate_static: Some(super::mongo_write::validate_static_input),
},
FunctionSchema {
name: "mongo_aggregate",
description: "Run an aggregation pipeline against a MongoDB connector (stage-allowlisted; $out/$merge behind a connector opt-in).",
category: "connector",
input_fields: MONGO_AGGREGATE_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::Read,
connector: ConnectorRule::mongo(&[ConnectorType::Db]),
deny_unknown: true,
validate_static: Some(super::mongo_aggregate::validate_static_input),
},
FunctionSchema {
name: "channel_call",
description: "Invoke another channel's workflow in-process (no HTTP hop).",
category: "control",
input_fields: CHANNEL_CALL_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::DependsOn { input: "channel" },
connector: None,
deny_unknown: false,
validate_static: None,
},
FunctionSchema {
name: "crypto",
description: "Digests, HMAC compute/verify, and password hashing — a self-contained operation envelope.",
category: "utility",
input_fields: CRYPTO_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::Pure,
connector: None,
deny_unknown: true,
validate_static: Some(super::crypto::validate_static_input),
},
FunctionSchema {
name: "jwt_sign",
description: "Mint a signed JWT (login, refresh, client assertions).",
category: "utility",
input_fields: JWT_SIGN_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::Pure,
connector: None,
deny_unknown: true,
validate_static: Some(super::jwt_sign::validate_static_input),
},
FunctionSchema {
name: "jwt_verify",
description: "Verify a JWT mid-workflow (provider id_tokens, refresh tokens) against static keys or a JWKS.",
category: "utility",
input_fields: JWT_VERIFY_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::Read,
connector: None,
deny_unknown: true,
validate_static: Some(super::jwt_verify::validate_static_input),
},
FunctionSchema {
name: "model_infer",
description: "Run an admitted ONNX model on the named runtime: the manifest's adapters turn `input` into tensors and the outputs back into JSON.",
category: "compute",
input_fields: MODEL_INFER_FIELDS,
writes: WriteShape::OutputPath {
default_root: Some(crate::model::handler::DEFAULT_OUTPUT),
},
retry_safety: RetrySafety::Pure,
connector: None,
deny_unknown: true,
validate_static: Some(super::model_infer::validate_static_input),
},
FunctionSchema {
name: "http_call",
description: "HTTP request to an HTTP connector with retry + circuit breaker.",
category: "connector",
input_fields: HTTP_CALL_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::DependsOn { input: "method" },
connector: ConnectorRule::of(&[ConnectorType::Http]),
deny_unknown: true,
validate_static: None,
},
FunctionSchema {
name: "send_email",
description: "Send an email through an SMTP connector.",
category: "connector",
input_fields: SEND_EMAIL_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::UnsafeWrite,
connector: ConnectorRule::of(&[ConnectorType::Smtp]),
deny_unknown: true,
validate_static: Some(super::send_email::validate_static_input),
},
FunctionSchema {
name: "storage_presign",
description: "Compute a time-limited presigned URL for one object — no data path.",
category: "connector",
input_fields: STORAGE_PRESIGN_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::Pure,
connector: ConnectorRule::of(&[ConnectorType::Storage]),
deny_unknown: true,
validate_static: Some(super::storage_presign::validate_static_input),
},
FunctionSchema {
name: "storage_head",
description: "Object metadata (exists/size/etag) from a storage connector.",
category: "connector",
input_fields: STORAGE_HEAD_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::Read,
connector: ConnectorRule::of(&[ConnectorType::Storage]),
deny_unknown: true,
validate_static: None,
},
FunctionSchema {
name: "publish_kafka",
description: "Publish a message to a Kafka topic via a Kafka connector.",
category: "connector",
input_fields: PUBLISH_KAFKA_FIELDS,
writes: WriteShape::OutputPath { default_root: None },
retry_safety: RetrySafety::UnsafeWrite,
connector: ConnectorRule::of(&[ConnectorType::Kafka]),
deny_unknown: true,
validate_static: None,
},
];
pub fn registry() -> &'static [FunctionSchema] {
REGISTRY
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Source {
Engine,
Orion,
Plugin,
}
impl Source {
pub fn as_str(self) -> &'static str {
match self {
Self::Engine => "engine",
Self::Orion => "orion",
Self::Plugin => "plugin",
}
}
}
pub(super) const ENGINE_BUILTINS: &[(&str, &str, &[&str], WriteShape, RetrySafety)] = &[
(
"parse_json",
"Parse the raw payload into the data context.",
&[],
WriteShape::Target,
RetrySafety::Pure,
),
(
"parse_xml",
"Parse an XML payload into the data context.",
&[],
WriteShape::Target,
RetrySafety::Pure,
),
(
"map",
"Transform and reshape data with JSONLogic mappings.",
&[],
WriteShape::Mappings,
RetrySafety::Pure,
),
(
"filter",
"Gate the pipeline on a JSONLogic condition.",
&[],
WriteShape::Nothing,
RetrySafety::Pure,
),
(
"validation",
"Collect validation errors from JSONLogic rules.",
&["validate"],
WriteShape::Nothing,
RetrySafety::Pure,
),
(
"log",
"Emit a structured log line.",
&[],
WriteShape::Nothing,
RetrySafety::Pure,
),
(
"publish_json",
"Serialize a context field to a JSON string.",
&[],
WriteShape::Target,
RetrySafety::Pure,
),
(
"publish_xml",
"Serialize a context field to an XML string.",
&[],
WriteShape::Target,
RetrySafety::Pure,
),
];
pub(super) fn static_field_name(
fields: &[FieldSchema],
key: &str,
fallback: &'static str,
) -> &'static str {
fields
.iter()
.map(|f| f.name)
.find(|n| *n == key)
.unwrap_or(fallback)
}