Skip to content

Commit e7979da

Browse files
authored
Merge pull request #539 from AmbireTech/sentry-accounting-last-approved-axum
Sentry GET accounting/last-approved routes with axum
2 parents 1db431f + 10d6c9c commit e7979da

File tree

2 files changed

+123
-62
lines changed

2 files changed

+123
-62
lines changed

sentry/src/routes/channel.rs

Lines changed: 118 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,55 @@ pub async fn last_approved<C: Locked + 'static>(
166166
.unwrap())
167167
}
168168

169+
pub async fn last_approved_axum<C: Locked + 'static>(
170+
Extension(app): Extension<Arc<Application<C>>>,
171+
Extension(channel_context): Extension<ChainOf<Channel>>,
172+
Qs(query): Qs<LastApprovedQuery>,
173+
) -> Result<Json<LastApprovedResponse<UncheckedState>>, ResponseError> {
174+
// get request Channel
175+
let channel = channel_context.context;
176+
177+
let default_response = Json(LastApprovedResponse::<UncheckedState> {
178+
last_approved: None,
179+
heartbeats: None,
180+
});
181+
182+
let approve_state = match latest_approve_state(&app.pool, &channel).await? {
183+
Some(approve_state) => approve_state,
184+
None => return Ok(default_response),
185+
};
186+
187+
let state_root = approve_state.msg.state_root.clone();
188+
189+
let new_state = latest_new_state(&app.pool, &channel, &state_root).await?;
190+
if new_state.is_none() {
191+
return Ok(default_response);
192+
}
193+
194+
let validators = vec![channel.leader, channel.follower];
195+
let channel_id = channel.id();
196+
197+
let heartbeats = if query.with_heartbeat.unwrap_or_default() {
198+
let result = try_join_all(
199+
validators
200+
.iter()
201+
.map(|validator| latest_heartbeats(&app.pool, &channel_id, validator)),
202+
)
203+
.await?;
204+
Some(result.into_iter().flatten().collect::<Vec<_>>())
205+
} else {
206+
None
207+
};
208+
209+
Ok(Json(LastApprovedResponse {
210+
last_approved: Some(LastApproved {
211+
new_state,
212+
approve_state: Some(approve_state),
213+
}),
214+
heartbeats,
215+
}))
216+
}
217+
169218
/// This will make sure to insert/get the `Channel` from DB before attempting to create the `Spendable`
170219
async fn create_or_update_spendable_document<A: Locked>(
171220
adapter: &Adapter<A>,
@@ -610,6 +659,40 @@ pub async fn get_accounting_for_channel<C: Locked + 'static>(
610659
Ok(success_response(serde_json::to_string(&res)?))
611660
}
612661

