cloudevents_sdk_reqwest/
client_request.rs1use super::headers;
2use cloudevents::event::SpecVersion;
3use cloudevents::message::{
4 BinaryDeserializer, BinarySerializer, MessageAttributeValue, Result, StructuredSerializer,
5};
6use cloudevents::Event;
7use reqwest::RequestBuilder;
8use std::str::FromStr;
9
10pub struct RequestSerializer {
12 req: RequestBuilder,
13}
14
15impl RequestSerializer {
16 pub fn new(req: RequestBuilder) -> RequestSerializer {
17 RequestSerializer { req }
18 }
19}
20
21impl BinarySerializer<RequestBuilder> for RequestSerializer {
22 fn set_spec_version(mut self, spec_version: SpecVersion) -> Result<Self> {
23 self.req = self
24 .req
25 .header(headers::SPEC_VERSION_HEADER.clone(), spec_version.as_str());
26 Ok(self)
27 }
28
29 fn set_attribute(mut self, name: &str, value: MessageAttributeValue) -> Result<Self> {
30 self.req = self.req.header(
31 headers::ATTRIBUTES_TO_HEADERS.get(name).unwrap().clone(),
32 value.to_string(),
33 );
34 Ok(self)
35 }
36
37 fn set_extension(mut self, name: &str, value: MessageAttributeValue) -> Result<Self> {
38 self.req = self
39 .req
40 .header(attribute_name_to_header!(name)?, value.to_string());
41 Ok(self)
42 }
43
44 fn end_with_data(self, bytes: Vec<u8>) -> Result<RequestBuilder> {
45 Ok(self.req.body(bytes))
46 }
47
48 fn end(self) -> Result<RequestBuilder> {
49 Ok(self.req)
50 }
51}
52
53impl StructuredSerializer<RequestBuilder> for RequestSerializer {
54 fn set_structured_event(self, bytes: Vec<u8>) -> Result<RequestBuilder> {
55 Ok(self
56 .req
57 .header(
58 reqwest::header::CONTENT_TYPE,
59 headers::CLOUDEVENTS_JSON_HEADER.clone(),
60 )
61 .body(bytes))
62 }
63}
64
65pub fn event_to_request(event: Event, request_builder: RequestBuilder) -> Result<RequestBuilder> {
67 BinaryDeserializer::deserialize_binary(event, RequestSerializer::new(request_builder))
68}
69
70pub trait RequestBuilderExt: private::Sealed {
74 fn event(self, event: Event) -> Result<RequestBuilder>;
76}
77
78impl RequestBuilderExt for RequestBuilder {
79 fn event(self, event: Event) -> Result<RequestBuilder> {
80 event_to_request(event, self)
81 }
82}
83
84mod private {
86 pub trait Sealed {}
87 impl Sealed for reqwest::RequestBuilder {}
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use mockito::{mock, Matcher};
94
95 use cloudevents::message::StructuredDeserializer;
96 use cloudevents::{EventBuilder, EventBuilderV10};
97 use serde_json::json;
98 use url::Url;
99
100 #[tokio::test]
101 async fn test_request() {
102 let url = mockito::server_url();
103 let m = mock("POST", "/")
104 .match_header("ce-specversion", "1.0")
105 .match_header("ce-id", "0001")
106 .match_header("ce-type", "example.test")
107 .match_header("ce-source", "http://localhost/")
108 .match_header("ce-someint", "10")
109 .match_body(Matcher::Missing)
110 .create();
111
112 let input = EventBuilderV10::new()
113 .id("0001")
114 .ty("example.test")
115 .source(Url::from_str("http://localhost/").unwrap())
116 .extension("someint", "10")
117 .build()
118 .unwrap();
119
120 let client = reqwest::Client::new();
121 client
122 .post(&url)
123 .event(input)
124 .unwrap()
125 .send()
126 .await
127 .unwrap();
128
129 m.assert();
130 }
131
132 #[tokio::test]
133 async fn test_request_with_full_data() {
134 let j = json!({"hello": "world"});
135
136 let url = mockito::server_url();
137 let m = mock("POST", "/")
138 .match_header("ce-specversion", "1.0")
139 .match_header("ce-id", "0001")
140 .match_header("ce-type", "example.test")
141 .match_header("ce-source", "http://localhost/")
142 .match_header("content-type", "application/json")
143 .match_header("ce-someint", "10")
144 .match_body(Matcher::Exact(j.to_string()))
145 .create();
146
147 let input = EventBuilderV10::new()
148 .id("0001")
149 .ty("example.test")
150 .source(Url::from_str("http://localhost").unwrap())
151 .data("application/json", j.clone())
152 .extension("someint", "10")
153 .build()
154 .unwrap();
155
156 let client = reqwest::Client::new();
157
158 client
159 .post(&url)
160 .event(input)
161 .unwrap()
162 .send()
163 .await
164 .unwrap();
165
166 m.assert();
167 }
168
169 #[tokio::test]
170 async fn test_structured_request_with_full_data() {
171 let j = json!({"hello": "world"});
172
173 let input = EventBuilderV10::new()
174 .id("0001")
175 .ty("example.test")
176 .source(Url::from_str("http://localhost").unwrap())
177 .data("application/json", j.clone())
178 .extension("someint", "10")
179 .build()
180 .unwrap();
181
182 let url = mockito::server_url();
183 let m = mock("POST", "/")
184 .match_header("content-type", "application/cloudevents+json")
185 .match_body(Matcher::Exact(serde_json::to_string(&input).unwrap()))
186 .create();
187
188 let client = reqwest::Client::new();
189 StructuredDeserializer::deserialize_structured(
190 input,
191 RequestSerializer::new(client.post(&url)),
192 )
193 .unwrap()
194 .send()
195 .await
196 .unwrap();
197
198 m.assert();
199 }
200}