From 4b2b00b33188deba56d4315593828e8c56239f4b Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 28 May 2019 13:26:43 +0300 Subject: [PATCH 1/6] domain - add dummy structs: - AdUnit - EventSubmission - TargetingTag --- sentry/src/domain.rs | 6 ++++++ sentry/src/domain/ad_unit.rs | 4 ++++ sentry/src/domain/event_submission.rs | 4 ++++ sentry/src/domain/targeting_tag.rs | 4 ++++ 4 files changed, 18 insertions(+) create mode 100644 sentry/src/domain/ad_unit.rs create mode 100644 sentry/src/domain/event_submission.rs create mode 100644 sentry/src/domain/targeting_tag.rs diff --git a/sentry/src/domain.rs b/sentry/src/domain.rs index 58d89f27a..6026e0fd7 100644 --- a/sentry/src/domain.rs +++ b/sentry/src/domain.rs @@ -4,15 +4,21 @@ use std::pin::Pin; use futures::Future; +pub use self::ad_unit::AdUnit; pub use self::asset::Asset; pub use self::bignum::BigNum; pub use self::channel::{Channel, ChannelId, ChannelListParams, ChannelRepository, ChannelSpec}; +pub use self::event_submission::EventSubmission; +pub use self::targeting_tag::TargetingTag; pub use self::validator::ValidatorDesc; pub mod bignum; pub mod channel; pub mod validator; pub mod asset; +pub mod targeting_tag; +pub mod ad_unit; +pub mod event_submission; #[cfg(test)] /// re-exports all the fixtures in one module diff --git a/sentry/src/domain/ad_unit.rs b/sentry/src/domain/ad_unit.rs new file mode 100644 index 000000000..8c59858c9 --- /dev/null +++ b/sentry/src/domain/ad_unit.rs @@ -0,0 +1,4 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AdUnit {} \ No newline at end of file diff --git a/sentry/src/domain/event_submission.rs b/sentry/src/domain/event_submission.rs new file mode 100644 index 000000000..93a2f77a2 --- /dev/null +++ b/sentry/src/domain/event_submission.rs @@ -0,0 +1,4 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct EventSubmission {} \ No newline at end of file diff --git a/sentry/src/domain/targeting_tag.rs b/sentry/src/domain/targeting_tag.rs new file mode 100644 index 000000000..91c11e2f1 --- /dev/null +++ b/sentry/src/domain/targeting_tag.rs @@ -0,0 +1,4 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct TargetingTag {} \ No newline at end of file From 0e16f0c459e6d17612c5a7db63747fdc0aefd992 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 28 May 2019 16:16:22 +0300 Subject: [PATCH 2/6] util - serde - ts_milliseconds_option - impl ser/deser for `Option>` --- sentry/src/util.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/sentry/src/util.rs b/sentry/src/util.rs index b00b4949f..e3721a104 100644 --- a/sentry/src/util.rs +++ b/sentry/src/util.rs @@ -1,2 +1,27 @@ #[cfg(test)] -pub mod tests; \ No newline at end of file +pub mod tests; + +pub mod serde { + pub mod ts_milliseconds_option { + use chrono::{DateTime, Utc}; + use chrono::serde::ts_milliseconds::deserialize as from_ts_milliseconds; + use chrono::serde::ts_milliseconds::serialize as to_ts_milliseconds; + use serde::{Deserializer, Serializer}; + + pub fn serialize(opt: &Option>, serializer: S) -> Result + where S: Serializer + { + match *opt { + Some(ref dt) => to_ts_milliseconds(dt, serializer), + None => serializer.serialize_none(), + } + } + + pub fn deserialize<'de, D>(de: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + from_ts_milliseconds(de).map(Some) + } + } +} \ No newline at end of file From ae9dca5cb2819156eeabd8d540a9b02eba29b5fc Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 28 May 2019 16:23:04 +0300 Subject: [PATCH 3/6] domain - ChannelSpec - adding all the fields in almost ready state: - domain - ValidatorDesc - rename `id` to `addr` - domain - ChannelSpec - add all the optional fields & optional DateTime for `active_from` - domain - channel - fixtures - update `get_channel_spec` fixture with **dummy** data for now --- sentry/src/domain/channel.rs | 47 ++++++++++++++++--- sentry/src/domain/channel_fixtures.rs | 16 ++++++- sentry/src/domain/validator.rs | 5 +- .../persistence/channel/memory.rs | 2 +- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/sentry/src/domain/channel.rs b/sentry/src/domain/channel.rs index 2f2bf6479..9c0e7cde7 100644 --- a/sentry/src/domain/channel.rs +++ b/sentry/src/domain/channel.rs @@ -1,11 +1,13 @@ use std::convert::TryFrom; use chrono::{DateTime, Utc}; +use chrono::serde::ts_milliseconds; use serde::{Deserialize, Serialize}; use serde_hex::{SerHex, StrictPfx}; -use crate::domain::{Asset, DomainError, RepositoryFuture, ValidatorDesc}; +use crate::domain::{AdUnit, Asset, DomainError, EventSubmission, RepositoryFuture, TargetingTag, ValidatorDesc}; use crate::domain::bignum::BigNum; +use crate::util::serde::ts_milliseconds_option; #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Copy, Clone)] #[serde(transparent)] @@ -55,6 +57,7 @@ pub struct Channel { pub creator: String, pub deposit_asset: Asset, pub deposit_amount: BigNum, + #[serde(with = "ts_milliseconds")] pub valid_until: DateTime, pub spec: ChannelSpec, } @@ -63,7 +66,40 @@ pub struct Channel { #[serde(rename_all = "camelCase")] pub struct ChannelSpec { // TODO: Add the rest of the fields Issue #24 + #[serde(default, skip_serializing_if = "Option::is_none")] + pub title: Option, + // TODO: Make a custom ser/deser 2 validators(leader, follower) array pub validators: Vec, + /// Maximum payment per impression + pub max_per_impression: BigNum, + /// Minimum payment offered per impression + pub min_per_impression: BigNum, + /// An array of TargetingTag (optional) + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub targeting: Vec, + /// Minimum targeting score (optional) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub min_targeting_score: Option, + /// EventSubmission object, applies to event submission (POST /channel/:id/events) + pub event_submission: EventSubmission, + /// A millisecond timestamp of when the campaign was created + #[serde(with = "ts_milliseconds")] + pub created: DateTime, + /// A millisecond timestamp representing the time you want this campaign to become active (optional) + /// Used by the AdViewManager + #[serde(default, skip_serializing_if = "Option::is_none", with = "ts_milliseconds_option")] + pub active_from: Option>, + /// A random number to ensure the campaignSpec hash is unique + pub nonce: BigNum, + /// A millisecond timestamp of when the campaign should enter a withdraw period + /// (no longer accept any events other than CHANNEL_CLOSE) + /// A sane value should be lower than channel.validUntil * 1000 and higher than created + /// It's recommended to set this at least one month prior to channel.validUntil * 1000 + #[serde(with = "ts_milliseconds")] + pub withdraw_period_start: DateTime, + /// An array of AdUnit (optional) + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub ad_units: Vec, } pub struct ChannelListParams { @@ -91,12 +127,9 @@ impl ChannelListParams { } let validator = validator - .and_then(|s| { - if s.is_empty() { - return None; - } - - Some(s) + .and_then(|s| match s.is_empty() { + true => None, + false => Some(s), }); Ok(Self { diff --git a/sentry/src/domain/channel_fixtures.rs b/sentry/src/domain/channel_fixtures.rs index 7841ce007..462afbc02 100644 --- a/sentry/src/domain/channel_fixtures.rs +++ b/sentry/src/domain/channel_fixtures.rs @@ -71,6 +71,20 @@ pub fn get_channel_spec(prefix: &str, validators_option: ValidatorsOption) -> Ch ValidatorsOption::Some(validators) => validators, ValidatorsOption::None => vec![], }; + use crate::domain::EventSubmission; - ChannelSpec { validators } + ChannelSpec { + validators, + title: Some("Title".to_string()), + max_per_impression: BigNum::try_from(1).unwrap(), + min_per_impression: BigNum::try_from(1).unwrap(), + targeting: Vec::new(), + min_targeting_score: Some(0), + event_submission: EventSubmission {}, + created: Utc::now(), + active_from: Some(Utc::now()), + nonce: BigNum::try_from(0).unwrap(), + withdraw_period_start: Utc::now(), + ad_units: Vec::new(), + } } \ No newline at end of file diff --git a/sentry/src/domain/validator.rs b/sentry/src/domain/validator.rs index bbc366433..ef9572006 100644 --- a/sentry/src/domain/validator.rs +++ b/sentry/src/domain/validator.rs @@ -5,7 +5,8 @@ use crate::domain::BigNum; #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct ValidatorDesc { - pub id: String, + // @TODO: UserId issue #36 + pub addr: String, pub url: String, pub fee: BigNum, } @@ -25,7 +26,7 @@ pub(crate) mod fixtures { let url = format!("http://{}-validator-url.com/validator", validator_id); ValidatorDesc { - id: validator_id.to_string(), + addr: validator_id.to_string(), url, fee, } diff --git a/sentry/src/infrastructure/persistence/channel/memory.rs b/sentry/src/infrastructure/persistence/channel/memory.rs index 431137bb9..a9f3b5559 100644 --- a/sentry/src/infrastructure/persistence/channel/memory.rs +++ b/sentry/src/infrastructure/persistence/channel/memory.rs @@ -38,7 +38,7 @@ impl ChannelRepository for MemoryChannelRepository { Some(validator_id) => { // check if there is any validator in the current // `channel.spec.validators` that has the same `id` - channel.spec.validators.iter().any(|validator| &validator.id == validator_id) + channel.spec.validators.iter().any(|validator| &validator.addr == validator_id) } // if None -> the current channel has passed, since we don't need to filter by anything None => true, From c849986757e8d4b7b6e0d38046a9da52961ab281 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 28 May 2019 17:01:39 +0300 Subject: [PATCH 4/6] util - test - take_one - Cargo.toml - add `rand` to `dev-dependencies` --- Cargo.lock | 1 + sentry/Cargo.toml | 3 ++- sentry/src/util/tests.rs | 11 ++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53d00ffab..0d9a3f670 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1230,6 +1230,7 @@ dependencies = [ "hyper 0.12.29 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hex 0.1.0 (git+https://github.com/forrest-marshall/serde-hex)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/sentry/Cargo.toml b/sentry/Cargo.toml index 22f6835a8..9febc92b5 100644 --- a/sentry/Cargo.toml +++ b/sentry/Cargo.toml @@ -37,4 +37,5 @@ num-bigint = { version = "0.2", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] } time = "0.1.42" [dev-dependencies] -fake = { version = "1.3", features = ["chrono"] } \ No newline at end of file +fake = { version = "^1.3", features = ["chrono"] } +rand = "^0.6" \ No newline at end of file diff --git a/sentry/src/util/tests.rs b/sentry/src/util/tests.rs index c25ca52e2..8da2c44b5 100644 --- a/sentry/src/util/tests.rs +++ b/sentry/src/util/tests.rs @@ -1 +1,10 @@ -pub mod time; \ No newline at end of file +use rand::seq::SliceRandom; +use rand::thread_rng; + +pub mod time; + +#[inline] +pub fn take_one<'a, T: ?Sized>(list: &[&'a T]) -> &'a T { + let mut rng = thread_rng(); + list.choose(&mut rng).expect("take_one got empty list") +} \ No newline at end of file From 81f84a94e8866a417a8bf25553a766cb43b9f239 Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 28 May 2019 17:40:01 +0300 Subject: [PATCH 5/6] - domain - channel - fixtures - get_channel_spec - add most of the fake data generated from `Faker` --- sentry/src/domain/channel_fixtures.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/sentry/src/domain/channel_fixtures.rs b/sentry/src/domain/channel_fixtures.rs index 462afbc02..2e8efabc7 100644 --- a/sentry/src/domain/channel_fixtures.rs +++ b/sentry/src/domain/channel_fixtures.rs @@ -72,18 +72,27 @@ pub fn get_channel_spec(prefix: &str, validators_option: ValidatorsOption) -> Ch ValidatorsOption::None => vec![], }; use crate::domain::EventSubmission; + let title_string = Some(::title_descriptor().to_string()); + + let title = test_util::take_one(&[&title_string, &None]).to_owned(); + let max_per_impression = BigNum::try_from(::between(250_u32, 500_u32)).expect("BigNum error when creating from random number"); + let min_per_impression = BigNum::try_from(::between(1_u32, 250_u32)).expect("BigNum error when creating from random number"); + let nonce = BigNum::try_from(::between(100_000_000_u32, 999_999_999_u32)).expect("BigNum error when creating from random number"); + let min_targeting_score = test_util::take_one(&[&None, &Some(::between(1, 500))]).to_owned(); ChannelSpec { validators, - title: Some("Title".to_string()), - max_per_impression: BigNum::try_from(1).unwrap(), - min_per_impression: BigNum::try_from(1).unwrap(), + title, + max_per_impression, + min_per_impression, + // @TODO: `TargetingTag`s fixtures issue #26 targeting: Vec::new(), - min_targeting_score: Some(0), + min_targeting_score, + // @TODO: `EventSubmission` fixture issue #27 event_submission: EventSubmission {}, created: Utc::now(), active_from: Some(Utc::now()), - nonce: BigNum::try_from(0).unwrap(), + nonce, withdraw_period_start: Utc::now(), ad_units: Vec::new(), } From e7f1e02f6ca47ee04ee26e3eebb94e9ae1867b9b Mon Sep 17 00:00:00 2001 From: Lachezar Lechev Date: Tue, 28 May 2019 17:47:24 +0300 Subject: [PATCH 6/6] - domain - channel - fixtures - get_channel_spec - small improvements --- sentry/src/domain/channel_fixtures.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sentry/src/domain/channel_fixtures.rs b/sentry/src/domain/channel_fixtures.rs index 2e8efabc7..a10e149e1 100644 --- a/sentry/src/domain/channel_fixtures.rs +++ b/sentry/src/domain/channel_fixtures.rs @@ -72,13 +72,15 @@ pub fn get_channel_spec(prefix: &str, validators_option: ValidatorsOption) -> Ch ValidatorsOption::None => vec![], }; use crate::domain::EventSubmission; - let title_string = Some(::title_descriptor().to_string()); + use test_util::take_one; - let title = test_util::take_one(&[&title_string, &None]).to_owned(); + let title_string = Some(::sentence(3, 4)); + + let title = take_one(&[&title_string, &None]).to_owned(); let max_per_impression = BigNum::try_from(::between(250_u32, 500_u32)).expect("BigNum error when creating from random number"); let min_per_impression = BigNum::try_from(::between(1_u32, 250_u32)).expect("BigNum error when creating from random number"); let nonce = BigNum::try_from(::between(100_000_000_u32, 999_999_999_u32)).expect("BigNum error when creating from random number"); - let min_targeting_score = test_util::take_one(&[&None, &Some(::between(1, 500))]).to_owned(); + let min_targeting_score = take_one(&[&None, &Some(::between(1, 500))]).to_owned(); ChannelSpec { validators,