Redesigned EventBuilder (#53)
* Progress Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> * Builder finished Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> * Fixed all integrations Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> * Fmt'ed Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> * Fmt'ed part 2 Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> * Fixed tests in reqwest integration Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> * fmt'ed again Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
This commit is contained in:
parent
2b91ee8d7a
commit
b832b6bcf4
|
@ -35,14 +35,15 @@ cloudevents-sdk = "0.1.0"
|
|||
Now you can start creating events:
|
||||
|
||||
```rust
|
||||
use cloudevents::EventBuilder;
|
||||
use cloudevents::{EventBuilder, EventBuilderV10};
|
||||
use url::Url;
|
||||
|
||||
let event = EventBuilder::v03()
|
||||
let event = EventBuilderV10::new()
|
||||
.id("aaa")
|
||||
.source(Url::parse("http://localhost").unwrap())
|
||||
.ty("example.demo")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
```
|
||||
|
||||
Checkout the examples using our integrations with `actix-web` and `reqwest` to learn how to send and receive events:
|
||||
|
|
|
@ -17,7 +17,8 @@ actix-rt = "1"
|
|||
lazy_static = "1.4.0"
|
||||
bytes = "^0.5"
|
||||
futures = "^0.3"
|
||||
serde_json = "^1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
url = { version = "^2.1", features = ["serde"] }
|
||||
url = { version = "^2.1", features = ["serde"] }
|
||||
serde_json = "^1.0"
|
||||
chrono = { version = "^0.4", features = ["serde"] }
|
|
@ -117,25 +117,32 @@ mod tests {
|
|||
use actix_web::test;
|
||||
use url::Url;
|
||||
|
||||
use cloudevents::EventBuilder;
|
||||
use chrono::Utc;
|
||||
use cloudevents::{EventBuilder, EventBuilderV10};
|
||||
use serde_json::json;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_request() {
|
||||
let expected = EventBuilder::new()
|
||||
let time = Utc::now();
|
||||
let expected = EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
.source(Url::from_str("http://localhost").unwrap())
|
||||
.source("http://localhost/")
|
||||
//TODO this is required now because the message deserializer implictly set default values
|
||||
// As soon as this defaulting doesn't happen anymore, we can remove it (Issues #40/#41)
|
||||
.time(time)
|
||||
.extension("someint", "10")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let (req, payload) = test::TestRequest::post()
|
||||
.header("ce-specversion", "1.0")
|
||||
.header("ce-id", "0001")
|
||||
.header("ce-type", "example.test")
|
||||
.header("ce-source", "http://localhost")
|
||||
.header("ce-source", "http://localhost/")
|
||||
.header("ce-someint", "10")
|
||||
.header("ce-time", time.to_rfc3339())
|
||||
.to_http_parts();
|
||||
|
||||
let resp = request_to_event(&req, web::Payload(payload)).await.unwrap();
|
||||
|
@ -144,15 +151,20 @@ mod tests {
|
|||
|
||||
#[actix_rt::test]
|
||||
async fn test_request_with_full_data() {
|
||||
let time = Utc::now();
|
||||
let j = json!({"hello": "world"});
|
||||
|
||||
let expected = EventBuilder::new()
|
||||
let expected = EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
.source(Url::from_str("http://localhost").unwrap())
|
||||
//TODO this is required now because the message deserializer implictly set default values
|
||||
// As soon as this defaulting doesn't happen anymore, we can remove it (Issues #40/#41)
|
||||
.time(time)
|
||||
.data("application/json", j.clone())
|
||||
.extension("someint", "10")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let (req, payload) = test::TestRequest::post()
|
||||
.header("ce-specversion", "1.0")
|
||||
|
@ -160,6 +172,7 @@ mod tests {
|
|||
.header("ce-type", "example.test")
|
||||
.header("ce-source", "http://localhost")
|
||||
.header("ce-someint", "10")
|
||||
.header("ce-time", time.to_rfc3339())
|
||||
.header("content-type", "application/json")
|
||||
.set_json(&j)
|
||||
.to_http_parts();
|
||||
|
|
|
@ -82,19 +82,20 @@ mod tests {
|
|||
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::test;
|
||||
use cloudevents::EventBuilder;
|
||||
use cloudevents::{EventBuilder, EventBuilderV10};
|
||||
use futures::TryStreamExt;
|
||||
use serde_json::json;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_response() {
|
||||
let input = EventBuilder::new()
|
||||
let input = EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
.source(Url::from_str("http://localhost/").unwrap())
|
||||
.extension("someint", "10")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let resp = event_to_response(input, HttpResponseBuilder::new(StatusCode::OK))
|
||||
.await
|
||||
|
@ -130,13 +131,14 @@ mod tests {
|
|||
async fn test_response_with_full_data() {
|
||||
let j = json!({"hello": "world"});
|
||||
|
||||
let input = EventBuilder::new()
|
||||
let input = EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
.source(Url::from_str("http://localhost").unwrap())
|
||||
.data("application/json", j.clone())
|
||||
.extension("someint", "10")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut resp = event_to_response(input, HttpResponseBuilder::new(StatusCode::OK))
|
||||
.await
|
||||
|
|
|
@ -14,7 +14,6 @@ repository = "https://github.com/cloudevents/sdk-rust"
|
|||
cloudevents-sdk = { version = "0.1.0", path = ".." }
|
||||
lazy_static = "1.4.0"
|
||||
bytes = "^0.5"
|
||||
serde_json = "^1.0"
|
||||
|
||||
[dependencies.reqwest]
|
||||
version = "0.10.4"
|
||||
|
@ -24,4 +23,6 @@ features = ["rustls-tls"]
|
|||
[dev-dependencies]
|
||||
mockito = "0.25.1"
|
||||
tokio = { version = "^0.2", features = ["full"] }
|
||||
url = { version = "^2.1" }
|
||||
url = { version = "^2.1" }
|
||||
serde_json = "^1.0"
|
||||
chrono = { version = "^0.4", features = ["serde"] }
|
|
@ -73,7 +73,7 @@ mod tests {
|
|||
use mockito::{mock, Matcher};
|
||||
|
||||
use cloudevents::message::StructuredDeserializer;
|
||||
use cloudevents::EventBuilder;
|
||||
use cloudevents::{EventBuilder, EventBuilderV10};
|
||||
use serde_json::json;
|
||||
use url::Url;
|
||||
|
||||
|
@ -89,12 +89,13 @@ mod tests {
|
|||
.match_body(Matcher::Missing)
|
||||
.create();
|
||||
|
||||
let input = EventBuilder::new()
|
||||
let input = EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
.source(Url::from_str("http://localhost/").unwrap())
|
||||
.extension("someint", "10")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
event_to_request(input, client.post(&url))
|
||||
|
@ -121,13 +122,14 @@ mod tests {
|
|||
.match_body(Matcher::Exact(j.to_string()))
|
||||
.create();
|
||||
|
||||
let input = EventBuilder::new()
|
||||
let input = EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
.source(Url::from_str("http://localhost").unwrap())
|
||||
.data("application/json", j.clone())
|
||||
.extension("someint", "10")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
event_to_request(input, client.post(&url))
|
||||
|
@ -143,13 +145,14 @@ mod tests {
|
|||
async fn test_structured_request_with_full_data() {
|
||||
let j = json!({"hello": "world"});
|
||||
|
||||
let input = EventBuilder::new()
|
||||
let input = EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
.source(Url::from_str("http://localhost").unwrap())
|
||||
.data("application/json", j.clone())
|
||||
.extension("someint", "10")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let url = mockito::server_url();
|
||||
let m = mock("POST", "/")
|
||||
|
|
|
@ -112,13 +112,15 @@ mod tests {
|
|||
use super::*;
|
||||
use mockito::mock;
|
||||
|
||||
use cloudevents::EventBuilder;
|
||||
use chrono::Utc;
|
||||
use cloudevents::{EventBuilder, EventBuilderV10};
|
||||
use serde_json::json;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_response() {
|
||||
let time = Utc::now();
|
||||
let url = mockito::server_url();
|
||||
let _m = mock("GET", "/")
|
||||
.with_status(200)
|
||||
|
@ -127,14 +129,19 @@ mod tests {
|
|||
.with_header("ce-type", "example.test")
|
||||
.with_header("ce-source", "http://localhost")
|
||||
.with_header("ce-someint", "10")
|
||||
.with_header("ce-time", &time.to_rfc3339())
|
||||
.create();
|
||||
|
||||
let expected = EventBuilder::new()
|
||||
let expected = EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
//TODO this is required now because the message deserializer implictly set default values
|
||||
// As soon as this defaulting doesn't happen anymore, we can remove it (Issues #40/#41)
|
||||
.time(time)
|
||||
.source(Url::from_str("http://localhost").unwrap())
|
||||
.extension("someint", "10")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let res = client.get(&url).send().await.unwrap();
|
||||
|
@ -145,6 +152,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_response_with_full_data() {
|
||||
let time = Utc::now();
|
||||
let j = json!({"hello": "world"});
|
||||
|
||||
let url = mockito::server_url();
|
||||
|
@ -156,16 +164,21 @@ mod tests {
|
|||
.with_header("ce-source", "http://localhost/")
|
||||
.with_header("content-type", "application/json")
|
||||
.with_header("ce-someint", "10")
|
||||
.with_header("ce-time", &time.to_rfc3339())
|
||||
.with_body(j.to_string())
|
||||
.create();
|
||||
|
||||
let expected = EventBuilder::new()
|
||||
let expected = EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
//TODO this is required now because the message deserializer implictly set default values
|
||||
// As soon as this defaulting doesn't happen anymore, we can remove it (Issues #40/#41)
|
||||
.time(time)
|
||||
.source(Url::from_str("http://localhost").unwrap())
|
||||
.data("application/json", j.clone())
|
||||
.extension("someint", "10")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let res = client.get(&url).send().await.unwrap();
|
||||
|
@ -176,14 +189,20 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_structured_response_with_full_data() {
|
||||
let time = Utc::now();
|
||||
|
||||
let j = json!({"hello": "world"});
|
||||
let expected = EventBuilder::new()
|
||||
let expected = EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
//TODO this is required now because the message deserializer implictly set default values
|
||||
// As soon as this defaulting doesn't happen anymore, we can remove it (Issues #40/#41)
|
||||
.time(time)
|
||||
.source(Url::from_str("http://localhost").unwrap())
|
||||
.data("application/json", j.clone())
|
||||
.extension("someint", "10")
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let url = mockito::server_url();
|
||||
let _m = mock("GET", "/")
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use actix_web::{get, post, web, App, HttpRequest, HttpResponse, HttpServer};
|
||||
use cloudevents::EventBuilder;
|
||||
use url::Url;
|
||||
use std::str::FromStr;
|
||||
use cloudevents::{EventBuilder, EventBuilderV10};
|
||||
use serde_json::json;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
#[post("/")]
|
||||
async fn post_event(req: HttpRequest, payload: web::Payload) -> Result<String, actix_web::Error> {
|
||||
|
@ -16,15 +16,17 @@ async fn get_event() -> Result<HttpResponse, actix_web::Error> {
|
|||
let payload = json!({"hello": "world"});
|
||||
|
||||
Ok(cloudevents_sdk_actix_web::event_to_response(
|
||||
EventBuilder::new()
|
||||
EventBuilderV10::new()
|
||||
.id("0001")
|
||||
.ty("example.test")
|
||||
.source(Url::from_str("http://localhost/").unwrap())
|
||||
.data("application/json", payload)
|
||||
.extension("someint", "10")
|
||||
.build(),
|
||||
HttpResponse::Ok()
|
||||
).await?)
|
||||
.build()
|
||||
.unwrap(),
|
||||
HttpResponse::Ok(),
|
||||
)
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[actix_rt::main]
|
||||
|
@ -39,8 +41,8 @@ async fn main() -> std::io::Result<()> {
|
|||
.service(post_event)
|
||||
.service(get_event)
|
||||
})
|
||||
.bind("127.0.0.1:9000")?
|
||||
.workers(1)
|
||||
.run()
|
||||
.await
|
||||
.bind("127.0.0.1:9000")?
|
||||
.workers(1)
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
use cloudevents::{EventBuilder, EventBuilderV10};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn run(target: String, ty: String, datacontenttype: String, data: String) -> Result<(), String> {
|
||||
let event = cloudevents::EventBuilder::new()
|
||||
pub async fn run(
|
||||
target: String,
|
||||
ty: String,
|
||||
datacontenttype: String,
|
||||
data: String,
|
||||
) -> Result<(), String> {
|
||||
let event = EventBuilderV10::new()
|
||||
.ty(ty)
|
||||
.data(datacontenttype, data)
|
||||
.build();
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
println!("Going to send event: {:?}", event);
|
||||
|
||||
|
@ -17,4 +24,4 @@ pub async fn run(target: String, ty: String, datacontenttype: String, data: Stri
|
|||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,55 @@
|
|||
use super::{EventBuilderV03, EventBuilderV10};
|
||||
use super::Event;
|
||||
use snafu::Snafu;
|
||||
|
||||
/// Builder to create [`super::Event`]:
|
||||
/// Trait to implement a builder for [`Event`]:
|
||||
/// ```
|
||||
/// use cloudevents::EventBuilder;
|
||||
/// use cloudevents::event::{EventBuilderV10, EventBuilder};
|
||||
/// use chrono::Utc;
|
||||
/// use url::Url;
|
||||
///
|
||||
/// let event = EventBuilder::v10()
|
||||
/// let event = EventBuilderV10::new()
|
||||
/// .id("my_event.my_application")
|
||||
/// .source(Url::parse("http://localhost:8080").unwrap())
|
||||
/// .source("http://localhost:8080")
|
||||
/// .ty("example.demo")
|
||||
/// .time(Utc::now())
|
||||
/// .build();
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub struct EventBuilder {}
|
||||
///
|
||||
/// You can create an [`EventBuilder`] starting from an existing [`Event`] using the [`From`] trait.
|
||||
/// You can create a default [`EventBuilder`] setting default values for some attributes.
|
||||
pub trait EventBuilder
|
||||
where
|
||||
Self: Clone + Sized + From<Event> + Default,
|
||||
{
|
||||
/// Create a new empty builder
|
||||
fn new() -> Self;
|
||||
|
||||
impl EventBuilder {
|
||||
/// Creates a new builder for latest CloudEvents version
|
||||
pub fn new() -> EventBuilderV10 {
|
||||
return Self::v10();
|
||||
}
|
||||
|
||||
/// Creates a new builder for CloudEvents V1.0
|
||||
pub fn v10() -> EventBuilderV10 {
|
||||
return EventBuilderV10::new();
|
||||
}
|
||||
|
||||
/// Creates a new builder for CloudEvents V0.3
|
||||
pub fn v03() -> EventBuilderV03 {
|
||||
return EventBuilderV03::new();
|
||||
}
|
||||
/// Build [`super::Event`]
|
||||
fn build(self) -> Result<super::Event, Error>;
|
||||
}
|
||||
|
||||
/// Represents an error during build process
|
||||
#[derive(Debug, Snafu, Clone)]
|
||||
pub enum Error {
|
||||
#[snafu(display("Missing required attribute {}", attribute_name))]
|
||||
MissingRequiredAttribute { attribute_name: &'static str },
|
||||
#[snafu(display(
|
||||
"Error while setting attribute '{}' with timestamp type: {}",
|
||||
attribute_name,
|
||||
source
|
||||
))]
|
||||
ParseTimeError {
|
||||
attribute_name: &'static str,
|
||||
source: chrono::ParseError,
|
||||
},
|
||||
#[snafu(display(
|
||||
"Error while setting attribute '{}' with uri/uriref type: {}",
|
||||
attribute_name,
|
||||
source
|
||||
))]
|
||||
ParseUrlError {
|
||||
attribute_name: &'static str,
|
||||
source: url::ParseError,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -7,15 +7,18 @@ mod extensions;
|
|||
mod format;
|
||||
mod message;
|
||||
mod spec_version;
|
||||
mod types;
|
||||
|
||||
pub use attributes::Attributes;
|
||||
pub use attributes::{AttributesReader, AttributesWriter};
|
||||
pub use builder::Error as EventBuilderError;
|
||||
pub use builder::EventBuilder;
|
||||
pub use data::Data;
|
||||
pub use event::Event;
|
||||
pub use extensions::ExtensionValue;
|
||||
pub use spec_version::InvalidSpecVersion;
|
||||
pub use spec_version::SpecVersion;
|
||||
pub use types::{TryIntoTime, TryIntoUrl};
|
||||
|
||||
mod v03;
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use url::Url;
|
||||
|
||||
/// Trait to define conversion to [`Url`]
|
||||
pub trait TryIntoUrl {
|
||||
fn into_url(self) -> Result<Url, url::ParseError>;
|
||||
}
|
||||
|
||||
impl TryIntoUrl for Url {
|
||||
fn into_url(self) -> Result<Url, url::ParseError> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryIntoUrl for &str {
|
||||
fn into_url(self) -> Result<Url, url::ParseError> {
|
||||
Url::parse(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryIntoUrl for String {
|
||||
fn into_url(self) -> Result<Url, url::ParseError> {
|
||||
self.as_str().into_url()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TryIntoTime {
|
||||
fn into_time(self) -> Result<DateTime<Utc>, chrono::ParseError>;
|
||||
}
|
||||
|
||||
impl TryIntoTime for DateTime<Utc> {
|
||||
fn into_time(self) -> Result<DateTime<Utc>, chrono::ParseError> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to define conversion to [`DateTime`]
|
||||
impl TryIntoTime for &str {
|
||||
fn into_time(self) -> Result<DateTime<Utc>, chrono::ParseError> {
|
||||
Ok(DateTime::<Utc>::from(DateTime::parse_from_rfc3339(self)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryIntoTime for String {
|
||||
fn into_time(self) -> Result<DateTime<Utc>, chrono::ParseError> {
|
||||
self.as_str().into_time()
|
||||
}
|
||||
}
|
|
@ -159,7 +159,7 @@ impl Default for Attributes {
|
|||
datacontenttype: None,
|
||||
schemaurl: None,
|
||||
subject: None,
|
||||
time: None,
|
||||
time: Some(Utc::now()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,58 +1,66 @@
|
|||
use super::Attributes as AttributesV03;
|
||||
use crate::event::{Attributes, AttributesWriter, Data, Event, ExtensionValue};
|
||||
use crate::event::{
|
||||
Attributes, Data, Event, EventBuilderError, ExtensionValue, TryIntoTime, TryIntoUrl,
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
|
||||
/// Builder to create a CloudEvent V0.3
|
||||
#[derive(Clone)]
|
||||
pub struct EventBuilder {
|
||||
event: Event,
|
||||
id: Option<String>,
|
||||
ty: Option<String>,
|
||||
source: Option<Url>,
|
||||
datacontenttype: Option<String>,
|
||||
schemaurl: Option<Url>,
|
||||
subject: Option<String>,
|
||||
time: Option<DateTime<Utc>>,
|
||||
data: Option<Data>,
|
||||
extensions: HashMap<String, ExtensionValue>,
|
||||
error: Option<EventBuilderError>,
|
||||
}
|
||||
|
||||
impl EventBuilder {
|
||||
pub fn from(event: Event) -> Self {
|
||||
EventBuilder {
|
||||
event: Event {
|
||||
attributes: event.attributes.into_v03(),
|
||||
data: event.data,
|
||||
extensions: event.extensions,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
EventBuilder {
|
||||
event: Event {
|
||||
attributes: Attributes::V03(AttributesV03::default()),
|
||||
data: None,
|
||||
extensions: HashMap::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(mut self, id: impl Into<String>) -> Self {
|
||||
self.event.set_id(id);
|
||||
return self;
|
||||
self.id = Some(id.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn source(mut self, source: impl Into<Url>) -> Self {
|
||||
self.event.set_source(source);
|
||||
return self;
|
||||
pub fn source(mut self, source: impl TryIntoUrl) -> Self {
|
||||
match source.into_url() {
|
||||
Ok(u) => self.source = Some(u),
|
||||
Err(e) => {
|
||||
self.error = Some(EventBuilderError::ParseUrlError {
|
||||
attribute_name: "source",
|
||||
source: e,
|
||||
})
|
||||
}
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ty(mut self, ty: impl Into<String>) -> Self {
|
||||
self.event.set_type(ty);
|
||||
return self;
|
||||
self.ty = Some(ty.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn subject(mut self, subject: impl Into<String>) -> Self {
|
||||
self.event.set_subject(Some(subject));
|
||||
return self;
|
||||
self.subject = Some(subject.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn time(mut self, time: impl Into<DateTime<Utc>>) -> Self {
|
||||
self.event.set_time(Some(time));
|
||||
return self;
|
||||
pub fn time(mut self, time: impl TryIntoTime) -> Self {
|
||||
match time.into_time() {
|
||||
Ok(u) => self.time = Some(u),
|
||||
Err(e) => {
|
||||
self.error = Some(EventBuilderError::ParseTimeError {
|
||||
attribute_name: "time",
|
||||
source: e,
|
||||
})
|
||||
}
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extension(
|
||||
|
@ -60,75 +68,108 @@ impl EventBuilder {
|
|||
extension_name: &str,
|
||||
extension_value: impl Into<ExtensionValue>,
|
||||
) -> Self {
|
||||
self.event.set_extension(extension_name, extension_value);
|
||||
return self;
|
||||
self.extensions
|
||||
.insert(extension_name.to_owned(), extension_value.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn data(mut self, datacontenttype: impl Into<String>, data: impl Into<Data>) -> Self {
|
||||
self.event.write_data(datacontenttype, data);
|
||||
return self;
|
||||
self.datacontenttype = Some(datacontenttype.into());
|
||||
self.data = Some(data.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn data_with_schema(
|
||||
mut self,
|
||||
datacontenttype: impl Into<String>,
|
||||
schemaurl: impl Into<Url>,
|
||||
schemaurl: impl TryIntoUrl,
|
||||
data: impl Into<Data>,
|
||||
) -> Self {
|
||||
self.event
|
||||
.write_data_with_schema(datacontenttype, schemaurl, data);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn build(self) -> Event {
|
||||
self.event
|
||||
self.datacontenttype = Some(datacontenttype.into());
|
||||
match schemaurl.into_url() {
|
||||
Ok(u) => self.schemaurl = Some(u),
|
||||
Err(e) => {
|
||||
self.error = Some(EventBuilderError::ParseUrlError {
|
||||
attribute_name: "schemaurl",
|
||||
source: e,
|
||||
})
|
||||
}
|
||||
};
|
||||
self.data = Some(data.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::event::{AttributesReader, SpecVersion};
|
||||
impl From<Event> for EventBuilder {
|
||||
fn from(event: Event) -> Self {
|
||||
let attributes = match event.attributes.into_v03() {
|
||||
Attributes::V03(attr) => attr,
|
||||
// This branch is unreachable because into_v03() returns
|
||||
// always a Attributes::V03
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn build_event() {
|
||||
let id = "aaa";
|
||||
let source = Url::parse("http://localhost:8080").unwrap();
|
||||
let ty = "bbb";
|
||||
let subject = "francesco";
|
||||
let time: DateTime<Utc> = Utc::now();
|
||||
let extension_name = "ext";
|
||||
let extension_value = 10i64;
|
||||
let content_type = "application/json";
|
||||
let schema = Url::parse("http://localhost:8080/schema").unwrap();
|
||||
let data = serde_json::json!({
|
||||
"hello": "world"
|
||||
});
|
||||
|
||||
let event = EventBuilder::new()
|
||||
.id(id)
|
||||
.source(source.clone())
|
||||
.ty(ty)
|
||||
.subject(subject)
|
||||
.time(time)
|
||||
.extension(extension_name, extension_value)
|
||||
.data_with_schema(content_type, schema.clone(), data.clone())
|
||||
.build();
|
||||
|
||||
assert_eq!(SpecVersion::V03, event.get_specversion());
|
||||
assert_eq!(id, event.get_id());
|
||||
assert_eq!(source, event.get_source().clone());
|
||||
assert_eq!(ty, event.get_type());
|
||||
assert_eq!(subject, event.get_subject().unwrap());
|
||||
assert_eq!(time, event.get_time().unwrap().clone());
|
||||
assert_eq!(
|
||||
ExtensionValue::from(extension_value),
|
||||
event.get_extension(extension_name).unwrap().clone()
|
||||
);
|
||||
assert_eq!(content_type, event.get_datacontenttype().unwrap());
|
||||
assert_eq!(schema, event.get_dataschema().unwrap().clone());
|
||||
|
||||
let event_data: serde_json::Value = event.try_get_data().unwrap().unwrap();
|
||||
assert_eq!(data, event_data);
|
||||
EventBuilder {
|
||||
id: Some(attributes.id),
|
||||
ty: Some(attributes.ty),
|
||||
source: Some(attributes.source),
|
||||
datacontenttype: attributes.datacontenttype,
|
||||
schemaurl: attributes.schemaurl,
|
||||
subject: attributes.subject,
|
||||
time: attributes.time,
|
||||
data: event.data,
|
||||
extensions: event.extensions,
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EventBuilder {
|
||||
fn default() -> Self {
|
||||
Self::from(Event::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::event::builder::EventBuilder for EventBuilder {
|
||||
fn new() -> Self {
|
||||
EventBuilder {
|
||||
id: None,
|
||||
ty: None,
|
||||
source: None,
|
||||
datacontenttype: None,
|
||||
schemaurl: None,
|
||||
subject: None,
|
||||
time: None,
|
||||
data: None,
|
||||
extensions: Default::default(),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn build(self) -> Result<Event, EventBuilderError> {
|
||||
match self.error {
|
||||
Some(e) => Err(e),
|
||||
None => Ok(Event {
|
||||
attributes: Attributes::V03(AttributesV03 {
|
||||
id: self.id.ok_or(EventBuilderError::MissingRequiredAttribute {
|
||||
attribute_name: "id",
|
||||
})?,
|
||||
ty: self.ty.ok_or(EventBuilderError::MissingRequiredAttribute {
|
||||
attribute_name: "type",
|
||||
})?,
|
||||
source: self
|
||||
.source
|
||||
.ok_or(EventBuilderError::MissingRequiredAttribute {
|
||||
attribute_name: "source",
|
||||
})?,
|
||||
datacontenttype: self.datacontenttype,
|
||||
schemaurl: self.schemaurl,
|
||||
subject: self.subject,
|
||||
time: self.time,
|
||||
}),
|
||||
data: self.data,
|
||||
extensions: self.extensions,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ mod builder;
|
|||
mod format;
|
||||
mod message;
|
||||
|
||||
pub(crate) use crate::event::v03::format::EventFormatDeserializer;
|
||||
pub(crate) use crate::event::v03::format::EventFormatSerializer;
|
||||
pub use attributes::Attributes;
|
||||
pub(crate) use attributes::ATTRIBUTE_NAMES;
|
||||
pub use builder::EventBuilder;
|
||||
pub(crate) use format::EventFormatDeserializer;
|
||||
pub(crate) use format::EventFormatSerializer;
|
||||
|
|
|
@ -158,7 +158,7 @@ impl Default for Attributes {
|
|||
datacontenttype: None,
|
||||
dataschema: None,
|
||||
subject: None,
|
||||
time: None,
|
||||
time: Some(Utc::now()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,58 +1,66 @@
|
|||
use super::Attributes as AttributesV10;
|
||||
use crate::event::{Attributes, AttributesWriter, Data, Event, ExtensionValue};
|
||||
use crate::event::{
|
||||
Attributes, Data, Event, EventBuilderError, ExtensionValue, TryIntoTime, TryIntoUrl,
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
|
||||
/// Builder to create a CloudEvent V1.0
|
||||
#[derive(Clone)]
|
||||
pub struct EventBuilder {
|
||||
event: Event,
|
||||
id: Option<String>,
|
||||
ty: Option<String>,
|
||||
source: Option<Url>,
|
||||
datacontenttype: Option<String>,
|
||||
dataschema: Option<Url>,
|
||||
subject: Option<String>,
|
||||
time: Option<DateTime<Utc>>,
|
||||
data: Option<Data>,
|
||||
extensions: HashMap<String, ExtensionValue>,
|
||||
error: Option<EventBuilderError>,
|
||||
}
|
||||
|
||||
impl EventBuilder {
|
||||
pub fn from(event: Event) -> Self {
|
||||
EventBuilder {
|
||||
event: Event {
|
||||
attributes: event.attributes.into_v10(),
|
||||
data: event.data,
|
||||
extensions: event.extensions,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
EventBuilder {
|
||||
event: Event {
|
||||
attributes: Attributes::V10(AttributesV10::default()),
|
||||
data: None,
|
||||
extensions: HashMap::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(mut self, id: impl Into<String>) -> Self {
|
||||
self.event.set_id(id);
|
||||
return self;
|
||||
self.id = Some(id.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn source(mut self, source: impl Into<Url>) -> Self {
|
||||
self.event.set_source(source);
|
||||
return self;
|
||||
pub fn source(mut self, source: impl TryIntoUrl) -> Self {
|
||||
match source.into_url() {
|
||||
Ok(u) => self.source = Some(u),
|
||||
Err(e) => {
|
||||
self.error = Some(EventBuilderError::ParseUrlError {
|
||||
attribute_name: "source",
|
||||
source: e,
|
||||
})
|
||||
}
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ty(mut self, ty: impl Into<String>) -> Self {
|
||||
self.event.set_type(ty);
|
||||
return self;
|
||||
self.ty = Some(ty.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn subject(mut self, subject: impl Into<String>) -> Self {
|
||||
self.event.set_subject(Some(subject));
|
||||
return self;
|
||||
self.subject = Some(subject.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn time(mut self, time: impl Into<DateTime<Utc>>) -> Self {
|
||||
self.event.set_time(Some(time));
|
||||
return self;
|
||||
pub fn time(mut self, time: impl TryIntoTime) -> Self {
|
||||
match time.into_time() {
|
||||
Ok(u) => self.time = Some(u),
|
||||
Err(e) => {
|
||||
self.error = Some(EventBuilderError::ParseTimeError {
|
||||
attribute_name: "time",
|
||||
source: e,
|
||||
})
|
||||
}
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn extension(
|
||||
|
@ -60,75 +68,108 @@ impl EventBuilder {
|
|||
extension_name: &str,
|
||||
extension_value: impl Into<ExtensionValue>,
|
||||
) -> Self {
|
||||
self.event.set_extension(extension_name, extension_value);
|
||||
return self;
|
||||
self.extensions
|
||||
.insert(extension_name.to_owned(), extension_value.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn data(mut self, datacontenttype: impl Into<String>, data: impl Into<Data>) -> Self {
|
||||
self.event.write_data(datacontenttype, data);
|
||||
return self;
|
||||
self.datacontenttype = Some(datacontenttype.into());
|
||||
self.data = Some(data.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn data_with_schema(
|
||||
mut self,
|
||||
datacontenttype: impl Into<String>,
|
||||
dataschema: impl Into<Url>,
|
||||
schemaurl: impl TryIntoUrl,
|
||||
data: impl Into<Data>,
|
||||
) -> Self {
|
||||
self.event
|
||||
.write_data_with_schema(datacontenttype, dataschema, data);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn build(self) -> Event {
|
||||
self.event
|
||||
self.datacontenttype = Some(datacontenttype.into());
|
||||
match schemaurl.into_url() {
|
||||
Ok(u) => self.dataschema = Some(u),
|
||||
Err(e) => {
|
||||
self.error = Some(EventBuilderError::ParseUrlError {
|
||||
attribute_name: "dataschema",
|
||||
source: e,
|
||||
})
|
||||
}
|
||||
};
|
||||
self.data = Some(data.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::event::{AttributesReader, SpecVersion};
|
||||
impl From<Event> for EventBuilder {
|
||||
fn from(event: Event) -> Self {
|
||||
let attributes = match event.attributes.into_v10() {
|
||||
Attributes::V10(attr) => attr,
|
||||
// This branch is unreachable because into_v10() returns
|
||||
// always a Attributes::V10
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn build_event() {
|
||||
let id = "aaa";
|
||||
let source = Url::parse("http://localhost:8080").unwrap();
|
||||
let ty = "bbb";
|
||||
let subject = "francesco";
|
||||
let time: DateTime<Utc> = Utc::now();
|
||||
let extension_name = "ext";
|
||||
let extension_value = 10i64;
|
||||
let content_type = "application/json";
|
||||
let schema = Url::parse("http://localhost:8080/schema").unwrap();
|
||||
let data = serde_json::json!({
|
||||
"hello": "world"
|
||||
});
|
||||
|
||||
let event = EventBuilder::new()
|
||||
.id(id)
|
||||
.source(source.clone())
|
||||
.ty(ty)
|
||||
.subject(subject)
|
||||
.time(time)
|
||||
.extension(extension_name, extension_value)
|
||||
.data_with_schema(content_type, schema.clone(), data.clone())
|
||||
.build();
|
||||
|
||||
assert_eq!(SpecVersion::V10, event.get_specversion());
|
||||
assert_eq!(id, event.get_id());
|
||||
assert_eq!(source, event.get_source().clone());
|
||||
assert_eq!(ty, event.get_type());
|
||||
assert_eq!(subject, event.get_subject().unwrap());
|
||||
assert_eq!(time, event.get_time().unwrap().clone());
|
||||
assert_eq!(
|
||||
ExtensionValue::from(extension_value),
|
||||
event.get_extension(extension_name).unwrap().clone()
|
||||
);
|
||||
assert_eq!(content_type, event.get_datacontenttype().unwrap());
|
||||
assert_eq!(schema, event.get_dataschema().unwrap().clone());
|
||||
|
||||
let event_data: serde_json::Value = event.try_get_data().unwrap().unwrap();
|
||||
assert_eq!(data, event_data);
|
||||
EventBuilder {
|
||||
id: Some(attributes.id),
|
||||
ty: Some(attributes.ty),
|
||||
source: Some(attributes.source),
|
||||
datacontenttype: attributes.datacontenttype,
|
||||
dataschema: attributes.dataschema,
|
||||
subject: attributes.subject,
|
||||
time: attributes.time,
|
||||
data: event.data,
|
||||
extensions: event.extensions,
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EventBuilder {
|
||||
fn default() -> Self {
|
||||
Self::from(Event::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::event::builder::EventBuilder for EventBuilder {
|
||||
fn new() -> Self {
|
||||
EventBuilder {
|
||||
id: None,
|
||||
ty: None,
|
||||
source: None,
|
||||
datacontenttype: None,
|
||||
dataschema: None,
|
||||
subject: None,
|
||||
time: None,
|
||||
data: None,
|
||||
extensions: Default::default(),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn build(self) -> Result<Event, EventBuilderError> {
|
||||
match self.error {
|
||||
Some(e) => Err(e),
|
||||
None => Ok(Event {
|
||||
attributes: Attributes::V10(AttributesV10 {
|
||||
id: self.id.ok_or(EventBuilderError::MissingRequiredAttribute {
|
||||
attribute_name: "id",
|
||||
})?,
|
||||
ty: self.ty.ok_or(EventBuilderError::MissingRequiredAttribute {
|
||||
attribute_name: "type",
|
||||
})?,
|
||||
source: self
|
||||
.source
|
||||
.ok_or(EventBuilderError::MissingRequiredAttribute {
|
||||
attribute_name: "source",
|
||||
})?,
|
||||
datacontenttype: self.datacontenttype,
|
||||
dataschema: self.dataschema,
|
||||
subject: self.subject,
|
||||
time: self.time,
|
||||
}),
|
||||
data: self.data,
|
||||
extensions: self.extensions,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ mod builder;
|
|||
mod format;
|
||||
mod message;
|
||||
|
||||
pub(crate) use crate::event::v10::format::EventFormatDeserializer;
|
||||
pub(crate) use crate::event::v10::format::EventFormatSerializer;
|
||||
pub use attributes::Attributes;
|
||||
pub(crate) use attributes::ATTRIBUTE_NAMES;
|
||||
pub use builder::EventBuilder;
|
||||
pub(crate) use format::EventFormatDeserializer;
|
||||
pub(crate) use format::EventFormatSerializer;
|
||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -1,15 +1,17 @@
|
|||
//! This crate implements the [CloudEvents](https://cloudevents.io/) Spec for Rust.
|
||||
//!
|
||||
//! ```
|
||||
//! use cloudevents::{EventBuilder, AttributesReader};
|
||||
//! use cloudevents::{EventBuilder, AttributesReader, EventBuilderV10};
|
||||
//! use chrono::Utc;
|
||||
//! use url::Url;
|
||||
//!
|
||||
//! let event = EventBuilder::v10()
|
||||
//! let event = EventBuilderV10::new()
|
||||
//! .id("my_event.my_application")
|
||||
//! .source(Url::parse("http://localhost:8080").unwrap())
|
||||
//! .source("http://localhost:8080")
|
||||
//! .ty("example.demo")
|
||||
//! .time(Utc::now())
|
||||
//! .build();
|
||||
//! .build()
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! println!("CloudEvent Id: {}", event.get_id());
|
||||
//! println!("CloudEvent Time: {}", event.get_time().unwrap());
|
||||
|
@ -32,5 +34,5 @@ pub mod event;
|
|||
pub mod message;
|
||||
|
||||
pub use event::Event;
|
||||
pub use event::EventBuilder;
|
||||
pub use event::{AttributesReader, AttributesWriter};
|
||||
pub use event::{EventBuilder, EventBuilderV03, EventBuilderV10};
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use cloudevents::event::{
|
||||
AttributesReader, EventBuilder, EventBuilderError, ExtensionValue, SpecVersion,
|
||||
};
|
||||
use cloudevents::EventBuilderV03;
|
||||
use url::Url;
|
||||
|
||||
#[test]
|
||||
fn build_event() {
|
||||
let id = "aaa";
|
||||
let source = Url::parse("http://localhost:8080").unwrap();
|
||||
let ty = "bbb";
|
||||
let subject = "francesco";
|
||||
let time: DateTime<Utc> = Utc::now();
|
||||
let extension_name = "ext";
|
||||
let extension_value = 10i64;
|
||||
let content_type = "application/json";
|
||||
let schema = Url::parse("http://localhost:8080/schema").unwrap();
|
||||
let data = serde_json::json!({
|
||||
"hello": "world"
|
||||
});
|
||||
|
||||
let event = EventBuilderV03::new()
|
||||
.id(id)
|
||||
.source(source.clone())
|
||||
.ty(ty)
|
||||
.subject(subject)
|
||||
.time(time)
|
||||
.extension(extension_name, extension_value)
|
||||
.data_with_schema(content_type, schema.clone(), data.clone())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(SpecVersion::V03, event.get_specversion());
|
||||
assert_eq!(id, event.get_id());
|
||||
assert_eq!(source, event.get_source().clone());
|
||||
assert_eq!(ty, event.get_type());
|
||||
assert_eq!(subject, event.get_subject().unwrap());
|
||||
assert_eq!(time, event.get_time().unwrap().clone());
|
||||
assert_eq!(
|
||||
ExtensionValue::from(extension_value),
|
||||
event.get_extension(extension_name).unwrap().clone()
|
||||
);
|
||||
assert_eq!(content_type, event.get_datacontenttype().unwrap());
|
||||
assert_eq!(schema, event.get_dataschema().unwrap().clone());
|
||||
|
||||
let event_data: serde_json::Value = event.try_get_data().unwrap().unwrap();
|
||||
assert_eq!(data, event_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_missing_id() {
|
||||
let res = EventBuilderV03::new()
|
||||
.source("http://localhost:8080")
|
||||
.build();
|
||||
assert_match_pattern!(
|
||||
res,
|
||||
Err(EventBuilderError::MissingRequiredAttribute {
|
||||
attribute_name: "id"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_invalid_url() {
|
||||
let res = EventBuilderV03::new().source("").build();
|
||||
assert_match_pattern!(
|
||||
res,
|
||||
Err(EventBuilderError::ParseUrlError {
|
||||
attribute_name: "source",
|
||||
..
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_builds() {
|
||||
let res = EventBuilderV03::default().build();
|
||||
assert_match_pattern!(res, Ok(_));
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use cloudevents::event::{
|
||||
AttributesReader, EventBuilder, EventBuilderError, ExtensionValue, SpecVersion,
|
||||
};
|
||||
use cloudevents::EventBuilderV10;
|
||||
use url::Url;
|
||||
|
||||
#[test]
|
||||
fn build_event() {
|
||||
let id = "aaa";
|
||||
let source = Url::parse("http://localhost:8080").unwrap();
|
||||
let ty = "bbb";
|
||||
let subject = "francesco";
|
||||
let time: DateTime<Utc> = Utc::now();
|
||||
let extension_name = "ext";
|
||||
let extension_value = 10i64;
|
||||
let content_type = "application/json";
|
||||
let schema = Url::parse("http://localhost:8080/schema").unwrap();
|
||||
let data = serde_json::json!({
|
||||
"hello": "world"
|
||||
});
|
||||
|
||||
let event = EventBuilderV10::new()
|
||||
.id(id)
|
||||
.source(source.clone())
|
||||
.ty(ty)
|
||||
.subject(subject)
|
||||
.time(time)
|
||||
.extension(extension_name, extension_value)
|
||||
.data_with_schema(content_type, schema.clone(), data.clone())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(SpecVersion::V10, event.get_specversion());
|
||||
assert_eq!(id, event.get_id());
|
||||
assert_eq!(source, event.get_source().clone());
|
||||
assert_eq!(ty, event.get_type());
|
||||
assert_eq!(subject, event.get_subject().unwrap());
|
||||
assert_eq!(time, event.get_time().unwrap().clone());
|
||||
assert_eq!(
|
||||
ExtensionValue::from(extension_value),
|
||||
event.get_extension(extension_name).unwrap().clone()
|
||||
);
|
||||
assert_eq!(content_type, event.get_datacontenttype().unwrap());
|
||||
assert_eq!(schema, event.get_dataschema().unwrap().clone());
|
||||
|
||||
let event_data: serde_json::Value = event.try_get_data().unwrap().unwrap();
|
||||
assert_eq!(data, event_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_missing_id() {
|
||||
let res = EventBuilderV10::new()
|
||||
.source("http://localhost:8080")
|
||||
.build();
|
||||
assert_match_pattern!(
|
||||
res,
|
||||
Err(EventBuilderError::MissingRequiredAttribute {
|
||||
attribute_name: "id"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_invalid_url() {
|
||||
let res = EventBuilderV10::new().source("").build();
|
||||
assert_match_pattern!(
|
||||
res,
|
||||
Err(EventBuilderError::ParseUrlError {
|
||||
attribute_name: "source",
|
||||
..
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_builds() {
|
||||
let res = EventBuilderV10::default().build();
|
||||
assert_match_pattern!(res, Ok(_));
|
||||
}
|
|
@ -1,15 +1,16 @@
|
|||
use super::*;
|
||||
use cloudevents::{Event, EventBuilder};
|
||||
use cloudevents::{Event, EventBuilder, EventBuilderV03};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use url::Url;
|
||||
|
||||
pub fn minimal() -> Event {
|
||||
EventBuilder::v03()
|
||||
EventBuilderV03::new()
|
||||
.id(id())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn minimal_json() -> Value {
|
||||
|
@ -26,7 +27,7 @@ pub fn full_no_data() -> Event {
|
|||
let (bool_ext_name, bool_ext_value) = bool_extension();
|
||||
let (int_ext_name, int_ext_value) = int_extension();
|
||||
|
||||
EventBuilder::v03()
|
||||
EventBuilderV03::new()
|
||||
.id(id())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
|
@ -36,6 +37,7 @@ pub fn full_no_data() -> Event {
|
|||
.extension(&bool_ext_name, bool_ext_value)
|
||||
.extension(&int_ext_name, int_ext_value)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn full_no_data_json() -> Value {
|
||||
|
@ -61,7 +63,7 @@ pub fn full_json_data() -> Event {
|
|||
let (bool_ext_name, bool_ext_value) = bool_extension();
|
||||
let (int_ext_name, int_ext_value) = int_extension();
|
||||
|
||||
EventBuilder::v03()
|
||||
EventBuilderV03::new()
|
||||
.id(id())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
|
@ -76,6 +78,7 @@ pub fn full_json_data() -> Event {
|
|||
json_data(),
|
||||
)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn full_json_data_json() -> Value {
|
||||
|
@ -126,7 +129,7 @@ pub fn full_xml_string_data() -> Event {
|
|||
let (bool_ext_name, bool_ext_value) = bool_extension();
|
||||
let (int_ext_name, int_ext_value) = int_extension();
|
||||
|
||||
EventBuilder::v03()
|
||||
EventBuilderV03::new()
|
||||
.id(id())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
|
@ -137,6 +140,7 @@ pub fn full_xml_string_data() -> Event {
|
|||
.extension(&int_ext_name, int_ext_value)
|
||||
.data(xml_datacontenttype(), xml_data())
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn full_xml_binary_data() -> Event {
|
||||
|
@ -144,7 +148,7 @@ pub fn full_xml_binary_data() -> Event {
|
|||
let (bool_ext_name, bool_ext_value) = bool_extension();
|
||||
let (int_ext_name, int_ext_value) = int_extension();
|
||||
|
||||
EventBuilder::v03()
|
||||
EventBuilderV03::new()
|
||||
.id(id())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
|
@ -155,6 +159,7 @@ pub fn full_xml_binary_data() -> Event {
|
|||
.extension(&int_ext_name, int_ext_value)
|
||||
.data(xml_datacontenttype(), Vec::from(xml_data()))
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn full_xml_string_data_json() -> Value {
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use super::*;
|
||||
use cloudevents::{Event, EventBuilder};
|
||||
use cloudevents::{Event, EventBuilder, EventBuilderV10};
|
||||
use serde_json::{json, Value};
|
||||
use url::Url;
|
||||
|
||||
pub fn minimal() -> Event {
|
||||
EventBuilder::v10()
|
||||
EventBuilderV10::new()
|
||||
.id(id())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn minimal_json() -> Value {
|
||||
|
@ -25,7 +26,7 @@ pub fn full_no_data() -> Event {
|
|||
let (bool_ext_name, bool_ext_value) = bool_extension();
|
||||
let (int_ext_name, int_ext_value) = int_extension();
|
||||
|
||||
EventBuilder::v10()
|
||||
EventBuilderV10::new()
|
||||
.id(id())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
|
@ -35,6 +36,7 @@ pub fn full_no_data() -> Event {
|
|||
.extension(&bool_ext_name, bool_ext_value)
|
||||
.extension(&int_ext_name, int_ext_value)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn full_no_data_json() -> Value {
|
||||
|
@ -60,7 +62,7 @@ pub fn full_json_data() -> Event {
|
|||
let (bool_ext_name, bool_ext_value) = bool_extension();
|
||||
let (int_ext_name, int_ext_value) = int_extension();
|
||||
|
||||
EventBuilder::v10()
|
||||
EventBuilderV10::new()
|
||||
.id(id())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
|
@ -75,6 +77,7 @@ pub fn full_json_data() -> Event {
|
|||
json_data(),
|
||||
)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn full_json_data_json() -> Value {
|
||||
|
@ -124,7 +127,7 @@ pub fn full_xml_string_data() -> Event {
|
|||
let (bool_ext_name, bool_ext_value) = bool_extension();
|
||||
let (int_ext_name, int_ext_value) = int_extension();
|
||||
|
||||
EventBuilder::v10()
|
||||
EventBuilderV10::new()
|
||||
.id(id())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
|
@ -135,6 +138,7 @@ pub fn full_xml_string_data() -> Event {
|
|||
.extension(&int_ext_name, int_ext_value)
|
||||
.data(xml_datacontenttype(), xml_data())
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn full_xml_binary_data() -> Event {
|
||||
|
@ -142,7 +146,7 @@ pub fn full_xml_binary_data() -> Event {
|
|||
let (bool_ext_name, bool_ext_value) = bool_extension();
|
||||
let (int_ext_name, int_ext_value) = int_extension();
|
||||
|
||||
EventBuilder::v10()
|
||||
EventBuilderV10::new()
|
||||
.id(id())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
|
@ -153,6 +157,7 @@ pub fn full_xml_binary_data() -> Event {
|
|||
.extension(&int_ext_name, int_ext_value)
|
||||
.data(xml_datacontenttype(), Vec::from(xml_data()))
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn full_xml_string_data_json() -> Value {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
macro_rules! assert_match_pattern (
|
||||
($e:expr, $p:pat) => (
|
||||
match $e {
|
||||
$p => (),
|
||||
_ => panic!(r#"assertion failed (value doesn't match pattern):
|
||||
value: `{:?}`,
|
||||
pattern: `{}`"#, $e, stringify!($p))
|
||||
}
|
||||
)
|
||||
);
|
|
@ -1,17 +1,18 @@
|
|||
mod test_data;
|
||||
use cloudevents::event::{EventBuilderV03, EventBuilderV10};
|
||||
use cloudevents::EventBuilder;
|
||||
use test_data::*;
|
||||
|
||||
#[test]
|
||||
fn v10_to_v03() {
|
||||
let in_event = v10::full_json_data();
|
||||
let out_event = EventBuilderV03::from(in_event).build();
|
||||
let out_event = EventBuilderV03::from(in_event).build().unwrap();
|
||||
assert_eq!(v03::full_json_data(), out_event)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v03_to_v10() {
|
||||
let in_event = v03::full_json_data();
|
||||
let out_event = EventBuilderV10::from(in_event).build();
|
||||
let out_event = EventBuilderV10::from(in_event).build().unwrap();
|
||||
assert_eq!(v10::full_json_data(), out_event)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue