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}