Skip to content

Commit 61a7acb

Browse files
authored
Issue #55 Initial validator crate skeleton (#57)
* validator crate * validator - Cargo.toml - add `futures-preview` * validator - domain, infrastructure skeleton modules * validator - lib - add `deny` attr for `rust_2018_idioms` & `clippy::all` * validator: - lib - add `async_await` & `await_macro` - Cargo.toml - add `reqwest` for the api calls towards Sentry * validator - domain - ChannelRepository * validator - domain - register `channel` module * validator - domain - fix `ChannelRepository::all` * validator - domain - ChannelRepository - fix clippy * sentry - domain - move ChannelRepository to Sentry * validator - add `repositories` feature + `futures 0.1` for `reqwest` * validator - Cargo.toml - `serde` & `serde_json` * validator - infrastructure - add simple WIP api implementation * sentry - domain - ChannelRepository - require `Debug` to be used with `Arc` * sentry - use `Arc` to pass the concrete impl of repository for a `Resource` * validator - persistence - channel - api - `page` to `u32` * domain - util - `ts_milliseconds_option` fix deserialization * validator - Cargo.toml - add `tokio` * validator - main.rs - Fetch Channels for validator by ApiChannelRepository
1 parent ec51ec4 commit 61a7acb

File tree

30 files changed

+635
-135
lines changed

30 files changed

+635
-135
lines changed

Cargo.lock

Lines changed: 297 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ members = [
44
"adapter",
55
"domain",
66
"sentry",
7+
"validator",
78
]

domain/src/channel.rs

Lines changed: 0 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ use crate::bignum::BigNum;
1010
use crate::util::serde::ts_milliseconds_option;
1111
use crate::{AdUnit, Asset, DomainError, EventSubmission, TargetingTag, ValidatorDesc};
1212

13-
#[cfg(feature = "repositories")]
14-
pub use self::repository::ChannelRepository;
15-
1613
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Copy, Clone)]
1714
#[serde(transparent)]
1815
pub struct ChannelId {
@@ -209,66 +206,6 @@ impl From<[ValidatorDesc; 2]> for SpecValidators {
209206
}
210207
}
211208

212-
pub struct ChannelListParams {
213-
/// page to show, should be >= 1
214-
pub page: u32,
215-
/// channels limit per page, should be >= 1
216-
pub limit: u32,
217-
/// filters `valid_until` to be `>= valid_until_ge`
218-
pub valid_until_ge: DateTime<Utc>,
219-
/// filters the channels containing a specific validator if provided
220-
pub validator: Option<String>,
221-
/// Ensures that this struct can only be created by calling `new()`
222-
_secret: (),
223-
}
224-
225-
impl ChannelListParams {
226-
pub fn new(
227-
valid_until_ge: DateTime<Utc>,
228-
limit: u32,
229-
page: u32,
230-
validator: Option<String>,
231-
) -> Result<Self, DomainError> {
232-
if page < 1 {
233-
return Err(DomainError::InvalidArgument(
234-
"Page should be >= 1".to_string(),
235-
));
236-
}
237-
238-
if limit < 1 {
239-
return Err(DomainError::InvalidArgument(
240-
"Limit should be >= 1".to_string(),
241-
));
242-
}
243-
244-
let validator = validator.and_then(|s| if s.is_empty() { None } else { Some(s) });
245-
246-
Ok(Self {
247-
valid_until_ge,
248-
page,
249-
limit,
250-
validator,
251-
_secret: (),
252-
})
253-
}
254-
}
255-
256-
#[cfg(feature = "repositories")]
257-
pub mod repository {
258-
use crate::RepositoryFuture;
259-
260-
use super::*;
261-
262-
pub trait ChannelRepository: Send + Sync {
263-
/// Returns a list of channels, based on the passed Parameters for this method
264-
fn list(&self, params: &ChannelListParams) -> RepositoryFuture<Vec<Channel>>;
265-
266-
fn save(&self, channel: Channel) -> RepositoryFuture<()>;
267-
268-
fn find(&self, channel_id: &ChannelId) -> RepositoryFuture<Option<Channel>>;
269-
}
270-
}
271-
272209
#[cfg(any(test, feature = "fixtures"))]
273210
#[path = "./channel_fixtures.rs"]
274211
pub mod fixtures;

