Skip to main content

spin_manifest/schema/v2/
trigger.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use spin_serde::KebabId;
4
5use super::{Component, Map, TriggerDependency, json_schema, one_or_many};
6
7/// Trigger configuration. A trigger maps an event of the trigger's type (e.g.
8/// an HTTP request on route `/shop`, a Redis message on channel `orders`) to
9/// a Spin component.
10///
11/// The trigger manifest contains additional fields which depend on the trigger
12/// type. For the `http` type, these additional fields are `route` (required) and
13/// `executor` (optional). For the `redis` type, the additional fields are
14/// `channel` (required) and `address` (optional). For other types, see the trigger
15/// documentation.
16///
17/// Learn more: https://spinframework.dev/http-trigger, https://spinframework.dev/redis-trigger
18#[derive(Clone, Debug, Serialize, Deserialize)]
19pub struct Trigger {
20    /// Optional identifier for the trigger.
21    ///
22    /// Example: `id = "trigger-id"`
23    #[serde(default, skip_serializing_if = "String::is_empty")]
24    pub id: String,
25    /// The component that Spin should run when the trigger occurs. For HTTP triggers,
26    /// this is the HTTP request handler for the trigger route. This is typically
27    /// the ID of an entry in the `[component]` table, although you can also write
28    /// the component out as the value of this field.
29    ///
30    /// Example: `component = "shop-handler"`
31    ///
32    /// Learn more: https://spinframework.dev/triggers#triggers-and-components
33    #[serde(default, skip_serializing_if = "Option::is_none")]
34    pub component: Option<ComponentSpec>,
35    /// Additional components used when the trigger occurs.
36    /// The meaning of entries in this table is trigger-specific.
37    ///
38    /// `components = { ... }`
39    #[serde(default, skip_serializing_if = "Map::is_empty")]
40    pub components: Map<String, OneOrManyComponentSpecs>,
41    /// Additional components to be invoked during trigger processing.
42    /// The meaning of entries in this table is trigger-specific.
43    ///
44    /// `dependencies = { ... }`
45    #[serde(default, skip_serializing_if = "Map::is_empty")]
46    pub dependencies: Map<String, TriggerDependencies>,
47    /// Opaque trigger-type-specific config
48    #[serde(flatten)]
49    pub config: toml::Table,
50}
51
52/// One or many `ComponentSpec`(s)
53#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
54#[serde(transparent)]
55pub struct OneOrManyComponentSpecs(
56    #[serde(with = "one_or_many")]
57    #[schemars(schema_with = "json_schema::one_or_many::<ComponentSpec>")]
58    pub Vec<ComponentSpec>,
59);
60
61/// One or many `ComponentSpec`(s)
62#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
63#[serde(transparent)]
64pub struct TriggerDependencies(pub Vec<TriggerDependency>);
65
66/// Component reference or inline definition
67#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
68#[serde(deny_unknown_fields, untagged, try_from = "toml::Value")]
69#[schemars(schema_with = "json_schema::id_or_component")]
70pub enum ComponentSpec {
71    /// `"component-id"`
72    Reference(KebabId),
73    /// `{ ... }`
74    Inline(Box<Component>),
75}
76
77impl TryFrom<toml::Value> for ComponentSpec {
78    type Error = toml::de::Error;
79
80    fn try_from(value: toml::Value) -> Result<Self, Self::Error> {
81        if value.is_str() {
82            Ok(ComponentSpec::Reference(KebabId::deserialize(value)?))
83        } else {
84            Ok(ComponentSpec::Inline(Box::new(Component::deserialize(
85                value,
86            )?)))
87        }
88    }
89}