Skip to content

Commit 4bc5a18

Browse files
authored
Merge pull request #536 from AmbireTech/finish-sentry-campaing-routes-to-axum
Finish sentry campaing routes refactor to axum
2 parents e7b0fd2 + 752ca76 commit 4bc5a18

File tree

5 files changed

+131
-80
lines changed

5 files changed

+131
-80
lines changed

sentry/src/application.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use adapter::client::Locked;
88
use axum::{
99
extract::{FromRequest, RequestParts},
1010
http::StatusCode,
11-
middleware, Extension, Router, routing::get,
11+
middleware,
12+
routing::get,
13+
Extension, Router,
1214
};
1315
use hyper::{
1416
service::{make_service_fn, service_fn},
@@ -33,11 +35,11 @@ use crate::{
3335
platform::PlatformApi,
3436
response::{map_response_error, ResponseError},
3537
routes::{
36-
get_cfg,
38+
get_cfg, get_cfg_axum,
3739
routers::{
3840
analytics_router, campaigns_router, campaigns_router_axum, channels_router,
3941
channels_router_axum,
40-
}, get_cfg_axum,
42+
},
4143
},
4244
};
4345
use adapter::Adapter;

sentry/src/middleware/campaign.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,12 @@ where
121121
let campaign_context = request
122122
.extensions()
123123
.get::<ChainOf<Campaign>>()
124-
.expect("We must have a campaign in extensions")
125-
.to_owned();
124+
.expect("We must have a campaign in extensions");
126125

127126
let auth = request
128127
.extensions()
129128
.get::<Auth>()
130-
.expect("request should have session")
131-
.to_owned();
129+
.expect("request should have session");
132130

