Skip to main content

cloudevents/binding/axum/
mod.rs

1//! This module integrates the [cloudevents-sdk](https://docs.rs/cloudevents-sdk) with [Axum web service framework](https://docs.rs/axum/)
2//! to easily send and receive CloudEvents.
3//!
4//! To deserialize an HTTP request as CloudEvent
5//!
6//! To echo events:
7//!
8//! ```
9//! use axum_lib as axum;
10//! use axum::{
11//!     routing::{get, post},
12//!     Router,
13//! };
14//! use cloudevents::Event;
15//! use http::StatusCode;
16//!
17//! fn app() -> Router {
18//!     Router::new()
19//!         .route("/", get(|| async { "hello from cloudevents server" }))
20//!         .route(
21//!             "/",
22//!             post(|event: Event| async move {
23//!                 println!("received cloudevent {}", &event);
24//!                 (StatusCode::OK, event)
25//!             }),
26//!         )
27//! }
28//!
29//! ```
30//!
31//! To create event inside request handlers and send them as responses:
32//!
33//! ```
34//! use axum_lib as axum;
35//! use axum::{
36//!     routing::{get, post},
37//!     Router,
38//! };
39//! use cloudevents::{Event, EventBuilder, EventBuilderV10};
40//! use http::StatusCode;
41//! use serde_json::json;
42//!
43//! fn app() -> Router {
44//!     Router::new()
45//!         .route("/", get(|| async { "hello from cloudevents server" }))
46//!         .route(
47//!             "/",
48//!             post(|| async move {
49//!                 let event = EventBuilderV10::new()
50//!                     .id("1")
51//!                     .source("url://example_response/")
52//!                     .ty("example.ce")
53//!                     .data(
54//!                         mime::APPLICATION_JSON.to_string(),
55//!                         json!({
56//!                             "name": "John Doe",
57//!                             "age": 43,
58//!                             "phones": [
59//!                                 "+44 1234567",
60//!                                 "+44 2345678"
61//!                             ]
62//!                         }),
63//!                     )
64//!                     .build()
65//!                     .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
66//!
67//!                 Ok::<Event, (StatusCode, String)>(event)
68//!             }),
69//!         )
70//! }
71//!
72//! ```
73
74pub mod extract;
75pub mod response;
76
77#[cfg(test)]
78mod tests {
79
80    use axum_lib as axum;
81
82    use axum::{
83        body::Body,
84        http::{self, Request, StatusCode},
85        routing::{get, post},
86        Router,
87    };
88    use chrono::Utc;
89    use serde_json::json;
90    use tower::ServiceExt; // for `app.oneshot()`
91
92    use crate::Event;
93
94    fn echo_app() -> Router {
95        Router::new()
96            .route("/", get(|| async { "hello from cloudevents server" }))
97            .route(
98                "/",
99                post(|event: Event| async move {
100                    println!("received cloudevent {}", &event);
101                    (StatusCode::OK, event)
102                }),
103            )
104    }
105
106    #[tokio::test]
107    async fn axum_mod_test() {
108        let app = echo_app();
109        let time = Utc::now();
110        let j = json!({"hello": "world"});
111        let request = Request::builder()
112            .method(http::Method::POST)
113            .header("ce-specversion", "1.0")
114            .header("ce-id", "0001")
115            .header("ce-type", "example.test")
116            .header("ce-source", "http://localhost/")
117            .header("ce-someint", "10")
118            .header("ce-time", time.to_rfc3339())
119            .header("content-type", "application/json")
120            .body(Body::from(serde_json::to_vec(&j).unwrap()))
121            .unwrap();
122
123        let resp = app.oneshot(request).await.unwrap();
124        assert_eq!(
125            resp.headers()
126                .get("ce-specversion")
127                .unwrap()
128                .to_str()
129                .unwrap(),
130            "1.0"
131        );
132        assert_eq!(
133            resp.headers().get("ce-id").unwrap().to_str().unwrap(),
134            "0001"
135        );
136        assert_eq!(
137            resp.headers().get("ce-type").unwrap().to_str().unwrap(),
138            "example.test"
139        );
140        assert_eq!(
141            resp.headers().get("ce-source").unwrap().to_str().unwrap(),
142            "http://localhost/"
143        );
144        assert_eq!(
145            resp.headers()
146                .get("content-type")
147                .unwrap()
148                .to_str()
149                .unwrap(),
150            "application/json"
151        );
152        assert_eq!(
153            resp.headers().get("ce-someint").unwrap().to_str().unwrap(),
154            "10"
155        );
156
157        let (_, body) = resp.into_parts();
158        let body = axum::body::to_bytes(body, usize::MAX).await.unwrap();
159
160        assert_eq!(j.to_string().as_bytes(), body);
161    }
162}