Skip to content

Commit c52ade4

Browse files
committed
sentry - route - channel last-approved docs
- primitives - examples - for query & response
1 parent f092274 commit c52ade4

File tree

5 files changed

+253
-35
lines changed

5 files changed

+253
-35
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use primitives::sentry::LastApprovedQuery;
2+
3+
fn main() {
4+
// An empty query - no heartbeats will be included in the response (default).
5+
{
6+
// This is treated the same as `withHeartbeat=false` in the route.
7+
let empty = "";
8+
let empty_expected = LastApprovedQuery {
9+
with_heartbeat: None,
10+
};
11+
12+
assert_eq!(empty_expected, serde_qs::from_str(empty).unwrap());
13+
}
14+
15+
// Query with `with_heartbeat` parameter - latest 2 Heartbeats from
16+
// each Channel Validator will be returned in the response.
17+
{
18+
let with_heartbeat = "withHeartbeat=true";
19+
let with_heartbeat_expected = LastApprovedQuery {
20+
with_heartbeat: Some(true),
21+
};
22+
23+
assert_eq!(
24+
with_heartbeat_expected,
25+
serde_qs::from_str(with_heartbeat).unwrap()
26+
);
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
use primitives::{balances::UncheckedState, sentry::LastApprovedResponse};
2+
use serde_json::{from_value, json};
3+
4+
fn main() {
5+
// An empty response - Channel is brand new and neither an NewState nor a ApproveState
6+
// have been generated yet.
7+
// This is threated the same as `with_heartbeat=false` in the route.
8+
{
9+
let empty_json = json!({});
10+
11+
let empty_expected = LastApprovedResponse::<UncheckedState> {
12+
last_approved: None,
13+
heartbeats: None,
14+
};
15+
16+
assert_eq!(
17+
empty_expected,
18+
from_value(empty_json).expect("Should deserialize")
19+
);
20+
}
21+
22+
// Response without `with_heartbeat=true` query parameter
23+
// or with a `with_heartbeat=false` query parameter
24+
{
25+
let response_json = json!({
26+
"lastApproved": {
27+
"newState": {
28+
"from": "0x80690751969B234697e9059e04ed72195c3507fa",
29+
"received": "2022-08-09T14:45:38.090Z",
30+
"msg": {
31+
"type": "NewState",
32+
"stateRoot": "a1e2f6ee08185ae06e3212e56ad1e0fcbae95ac8939871eb96e1ee3016234321",
33+
"signature": "0x508bef21e91d5337ad71791503748fe0d7ee7592db90179be6f948570290d00b72e103b0d262452809ace183ebf83375072b4a359b6e441f2ad6f58b8552c8fa1b",
34+
"earners": {
35+
"0x80690751969B234697e9059e04ed72195c3507fa": "5",
36+
"0xE882ebF439207a70dDcCb39E13CA8506c9F45fD9": "100000",
37+
"0xf3f583AEC5f7C030722Fe992A5688557e1B86ef7": "3"
38+
},
39+
"spenders": {
40+
"0xaCBaDA2d5830d1875ae3D2de207A1363B316Df2F": "100008"
41+
}
42+
}
43+
},
44+
"approveState": {
45+
"from": "0xf3f583AEC5f7C030722Fe992A5688557e1B86ef7",
46+
"received": "2022-08-09T14:45:43.110Z",
47+
"msg": {
48+
"type": "ApproveState",
49+
"stateRoot": "a1e2f6ee08185ae06e3212e56ad1e0fcbae95ac8939871eb96e1ee3016234321",
50+
"signature": "0xb2ce0010ad5867a4fb4acbde6525c261d76b592d290cb22af120573565168a2e49381e84d4f409c0989fa171dd687bf68b7eeff5b595c845cec8e9b8b1738dbd1c",
51+
"isHealthy": true
52+
}
53+
}
54+
}
55+
});
56+
57+
let response: LastApprovedResponse<UncheckedState> =
58+
from_value(response_json).expect("Should deserialize");
59+
assert!(response.heartbeats.is_none());
60+
}
61+
62+
{
63+
let response_json = json!({
64+
"lastApproved": {
65+
"newState": {
66+
"from": "0x80690751969B234697e9059e04ed72195c3507fa",
67+
"received": "2022-08-09T14:45:38.090Z",
68+
"msg": {
69+
"type": "NewState",
70+
"stateRoot": "a1e2f6ee08185ae06e3212e56ad1e0fcbae95ac8939871eb96e1ee3016234321",
71+
"signature": "0x508bef21e91d5337ad71791503748fe0d7ee7592db90179be6f948570290d00b72e103b0d262452809ace183ebf83375072b4a359b6e441f2ad6f58b8552c8fa1b",
72+
"earners": {
73+
"0x80690751969B234697e9059e04ed72195c3507fa": "5",
74+
"0xE882ebF439207a70dDcCb39E13CA8506c9F45fD9": "100000",
75+
"0xf3f583AEC5f7C030722Fe992A5688557e1B86ef7": "3"
76+
},
77+
"spenders": {
78+
"0xaCBaDA2d5830d1875ae3D2de207A1363B316Df2F": "100008"
79+
}
80+
}
81+
},
82+
"approveState": {
83+
"from": "0xf3f583AEC5f7C030722Fe992A5688557e1B86ef7",
84+
"received": "2022-08-09T14:45:43.110Z",
85+
"msg": {
86+
"type": "ApproveState",
87+
"stateRoot": "a1e2f6ee08185ae06e3212e56ad1e0fcbae95ac8939871eb96e1ee3016234321",
88+
"signature": "0xb2ce0010ad5867a4fb4acbde6525c261d76b592d290cb22af120573565168a2e49381e84d4f409c0989fa171dd687bf68b7eeff5b595c845cec8e9b8b1738dbd1c",
89+
"isHealthy": true
90+
}
91+
}
92+
},
93+
"heartbeats": [
94+
{
95+
"from": "0x80690751969B234697e9059e04ed72195c3507fa",
96+
"received": "2022-08-09T14:45:58.110Z",
97+
"msg": {
98+
"type": "Heartbeat",
99+
"signature": "0xc96ed701fc53e27cb28b323c26060273a5bdbad07c4b5470d8090c6a8b03954422b5d0dcc791cc47b5e4186045f60971d7e8e4d69b380cf250fe416cf4aac6901b",
100+
"stateRoot": "888f915d6b84ccfa80d3cc6536021efab05f63293ddfb66f1bb1c191909d1372",
101+
"timestamp": "2022-08-09T14:45:58.097376454Z"
102+
}
103+
},
104+
{
105+
"from": "0x80690751969B234697e9059e04ed72195c3507fa",
106+
"received": "2022-08-09T14:45:28.090Z",
107+
"msg": {
108+
"type": "Heartbeat",
109+
"signature": "0x3f780e2abe0d704428a7921c2f18c070ad503953ef248f533b4ad13fa97c239c5e43a9f3db5077b24d4912cb13367337dc4a6c26976a15811a728e316e7275c41c",
110+
"stateRoot": "799c72322f9c35840c4bf41045df2623b33a97a5dfa3994022389ddf8930aac6",
111+
"timestamp": "2022-08-09T14:45:28.084928288Z"
112+
}
113+
},
114+
{
115+
"from": "0xf3f583AEC5f7C030722Fe992A5688557e1B86ef7",
116+
"received": "2022-08-09T14:46:03.170Z",
117+
"msg": {
118+
"type": "Heartbeat",
119+
"signature": "0xd4ce3c8e1cc8ab690cddc2a6cd229311e91e13a53e59b1ec80d8f877afd241af2c86c5fade37be5057d36c8fc5e69d3222b49b98bf686ee00e73005cc280ebc41b",
120+
"stateRoot": "d0dc740d3352cdd7da9b823aa4051830f9757fae66c553a88176ce0001e378fb",
121+
"timestamp": "2022-08-09T14:46:03.127887835Z"
122+
}
123+
},
124+
{
125+
"from": "0xf3f583AEC5f7C030722Fe992A5688557e1B86ef7",
126+
"received": "2022-08-09T14:45:28.160Z",
127+
"msg": {
128+
"type": "Heartbeat",
129+
"signature": "0x3afda200de4ac36d5c8f1a53da0ffdca5077b556a53fb56bb9a79def1c06f972547b0099731b1ac9b4a26c183e2ea66b8cd1759cdc1513e3436d182e9592ae0e1b",
130+
"stateRoot": "fa8f11b8aa6322905846f96219c855920b4449b18f0ceea97552e3880c5e4a9a",
131+
"timestamp": "2022-08-09T14:45:28.121225273Z"
132+
}
133+
}
134+
]
135+
});
136+
137+
let response: LastApprovedResponse<UncheckedState> =
138+
from_value(response_json).expect("Should deserialize");
139+
140+
assert!(response.heartbeats.is_some());
141+
}
142+
}

primitives/src/sentry.rs

+28-8
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,22 @@ pub use event::{Event, EventType, CLICK, IMPRESSION};
2222

2323
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
2424
#[serde(rename_all = "camelCase")]
25-
/// Channel Accounting response
26-
/// A collection of all `Accounting`s for a specific `Channel`
25+
/// Channel Accounting response.
26+
///
27+
/// A collection of all `Accounting`s for a specific [`Channel`](crate::Channel).
2728
pub struct AccountingResponse<S: BalancesState> {
2829
#[serde(flatten, bound = "S: BalancesState")]
2930
pub balances: Balances<S>,
3031
}
3132

33+
/// The last approved [`NewState`] and [`ApproveState`] accordingly to the validator.
3234
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
3335
#[serde(rename_all = "camelCase")]
3436
pub struct LastApproved<S: BalancesState> {
35-
/// NewState can be None if the channel is brand new
37+
/// [`NewState`] can be `None` if the [`Channel`](crate::Channel) is brand new.
3638
#[serde(bound = "S: BalancesState")]
3739
pub new_state: Option<MessageResponse<NewState<S>>>,
38-
/// ApproveState can be None if the channel is brand new
40+
/// [`ApproveState`] can be `None` if the [`Channel`](crate::Channel) is brand new.
3941
pub approve_state: Option<MessageResponse<ApproveState>>,
4042
}
4143

@@ -586,21 +588,39 @@ pub struct Pagination {
586588
pub page: u64,
587589
}
588590

591+
/// GET `/v5/channel/0xXXX.../last-approved` response
592+
///
593+
/// # Examples
594+
///
595+
/// ```
596+
#[doc = include_str!("../../primitives/examples/channel_last_approved_response.rs")]
597+
/// ```
589598
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
590599
#[serde(rename_all = "camelCase")]
591600
pub struct LastApprovedResponse<S: BalancesState> {
601+
/// The last approved [`NewState`] and [`ApproveState`] accordingly to the validator.
592602
#[serde(bound = "S: BalancesState")]
593603
pub last_approved: Option<LastApproved<S>>,
594-
/// None -> withHeartbeat=true wasn't passed
595-
/// Some(vec![]) (empty vec) or Some(heartbeats) - withHeartbeat=true was passed
604+
/// - None -> `withHeartbeat=true` wasn't passed
605+
/// - `Some(vec![])` (empty vec) or `Some(heartbeats)` - `withHeartbeat=true` was passed
596606
#[serde(default, skip_serializing_if = "Option::is_none")]
597607
pub heartbeats: Option<Vec<MessageResponse<Heartbeat>>>,
598608
}
599609

600-
#[derive(Debug, Deserialize)]
610+
/// GET `/v5/channel/0xXXX.../last-approved` query parameters
611+
///
612+
/// # Examples
613+
///
614+
/// ```
615+
#[doc = include_str!("../../primitives/examples/channel_last_approved_query.rs")]
616+
/// ```
617+
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
601618
#[serde(rename_all = "camelCase")]
602619
pub struct LastApprovedQuery {
603-
pub with_heartbeat: Option<String>,
620+
/// Whether or not to return the latest 2 [`Heartbeat`] validator messages
621+
/// from each of the [`Channel`](crate::Channel) validators.
622+
#[serde(default)]
623+
pub with_heartbeat: Option<bool>,
604624
}
605625

606626
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]

sentry/src/routes.rs

+28-6
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,33 @@
146146
//!
147147
//! #### GET `/v5/channel/:id/last-approved`
148148
//!
149+
//! Retrieves the latest [`ApproveState`] and the corresponding [`NewState`]
150+
//! validator messages for the given [`Channel`].
151+
//!
152+
//! If the [`Channel`] is new one or both of the states might have not been generated yet.
153+
//!
154+
//! The same is true of the [`Heartbeat`]s messages if they are requested with the query parameter.
155+
//!
149156
//! The route is handled by [`channel::last_approved()`].
150157
//!
151158
//! Request query parameters: [`LastApprovedQuery`][primitives::sentry::LastApprovedQuery]
152159
//!
153160
//! Response: [`LastApprovedResponse`][primitives::sentry::LastApprovedResponse]
154161
//!
162+
//! ##### Examples
163+
//!
164+
//! Query:
165+
//!
166+
//! ```
167+
#![doc = include_str!("../../primitives/examples/channel_last_approved_query.rs")]
168+
//! ```
169+
//!
170+
//! Response:
171+
//!
172+
//! ```
173+
#![doc = include_str!("../../primitives/examples/channel_last_approved_response.rs")]
174+
//! ```
175+
//!
155176
//! #### POST `/v5/channel/:id/pay` (auth required)
156177
//!
157178
//! Channel Payout with authentication of the spender.
@@ -395,24 +416,25 @@
395416
//! [`ApproveState`]: primitives::validator::ApproveState
396417
//! [`Accounting`]: crate::db::accounting::Accounting
397418
//! [`AccountingResponse`]: primitives::sentry::AccountingResponse
419+
//! [`AnalyticsResponse`]: primitives::sentry::AnalyticsResponse
420+
//! [`AnalyticsQuery`]: primitives::analytics::AnalyticsQuery
398421
//! [`Auth.uid`]: crate::Auth::uid
399422
//! [`Campaign`]: primitives::Campaign
400423
//! [`Campaign.creator`]: primitives::Campaign::creator
424+
//! [`Campaign.event_submission`]: primitives::Campaign::event_submission
401425
//! [`CampaignId`]: primitives::CampaignId
402-
//! [`ChannelId`]: primitives::ChannelId
403426
//! [`Channel`]: primitives::Channel
404427
//! [`Channel.leader`]: primitives::Channel::leader
405428
//! [`Channel.follower`]: primitives::Channel::follower
429+
//! [`ChannelId`]: primitives::ChannelId
430+
//! [`check_access()`]: crate::access::check_access
406431
//! [`Config.msgs_find_limit`]: primitives::Config::msgs_find_limit
432+
//! [`Event`]: primitives::sentry::Event
433+
//! [`Heartbeat`]: primitives::validator::Heartbeat
407434
//! [`MessageTypes`]: primitives::validator::MessageTypes
408435
//! [`NewState`]: primitives::validator::NewState
409-
//! [`Event`]: primitives::sentry::Event
410-
//! [`Campaign.event_submission`]: primitives::Campaign::event_submission
411-
//! [`check_access()`]: crate::access::check_access
412436
//! [`SuccessResponse`]: primitives::sentry::SuccessResponse
413437
//! [`ValidatorId`]: primitives::ValidatorId
414-
//! [`AnalyticsResponse`]: primitives::sentry::AnalyticsResponse
415-
//! [`AnalyticsQuery`]: primitives::analytics::AnalyticsQuery
416438
417439
pub use analytics::analytics as get_analytics;
418440

0 commit comments

Comments
 (0)