EventBuilder (#14)
* Created Builder * Now all DateTimes used are Utc (as spec states) * Splitted `write_data` to provide one version with schema and one version without schema Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
This commit is contained in:
parent
5bd7fe3ee1
commit
9e9122385e
|
@ -1,6 +1,6 @@
|
|||
use super::SpecVersion;
|
||||
use crate::event::{AttributesV10, ExtensionValue};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
/// Trait to get [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes).
|
||||
pub trait AttributesReader {
|
||||
|
@ -19,7 +19,7 @@ pub trait AttributesReader {
|
|||
/// Get the [subject](https://github.com/cloudevents/spec/blob/master/spec.md#subject).
|
||||
fn get_subject(&self) -> Option<&str>;
|
||||
/// Get the [time](https://github.com/cloudevents/spec/blob/master/spec.md#time).
|
||||
fn get_time(&self) -> Option<&DateTime<FixedOffset>>;
|
||||
fn get_time(&self) -> Option<&DateTime<Utc>>;
|
||||
/// Get the [extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) named `extension_name`
|
||||
fn get_extension(&self, extension_name: &str) -> Option<&ExtensionValue>;
|
||||
/// Get all the [extensions](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes)
|
||||
|
@ -31,7 +31,7 @@ pub trait AttributesWriter {
|
|||
fn set_source(&mut self, source: impl Into<String>);
|
||||
fn set_type(&mut self, ty: impl Into<String>);
|
||||
fn set_subject(&mut self, subject: Option<impl Into<String>>);
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<FixedOffset>>>);
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>);
|
||||
fn set_extension<'name, 'event: 'name>(
|
||||
&'event mut self,
|
||||
extension_name: &'name str,
|
||||
|
@ -48,6 +48,7 @@ pub(crate) trait DataAttributesWriter {
|
|||
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Attributes {
|
||||
V10(AttributesV10),
|
||||
}
|
||||
|
@ -95,7 +96,7 @@ impl AttributesReader for Attributes {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_time(&self) -> Option<&DateTime<FixedOffset>> {
|
||||
fn get_time(&self) -> Option<&DateTime<Utc>> {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_time(),
|
||||
}
|
||||
|
@ -139,7 +140,7 @@ impl AttributesWriter for Attributes {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<FixedOffset>>>) {
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>) {
|
||||
match self {
|
||||
Attributes::V10(a) => a.set_time(time),
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
use super::EventBuilderV10;
|
||||
|
||||
/// Builder to create [`Event`]:
|
||||
/// ```
|
||||
/// use cloudevents::EventBuilder;
|
||||
/// use chrono::Utc;
|
||||
///
|
||||
/// let event = EventBuilder::v10()
|
||||
/// .id("my_event.my_application")
|
||||
/// .source("http://localhost:8080")
|
||||
/// .time(Utc::now())
|
||||
/// .build();
|
||||
/// ```
|
||||
pub struct EventBuilder {}
|
||||
|
||||
impl EventBuilder {
|
||||
/// Creates a new builder for CloudEvents V1.0
|
||||
pub fn v10() -> EventBuilderV10 {
|
||||
return EventBuilderV10::new();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::{Into, TryFrom};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
/// Possible data values
|
||||
pub enum Data {
|
||||
String(String),
|
||||
|
|
|
@ -3,14 +3,16 @@ use super::{
|
|||
SpecVersion,
|
||||
};
|
||||
use crate::event::attributes::DataAttributesWriter;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use chrono::{DateTime, Utc};
|
||||
use delegate::delegate;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// Data structure that represents a [CloudEvent](https://github.com/cloudevents/spec/blob/master/spec.md).
|
||||
/// It provides methods to get the attributes through [`AttributesReader`]
|
||||
/// and write them through [`AttributesWriter`].
|
||||
/// It also provides methods to read and write the [event data](https://github.com/cloudevents/spec/blob/master/spec.md#event-data)
|
||||
/// It also provides methods to read and write the [event data](https://github.com/cloudevents/spec/blob/master/spec.md#event-data).
|
||||
///
|
||||
/// You can build events using [`EventBuilder`]
|
||||
/// ```
|
||||
/// use cloudevents::Event;
|
||||
/// use cloudevents::event::AttributesReader;
|
||||
|
@ -19,7 +21,6 @@ use std::convert::TryFrom;
|
|||
/// let mut e = Event::default();
|
||||
/// e.write_data(
|
||||
/// "application/json",
|
||||
/// None,
|
||||
/// serde_json::json!({"hello": "world"})
|
||||
/// );
|
||||
///
|
||||
|
@ -30,6 +31,7 @@ use std::convert::TryFrom;
|
|||
/// let data: serde_json::Value = e.try_get_data().unwrap().unwrap();
|
||||
/// println!("Event data: {}", data)
|
||||
/// ```
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct Event {
|
||||
pub attributes: Attributes,
|
||||
pub data: Option<Data>,
|
||||
|
@ -45,7 +47,7 @@ impl AttributesReader for Event {
|
|||
fn get_datacontenttype(&self) -> Option<&str>;
|
||||
fn get_dataschema(&self) -> Option<&str>;
|
||||
fn get_subject(&self) -> Option<&str>;
|
||||
fn get_time(&self) -> Option<&DateTime<FixedOffset>>;
|
||||
fn get_time(&self) -> Option<&DateTime<Utc>>;
|
||||
fn get_extension(&self, extension_name: &str) -> Option<&ExtensionValue>;
|
||||
fn get_extensions(&self) -> Vec<(&str, &ExtensionValue)>;
|
||||
}
|
||||
|
@ -59,7 +61,7 @@ impl AttributesWriter for Event {
|
|||
fn set_source(&mut self, source: impl Into<String>);
|
||||
fn set_type(&mut self, ty: impl Into<String>);
|
||||
fn set_subject(&mut self, subject: Option<impl Into<String>>);
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<FixedOffset>>>);
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>);
|
||||
fn set_extension<'name, 'event: 'name>(
|
||||
&'event mut self,
|
||||
extension_name: &'name str,
|
||||
|
@ -85,9 +87,11 @@ impl Default for Event {
|
|||
impl Event {
|
||||
pub fn remove_data(&mut self) {
|
||||
self.data = None;
|
||||
self.attributes.set_dataschema(None as Option<String>);
|
||||
self.attributes.set_datacontenttype(None as Option<String>);
|
||||
}
|
||||
|
||||
/// Write data into the `Event`. You must provide a `content_type` and you can optionally provide a `schema`.
|
||||
/// Write data into the `Event` with the specified `datacontenttype`.
|
||||
///
|
||||
/// ```
|
||||
/// use cloudevents::Event;
|
||||
|
@ -95,17 +99,33 @@ impl Event {
|
|||
/// use std::convert::Into;
|
||||
///
|
||||
/// let mut e = Event::default();
|
||||
/// e.write_data("application/json", None, json!({}))
|
||||
/// e.write_data("application/json", json!({}))
|
||||
/// ```
|
||||
pub fn write_data<S: Into<String>, D: Into<Data>>(
|
||||
pub fn write_data(&mut self, datacontenttype: impl Into<String>, data: impl Into<Data>) {
|
||||
self.attributes.set_datacontenttype(Some(datacontenttype));
|
||||
self.attributes.set_dataschema(None as Option<&str>);
|
||||
self.data = Some(data.into());
|
||||
}
|
||||
|
||||
/// Write data into the `Event` with the specified `datacontenttype` and `dataschema`.
|
||||
///
|
||||
/// ```
|
||||
/// use cloudevents::Event;
|
||||
/// use serde_json::json;
|
||||
/// use std::convert::Into;
|
||||
///
|
||||
/// let mut e = Event::default();
|
||||
/// e.write_data_with_schema("application/json", "http://myapplication.com/schema", json!({}))
|
||||
/// ```
|
||||
pub fn write_data_with_schema(
|
||||
&mut self,
|
||||
content_type: S,
|
||||
schema: Option<S>,
|
||||
value: D,
|
||||
datacontenttype: impl Into<String>,
|
||||
dataschema: impl Into<String>,
|
||||
data: impl Into<Data>,
|
||||
) {
|
||||
self.attributes.set_datacontenttype(Some(content_type));
|
||||
self.attributes.set_dataschema(schema);
|
||||
self.data = Some(value.into());
|
||||
self.attributes.set_datacontenttype(Some(datacontenttype));
|
||||
self.attributes.set_dataschema(Some(dataschema));
|
||||
self.data = Some(data.into());
|
||||
}
|
||||
|
||||
pub fn get_data<T: Sized + From<Data>>(&self) -> Option<T> {
|
||||
|
@ -145,10 +165,34 @@ mod tests {
|
|||
});
|
||||
|
||||
let mut e = Event::default();
|
||||
e.write_data("application/json", None, expected_data.clone());
|
||||
e.write_data_with_schema(
|
||||
"application/json",
|
||||
"http://localhost:8080/schema",
|
||||
expected_data.clone(),
|
||||
);
|
||||
|
||||
let data: serde_json::Value = e.try_get_data().unwrap().unwrap();
|
||||
assert_eq!(expected_data, data);
|
||||
assert_eq!("application/json", e.get_datacontenttype().unwrap())
|
||||
assert_eq!("application/json", e.get_datacontenttype().unwrap());
|
||||
assert_eq!("http://localhost:8080/schema", e.get_dataschema().unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_data() {
|
||||
let mut e = Event::default();
|
||||
e.write_data(
|
||||
"application/json",
|
||||
serde_json::json!({
|
||||
"hello": "world"
|
||||
}),
|
||||
);
|
||||
|
||||
e.remove_data();
|
||||
|
||||
assert!(e
|
||||
.try_get_data::<serde_json::Value, serde_json::Error>()
|
||||
.is_none());
|
||||
assert!(e.get_dataschema().is_none());
|
||||
assert!(e.get_datacontenttype().is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::convert::From;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)]
|
||||
#[serde(untagged)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
/// Represents all the possible [CloudEvents extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) values
|
||||
pub enum ExtensionValue {
|
||||
/// Represents a [`String`](std::string::String) value.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod attributes;
|
||||
mod builder;
|
||||
mod data;
|
||||
mod event;
|
||||
mod extensions;
|
||||
|
@ -6,6 +7,7 @@ mod spec_version;
|
|||
|
||||
pub use attributes::Attributes;
|
||||
pub use attributes::{AttributesReader, AttributesWriter};
|
||||
pub use builder::EventBuilder;
|
||||
pub use data::Data;
|
||||
pub use event::Event;
|
||||
pub use extensions::ExtensionValue;
|
||||
|
@ -14,3 +16,4 @@ pub use spec_version::SpecVersion;
|
|||
mod v10;
|
||||
|
||||
pub use v10::Attributes as AttributesV10;
|
||||
pub use v10::EventBuilder as EventBuilderV10;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::event::attributes::DataAttributesWriter;
|
||||
use crate::event::{AttributesReader, AttributesWriter, ExtensionValue, SpecVersion};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use chrono::{DateTime, Utc};
|
||||
use hostname::get_hostname;
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct Attributes {
|
||||
id: String,
|
||||
ty: String,
|
||||
|
@ -12,7 +13,7 @@ pub struct Attributes {
|
|||
datacontenttype: Option<String>,
|
||||
dataschema: Option<String>,
|
||||
subject: Option<String>,
|
||||
time: Option<DateTime<FixedOffset>>,
|
||||
time: Option<DateTime<Utc>>,
|
||||
extensions: HashMap<String, ExtensionValue>,
|
||||
}
|
||||
|
||||
|
@ -54,7 +55,7 @@ impl AttributesReader for Attributes {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_time(&self) -> Option<&DateTime<FixedOffset>> {
|
||||
fn get_time(&self) -> Option<&DateTime<Utc>> {
|
||||
self.time.as_ref()
|
||||
}
|
||||
|
||||
|
@ -87,7 +88,7 @@ impl AttributesWriter for Attributes {
|
|||
self.subject = subject.map(Into::into)
|
||||
}
|
||||
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<FixedOffset>>>) {
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>) {
|
||||
self.time = time.map(Into::into)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
use super::Attributes as AttributesV10;
|
||||
use crate::event::{Attributes, AttributesWriter, Data, Event, ExtensionValue};
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
pub struct EventBuilder {
|
||||
event: Event,
|
||||
}
|
||||
|
||||
impl EventBuilder {
|
||||
// This works as soon as we have an event version converter
|
||||
// pub fn from(event: Event) -> Self {
|
||||
// EventBuilder { event }
|
||||
// }
|
||||
|
||||
pub fn new() -> Self {
|
||||
EventBuilder {
|
||||
event: Event {
|
||||
attributes: Attributes::V10(AttributesV10::default()),
|
||||
data: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(mut self, id: impl Into<String>) -> Self {
|
||||
self.event.set_id(id);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn source(mut self, source: impl Into<String>) -> Self {
|
||||
self.event.set_source(source);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn ty(mut self, ty: impl Into<String>) -> Self {
|
||||
self.event.set_type(ty);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn subject(mut self, subject: impl Into<String>) -> Self {
|
||||
self.event.set_subject(Some(subject));
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn time(mut self, time: impl Into<DateTime<Utc>>) -> Self {
|
||||
self.event.set_time(Some(time));
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn extension(
|
||||
mut self,
|
||||
extension_name: &str,
|
||||
extension_value: impl Into<ExtensionValue>,
|
||||
) -> Self {
|
||||
self.event.set_extension(extension_name, extension_value);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn data(mut self, datacontenttype: impl Into<String>, data: impl Into<Data>) -> Self {
|
||||
self.event.write_data(datacontenttype, data);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn data_with_schema(
|
||||
mut self,
|
||||
datacontenttype: impl Into<String>,
|
||||
dataschema: impl Into<String>,
|
||||
data: impl Into<Data>,
|
||||
) -> Self {
|
||||
self.event
|
||||
.write_data_with_schema(datacontenttype, dataschema, data);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn build(self) -> Event {
|
||||
self.event
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::event::{AttributesReader, SpecVersion};
|
||||
|
||||
#[test]
|
||||
fn build_event() {
|
||||
let id = "aaa";
|
||||
let source = "http://localhost:8080";
|
||||
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 = "http://localhost:8080/schema";
|
||||
let data = serde_json::json!({
|
||||
"hello": "world"
|
||||
});
|
||||
|
||||
let event = EventBuilder::new()
|
||||
.id(id)
|
||||
.source(source)
|
||||
.ty(ty)
|
||||
.subject(subject)
|
||||
.time(time)
|
||||
.extension(extension_name, extension_value)
|
||||
.data_with_schema(content_type, schema, data.clone())
|
||||
.build();
|
||||
|
||||
assert_eq!(SpecVersion::V10, event.get_specversion());
|
||||
assert_eq!(id, event.get_id());
|
||||
assert_eq!(source, event.get_source());
|
||||
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());
|
||||
|
||||
let event_data: serde_json::Value = event.try_get_data().unwrap().unwrap();
|
||||
assert_eq!(data, event_data);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
mod attributes;
|
||||
mod builder;
|
||||
|
||||
pub use attributes::Attributes;
|
||||
pub use builder::EventBuilder;
|
||||
|
|
|
@ -4,3 +4,4 @@ extern crate serde_json;
|
|||
pub mod event;
|
||||
|
||||
pub use event::Event;
|
||||
pub use event::EventBuilder;
|
||||
|
|
Loading…
Reference in New Issue