133131
if auth.uid.to_address() != campaign_context.context.creator {
134132
return Err(ResponseError::Forbidden(

sentry/src/routes/campaign.rs

Lines changed: 112 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! `/v5/campaign` routes
22
use crate::{
3+
application::Qs,
34
db::{
45
accounting::{get_accounting, Side},
56
campaign::{
@@ -21,8 +22,10 @@ use hyper::{Body, Request, Response};
2122
use primitives::{
2223
campaign_validator::Validator,
2324
sentry::{
24-
campaign_create::CreateCampaign, campaign_list::CampaignListQuery,
25-
campaign_modify::ModifyCampaign, SuccessResponse,
25+
campaign_create::CreateCampaign,
26+
campaign_list::{CampaignListQuery, CampaignListResponse},
27+
campaign_modify::ModifyCampaign,
28+
SuccessResponse,
2629
},
2730
spender::Spendable,
2831
Address, Campaign, CampaignId, ChainOf, Channel, ChannelId, Deposit, UnifiedNum,
@@ -425,6 +428,29 @@ where
425428
Ok(success_response(serde_json::to_string(&campaign)?))
426429
}
427430

431+
/// GET `/v5/campaign/list`
432+
pub async fn campaign_list_axum<C: Locked + 'static>(
433+
Extension(app): Extension<Arc<Application<C>>>,
434+
Qs(query): Qs<CampaignListQuery>,
435+
) -> Result<Json<CampaignListResponse>, ResponseError> {
436+
let limit = app.config.campaigns_find_limit;
437+
let skip = query
438+
.page
439+
.checked_mul(limit.into())
440+
.ok_or_else(|| ResponseError::BadRequest("Page and/or limit is too large".into()))?;
441+
let list_response = list_campaigns(
442+
&app.pool,
443+
skip,
444+
limit,
445+
query.creator,
446+
query.validator,
447+
&query.active_to_ge,
448+
)
449+
.await?;
450+
451+
Ok(Json(list_response))
452+
}
453+
428454
/// GET `/v5/campaign/list`
429455
pub async fn campaign_list<C: Locked + 'static>(
430456
req: Request<Body>,
@@ -450,6 +476,41 @@ pub async fn campaign_list<C: Locked + 'static>(
450476
Ok(success_response(serde_json::to_string(&list_response)?))
451477
}
452478

479+
/// POST `/v5/campaign/:id/close` (auth required)
480+
///
481+
/// **Can only be called by the [`Campaign.creator`]!**
482+
/// To close a campaign, just set it's budget to what it's spent so far (so that remaining == 0)
483+
/// newBudget = totalSpent, i.e. newBudget = oldBudget - remaining
484+
pub async fn close_campaign_axum<C: Locked + 'static>(
485+
Extension(app): Extension<Arc<Application<C>>>,
486+
Extension(auth): Extension<Auth>,
487+
Extension(campaign_context): Extension<ChainOf<Campaign>>,
488+
) -> Result<Json<SuccessResponse>, ResponseError> {
489+
let mut campaign = campaign_context.context;
490+
491+
if auth.uid.to_address() != campaign.creator {
492+
Err(ResponseError::Forbidden(
493+
"Request not sent by campaign creator".to_string(),
494+
))
495+
} else {
496+
let old_remaining = app
497+
.campaign_remaining
498+
.getset_remaining_to_zero(campaign.id)
499+
.await
500+
.map_err(|e| ResponseError::BadRequest(e.to_string()))?;
501+
502+
campaign.budget = campaign
503+
.budget
504+
.checked_sub(&UnifiedNum::from(old_remaining))
505+
.ok_or_else(|| {
506+
ResponseError::BadRequest("Campaign budget overflow/underflow".to_string())
507+
})?;
508+
update_campaign(&app.pool, &campaign).await?;
509+
510+
Ok(Json(SuccessResponse { success: true }))
511+
}
512+
}
513+
453514
/// POST `/v5/campaign/:id/close` (auth required)
454515
///
455516
/// **Can only be called by the [`Campaign.creator`]!**
@@ -1259,7 +1320,7 @@ mod test {
12591320
use primitives::{
12601321
campaign::validators::Validators,
12611322
config::GANACHE_CONFIG,
1262-
sentry::campaign_list::{CampaignListResponse, ValidatorParam},
1323+
sentry::campaign_list::ValidatorParam,
12631324
test_util::{
12641325
CREATOR, DUMMY_CAMPAIGN, DUMMY_VALIDATOR_FOLLOWER, DUMMY_VALIDATOR_LEADER, FOLLOWER,
12651326
GUARDIAN, IDS, LEADER, LEADER_2, PUBLISHER_2,
@@ -1313,7 +1374,6 @@ mod test {
13131374
.expect("Should create campaign")
13141375
.0;
13151376

1316-
13171377
assert_ne!(DUMMY_CAMPAIGN.id, create_response.id);
13181378

13191379
let campaign_remaining = CampaignRemaining::new(app.redis.clone());
@@ -1378,7 +1438,8 @@ mod test {
13781438

13791439
create_campaign_axum(Json(create_second), auth.clone(), app.clone())
13801440
.await
1381-
.expect("Should create campaign").0
1441+
.expect("Should create campaign")
1442+
.0
13821443
};
13831444

13841445
// No budget left for new campaigns
@@ -1554,12 +1615,15 @@ mod test {
15541615
let campaign =
15551616
CreateCampaign::from_campaign_erased(DUMMY_CAMPAIGN.clone(), None).into_campaign();
15561617

1557-
let app = setup_dummy_app().await;
1618+
let app_guard = setup_dummy_app().await;
15581619

1559-
let channel_chain = app
1620+
let channel_chain = app_guard
15601621
.config
15611622
.find_chain_of(DUMMY_CAMPAIGN.channel.token)
15621623
.expect("Channel token should be whitelisted in config!");
1624+
1625+
let app = Extension(Arc::new(app_guard.app.clone()));
1626+
15631627
let channel_context = channel_chain.with_channel(DUMMY_CAMPAIGN.channel);
15641628

15651629
insert_channel(&app.pool, &channel_context)
@@ -1569,11 +1633,12 @@ mod test {
15691633
.await
15701634
.expect("Should insert dummy campaign");
15711635

1572-
let campaign_context = app
1573-
.config
1574-
.find_chain_of(campaign.channel.token)
1575-
.expect("Config should have the Dummy campaign.channel.token")
1576-
.with(campaign.clone());
1636+
let campaign_context = Extension(
1637+
app.config
1638+
.find_chain_of(campaign.channel.token)
1639+
.expect("Config should have the Dummy campaign.channel.token")
1640+
.with(campaign.clone()),
1641+
);
15771642

15781643
// Test if remaining is set to 0
15791644
{
@@ -1582,19 +1647,13 @@ mod test {
15821647
.await
15831648
.expect("should set");
15841649

1585-
let auth = Auth {
1650+
let auth = Extension(Auth {
15861651
era: 0,
15871652
uid: ValidatorId::from(campaign.creator),
15881653
chain: campaign_context.chain.clone(),
1589-
};
1654+
});
15901655

1591-
let req = Request::builder()
1592-
.extension(auth)
1593-
.extension(campaign_context.clone())
1594-
.body(Body::empty())
1595-
.expect("Should build Request");
1596-
1597-
close_campaign(req, &app)
1656+
close_campaign_axum(app.clone(), auth.clone(), campaign_context.clone())
15981657
.await
15991658
.expect("Should close campaign");
16001659

@@ -1618,19 +1677,13 @@ mod test {
16181677

16191678
// Test if an error is returned when request is not sent by creator
16201679
{
1621-
let auth = Auth {
1680+
let auth = Extension(Auth {
16221681
era: 0,
16231682
uid: IDS[&LEADER],
16241683
chain: campaign_context.chain.clone(),
1625-
};
1626-
1627-
let req = Request::builder()
1628-
.extension(auth)
1629-
.extension(campaign_context.clone())
1630-
.body(Body::empty())
1631-
.expect("Should build Request");
1684+
});
16321685

1633-
let res = close_campaign(req, &app)
1686+
let res = close_campaign_axum(app.clone(), auth, campaign_context.clone())
16341687
.await
16351688
.expect_err("Should return error for Bad Campaign");
16361689

@@ -1641,18 +1694,12 @@ mod test {
16411694
}
16421695
}
16431696

1644-
async fn res_to_campaign_list_response(res: Response<Body>) -> CampaignListResponse {
1645-
let json = hyper::body::to_bytes(res.into_body())
1646-
.await
1647-
.expect("Should get json");
1648-
1649-
serde_json::from_slice(&json).expect("Should deserialize CampaignListResponse")
1650-
}
1651-
16521697
#[tokio::test]
16531698
async fn test_campaign_list() {
1654-
let mut app = setup_dummy_app().await;
1655-
app.config.campaigns_find_limit = 2;
1699+
let mut app_guard = setup_dummy_app().await;
1700+
app_guard.config.campaigns_find_limit = 2;
1701+
let app = Extension(Arc::new(app_guard.app.clone()));
1702+
16561703
// Setting up new leader and a channel and campaign which use it on Ganache #1337
16571704
let dummy_leader_2 = ValidatorDesc {
16581705
id: IDS[&LEADER_2],
@@ -1770,14 +1817,6 @@ mod test {
17701817
.await
17711818
.expect("Should insert dummy campaign");
17721819

1773-
let build_request = |query: CampaignListQuery| {
1774-
let query = serde_qs::to_string(&query).expect("should parse query");
1775-
Request::builder()
1776-
.uri(format!("http://127.0.0.1/v5/campaign/list?{}", query))
1777-
.body(Body::empty())
1778-
.expect("Should build Request")
1779-
};
1780-
17811820
// Test for dummy leader
17821821
{
17831822
let query = CampaignListQuery {
@@ -1786,10 +1825,11 @@ mod test {
17861825
creator: None,
17871826
validator: Some(ValidatorParam::Leader(DUMMY_VALIDATOR_LEADER.id)),
17881827
};
1789-
let res = campaign_list(build_request(query), &app)
1828+
1829+
let res = campaign_list_axum(app.clone(), Qs(query))
17901830
.await
1791-
.expect("should get campaigns");
1792-
let res = res_to_campaign_list_response(res).await;
1831+
.expect("should get campaigns")
1832+
.0;
17931833

17941834
assert_eq!(
17951835
res.campaigns,
@@ -1804,10 +1844,10 @@ mod test {
18041844
creator: None,
18051845
validator: Some(ValidatorParam::Leader(DUMMY_VALIDATOR_LEADER.id)),
18061846
};
1807-
let res = campaign_list(build_request(query), &app)
1847+
let res = campaign_list_axum(app.clone(), Qs(query))
18081848
.await
1809-
.expect("should get campaigns");
1810-
let res = res_to_campaign_list_response(res).await;
1849+
.expect("should get campaigns")
1850+
.0;
18111851

18121852
assert_eq!(
18131853
res.campaigns,
@@ -1827,10 +1867,10 @@ mod test {
18271867
creator: None,
18281868
validator: Some(ValidatorParam::Validator(DUMMY_VALIDATOR_FOLLOWER.id)),
18291869
};
1830-
let res = campaign_list(build_request(query), &app)
1870+
let res = campaign_list_axum(app.clone(), Qs(query))
18311871
.await
1832-
.expect("should get campaigns");
1833-
let res = res_to_campaign_list_response(res).await;
1872+
.expect("should get campaigns")
1873+
.0;
18341874

18351875
assert_eq!(
18361876
res.campaigns,
@@ -1845,10 +1885,10 @@ mod test {
18451885
creator: None,
18461886
validator: Some(ValidatorParam::Validator(DUMMY_VALIDATOR_FOLLOWER.id)),
18471887
};
1848-
let res = campaign_list(build_request(query), &app)
1888+
let res = campaign_list_axum(app.clone(), Qs(query))
18491889
.await
1850-
.expect("should get campaigns");
1851-
let res = res_to_campaign_list_response(res).await;
1890+
.expect("should get campaigns")
1891+
.0;
18521892

18531893
assert_eq!(
18541894
res.campaigns,
@@ -1868,10 +1908,10 @@ mod test {
18681908
creator: None,
18691909
validator: Some(ValidatorParam::Leader(dummy_leader_2.id)),
18701910
};
1871-
let res = campaign_list(build_request(query), &app)
1911+
let res = campaign_list_axum(app.clone(), Qs(query))
18721912
.await
1873-
.expect("should get campaigns");
1874-
let res = res_to_campaign_list_response(res).await;
1913+
.expect("should get campaigns")
1914+
.0;
18751915

18761916
assert_eq!(
18771917
res.campaigns,
@@ -1892,10 +1932,10 @@ mod test {
18921932
creator: None,
18931933
validator: Some(ValidatorParam::Validator(dummy_follower_2.id)),
18941934
};
1895-
let res = campaign_list(build_request(query), &app)
1935+
let res = campaign_list_axum(app.clone(), Qs(query))
18961936
.await
1897-
.expect("should get campaigns");
1898-
let res = res_to_campaign_list_response(res).await;
1937+
.expect("should get campaigns")
1938+
.0;
18991939

19001940
assert_eq!(
19011941
res.campaigns,
@@ -1916,10 +1956,10 @@ mod test {
19161956
creator: Some(*PUBLISHER_2),
19171957
validator: None,
19181958
};
1919-
let res = campaign_list(build_request(query), &app)
1959+
let res = campaign_list_axum(app.clone(), Qs(query))
19201960
.await
1921-
.expect("should get campaigns");
1922-
let res = res_to_campaign_list_response(res).await;
1961+
.expect("should get campaigns")
1962+
.0;
19231963

19241964
assert_eq!(
19251965
res.campaigns,
@@ -1937,10 +1977,10 @@ mod test {
19371977
creator: None,
19381978
validator: None,
19391979
};
1940-
let res = campaign_list(build_request(query), &app)
1980+
let res = campaign_list_axum(app.clone(), Qs(query))
19411981
.await
1942-
.expect("should get campaigns");
1943-
let res = res_to_campaign_list_response(res).await;
1982+
.expect("should get campaigns")
1983+
.0;
19441984

19451985
assert_eq!(
19461986
res.campaigns,

0 commit comments

Comments
 (0)