Skip to content

Commit 0618f2e

Browse files
committed
sentry - db - accounting - get accounting spent amount
1 parent ba06705 commit 0618f2e

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

sentry/src/db.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use tokio_postgres::NoTls;
55

66
use lazy_static::lazy_static;
77

8+
pub mod accounting;
89
pub mod analytics;
910
mod campaign;
1011
mod channel;

sentry/src/db/accounting.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use std::convert::TryFrom;
2+
3+
use chrono::{DateTime, Utc};
4+
use primitives::{
5+
channel_v5::Channel,
6+
sentry::accounting::{Accounting, Balances, CheckedState},
7+
Address, ChannelId, UnifiedNum,
8+
};
9+
use tokio_postgres::types::Json;
10+
11+
use super::{DbPool, PoolError};
12+
use thiserror::Error;
13+
14+
#[derive(Debug, Error)]
15+
pub enum Error {
16+
#[error("Accounting Balances error: {0}")]
17+
Balances(#[from] primitives::sentry::accounting::Error),
18+
#[error("Fetching Accounting from postgres error: {0}")]
19+
Postgres(#[from] PoolError),
20+
}
21+
22+
/// ```text
23+
/// SELECT (spenders ->> $1)::bigint as spent FROM accounting WHERE channel_id = $2
24+
/// ```
25+
/// This function returns the spent amount in a `Channel` of a given spender
26+
pub async fn get_accounting_spent(
27+
pool: DbPool,
28+
spender: &Address,
29+
channel_id: &ChannelId,
30+
) -> Result<UnifiedNum, PoolError> {
31+
let client = pool.get().await?;
32+
let statement = client
33+
.prepare("SELECT (spenders ->> $1)::bigint as spent FROM accounting WHERE channel_id = $2")
34+
.await?;
35+
36+
let row = client.query_one(&statement, &[spender, channel_id]).await?;
37+
38+
Ok(row.get("spent"))
39+
}
40+
41+
// TODO This is still WIP
42+
#[allow(dead_code)]
43+
async fn insert_accounting(
44+
pool: DbPool,
45+
channel: Channel,
46+
balances: Balances<CheckedState>,
47+
) -> Result<Accounting<CheckedState>, Error> {
48+
let client = pool.get().await?;
49+
50+
let statement = client.prepare("INSERT INTO accounting (channel_id, channel, earners, spenders, updated, created) VALUES ($1, $2, $3, $4, $5, NOW()) RETURNING channel, earners, spenders, updated, created").await.map_err(PoolError::Backend)?;
51+
52+
let earners = Json(&balances.earners);
53+
let spenders = Json(&balances.spenders);
54+
let updated: Option<DateTime<Utc>> = None;
55+
56+
let row = client
57+
.query_one(
58+
&statement,
59+
&[&channel.id(), &channel, &earners, &spenders, &updated],
60+
)
61+
.await
62+
.map_err(PoolError::Backend)?;
63+
64+
Accounting::try_from(&row).map_err(Error::Balances)
65+
}
66+
67+
#[cfg(test)]
68+
mod test {
69+
use primitives::util::tests::prep_db::{ADDRESSES, DUMMY_CAMPAIGN};
70+
71+
use crate::db::tests_postgres::{setup_test_migrations, DATABASE_POOL};
72+
73+
use super::*;
74+
75+
#[tokio::test]
76+
async fn get_spent() {
77+
let database = DATABASE_POOL.get().await.expect("Should get a DB pool");
78+
79+
setup_test_migrations(database.pool.clone())
80+
.await
81+
.expect("Migrations should succeed");
82+
83+
let channel = DUMMY_CAMPAIGN.channel.clone();
84+
85+
let spender = ADDRESSES["creator"];
86+
let earner = ADDRESSES["publisher"];
87+
88+
let mut balances = Balances::default();
89+
let spend_amount = UnifiedNum::from(100);
90+
balances
91+
.spend(spender, earner, spend_amount)
92+
.expect("Should be ok");
93+
94+
let accounting = insert_accounting(database.pool.clone(), channel.clone(), balances)
95+
.await
96+
.expect("Should insert");
97+
98+
let spent = get_accounting_spent(database.pool.clone(), &spender, &channel.id())
99+
.await
100+
.expect("Should get spent");
101+
102+
assert_eq!(spend_amount, spent);
103+
assert_eq!(
104+
accounting
105+
.balances
106+
.spenders
107+
.get(&spender)
108+
.expect("Should contain value"),
109+
&spent
110+
);
111+
}
112+
}

0 commit comments

Comments
 (0)