drogue_client/registry/v1/data/app/
process.rs

1use crate::{dialect, registry::v1::ExternalEndpoint, serde::is_default};
2use serde::{Deserialize, Serialize};
3
4#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
5#[serde(rename_all = "camelCase")]
6pub struct PublishSpec {
7    #[serde(default)]
8    #[serde(skip_serializing_if = "Vec::is_empty")]
9    pub rules: Vec<Rule>,
10}
11
12dialect!(PublishSpec[crate::Section::Spec => "publish"]);
13
14#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct CommandSpec {
17    #[serde(default)]
18    #[serde(skip_serializing_if = "Vec::is_empty")]
19    pub rules: Vec<Rule>,
20}
21
22dialect!(CommandSpec[crate::Section::Spec => "command"]);
23
24#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct Rule {
27    #[serde(default)]
28    pub when: When,
29    #[serde(default)]
30    pub then: Vec<Step>,
31}
32
33#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
34#[serde(rename_all = "camelCase")]
35pub enum When {
36    Always,
37    IsChannel(String),
38    Not(Box<When>),
39    And(Vec<When>),
40    Or(Vec<When>),
41}
42
43impl Default for When {
44    fn default() -> Self {
45        Self::Always
46    }
47}
48
49#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(rename_all = "camelCase")]
51pub enum Step {
52    /// Drop the event.
53    Drop,
54    /// Reject the event.
55    Reject(String),
56    /// Stop processing and accept the event.
57    Break,
58    /// Set (replace or add) a cloud events attribute.
59    SetAttribute { name: String, value: String },
60    /// Remove a cloud events attribute. Ensure that you don't remove a require one.
61    RemoveAttribute(String),
62    /// Set (replace or add) an extension.
63    SetExtension { name: String, value: String },
64    /// Remove an extension.
65    RemoveExtension(String),
66    /// Validate the event using an external endpoint.
67    Validate(ValidateSpec),
68    /// Enrich the event using an external endpoint.
69    Enrich(EnrichSpec),
70}
71
72#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
73#[serde(rename_all = "camelCase")]
74pub struct EnrichSpec {
75    #[serde(default)]
76    #[serde(skip_serializing_if = "is_default")]
77    pub request: RequestType,
78    #[serde(default)]
79    #[serde(skip_serializing_if = "is_default")]
80    pub response: ResponseType,
81    pub endpoint: ExternalEndpoint,
82}
83
84#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
85#[serde(rename_all = "camelCase")]
86pub struct ValidateSpec {
87    #[serde(default)]
88    #[serde(skip_serializing_if = "is_default")]
89    pub request: RequestType,
90    pub endpoint: ExternalEndpoint,
91}
92
93#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
94#[serde(rename_all = "camelCase")]
95#[serde(tag = "type")]
96pub enum RequestType {
97    /// Send a cloud event
98    CloudEvent {
99        #[serde(default)]
100        #[serde(skip_serializing_if = "is_default")]
101        mode: ContentMode,
102    },
103}
104
105impl Default for RequestType {
106    fn default() -> Self {
107        Self::CloudEvent {
108            mode: Default::default(),
109        }
110    }
111}
112
113#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
114#[serde(rename_all = "camelCase")]
115pub enum ContentMode {
116    Binary,
117    Structured,
118}
119
120impl Default for ContentMode {
121    fn default() -> Self {
122        Self::Binary
123    }
124}
125
126#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
127#[serde(rename_all = "camelCase")]
128#[serde(tag = "type")]
129pub enum ResponseType {
130    /// Expect a cloud event, fails otherwise.
131    CloudEvent,
132    /// Only consume payload, keep metadata, except content-type.
133    Raw,
134    /// Assume content to be a structured mode cloud event, ignoring the content type.
135    AssumeStructuredCloudEvent,
136}
137
138impl Default for ResponseType {
139    fn default() -> Self {
140        Self::CloudEvent
141    }
142}
143
144#[cfg(test)]
145mod test {
146
147    use super::*;
148    use serde_json::json;
149
150    #[test]
151    fn default_empty() {
152        let spec: PublishSpec = serde_json::from_value(json!({})).unwrap();
153        assert_eq!(PublishSpec::default(), spec);
154    }
155
156    #[test]
157    fn example1() {
158        let spec: PublishSpec = serde_json::from_value(json!({
159            "rules":[
160                {
161                    "when": {
162                        "and": [
163                            { "isChannel": "chan1" },
164                            { "not": {
165                                "or" : [
166                                    { "isChannel": "chan2" },
167                                    { "isChannel": "chan3" },
168                                ],
169                            } },
170                        ]
171                    },
172                    "then": [
173                        {
174                            "setExtension": {
175                                "name": "ext1",
176                                "value": "value1",
177                            },
178                        },
179                        {
180                            "removeExtension": "ext2",
181                        },
182                    ],
183                }
184            ]
185        }))
186        .unwrap();
187        assert_eq!(
188            PublishSpec {
189                rules: vec![Rule {
190                    when: When::And(vec![
191                        When::IsChannel("chan1".to_string()),
192                        When::Not(Box::new(When::Or(vec![
193                            When::IsChannel("chan2".to_string()),
194                            When::IsChannel("chan3".to_string()),
195                        ])))
196                    ]),
197                    then: vec![
198                        Step::SetExtension {
199                            name: "ext1".to_string(),
200                            value: "value1".to_string()
201                        },
202                        Step::RemoveExtension("ext2".to_string()),
203                    ],
204                }],
205            },
206            spec
207        );
208    }
209
210    #[test]
211    fn test_deser_1() {
212        assert_eq!(
213            EnrichSpec {
214                request: Default::default(),
215                response: Default::default(),
216                endpoint: ExternalEndpoint {
217                    method: None,
218                    url: "http://localhost:1234".to_string(),
219                    tls: None,
220                    auth: Default::default(),
221                    headers: vec![],
222                    timeout: None
223                }
224            },
225            serde_json::from_value(json!({
226                "request": {
227                    "type": "cloudEvent",
228                },
229                "response": {
230                    "type": "cloudEvent",
231                },
232                "endpoint": {
233                    "url": "http://localhost:1234"
234                }
235            }))
236            .unwrap()
237        );
238    }
239
240    #[test]
241    fn test_deser_2() {
242        assert_eq!(
243            EnrichSpec {
244                request: RequestType::CloudEvent {
245                    mode: ContentMode::Structured
246                },
247                response: Default::default(),
248                endpoint: ExternalEndpoint {
249                    method: None,
250                    url: "http://localhost:1234".to_string(),
251                    tls: None,
252                    auth: Default::default(),
253                    headers: vec![],
254                    timeout: None
255                }
256            },
257            serde_json::from_value(json!({
258                "request": {
259                    "type": "cloudEvent",
260                    "mode": "structured",
261                },
262                "response": {
263                    "type": "cloudEvent",
264                },
265                "endpoint": {
266                    "url": "http://localhost:1234"
267                }
268            }))
269            .unwrap()
270        );
271    }
272
273    #[test]
274    fn test_deser_32() {
275        let spec: PublishSpec = serde_json::from_value(json!({
276            "rules":[
277                {
278                    "when": {
279                        "not": "always",
280                    },
281                    "then": [],
282                }
283            ]
284        }))
285        .unwrap();
286        assert_eq!(
287            PublishSpec {
288                rules: vec![Rule {
289                    when: When::Not(Box::new(When::Always)),
290                    then: vec![],
291                }],
292            },
293            spec
294        );
295    }
296}