Introduced URL support (#27)
* Introduced url library Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> * Fixed lock Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> * Integrated changes with url library Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> * Cargo fmt Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
This commit is contained in:
parent
4c25539abf
commit
9b2f26223f
|
@ -53,6 +53,7 @@ dependencies = [
|
|||
"serde-value",
|
||||
"serde_json",
|
||||
"snafu",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
@ -94,6 +95,17 @@ dependencies = [
|
|||
"winutil",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.5"
|
||||
|
@ -106,6 +118,12 @@ version = "0.2.67"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.42"
|
||||
|
@ -134,6 +152,12 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.6"
|
||||
|
@ -289,6 +313,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
|
||||
|
||||
[[package]]
|
||||
name = "snafu"
|
||||
version = "0.6.6"
|
||||
|
@ -332,12 +362,42 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
dependencies = [
|
||||
"matches",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.1"
|
||||
|
|
|
@ -19,6 +19,7 @@ delegate = "^0.4"
|
|||
uuid = { version = "^0.8", features = ["serde", "v4"] }
|
||||
hostname = "^0.1"
|
||||
base64 = "^0.12"
|
||||
url = { version = "^2.1", features = ["serde"] }
|
||||
snafu = "^0.6"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use super::{AttributesV03, AttributesV10, SpecVersion};
|
||||
use chrono::{DateTime, Utc};
|
||||
use url::Url;
|
||||
|
||||
/// Trait to get [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes).
|
||||
pub trait AttributesReader {
|
||||
/// Get the [id](https://github.com/cloudevents/spec/blob/master/spec.md#id).
|
||||
fn get_id(&self) -> &str;
|
||||
/// Get the [source](https://github.com/cloudevents/spec/blob/master/spec.md#source-1).
|
||||
fn get_source(&self) -> &str;
|
||||
fn get_source(&self) -> &Url;
|
||||
/// Get the [specversion](https://github.com/cloudevents/spec/blob/master/spec.md#specversion).
|
||||
fn get_specversion(&self) -> SpecVersion;
|
||||
/// Get the [type](https://github.com/cloudevents/spec/blob/master/spec.md#type).
|
||||
|
@ -14,7 +15,7 @@ pub trait AttributesReader {
|
|||
/// Get the [datacontenttype](https://github.com/cloudevents/spec/blob/master/spec.md#datacontenttype).
|
||||
fn get_datacontenttype(&self) -> Option<&str>;
|
||||
/// Get the [dataschema](https://github.com/cloudevents/spec/blob/master/spec.md#dataschema).
|
||||
fn get_dataschema(&self) -> Option<&str>;
|
||||
fn get_dataschema(&self) -> Option<&Url>;
|
||||
/// 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).
|
||||
|
@ -23,7 +24,7 @@ pub trait AttributesReader {
|
|||
|
||||
pub trait AttributesWriter {
|
||||
fn set_id(&mut self, id: impl Into<String>);
|
||||
fn set_source(&mut self, source: impl Into<String>);
|
||||
fn set_source(&mut self, source: impl Into<Url>);
|
||||
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<Utc>>>);
|
||||
|
@ -36,7 +37,7 @@ pub(crate) trait AttributesConverter {
|
|||
|
||||
pub(crate) trait DataAttributesWriter {
|
||||
fn set_datacontenttype(&mut self, datacontenttype: Option<impl Into<String>>);
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>);
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
|
@ -53,7 +54,7 @@ impl AttributesReader for Attributes {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_source(&self) -> &str {
|
||||
fn get_source(&self) -> &Url {
|
||||
match self {
|
||||
Attributes::V03(a) => a.get_source(),
|
||||
Attributes::V10(a) => a.get_source(),
|
||||
|
@ -81,7 +82,7 @@ impl AttributesReader for Attributes {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_dataschema(&self) -> Option<&str> {
|
||||
fn get_dataschema(&self) -> Option<&Url> {
|
||||
match self {
|
||||
Attributes::V03(a) => a.get_dataschema(),
|
||||
Attributes::V10(a) => a.get_dataschema(),
|
||||
|
@ -111,7 +112,7 @@ impl AttributesWriter for Attributes {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_source(&mut self, source: impl Into<String>) {
|
||||
fn set_source(&mut self, source: impl Into<Url>) {
|
||||
match self {
|
||||
Attributes::V03(a) => a.set_source(source),
|
||||
Attributes::V10(a) => a.set_source(source),
|
||||
|
@ -148,7 +149,7 @@ impl DataAttributesWriter for Attributes {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>) {
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) {
|
||||
match self {
|
||||
Attributes::V03(a) => a.set_dataschema(dataschema),
|
||||
Attributes::V10(a) => a.set_dataschema(dataschema),
|
||||
|
|
|
@ -4,10 +4,11 @@ use super::{EventBuilderV03, EventBuilderV10};
|
|||
/// ```
|
||||
/// use cloudevents::EventBuilder;
|
||||
/// use chrono::Utc;
|
||||
/// use url::Url;
|
||||
///
|
||||
/// let event = EventBuilder::v10()
|
||||
/// .id("my_event.my_application")
|
||||
/// .source("http://localhost:8080")
|
||||
/// .source(Url::parse("http://localhost:8080").unwrap())
|
||||
/// .time(Utc::now())
|
||||
/// .build();
|
||||
/// ```
|
||||
|
|
|
@ -7,6 +7,7 @@ use chrono::{DateTime, Utc};
|
|||
use delegate::delegate;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use url::Url;
|
||||
|
||||
/// Data structure that represents a [CloudEvent](https://github.com/cloudevents/spec/blob/master/spec.md).
|
||||
/// It provides methods to get the attributes through [`AttributesReader`]
|
||||
|
@ -43,11 +44,11 @@ impl AttributesReader for Event {
|
|||
delegate! {
|
||||
to self.attributes {
|
||||
fn get_id(&self) -> &str;
|
||||
fn get_source(&self) -> &str;
|
||||
fn get_source(&self) -> &Url;
|
||||
fn get_specversion(&self) -> SpecVersion;
|
||||
fn get_type(&self) -> &str;
|
||||
fn get_datacontenttype(&self) -> Option<&str>;
|
||||
fn get_dataschema(&self) -> Option<&str>;
|
||||
fn get_dataschema(&self) -> Option<&Url>;
|
||||
fn get_subject(&self) -> Option<&str>;
|
||||
fn get_time(&self) -> Option<&DateTime<Utc>>;
|
||||
}
|
||||
|
@ -58,7 +59,7 @@ impl AttributesWriter for Event {
|
|||
delegate! {
|
||||
to self.attributes {
|
||||
fn set_id(&mut self, id: impl Into<String>);
|
||||
fn set_source(&mut self, source: impl Into<String>);
|
||||
fn set_source(&mut self, source: impl Into<Url>);
|
||||
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<Utc>>>);
|
||||
|
@ -79,7 +80,7 @@ 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_dataschema(None as Option<Url>);
|
||||
self.attributes.set_datacontenttype(None as Option<String>);
|
||||
}
|
||||
|
||||
|
@ -95,7 +96,7 @@ impl Event {
|
|||
/// ```
|
||||
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.attributes.set_dataschema(None as Option<Url>);
|
||||
self.data = Some(data.into());
|
||||
}
|
||||
|
||||
|
@ -105,14 +106,19 @@ impl Event {
|
|||
/// use cloudevents::Event;
|
||||
/// use serde_json::json;
|
||||
/// use std::convert::Into;
|
||||
/// use url::Url;
|
||||
///
|
||||
/// let mut e = Event::default();
|
||||
/// e.write_data_with_schema("application/json", "http://myapplication.com/schema", json!({}))
|
||||
/// e.write_data_with_schema(
|
||||
/// "application/json",
|
||||
/// Url::parse("http://myapplication.com/schema").unwrap(),
|
||||
/// json!({})
|
||||
/// )
|
||||
/// ```
|
||||
pub fn write_data_with_schema(
|
||||
&mut self,
|
||||
datacontenttype: impl Into<String>,
|
||||
dataschema: impl Into<String>,
|
||||
dataschema: impl Into<Url>,
|
||||
data: impl Into<Data>,
|
||||
) {
|
||||
self.attributes.set_datacontenttype(Some(datacontenttype));
|
||||
|
@ -186,14 +192,17 @@ mod tests {
|
|||
let mut e = Event::default();
|
||||
e.write_data_with_schema(
|
||||
"application/json",
|
||||
"http://localhost:8080/schema",
|
||||
Url::parse("http://localhost:8080/schema").unwrap(),
|
||||
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!("http://localhost:8080/schema", e.get_dataschema().unwrap())
|
||||
assert_eq!(
|
||||
&Url::parse("http://localhost:8080/schema").unwrap(),
|
||||
e.get_dataschema().unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -20,6 +20,23 @@ macro_rules! parse_optional_field {
|
|||
})
|
||||
.transpose()
|
||||
};
|
||||
|
||||
($map:ident, $name:literal, $value_variant:ident, $error:ty, $mapper:expr) => {
|
||||
$map.remove($name)
|
||||
.map(|val| match val {
|
||||
Value::$value_variant(v) => $mapper(&v).map_err(|e| {
|
||||
<$error>::invalid_value(
|
||||
crate::event::serde::value_to_unexpected(&Value::$value_variant(v)),
|
||||
&e.to_string().as_str(),
|
||||
)
|
||||
}),
|
||||
other => Err(<$error>::invalid_type(
|
||||
crate::event::serde::value_to_unexpected(&other),
|
||||
&stringify!($value_variant),
|
||||
)),
|
||||
})
|
||||
.transpose()
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! parse_field {
|
||||
|
@ -27,6 +44,11 @@ macro_rules! parse_field {
|
|||
parse_optional_field!($map, $name, $value_variant, $error)?
|
||||
.ok_or_else(|| <$error>::missing_field($name))
|
||||
};
|
||||
|
||||
($map:ident, $name:literal, $value_variant:ident, $error:ty, $mapper:expr) => {
|
||||
parse_optional_field!($map, $name, $value_variant, $error, $mapper)?
|
||||
.ok_or_else(|| <$error>::missing_field($name))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! parse_data_json {
|
||||
|
|
|
@ -3,15 +3,16 @@ use crate::event::AttributesV10;
|
|||
use crate::event::{AttributesReader, AttributesWriter, SpecVersion};
|
||||
use chrono::{DateTime, Utc};
|
||||
use hostname::get_hostname;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct Attributes {
|
||||
pub(crate) id: String,
|
||||
pub(crate) ty: String,
|
||||
pub(crate) source: String,
|
||||
pub(crate) source: Url,
|
||||
pub(crate) datacontenttype: Option<String>,
|
||||
pub(crate) schemaurl: Option<String>,
|
||||
pub(crate) schemaurl: Option<Url>,
|
||||
pub(crate) subject: Option<String>,
|
||||
pub(crate) time: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
@ -21,7 +22,7 @@ impl AttributesReader for Attributes {
|
|||
&self.id
|
||||
}
|
||||
|
||||
fn get_source(&self) -> &str {
|
||||
fn get_source(&self) -> &Url {
|
||||
&self.source
|
||||
}
|
||||
|
||||
|
@ -34,24 +35,15 @@ impl AttributesReader for Attributes {
|
|||
}
|
||||
|
||||
fn get_datacontenttype(&self) -> Option<&str> {
|
||||
match self.datacontenttype.as_ref() {
|
||||
Some(s) => Some(&s),
|
||||
None => None,
|
||||
}
|
||||
self.datacontenttype.as_deref()
|
||||
}
|
||||
|
||||
fn get_dataschema(&self) -> Option<&str> {
|
||||
match self.schemaurl.as_ref() {
|
||||
Some(s) => Some(&s),
|
||||
None => None,
|
||||
}
|
||||
fn get_dataschema(&self) -> Option<&Url> {
|
||||
self.schemaurl.as_ref()
|
||||
}
|
||||
|
||||
fn get_subject(&self) -> Option<&str> {
|
||||
match self.subject.as_ref() {
|
||||
Some(s) => Some(&s),
|
||||
None => None,
|
||||
}
|
||||
self.subject.as_deref()
|
||||
}
|
||||
|
||||
fn get_time(&self) -> Option<&DateTime<Utc>> {
|
||||
|
@ -64,7 +56,7 @@ impl AttributesWriter for Attributes {
|
|||
self.id = id.into()
|
||||
}
|
||||
|
||||
fn set_source(&mut self, source: impl Into<String>) {
|
||||
fn set_source(&mut self, source: impl Into<Url>) {
|
||||
self.source = source.into()
|
||||
}
|
||||
|
||||
|
@ -86,7 +78,7 @@ impl DataAttributesWriter for Attributes {
|
|||
self.datacontenttype = datacontenttype.map(Into::into)
|
||||
}
|
||||
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>) {
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) {
|
||||
self.schemaurl = dataschema.map(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +88,14 @@ impl Default for Attributes {
|
|||
Attributes {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
ty: "type".to_string(),
|
||||
source: get_hostname().unwrap_or("http://localhost/".to_string()),
|
||||
source: Url::parse(
|
||||
format!(
|
||||
"http://{}",
|
||||
get_hostname().unwrap_or("localhost".to_string())
|
||||
)
|
||||
.as_ref(),
|
||||
)
|
||||
.unwrap(),
|
||||
datacontenttype: None,
|
||||
schemaurl: None,
|
||||
subject: None,
|
||||
|
|
|
@ -2,6 +2,7 @@ use super::Attributes as AttributesV03;
|
|||
use crate::event::{Attributes, AttributesWriter, Data, Event, ExtensionValue};
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
|
||||
pub struct EventBuilder {
|
||||
event: Event,
|
||||
|
@ -33,7 +34,7 @@ impl EventBuilder {
|
|||
return self;
|
||||
}
|
||||
|
||||
pub fn source(mut self, source: impl Into<String>) -> Self {
|
||||
pub fn source(mut self, source: impl Into<Url>) -> Self {
|
||||
self.event.set_source(source);
|
||||
return self;
|
||||
}
|
||||
|
@ -70,7 +71,7 @@ impl EventBuilder {
|
|||
pub fn data_with_schema(
|
||||
mut self,
|
||||
datacontenttype: impl Into<String>,
|
||||
schemaurl: impl Into<String>,
|
||||
schemaurl: impl Into<Url>,
|
||||
data: impl Into<Data>,
|
||||
) -> Self {
|
||||
self.event
|
||||
|
@ -91,31 +92,31 @@ mod tests {
|
|||
#[test]
|
||||
fn build_event() {
|
||||
let id = "aaa";
|
||||
let source = "http://localhost:8080";
|
||||
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 = "http://localhost:8080/schema";
|
||||
let schema = Url::parse("http://localhost:8080/schema").unwrap();
|
||||
let data = serde_json::json!({
|
||||
"hello": "world"
|
||||
});
|
||||
|
||||
let event = EventBuilder::new()
|
||||
.id(id)
|
||||
.source(source)
|
||||
.source(source.clone())
|
||||
.ty(ty)
|
||||
.subject(subject)
|
||||
.time(time)
|
||||
.extension(extension_name, extension_value)
|
||||
.data_with_schema(content_type, schema, data.clone())
|
||||
.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());
|
||||
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());
|
||||
|
@ -124,7 +125,7 @@ mod tests {
|
|||
event.get_extension(extension_name).unwrap().clone()
|
||||
);
|
||||
assert_eq!(content_type, event.get_datacontenttype().unwrap());
|
||||
assert_eq!(schema, event.get_dataschema().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);
|
||||
|
|
|
@ -42,9 +42,9 @@ impl crate::event::deserializer::AttributesSerializer for super::Attributes {
|
|||
match name {
|
||||
"id" => self.id = value.to_string(),
|
||||
"type" => self.ty = value.to_string(),
|
||||
"source" => self.source = value.to_string(),
|
||||
"source" => self.source = value.try_into()?,
|
||||
"datacontenttype" => self.datacontenttype = Some(value.to_string()),
|
||||
"schemaurl" => self.schemaurl = Some(value.to_string()),
|
||||
"schemaurl" => self.schemaurl = Some(value.try_into()?),
|
||||
"subject" => self.subject = Some(value.to_string()),
|
||||
"time" => self.time = Some(value.try_into()?),
|
||||
_ => {
|
||||
|
|
|
@ -2,11 +2,12 @@ use super::Attributes;
|
|||
use crate::event::data::is_json_content_type;
|
||||
use crate::event::{Data, ExtensionValue};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::de::{IntoDeserializer, Unexpected};
|
||||
use serde::de::IntoDeserializer;
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::{Deserialize, Serializer};
|
||||
use serde_value::Value;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use url::Url;
|
||||
|
||||
pub(crate) struct EventDeserializer {}
|
||||
|
||||
|
@ -17,19 +18,14 @@ impl crate::event::serde::EventDeserializer for EventDeserializer {
|
|||
Ok(crate::event::Attributes::V03(Attributes {
|
||||
id: parse_field!(map, "id", String, E)?,
|
||||
ty: parse_field!(map, "type", String, E)?,
|
||||
source: parse_field!(map, "source", String, E)?,
|
||||
source: parse_field!(map, "source", String, E, Url::parse)?,
|
||||
datacontenttype: parse_optional_field!(map, "datacontenttype", String, E)?,
|
||||
schemaurl: parse_optional_field!(map, "schemaurl", String, E)?,
|
||||
schemaurl: parse_optional_field!(map, "schemaurl", String, E, Url::parse)?,
|
||||
subject: parse_optional_field!(map, "subject", String, E)?,
|
||||
time: parse_optional_field!(map, "time", String, E)?
|
||||
.map(|s| match DateTime::parse_from_rfc3339(&s) {
|
||||
Ok(d) => Ok(DateTime::<Utc>::from(d)),
|
||||
Err(e) => Err(E::invalid_value(
|
||||
Unexpected::Str(&s),
|
||||
&e.to_string().as_str(),
|
||||
)),
|
||||
})
|
||||
.transpose()?,
|
||||
time: parse_optional_field!(map, "time", String, E, |s| DateTime::parse_from_rfc3339(
|
||||
s
|
||||
)
|
||||
.map(DateTime::<Utc>::from))?,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
@ -2,15 +2,16 @@ use crate::event::attributes::{AttributesConverter, DataAttributesWriter};
|
|||
use crate::event::{AttributesReader, AttributesV03, AttributesWriter, SpecVersion};
|
||||
use chrono::{DateTime, Utc};
|
||||
use hostname::get_hostname;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct Attributes {
|
||||
pub(crate) id: String,
|
||||
pub(crate) ty: String,
|
||||
pub(crate) source: String,
|
||||
pub(crate) source: Url,
|
||||
pub(crate) datacontenttype: Option<String>,
|
||||
pub(crate) dataschema: Option<String>,
|
||||
pub(crate) dataschema: Option<Url>,
|
||||
pub(crate) subject: Option<String>,
|
||||
pub(crate) time: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
@ -20,7 +21,7 @@ impl AttributesReader for Attributes {
|
|||
&self.id
|
||||
}
|
||||
|
||||
fn get_source(&self) -> &str {
|
||||
fn get_source(&self) -> &Url {
|
||||
&self.source
|
||||
}
|
||||
|
||||
|
@ -33,24 +34,15 @@ impl AttributesReader for Attributes {
|
|||
}
|
||||
|
||||
fn get_datacontenttype(&self) -> Option<&str> {
|
||||
match self.datacontenttype.as_ref() {
|
||||
Some(s) => Some(&s),
|
||||
None => None,
|
||||
}
|
||||
self.datacontenttype.as_deref()
|
||||
}
|
||||
|
||||
fn get_dataschema(&self) -> Option<&str> {
|
||||
match self.dataschema.as_ref() {
|
||||
Some(s) => Some(&s),
|
||||
None => None,
|
||||
}
|
||||
fn get_dataschema(&self) -> Option<&Url> {
|
||||
self.dataschema.as_ref()
|
||||
}
|
||||
|
||||
fn get_subject(&self) -> Option<&str> {
|
||||
match self.subject.as_ref() {
|
||||
Some(s) => Some(&s),
|
||||
None => None,
|
||||
}
|
||||
self.subject.as_deref()
|
||||
}
|
||||
|
||||
fn get_time(&self) -> Option<&DateTime<Utc>> {
|
||||
|
@ -63,7 +55,7 @@ impl AttributesWriter for Attributes {
|
|||
self.id = id.into()
|
||||
}
|
||||
|
||||
fn set_source(&mut self, source: impl Into<String>) {
|
||||
fn set_source(&mut self, source: impl Into<Url>) {
|
||||
self.source = source.into()
|
||||
}
|
||||
|
||||
|
@ -85,7 +77,7 @@ impl DataAttributesWriter for Attributes {
|
|||
self.datacontenttype = datacontenttype.map(Into::into)
|
||||
}
|
||||
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>) {
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) {
|
||||
self.dataschema = dataschema.map(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +87,14 @@ impl Default for Attributes {
|
|||
Attributes {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
ty: "type".to_string(),
|
||||
source: get_hostname().unwrap_or("http://localhost/".to_string()),
|
||||
source: Url::parse(
|
||||
format!(
|
||||
"http://{}",
|
||||
get_hostname().unwrap_or("localhost".to_string())
|
||||
)
|
||||
.as_ref(),
|
||||
)
|
||||
.unwrap(),
|
||||
datacontenttype: None,
|
||||
dataschema: None,
|
||||
subject: None,
|
||||
|
|
|
@ -2,6 +2,7 @@ use super::Attributes as AttributesV10;
|
|||
use crate::event::{Attributes, AttributesWriter, Data, Event, ExtensionValue};
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
|
||||
pub struct EventBuilder {
|
||||
event: Event,
|
||||
|
@ -33,7 +34,7 @@ impl EventBuilder {
|
|||
return self;
|
||||
}
|
||||
|
||||
pub fn source(mut self, source: impl Into<String>) -> Self {
|
||||
pub fn source(mut self, source: impl Into<Url>) -> Self {
|
||||
self.event.set_source(source);
|
||||
return self;
|
||||
}
|
||||
|
@ -70,7 +71,7 @@ impl EventBuilder {
|
|||
pub fn data_with_schema(
|
||||
mut self,
|
||||
datacontenttype: impl Into<String>,
|
||||
dataschema: impl Into<String>,
|
||||
dataschema: impl Into<Url>,
|
||||
data: impl Into<Data>,
|
||||
) -> Self {
|
||||
self.event
|
||||
|
@ -91,31 +92,31 @@ mod tests {
|
|||
#[test]
|
||||
fn build_event() {
|
||||
let id = "aaa";
|
||||
let source = "http://localhost:8080";
|
||||
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 = "http://localhost:8080/schema";
|
||||
let schema = Url::parse("http://localhost:8080/schema").unwrap();
|
||||
let data = serde_json::json!({
|
||||
"hello": "world"
|
||||
});
|
||||
|
||||
let event = EventBuilder::new()
|
||||
.id(id)
|
||||
.source(source)
|
||||
.source(source.clone())
|
||||
.ty(ty)
|
||||
.subject(subject)
|
||||
.time(time)
|
||||
.extension(extension_name, extension_value)
|
||||
.data_with_schema(content_type, schema, data.clone())
|
||||
.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());
|
||||
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());
|
||||
|
@ -124,7 +125,7 @@ mod tests {
|
|||
event.get_extension(extension_name).unwrap().clone()
|
||||
);
|
||||
assert_eq!(content_type, event.get_datacontenttype().unwrap());
|
||||
assert_eq!(schema, event.get_dataschema().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);
|
||||
|
|
|
@ -42,9 +42,9 @@ impl crate::event::deserializer::AttributesSerializer for super::Attributes {
|
|||
match name {
|
||||
"id" => self.id = value.to_string(),
|
||||
"type" => self.ty = value.to_string(),
|
||||
"source" => self.source = value.to_string(),
|
||||
"source" => self.source = value.try_into()?,
|
||||
"datacontenttype" => self.datacontenttype = Some(value.to_string()),
|
||||
"dataschema" => self.dataschema = Some(value.to_string()),
|
||||
"dataschema" => self.dataschema = Some(value.try_into()?),
|
||||
"subject" => self.subject = Some(value.to_string()),
|
||||
"time" => self.time = Some(value.try_into()?),
|
||||
_ => {
|
||||
|
|
|
@ -2,11 +2,12 @@ use super::Attributes;
|
|||
use crate::event::data::is_json_content_type;
|
||||
use crate::event::{Data, ExtensionValue};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::de::{IntoDeserializer, Unexpected};
|
||||
use serde::de::IntoDeserializer;
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::{Deserialize, Serializer};
|
||||
use serde_value::Value;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use url::Url;
|
||||
|
||||
pub(crate) struct EventDeserializer {}
|
||||
|
||||
|
@ -17,19 +18,14 @@ impl crate::event::serde::EventDeserializer for EventDeserializer {
|
|||
Ok(crate::event::Attributes::V10(Attributes {
|
||||
id: parse_field!(map, "id", String, E)?,
|
||||
ty: parse_field!(map, "type", String, E)?,
|
||||
source: parse_field!(map, "source", String, E)?,
|
||||
source: parse_field!(map, "source", String, E, Url::parse)?,
|
||||
datacontenttype: parse_optional_field!(map, "datacontenttype", String, E)?,
|
||||
dataschema: parse_optional_field!(map, "dataschema", String, E)?,
|
||||
dataschema: parse_optional_field!(map, "dataschema", String, E, Url::parse)?,
|
||||
subject: parse_optional_field!(map, "subject", String, E)?,
|
||||
time: parse_optional_field!(map, "time", String, E)?
|
||||
.map(|s| match DateTime::parse_from_rfc3339(&s) {
|
||||
Ok(d) => Ok(DateTime::<Utc>::from(d)),
|
||||
Err(e) => Err(E::invalid_value(
|
||||
Unexpected::Str(&s),
|
||||
&e.to_string().as_str(),
|
||||
)),
|
||||
})
|
||||
.transpose()?,
|
||||
time: parse_optional_field!(map, "time", String, E, |s| DateTime::parse_from_rfc3339(
|
||||
s
|
||||
)
|
||||
.map(DateTime::<Utc>::from))?,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,12 @@ use std::io::Read;
|
|||
pub enum Error {
|
||||
#[snafu(display("Unrecognized attribute name: {}", name))]
|
||||
UnrecognizedAttributeName { name: String },
|
||||
#[snafu(display("Error while decoding base64: {}", source))]
|
||||
#[snafu(display("Error while parsing a time string: {}", source))]
|
||||
#[snafu(context(false))]
|
||||
ParseTimeError { source: chrono::ParseError },
|
||||
#[snafu(display("Error while parsing a url: {}", source))]
|
||||
#[snafu(context(false))]
|
||||
ParseUrlError { source: url::ParseError },
|
||||
#[snafu(display("Error while decoding base64: {}", source))]
|
||||
#[snafu(context(false))]
|
||||
Base64DecodingError { source: base64::DecodeError },
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use crate::event::ExtensionValue;
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::convert::TryInto;
|
||||
use url::Url;
|
||||
|
||||
pub enum MessageAttributeValue {
|
||||
Boolean(bool),
|
||||
Integer(i64),
|
||||
String(String),
|
||||
Binary(Vec<u8>),
|
||||
Uri(String),
|
||||
UriRef(String),
|
||||
Uri(Url),
|
||||
UriRef(Url),
|
||||
DateTime(DateTime<Utc>),
|
||||
}
|
||||
|
||||
|
@ -25,6 +26,18 @@ impl TryInto<DateTime<Utc>> for MessageAttributeValue {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryInto<Url> for MessageAttributeValue {
|
||||
type Error = super::Error;
|
||||
|
||||
fn try_into(self) -> Result<Url, Self::Error> {
|
||||
match self {
|
||||
MessageAttributeValue::Uri(u) => Ok(u),
|
||||
MessageAttributeValue::UriRef(u) => Ok(u),
|
||||
v => Ok(Url::parse(v.to_string().as_ref())?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for MessageAttributeValue {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
|
@ -32,8 +45,8 @@ impl ToString for MessageAttributeValue {
|
|||
MessageAttributeValue::Integer(i) => i.to_string(),
|
||||
MessageAttributeValue::String(s) => s.clone(),
|
||||
MessageAttributeValue::Binary(v) => base64::encode(v),
|
||||
MessageAttributeValue::Uri(s) => s.clone(),
|
||||
MessageAttributeValue::UriRef(s) => s.clone(),
|
||||
MessageAttributeValue::Uri(u) => u.to_string(),
|
||||
MessageAttributeValue::UriRef(u) => u.to_string(),
|
||||
MessageAttributeValue::DateTime(d) => d.to_rfc3339(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ pub fn ty() -> String {
|
|||
}
|
||||
|
||||
pub fn source() -> String {
|
||||
"http://localhost".to_string()
|
||||
"http://localhost/".to_string()
|
||||
}
|
||||
|
||||
pub fn json_datacontenttype() -> String {
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use super::*;
|
||||
use cloudevents::{Event, EventBuilder};
|
||||
use serde_json::{json, Value};
|
||||
use std::convert::TryInto;
|
||||
use url::Url;
|
||||
|
||||
pub fn minimal() -> Event {
|
||||
EventBuilder::v03()
|
||||
.id(id())
|
||||
.source(source())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.build()
|
||||
}
|
||||
|
@ -26,7 +28,7 @@ pub fn full_no_data() -> Event {
|
|||
|
||||
EventBuilder::v03()
|
||||
.id(id())
|
||||
.source(source())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.subject(subject())
|
||||
.time(time())
|
||||
|
@ -61,14 +63,18 @@ pub fn full_json_data() -> Event {
|
|||
|
||||
EventBuilder::v03()
|
||||
.id(id())
|
||||
.source(source())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.subject(subject())
|
||||
.time(time())
|
||||
.extension(&string_ext_name, string_ext_value)
|
||||
.extension(&bool_ext_name, bool_ext_value)
|
||||
.extension(&int_ext_name, int_ext_value)
|
||||
.data_with_schema(json_datacontenttype(), dataschema(), json_data())
|
||||
.data_with_schema(
|
||||
json_datacontenttype(),
|
||||
Url::parse(dataschema().as_ref()).unwrap(),
|
||||
json_data(),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
|
@ -122,7 +128,7 @@ pub fn full_xml_string_data() -> Event {
|
|||
|
||||
EventBuilder::v03()
|
||||
.id(id())
|
||||
.source(source())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.subject(subject())
|
||||
.time(time())
|
||||
|
@ -140,7 +146,7 @@ pub fn full_xml_binary_data() -> Event {
|
|||
|
||||
EventBuilder::v03()
|
||||
.id(id())
|
||||
.source(source())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.subject(subject())
|
||||
.time(time())
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use super::*;
|
||||
use cloudevents::{Event, EventBuilder};
|
||||
use serde_json::{json, Value};
|
||||
use url::Url;
|
||||
|
||||
pub fn minimal() -> Event {
|
||||
EventBuilder::v10()
|
||||
.id(id())
|
||||
.source(source())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.build()
|
||||
}
|
||||
|
@ -26,7 +27,7 @@ pub fn full_no_data() -> Event {
|
|||
|
||||
EventBuilder::v10()
|
||||
.id(id())
|
||||
.source(source())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.subject(subject())
|
||||
.time(time())
|
||||
|
@ -61,14 +62,18 @@ pub fn full_json_data() -> Event {
|
|||
|
||||
EventBuilder::v10()
|
||||
.id(id())
|
||||
.source(source())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.subject(subject())
|
||||
.time(time())
|
||||
.extension(&string_ext_name, string_ext_value)
|
||||
.extension(&bool_ext_name, bool_ext_value)
|
||||
.extension(&int_ext_name, int_ext_value)
|
||||
.data_with_schema(json_datacontenttype(), dataschema(), json_data())
|
||||
.data_with_schema(
|
||||
json_datacontenttype(),
|
||||
Url::parse(dataschema().as_ref()).unwrap(),
|
||||
json_data(),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
|
@ -121,7 +126,7 @@ pub fn full_xml_string_data() -> Event {
|
|||
|
||||
EventBuilder::v10()
|
||||
.id(id())
|
||||
.source(source())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.subject(subject())
|
||||
.time(time())
|
||||
|
@ -139,7 +144,7 @@ pub fn full_xml_binary_data() -> Event {
|
|||
|
||||
EventBuilder::v10()
|
||||
.id(id())
|
||||
.source(source())
|
||||
.source(Url::parse(source().as_ref()).unwrap())
|
||||
.ty(ty())
|
||||
.subject(subject())
|
||||
.time(time())
|
||||
|
|
Loading…
Reference in New Issue