662+
pub async fn get_accounting_for_channel_axum<C: Locked + 'static>(
663+
Extension(app): Extension<Arc<Application<C>>>,
664+
Extension(channel_context): Extension<ChainOf<Channel>>,
665+
) -> Result<Json<AccountingResponse<CheckedState>>, ResponseError> {
666+
let channel = channel_context.context;
667+
668+
let accountings = get_all_accountings_for_channel(app.pool.clone(), channel.id()).await?;
669+
670+
let mut unchecked_balances: Balances<UncheckedState> = Balances::default();
671+
672+
for accounting in accountings {
673+
match accounting.side {
674+
Side::Earner => unchecked_balances
675+
.earners
676+
.insert(accounting.address, accounting.amount),
677+
Side::Spender => unchecked_balances
678+
.spenders
679+
.insert(accounting.address, accounting.amount),
680+
};
681+
}
682+
683+
let balances = match unchecked_balances.check() {
684+
Ok(balances) => balances,
685+
Err(error) => {
686+
error!(&app.logger, "{}", &error; "module" => "channel_accounting");
687+
return Err(ResponseError::FailedValidation(
688+
"Earners sum is not equal to spenders sum for channel".to_string(),
689+
));
690+
}
691+
};
692+
693+
Ok(Json(AccountingResponse::<CheckedState> { balances }))
694+
}
695+
613696
pub async fn channel_payout_axum<C: Locked + 'static>(
614697
Extension(app): Extension<Arc<Application<C>>>,
615698
Extension(channel_context): Extension<ChainOf<Channel>>,
@@ -1087,7 +1170,6 @@ mod test {
10871170
ethereum::test_util::{GANACHE_INFO_1, GANACHE_INFO_1337},
10881171
primitives::Deposit as AdapterDeposit,
10891172
};
1090-
use hyper::StatusCode;
10911173
use primitives::{
10921174
channel::Nonce,
10931175
test_util::{
@@ -1170,19 +1252,11 @@ mod test {
11701252
assert_eq!(updated_spendable.spender, *CREATOR);
11711253
}
11721254

1173-
async fn res_to_accounting_response(res: Response<Body>) -> AccountingResponse<CheckedState> {
1174-
let json = hyper::body::to_bytes(res.into_body())
1175-
.await
1176-
.expect("Should get json");
1177-
1178-
let accounting_response: AccountingResponse<CheckedState> =
1179-
serde_json::from_slice(&json).expect("Should get AccountingResponse");
1180-
accounting_response
1181-
}
1182-
11831255
#[tokio::test]
11841256
async fn get_accountings_for_channel() {
1185-
let app = setup_dummy_app().await;
1257+
let app_guard = setup_dummy_app().await;
1258+
1259+
let app = Extension(Arc::new(app_guard.app.clone()));
11861260
let channel_context = app
11871261
.config
11881262
.find_chain_of(DUMMY_CAMPAIGN.channel.token)
@@ -1192,20 +1266,16 @@ mod test {
11921266
insert_channel(&app.pool, &channel_context)
11931267
.await
11941268
.expect("should insert channel");
1195-
let build_request = |channel_context: &ChainOf<Channel>| {
1196-
Request::builder()
1197-
.extension(channel_context.clone())
1198-
.body(Body::empty())
1199-
.expect("Should build Request")
1200-
};
1269+
12011270
// Testing for no accounting yet
12021271
{
1203-
let res = get_accounting_for_channel(build_request(&channel_context), &app)
1204-
.await
1205-
.expect("should get response");
1206-
assert_eq!(StatusCode::OK, res.status());
1272+
let res =
1273+
get_accounting_for_channel_axum(app.clone(), Extension(channel_context.clone()))
1274+
.await;
1275+
assert!(res.is_ok());
1276+
1277+
let accounting_response = res.unwrap();
12071278

1208-
let accounting_response = res_to_accounting_response(res).await;
12091279
assert_eq!(accounting_response.balances.earners.len(), 0);
12101280
assert_eq!(accounting_response.balances.spenders.len(), 0);
12111281
}
@@ -1227,12 +1297,12 @@ mod test {
12271297
.await
12281298
.expect("should spend");
12291299

1230-
let res = get_accounting_for_channel(build_request(&channel_context), &app)
1231-
.await
1232-
.expect("should get response");
1233-
assert_eq!(StatusCode::OK, res.status());
1300+
let res =
1301+
get_accounting_for_channel_axum(app.clone(), Extension(channel_context.clone()))
1302+
.await;
1303+
assert!(res.is_ok());
12341304

1235-
let accounting_response = res_to_accounting_response(res).await;
1305+
let accounting_response = res.unwrap();
12361306

12371307
assert_eq!(balances, accounting_response.balances);
12381308
}
@@ -1263,15 +1333,12 @@ mod test {
12631333
.await
12641334
.expect("should spend");
12651335

1266-
let res = get_accounting_for_channel(
1267-
build_request(&channel_context.clone().with(second_channel)),
1268-
&app,
1269-
)
1270-
.await
1271-
.expect("should get response");
1272-
assert_eq!(StatusCode::OK, res.status());
1336+
let res =
1337+
get_accounting_for_channel_axum(app.clone(), Extension(channel_context.clone()))
1338+
.await;
1339+
assert!(res.is_ok());
12731340

1274-
let accounting_response = res_to_accounting_response(res).await;
1341+
let accounting_response = res.unwrap();
12751342

12761343
assert_eq!(balances, accounting_response.balances)
12771344
}
@@ -1289,7 +1356,9 @@ mod test {
12891356
.await
12901357
.expect("should spend");
12911358

1292-
let res = get_accounting_for_channel(build_request(&channel_context), &app).await;
1359+
let res =
1360+
get_accounting_for_channel_axum(app.clone(), Extension(channel_context.clone()))
1361+
.await;
12931362
let expected = ResponseError::FailedValidation(
12941363
"Earners sum is not equal to spenders sum for channel".to_string(),
12951364
);
@@ -1320,13 +1389,6 @@ mod test {
13201389
.await
13211390
.expect("should insert channel");
13221391

1323-
let get_accounting_request = |channel_context: &ChainOf<Channel>| {
1324-
Request::builder()
1325-
.extension(channel_context.clone())
1326-
.body(Body::empty())
1327-
.expect("Should build Request")
1328-
};
1329-
13301392
// Calling with non existent accounting
13311393
let res = add_spender_leaf_axum(
13321394
app.clone(),
@@ -1336,12 +1398,11 @@ mod test {
13361398
.await;
13371399
assert!(res.is_ok());
13381400

1339-
let res = get_accounting_for_channel(get_accounting_request(&channel_context), &app)
1340-
.await
1341-
.expect("should get response");
1342-
assert_eq!(StatusCode::OK, res.status());
1401+
let res =
1402+
get_accounting_for_channel_axum(app.clone(), Extension(channel_context.clone())).await;
1403+
assert!(res.is_ok());
13431404

1344-
let accounting_response = res_to_accounting_response(res).await;
1405+
let accounting_response = res.unwrap();
13451406

13461407
// Making sure a new entry has been created
13471408
assert_eq!(
@@ -1364,12 +1425,11 @@ mod test {
13641425
.await
13651426
.expect("should spend");
13661427

1367-
let res = get_accounting_for_channel(get_accounting_request(&channel_context), &app)
1368-
.await
1369-
.expect("should get response");
1370-
assert_eq!(StatusCode::OK, res.status());
1428+
let res =
1429+
get_accounting_for_channel_axum(app.clone(), Extension(channel_context.clone())).await;
1430+
assert!(res.is_ok());
13711431

1372-
let accounting_response = res_to_accounting_response(res).await;
1432+
let accounting_response = res.unwrap();
13731433

13741434
assert_eq!(balances, accounting_response.balances);
13751435

@@ -1381,12 +1441,11 @@ mod test {
13811441
.await;
13821442
assert!(res.is_ok());
13831443

1384-
let res = get_accounting_for_channel(get_accounting_request(&channel_context), &app)
1385-
.await
1386-
.expect("should get response");
1387-
assert_eq!(StatusCode::OK, res.status());
1444+
let res =
1445+
get_accounting_for_channel_axum(app.clone(), Extension(channel_context.clone())).await;
1446+
assert!(res.is_ok());
13881447

1389-
let accounting_response = res_to_accounting_response(res).await;
1448+
let accounting_response = res.unwrap();
13901449

13911450
// Balances shouldn't change
13921451
assert_eq!(

sentry/src/routes/routers.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ use crate::{
2828
campaign::{campaign_list, create_campaign, update_campaign},
2929
channel::{
3030
add_spender_leaf, add_spender_leaf_axum, channel_dummy_deposit, channel_list,
31-
channel_payout, get_accounting_for_channel, get_all_spender_limits,
32-
get_all_spender_limits_axum, get_spender_limits, get_spender_limits_axum,
33-
last_approved,
31+
channel_payout, get_accounting_for_channel, get_accounting_for_channel_axum,
32+
get_all_spender_limits, get_all_spender_limits_axum, get_spender_limits,
33+
get_spender_limits_axum, last_approved, last_approved_axum,
3434
validator_message::{
3535
create_validator_messages, extract_params, list_validator_messages,
3636
},
@@ -154,6 +154,8 @@ pub fn channels_router_axum<C: Locked + 'static>() -> Router {
154154
post(channel_payout_axum::<C>)
155155
.route_layer(middleware::from_fn(authentication_required::<C, _>)),
156156
)
157+
.route("/accounting", get(get_accounting_for_channel_axum::<C>))
158+
.route("/last-approved", get(last_approved_axum::<C>))
157159
.nest("/spender", spender_routes)
158160
.layer(
159161
// keeps the order from top to bottom!

0 commit comments

Comments
 (0)