domain/src/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ pub use util::tests as test_util;
99
pub use self::ad_unit::AdUnit;
1010
pub use self::asset::Asset;
1111
pub use self::bignum::BigNum;
12-
#[cfg(feature = "repositories")]
13-
pub use self::channel::ChannelRepository;
14-
pub use self::channel::{Channel, ChannelId, ChannelListParams, ChannelSpec};
12+
pub use self::channel::{Channel, ChannelId, ChannelSpec, SpecValidators};
1513
pub use self::event_submission::EventSubmission;
1614
#[cfg(feature = "repositories")]
1715
pub use self::repository::*;

domain/src/util.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ pub mod serde {
66
use chrono::serde::ts_milliseconds::deserialize as from_ts_milliseconds;
77
use chrono::serde::ts_milliseconds::serialize as to_ts_milliseconds;
88
use chrono::{DateTime, Utc};
9-
use serde::{Deserializer, Serializer};
9+
use serde::{de, Serializer};
10+
use std::fmt;
1011

1112
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
1213
where
@@ -20,9 +21,37 @@ pub mod serde {
2021

2122
pub fn deserialize<'de, D>(de: D) -> Result<Option<DateTime<Utc>>, D::Error>
2223
where
23-
D: Deserializer<'de>,
24+
D: de::Deserializer<'de>,
2425
{
25-
from_ts_milliseconds(de).map(Some)
26+
Ok(de
27+
.deserialize_option(OptionMilliSecondsTimestampVisitor)
28+
.map(|opt| opt.map(|dt| dt.with_timezone(&Utc))))?
29+
}
30+
31+
struct OptionMilliSecondsTimestampVisitor;
32+
33+
impl<'de> de::Visitor<'de> for OptionMilliSecondsTimestampVisitor {
34+
type Value = Option<DateTime<Utc>>;
35+
36+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
37+
formatter.write_str("a unix timestamp in milliseconds or none")
38+
}
39+
40+
/// Deserialize a timestamp in seconds since the epoch
41+
fn visit_none<E>(self) -> Result<Option<DateTime<Utc>>, E>
42+
where
43+
E: de::Error,
44+
{
45+
Ok(None)
46+
}
47+
48+
/// Deserialize a timestamp in seconds since the epoch
49+
fn visit_some<D>(self, de: D) -> Result<Option<DateTime<Utc>>, D::Error>
50+
where
51+
D: de::Deserializer<'de>,
52+
{
53+
from_ts_milliseconds(de).map(Some)
54+
}
2655
}
2756
}
2857
}

sentry/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ name = "sentry"
99
path = "src/lib.rs"
1010

1111
[dependencies]
12+
# Domain
13+
chrono = { version = "0.4", features = ["serde"] }
14+
time = "0.1.42"
15+
domain = { version = "0.1", path = "../domain", features = ["repositories"] }
1216
# Futures
1317
futures-preview = { version = "=0.3.0-alpha.16", features = ["compat", "io-compat"] }
1418
futures_legacy = { version = "0.1", package = "futures" }
@@ -31,10 +35,6 @@ serde = { version = "^1.0", features = ['derive'] }
3135
serde_json = "1.0"
3236
# Utils
3337
try_future = "0.1.3"
34-
# Domain
35-
chrono = { version = "0.4", features = ["serde"] }
36-
time = "0.1.42"
37-
domain = { version = "0.1", path = "../domain", features = ["repositories"] }
3838
[dev-dependencies]
3939
domain = { version = "0.1", path = "../domain", features = ["fixtures", "repositories"] }
4040
fake = { version = "^1.3", features = ["chrono"] }

sentry/src/application/resource/channel.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,16 @@ use tower_web::{derive_resource_impl, impl_web, Deserialize, Extract};
66
use channel_create::{ChannelCreateHandler, ChannelCreateResponse, ChannelInput};
77
use channel_list::{ChannelListHandler, ChannelListResponse};
88

9-
use crate::infrastructure::persistence::channel::{
10-
MemoryChannelRepository, PostgresChannelRepository,
11-
};
12-
use crate::infrastructure::persistence::DbPool;
9+
use crate::domain::channel::ChannelRepository;
10+
use std::sync::Arc;
1311

1412
mod channel_create;
1513
mod channel_list;
1614

1715
#[derive(Clone, Debug)]
1816
pub struct ChannelResource {
19-
pub db_pool: DbPool,
2017
pub channel_list_limit: u32,
18+
pub channel_repository: Arc<dyn ChannelRepository>,
2119
}
2220

