Skip to content

Commit 65b7524

Browse files
authored
Merge pull request #441 from AdExNetwork/issue-392-get-all-spenders-info
Added Pagination, added created field to Spendable, added limit to config
2 parents aecadaa + e58abe2 commit 65b7524

File tree

10 files changed

+77
-24
lines changed

10 files changed

+77
-24
lines changed

docs/config/dev.toml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ max_channels = 512
33

44
channels_find_limit = 200
55
campaigns_find_limit = 200
6+
spendable_find_limit = 200
67
wait_time = 500
78

89
# V4 Deprecated

docs/config/prod.toml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ max_channels = 512
33

44
channels_find_limit = 512
55
campaigns_find_limit = 512
6+
spendable_find_limit = 512
67

78
wait_time = 40000
89

primitives/src/config.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub struct Config {
2626
pub max_channels: u32,
2727
pub channels_find_limit: u32,
2828
pub campaigns_find_limit: u32,
29+
pub spendable_find_limit: u32,
2930
pub wait_time: u32,
3031
#[deprecated = "redundant V4 value. No aggregates are needed for V5"]
3132
pub aggr_throttle: u32,

primitives/src/sentry.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,6 @@ pub struct AggregateEvents {
185185
#[serde(rename_all = "camelCase")]
186186
pub struct Pagination {
187187
pub total_pages: u64,
188-
pub total: u64,
189188
pub page: u64,
190189
}
191190

@@ -225,6 +224,12 @@ pub struct AllSpendersResponse {
225224
pub pagination: Pagination,
226225
}
227226

227+
#[derive(Debug, Serialize, Deserialize)]
228+
pub struct AllSpendersQuery {
229+
// default is `u64::default()` = `0`
230+
pub page: u64,
231+
}
232+
228233
#[derive(Serialize, Deserialize, Debug)]
229234
pub struct ValidatorMessage {
230235
pub from: ValidatorId,

sentry/migrations/20190806011140_initial-tables/up.sql

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ CREATE TABLE spendable (
4343
channel_id varchar(66) NOT NULL,
4444
total bigint NOT NULL,
4545
still_on_create2 bigint NOT NULL,
46+
created timestamp(2) with time zone NOT NULL,
4647
PRIMARY KEY (spender, channel_id),
4748
CONSTRAINT fk_spendable_channel_id FOREIGN KEY (channel_id) REFERENCES channels (id) ON DELETE RESTRICT ON UPDATE RESTRICT
4849
);

sentry/src/db/campaign.rs

-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ pub async fn list_campaigns(
9595

9696
let pagination = Pagination {
9797
total_pages,
98-
total: total_pages,
9998
page: skip / limit as u64,
10099
};
101100

sentry/src/db/channel.rs

-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ mod list_channels {
147147
channels,
148148
pagination: Pagination {
149149
total_pages,
150-
total: total_pages,
151150
page: skip / limit as u64,
152151
},
153152
})

sentry/src/db/spendable.rs

+48-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use primitives::{spender::Spendable, Address, ChannelId};
1+
use crate::db::TotalCount;
2+
use chrono::Utc;
3+
use primitives::{sentry::Pagination, spender::Spendable, Address, ChannelId};
24

35
use super::{DbPool, PoolError};
46

@@ -8,7 +10,7 @@ use super::{DbPool, PoolError};
810
/// ```
911
pub async fn insert_spendable(pool: DbPool, spendable: &Spendable) -> Result<bool, PoolError> {
1012
let client = pool.get().await?;
11-
let stmt = client.prepare("INSERT INTO spendable (spender, channel_id, total, still_on_create2) values ($1, $2, $3, $4)").await?;
13+
let stmt = client.prepare("INSERT INTO spendable (spender, channel_id, total, still_on_create2, created) values ($1, $2, $3, $4, $5)").await?;
1214

1315
let row = client
1416
.execute(
@@ -18,6 +20,7 @@ pub async fn insert_spendable(pool: DbPool, spendable: &Spendable) -> Result<boo
1820
&spendable.channel.id(),
1921
&spendable.deposit.total,
2022
&spendable.deposit.still_on_create2,
23+
&Utc::now(),
2124
],
2225
)
2326
.await?;
@@ -35,29 +38,50 @@ pub async fn fetch_spendable(
3538
channel_id: &ChannelId,
3639
) -> Result<Option<Spendable>, PoolError> {
3740
let client = pool.get().await?;
38-
let statement = client.prepare("SELECT spender, total, still_on_create2, channels.leader, channels.follower, channels.guardian, channels.token, channels.nonce FROM spendable INNER JOIN channels ON channels.id = spendable.channel_id WHERE spender = $1 AND channel_id = $2").await?;
41+
let statement = client.prepare("SELECT spender, total, still_on_create2, spendable.created, channels.leader, channels.follower, channels.guardian, channels.token, channels.nonce FROM spendable INNER JOIN channels ON channels.id = spendable.channel_id WHERE spender = $1 AND channel_id = $2").await?;
3942

4043
let row = client.query_opt(&statement, &[spender, channel_id]).await?;
4144

4245
Ok(row.as_ref().map(Spendable::from))
4346
}
4447

45-
static GET_ALL_SPENDERS_STATEMENT: &str = "SELECT spender, total, still_on_create2, channels.leader, channels.follower, channels.guardian, channels.token, channels.nonce FROM spendable INNER JOIN channels ON channels.id = spendable.channel_id WHERE channel_id = $1";
48+
static GET_ALL_SPENDERS_STATEMENT: &str = "SELECT spender, total, still_on_create2, spendable.created, channels.leader, channels.follower, channels.guardian, channels.token, channels.nonce FROM spendable INNER JOIN channels ON channels.id = spendable.channel_id WHERE channel_id = $1 ORDER BY spendable.created ASC LIMIT $2 OFFSET $3";
4649

47-
// TODO: Include pagination
4850
pub async fn get_all_spendables_for_channel(
4951
pool: DbPool,
5052
channel_id: &ChannelId,
51-
) -> Result<Vec<Spendable>, PoolError> {
53+
skip: u64,
54+
limit: u64,
55+
) -> Result<(Vec<Spendable>, Pagination), PoolError> {
5256
let client = pool.get().await?;
5357
let statement = client.prepare(GET_ALL_SPENDERS_STATEMENT).await?;
5458

55-
let rows = client.query(&statement, &[channel_id]).await?;
59+
let rows = client
60+
.query(
61+
&statement,
62+
&[channel_id, &limit.to_string(), &skip.to_string()],
63+
)
64+
.await?;
65+
let spendables = rows.iter().map(Spendable::from).collect();
66+
67+
let total_count = list_spendable_total_count(&pool, channel_id).await?;
68+
69+
// fast ceil for total_pages
70+
let total_pages = if total_count == 0 {
71+
1
72+
} else {
73+
1 + ((total_count - 1) / limit as u64)
74+
};
75+
76+
let pagination = Pagination {
77+
total_pages,
78+
page: skip / limit as u64,
79+
};
5680

57-
Ok(rows.iter().map(Spendable::from).collect())
81+
Ok((spendables, pagination))
5882
}
5983

60-
static UPDATE_SPENDABLE_STATEMENT: &str = "WITH inserted_spendable AS (INSERT INTO spendable(spender, channel_id, total, still_on_create2) VALUES($1, $2, $3, $4) ON CONFLICT ON CONSTRAINT spendable_pkey DO UPDATE SET total = $3, still_on_create2 = $4 WHERE spendable.spender = $1 AND spendable.channel_id = $2 RETURNING *) SELECT inserted_spendable.*, channels.leader, channels.follower, channels.guardian, channels.token, channels.nonce FROM inserted_spendable INNER JOIN channels ON inserted_spendable.channel_id = channels.id";
84+
static UPDATE_SPENDABLE_STATEMENT: &str = "WITH inserted_spendable AS (INSERT INTO spendable(spender, channel_id, total, still_on_create2, created) VALUES($1, $2, $3, $4, $5) ON CONFLICT ON CONSTRAINT spendable_pkey DO UPDATE SET total = $3, still_on_create2 = $4 WHERE spendable.spender = $1 AND spendable.channel_id = $2 RETURNING *) SELECT inserted_spendable.*, channels.leader, channels.follower, channels.guardian, channels.token, channels.nonce FROM inserted_spendable INNER JOIN channels ON inserted_spendable.channel_id = channels.id";
6185

6286
// Updates spendable entry deposit or inserts a new spendable entry if it doesn't exist
6387
pub async fn update_spendable(pool: DbPool, spendable: &Spendable) -> Result<Spendable, PoolError> {
@@ -72,13 +96,28 @@ pub async fn update_spendable(pool: DbPool, spendable: &Spendable) -> Result<Spe
7296
&spendable.channel.id(),
7397
&spendable.deposit.total,
7498
&spendable.deposit.still_on_create2,
99+
&Utc::now(),
75100
],
76101
)
77102
.await?;
78103

79104
Ok(Spendable::from(&row))
80105
}
81106

107+
async fn list_spendable_total_count<'a>(
108+
pool: &DbPool,
109+
channel_id: &ChannelId,
110+
) -> Result<u64, PoolError> {
111+
let client = pool.get().await?;
112+
113+
let statement =
114+
"SELECT COUNT(spendable.id)::varchar FROM spendable INNER JOIN channels ON spendable.channel_id=channels.id WHERE spendable.channel_id = $1";
115+
let stmt = client.prepare(statement).await?;
116+
let row = client.query_one(&stmt, &[&channel_id]).await?;
117+
118+
Ok(row.get::<_, TotalCount>(0).0)
119+
}
120+
82121
#[cfg(test)]
83122
mod test {
84123
use primitives::{

sentry/src/routes/campaign.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,14 @@ pub async fn campaign_list<A: Adapter>(
225225
let mut query =
226226
serde_urlencoded::from_str::<CampaignListQuery>(req.uri().query().unwrap_or(""))?;
227227

228-
query.validator = match (query.validator, query.is_leader, req.extensions().get::<Auth>()) {
229-
(None, Some(true), Some(session)) => Some(session.uid), // only case where session.uid is used
228+
query.validator = match (
229+
query.validator,
230+
query.is_leader,
231+
req.extensions().get::<Auth>(),
232+
) {
233+
(None, Some(true), Some(auth)) => Some(auth.uid), // only case where Auth.uid is used
230234
(Some(validator), _, _) => Some(validator), // for all cases with a validator passed
231-
_ => None, // default, no filtration by validator
235+
_ => None, // default, no filtration by validator
232236
};
233237

234238
let limit = app.config.campaigns_find_limit;

sentry/src/routes/channel.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use primitives::{
1313
balances::{Balances, CheckedState, UncheckedState},
1414
config::TokenInfo,
1515
sentry::{
16-
channel_list::ChannelListQuery, AccountingResponse, AllSpendersResponse, LastApproved,
17-
LastApprovedQuery, LastApprovedResponse, Pagination, SpenderResponse, SuccessResponse,
16+
channel_list::ChannelListQuery, AccountingResponse, AllSpendersQuery, AllSpendersResponse,
17+
LastApproved, LastApprovedQuery, LastApprovedResponse, SpenderResponse, SuccessResponse,
1818
},
1919
spender::{Spendable, Spender, SpenderLeaf},
2020
validator::{MessageTypes, NewState},
@@ -267,11 +267,19 @@ pub async fn get_all_spender_limits<A: Adapter + 'static>(
267267
.expect("Request should have Channel")
268268
.to_owned();
269269

270+
let query = serde_urlencoded::from_str::<AllSpendersQuery>(req.uri().query().unwrap_or(""))?;
271+
let limit = app.config.spendable_find_limit;
272+
let skip = query
273+
.page
274+
.checked_mul(limit.into())
275+
.ok_or_else(|| ResponseError::FailedValidation("Page and/or limit is too large".into()))?;
276+
270277
let new_state = get_corresponding_new_state(&app.pool, &app.logger, &channel).await?;
271278

272279
let mut all_spender_limits: HashMap<Address, Spender> = HashMap::new();
273280

274-
let all_spendables = get_all_spendables_for_channel(app.pool.clone(), &channel.id()).await?;
281+
let (all_spendables, pagination) =
282+
get_all_spendables_for_channel(app.pool.clone(), &channel.id(), skip, limit.into()).await?;
275283

276284
// Using for loop to avoid async closures
277285
for spendable in all_spendables {
@@ -300,12 +308,7 @@ pub async fn get_all_spender_limits<A: Adapter + 'static>(
300308

301309
let res = AllSpendersResponse {
302310
spenders: all_spender_limits,
303-
pagination: Pagination {
304-
// TODO
305-
page: 1,
306-
total: 1,
307-
total_pages: 1,
308-
},
311+
pagination,
309312
};
310313

311314
Ok(success_response(serde_json::to_string(&res)?))

0 commit comments

Comments
 (0)