Skip to content

Commit 304984b

Browse files
authored
Merge pull request #477 from AmbireTech/channel-pay-route
POST /v5/channel/:id/pay
2 parents 25290e1 + d6fdfe9 commit 304984b

File tree

7 files changed

+426
-34
lines changed

7 files changed

+426
-34
lines changed

adview-manager/serve/src/main.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,19 @@ use wiremock::{
1313
Mock, MockServer, ResponseTemplate,
1414
};
1515

16-
17-
use tera::{Tera, Context};
16+
use tera::{Context, Tera};
1817

1918
#[tokio::main]
2019
async fn main() -> Result<(), Box<dyn std::error::Error>> {
2120
env_logger::init();
2221

2322
let serve_dir = match std::env::current_dir().unwrap() {
2423
serve_path if serve_path.ends_with("serve") => serve_path,
25-
adview_manager_path if adview_manager_path.ends_with("adview-manager") => adview_manager_path.join("serve"),
24+
adview_manager_path if adview_manager_path.ends_with("adview-manager") => {
25+
adview_manager_path.join("serve")
26+
}
2627
// running from the Validator stack workspace
27-
workspace_path => workspace_path.join("adview-manager/serve")
28+
workspace_path => workspace_path.join("adview-manager/serve"),
2829
};
2930

3031
let templates_glob = format!("{}/templates/**/*.html", serve_dir.display());

primitives/src/sentry.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
balances::BalancesState,
44
spender::Spender,
55
validator::{ApproveState, Heartbeat, MessageTypes, NewState, Type as MessageType},
6-
Address, Balances, CampaignId, UnifiedNum, ValidatorId, IPFS,
6+
Address, Balances, CampaignId, UnifiedMap, UnifiedNum, ValidatorId, IPFS,
77
};
88
use chrono::{
99
serde::ts_milliseconds, Date, DateTime, Datelike, Duration, NaiveDate, TimeZone, Timelike, Utc,
@@ -590,6 +590,11 @@ pub struct AllSpendersQuery {
590590
pub page: u64,
591591
}
592592

593+
#[derive(Debug, Serialize, Deserialize)]
594+
pub struct ChannelPayRequest {
595+
pub payouts: UnifiedMap,
596+
}
597+
593598
#[derive(Serialize, Deserialize, Debug)]
594599
pub struct ValidatorMessage {
595600
pub from: ValidatorId,

sentry/src/db/channel.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use primitives::{Channel, ChannelId, ValidatorId};
1+
use primitives::{Channel, ChannelId};
22

33
pub use list_channels::list_channels;
44

sentry/src/lib.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ use {
2626
campaign,
2727
campaign::{campaign_list, create_campaign, update_campaign},
2828
channel::{
29-
add_spender_leaf, channel_list, get_accounting_for_channel, get_all_spender_limits,
30-
get_spender_limits, last_approved,
29+
add_spender_leaf, channel_list, channel_payout, get_accounting_for_channel,
30+
get_all_spender_limits, get_spender_limits, last_approved,
3131
validator_message::{
3232
create_validator_messages, extract_params, list_validator_messages,
3333
},
@@ -77,6 +77,9 @@ static CHANNEL_ACCOUNTING: Lazy<Regex> = Lazy::new(|| {
7777
Regex::new(r"^/v5/channel/0x([a-zA-Z0-9]{64})/accounting/?$")
7878
.expect("The regex should be valid")
7979
});
80+
static CHANNEL_PAY: Lazy<Regex> = Lazy::new(|| {
81+
Regex::new(r"^/v5/channel/0x([a-zA-Z0-9]{64})/pay/?$").expect("The regex should be valid")
82+
});
8083

8184
/// Regex extracted parameters.
8285
/// This struct is created manually on each of the matched routes.
@@ -210,10 +213,6 @@ async fn campaigns_router<C: Locked + 'static>(
210213
}
211214

212215
// TODO AIP#61: Add routes for:
213-
// - POST /channel/:id/pay
214-
// #[serde(rename_all = "camelCase")]
215-
// Pay { payout: BalancesMap },
216-
//
217216
// - GET /channel/:id/get-leaf
218217
async fn channels_router<C: Locked + 'static>(
219218
mut req: Request<Body>,
@@ -345,6 +344,21 @@ async fn channels_router<C: Locked + 'static>(
345344
.await?;
346345

347346
get_accounting_for_channel(req, app).await
347+
}
348+
// POST /v5/channel/:id/pay
349+
else if let (Some(caps), &Method::POST) = (CHANNEL_PAY.captures(&path), method) {
350+
let param = RouteParams(vec![caps
351+
.get(1)
352+
.map_or("".to_string(), |m| m.as_str().to_string())]);
353+
req.extensions_mut().insert(param);
354+
355+
req = Chain::new()
356+
.chain(AuthRequired)
357+
.chain(ChannelLoad)
358+
.apply(req, app)
359+
.await?;
360+
361+
channel_payout(req, app).await
348362
} else {
349363
Err(ResponseError::NotFound)
350364
}

sentry/src/routes.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,19 @@
7676
//!
7777
//! Response: [`LastApprovedResponse`][primitives::sentry::LastApprovedResponse]
7878
//!
79-
//! - `POST /v5/channel/:id/pay` (auth required)
79+
//! - [`POST /v5/channel/:id/pay`](channel::channel_payout) (auth required)
8080
//!
81-
//! TODO: implement and document as part of issue #382
81+
//! Channel Payout with authentication of the spender.
82+
//!
83+
//! This route handles withdrawals of advertiser funds for the authenticated spender.
84+
//! It needs to ensure all campaigns are closed. It accepts a JSON body in the request which contains
85+
//! all of the earners and updates their balances accordingly. Used when an advertiser/spender wants
86+
//! to get their remaining funds back.
87+
//!
88+
//! Request JSON body: [`ChannelPayRequest`](primitives::sentry::ChannelPayRequest)
8289
//!
83-
//! Channel Payout with authentication of the spender
90+
//! Response: [`SuccessResponse`](primitives::sentry::SuccessResponse)
8491
//!
85-
//! Withdrawals of advertiser funds - re-introduces the PAY event with a separate route.
8692
//!
8793
//! - `GET /v5/channel/:id/get-leaf`
8894
//!

sentry/src/routes/campaign.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
};
1515
use adapter::{prelude::*, Adapter, Error as AdaptorError};
1616
use deadpool_postgres::PoolError;
17-
use futures::future::try_join_all;
17+
use futures::{future::try_join_all, TryFutureExt};
1818
use hyper::{Body, Request, Response};
1919
use primitives::{
2020
campaign_validator::Validator,
@@ -115,17 +115,24 @@ pub async fn fetch_campaign_ids_for_channel(
115115
if total_pages < 2 {
116116
Ok(campaign_ids)
117117
} else {
118-
let other_pages: Vec<Vec<CampaignId>> = try_join_all((1..total_pages).map(|i| {
119-
get_campaign_ids_by_channel(
120-
pool,
121-
&channel_id,
122-
limit.into(),
123-
i.checked_mul(limit.into()).expect("TODO"),
124-
)
118+
let pages_skip: Vec<u64> = (1..total_pages)
119+
.map(|i| {
120+
i.checked_mul(limit.into()).ok_or_else(|| {
121+
ResponseError::FailedValidation(
122+
"Calculating skip while fetching campaign ids results in an overflow"
123+
.to_string(),
124+
)
125+
})
126+
})
127+
.collect::<Result<_, _>>()?;
128+
129+
let other_pages = try_join_all(pages_skip.into_iter().map(|skip| {
130+
get_campaign_ids_by_channel(pool, &channel_id, limit.into(), skip)
131+
.map_err(|e| ResponseError::BadRequest(e.to_string()))
125132
}))
126133
.await?;
127134

128-
let all_campaigns: Vec<CampaignId> = std::iter::once(campaign_ids)
135+
let all_campaigns = std::iter::once(campaign_ids)
129136
.chain(other_pages.into_iter())
130137
.flat_map(|campaign_ids| campaign_ids.into_iter())
131138
.collect();
@@ -1279,13 +1286,19 @@ mod test {
12791286
let new_budget = UnifiedNum::from_u64(300 * multiplier);
12801287
let delta_budget = get_delta_budget(&campaign_remaining, &campaign, new_budget).await;
12811288

1282-
assert!(matches!(&delta_budget, Err(Error::NewBudget(_))), "Got result: {delta_budget:?}");
1289+
assert!(
1290+
matches!(&delta_budget, Err(Error::NewBudget(_))),
1291+
"Got result: {delta_budget:?}"
1292+
);
12831293

12841294
// campaign_spent == new_budget
12851295
let new_budget = UnifiedNum::from_u64(400 * multiplier);
12861296
let delta_budget = get_delta_budget(&campaign_remaining, &campaign, new_budget).await;
12871297

1288-
assert!(matches!(&delta_budget, Err(Error::NewBudget(_))), "Got result: {delta_budget:?}");
1298+
assert!(
1299+
matches!(&delta_budget, Err(Error::NewBudget(_))),
1300+
"Got result: {delta_budget:?}"
1301+
);
12891302
}
12901303
// Increasing budget
12911304
{

0 commit comments

Comments
 (0)