2321
impl_web! {
@@ -26,21 +24,16 @@ impl_web! {
2624
#[post("/channel")]
2725
#[content_type("application/json")]
2826
async fn create_channel(&self, body: ChannelInput) -> ChannelCreateResponse {
29-
let _channel_repository = PostgresChannelRepository::new(self.db_pool.clone());
30-
let channel_repository = MemoryChannelRepository::new(None);
3127

32-
let handler = ChannelCreateHandler::new(&channel_repository);
28+
let handler = ChannelCreateHandler::new(self.channel_repository.clone());
3329

3430
await!(handler.handle(body).boxed().compat()).unwrap()
3531
}
3632

3733
#[get("/channel/list")]
3834
#[content_type("application/json")]
3935
async fn channel_list(&self, query_string: ChannelListQuery) -> ChannelListResponse {
40-
let _channel_repository = PostgresChannelRepository::new(self.db_pool.clone());
41-
let channel_repository = MemoryChannelRepository::new(None);
42-
43-
let handler = ChannelListHandler::new(self.channel_list_limit, &channel_repository);
36+
let handler = ChannelListHandler::new(self.channel_list_limit, self.channel_repository.clone());
4437

4538
await!(handler.handle(query_string.page(), query_string.validator()).boxed().compat()).unwrap()
4639
}

sentry/src/application/resource/channel/channel_create/handler.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
use tokio::await;
22

3-
use domain::{Channel, ChannelRepository};
3+
use crate::domain::channel::ChannelRepository;
4+
use domain::Channel;
45

56
use super::ChannelCreateResponse;
67
use super::ChannelInput;
8+
use std::sync::Arc;
79

8-
pub struct ChannelCreateHandler<'a> {
9-
channel_repository: &'a dyn ChannelRepository,
10+
pub struct ChannelCreateHandler {
11+
channel_repository: Arc<dyn ChannelRepository>,
1012
}
1113

12-
impl<'a> ChannelCreateHandler<'a> {
13-
pub fn new(channel_repository: &'a dyn ChannelRepository) -> Self {
14+
impl ChannelCreateHandler {
15+
pub fn new(channel_repository: Arc<dyn ChannelRepository>) -> Self {
1416
Self { channel_repository }
1517
}
1618
}
1719

18-
impl<'a> ChannelCreateHandler<'a> {
20+
impl ChannelCreateHandler {
21+
#[allow(clippy::needless_lifetimes)]
1922
pub async fn handle(&self, channel_input: ChannelInput) -> Result<ChannelCreateResponse, ()> {
2023
// @TODO: Creating Channel Validation
2124

@@ -28,7 +31,7 @@ impl<'a> ChannelCreateHandler<'a> {
2831
spec: channel_input.spec,
2932
};
3033

31-
let success = await!(self.channel_repository.save(channel)).is_ok();
34+
let success = await!(self.channel_repository.create(channel)).is_ok();
3235

3336
Ok(ChannelCreateResponse { success })
3437
}

sentry/src/application/resource/channel/channel_list/handler.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
use chrono::Utc;
22
use tokio::await;
33

4-
use domain::{ChannelListParams, ChannelRepository};
4+
use crate::domain::channel::{ChannelListParams, ChannelRepository};
55

66
use super::ChannelListResponse;
7+
use std::sync::Arc;
78

8-
pub struct ChannelListHandler<'a> {
9+
pub struct ChannelListHandler {
910
limit_per_page: u32,
10-
channel_repository: &'a dyn ChannelRepository,
11+
channel_repository: Arc<dyn ChannelRepository>,
1112
}
1213

13-
impl<'a> ChannelListHandler<'a> {
14-
pub fn new(limit_per_page: u32, channel_repository: &'a dyn ChannelRepository) -> Self {
14+
impl ChannelListHandler {
15+
pub fn new(limit_per_page: u32, channel_repository: Arc<dyn ChannelRepository>) -> Self {
1516
Self {
1617
limit_per_page,
1718
channel_repository,
1819
}
1920
}
2021
}
2122

22-
impl<'a> ChannelListHandler<'a> {
23+
impl ChannelListHandler {
24+
#[allow(clippy::needless_lifetimes)]
2325
pub async fn handle(
2426
&self,
2527
page: u32,

sentry/src/domain.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod channel;

0 commit comments

Comments
 (0)