Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: members query in account set #165

Merged
merged 9 commits into from
Jul 12, 2024
15 changes: 14 additions & 1 deletion cala-ledger-core-types/src/account_set.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

use super::primitives::*;
Expand All @@ -13,7 +14,13 @@ pub struct AccountSetValues {
pub normal_balance_type: DebitOrCredit,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug)]
pub struct AccountSetMember {
pub id: AccountSetMemberId,
pub created_at: DateTime<Utc>,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", content = "id")]
pub enum AccountSetMemberId {
Account(AccountId),
Expand All @@ -31,3 +38,9 @@ impl From<AccountSetId> for AccountSetMemberId {
Self::AccountSet(id)
}
}

impl From<(AccountSetMemberId, DateTime<Utc>)> for AccountSetMember {
fn from((id, created_at): (AccountSetMemberId, DateTime<Utc>)) -> Self {
Self { id, created_at }
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion cala-ledger/src/account_set/cursor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use cala_types::{account_set::AccountSetValues, primitives::AccountSetId};
use cala_types::{
account_set::{AccountSetMember, AccountSetValues},
primitives::AccountSetId,
};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
Expand All @@ -20,3 +23,11 @@ impl From<&AccountSetValues> for AccountSetByNameCursor {
pub struct AccountSetMemberCursor {
pub member_created_at: chrono::DateTime<chrono::Utc>,
}

impl From<AccountSetMember> for AccountSetMemberCursor {
fn from(member: AccountSetMember) -> Self {
Self {
member_created_at: member.created_at,
}
}
}
6 changes: 2 additions & 4 deletions cala-ledger/src/account_set/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,7 @@ impl AccountSets {
&self,
id: AccountSetId,
args: PaginatedQueryArgs<AccountSetMemberCursor>,
) -> Result<PaginatedQueryRet<AccountSetMemberId, AccountSetMemberCursor>, AccountSetError>
{
) -> Result<PaginatedQueryRet<AccountSetMember, AccountSetMemberCursor>, AccountSetError> {
self.repo.list_children(id, args).await
}

Expand All @@ -358,8 +357,7 @@ impl AccountSets {
op: &mut AtomicOperation<'_>,
id: AccountSetId,
args: PaginatedQueryArgs<AccountSetMemberCursor>,
) -> Result<PaginatedQueryRet<AccountSetMemberId, AccountSetMemberCursor>, AccountSetError>
{
) -> Result<PaginatedQueryRet<AccountSetMember, AccountSetMemberCursor>, AccountSetError> {
self.repo.list_children_in_tx(op.tx(), id, args).await
}

Expand Down
55 changes: 32 additions & 23 deletions cala-ledger/src/account_set/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl AccountSetRepo {
&self,
id: AccountSetId,
args: query::PaginatedQueryArgs<AccountSetMemberCursor>,
) -> Result<query::PaginatedQueryRet<AccountSetMemberId, AccountSetMemberCursor>, AccountSetError>
) -> Result<query::PaginatedQueryRet<AccountSetMember, AccountSetMemberCursor>, AccountSetError>
{
self.list_children_in_executor(&self.pool, id, args).await
}
Expand All @@ -57,7 +57,7 @@ impl AccountSetRepo {
db: &mut Transaction<'_, Postgres>,
id: AccountSetId,
args: query::PaginatedQueryArgs<AccountSetMemberCursor>,
) -> Result<query::PaginatedQueryRet<AccountSetMemberId, AccountSetMemberCursor>, AccountSetError>
) -> Result<query::PaginatedQueryRet<AccountSetMember, AccountSetMemberCursor>, AccountSetError>
{
self.list_children_in_executor(&mut **db, id, args).await
}
Expand All @@ -67,8 +67,9 @@ impl AccountSetRepo {
executor: impl Executor<'_, Database = Postgres>,
id: AccountSetId,
args: query::PaginatedQueryArgs<AccountSetMemberCursor>,
) -> Result<query::PaginatedQueryRet<AccountSetMemberId, AccountSetMemberCursor>, AccountSetError>
) -> Result<query::PaginatedQueryRet<AccountSetMember, AccountSetMemberCursor>, AccountSetError>
{
let after = args.after.map(|c| c.member_created_at) as Option<DateTime<Utc>>;
let rows = sqlx::query!(
r#"
WITH member_accounts AS (
Expand All @@ -82,8 +83,8 @@ impl AccountSetRepo {
transitive IS FALSE
AND account_set_id = $1
AND (created_at < $2 OR $2 IS NULL)
ORDER BY created_at DESC
LIMIT $3
ORDER BY created_at DESC
LIMIT $3
), member_sets AS (
SELECT
member_account_set_id AS member_id,
Expand All @@ -94,17 +95,19 @@ impl AccountSetRepo {
WHERE
account_set_id = $1
AND (created_at < $2 OR $2 IS NULL)
ORDER BY created_at DESC
LIMIT $3
ORDER BY created_at DESC
LIMIT $3
), all_members AS (
SELECT * FROM member_accounts
UNION ALL
SELECT * FROM member_sets
)
SELECT * FROM member_accounts
UNION ALL
SELECT * FROM member_sets
SELECT * FROM all_members
ORDER BY created_at DESC
LIMIT $3
"#,
id as AccountSetId,
args.after.map(|c| c.member_created_at) as Option<DateTime<Utc>>,
after,
args.first as i64 + 1,
)
.fetch_all(executor)
Expand All @@ -116,21 +119,27 @@ impl AccountSetRepo {
member_created_at: last.created_at.expect("created_at not set"),
});
}
let ids = rows

let account_set_members = rows
.into_iter()
.take(args.first)
.map(|row| {
if let Some(member_account_id) = row.member_account_id {
AccountSetMemberId::Account(AccountId::from(member_account_id))
} else if let Some(member_account_set_id) = row.member_account_set_id {
AccountSetMemberId::AccountSet(AccountSetId::from(member_account_set_id))
} else {
unreachable!()
}
})
.collect::<Vec<_>>();
.map(
|row| match (row.member_account_id, row.member_account_set_id) {
(Some(member_account_id), _) => AccountSetMember::from((
AccountSetMemberId::Account(AccountId::from(member_account_id)),
row.created_at.expect("created at should always be present"),
)),
(_, Some(member_account_set_id)) => AccountSetMember::from((
AccountSetMemberId::AccountSet(AccountSetId::from(member_account_set_id)),
row.created_at.expect("created at should always be present"),
)),
_ => unreachable!(),
},
)
.collect::<Vec<AccountSetMember>>();

Ok(query::PaginatedQueryRet {
entities: ids,
entities: account_set_members,
has_next_page,
end_cursor,
})
Expand Down
118 changes: 118 additions & 0 deletions cala-ledger/tests/account_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,121 @@ async fn account_set_update() -> anyhow::Result<()> {
assert_eq!(updated_name, account_set.values().name);
Ok(())
}

#[tokio::test]
async fn members_pagination() -> anyhow::Result<()> {
let pool = helpers::init_pool().await?;
let cala_config = CalaLedgerConfig::builder()
.pool(pool)
.exec_migrations(false)
.build()?;
let cala = CalaLedger::init(cala_config).await?;
let new_journal = helpers::test_journal();
let journal = cala.journals().create(new_journal).await.unwrap();

let (one, two) = helpers::test_accounts();
let account_one = cala.accounts().create(one).await.unwrap();
let account_two = cala.accounts().create(two).await.unwrap();

let set_one = NewAccountSet::builder()
.id(AccountSetId::new())
.name("SET ONE")
.journal_id(journal.id())
.build()
.unwrap();
let set_one = cala.account_sets().create(set_one).await.unwrap();
let set_two = NewAccountSet::builder()
.id(AccountSetId::new())
.name("SET TWO")
.journal_id(journal.id())
.build()
.unwrap();
let set_two = cala.account_sets().create(set_two).await.unwrap();

let parent = NewAccountSet::builder()
.id(AccountSetId::new())
.name("parent")
.journal_id(journal.id())
.build()
.unwrap();
let parent = cala.account_sets().create(parent).await.unwrap();

cala.account_sets()
.add_member(parent.id(), account_two.id())
.await
.unwrap();

cala.account_sets()
.add_member(parent.id(), set_one.id())
.await
.unwrap();

cala.account_sets()
.add_member(parent.id(), account_one.id())
.await
.unwrap();

cala.account_sets()
.add_member(parent.id(), set_two.id())
.await
.unwrap();

let query_args = cala_ledger::query::PaginatedQueryArgs {
first: 2,
after: None,
};

let ret = cala
.account_sets()
.list_members(parent.id(), query_args)
.await?;

assert_eq!(ret.entities.len(), 2);
assert_eq!(ret.has_next_page, true);
assert_eq!(
ret.entities[0].id.clone(),
AccountSetMemberId::from(set_two.id())
);
assert_eq!(
ret.entities[1].id.clone(),
AccountSetMemberId::from(account_one.id())
);

let query_args = cala_ledger::query::PaginatedQueryArgs {
first: 2,
after: Some(AccountSetMemberCursor::from(ret.entities[0].created_at)),
};

let ret = cala
.account_sets()
.list_members(parent.id(), query_args)
.await?;
assert_eq!(ret.entities.len(), 2);
assert_eq!(ret.has_next_page, true);
assert_eq!(
ret.entities[0].id.clone(),
AccountSetMemberId::from(account_one.id())
);
assert_eq!(
ret.entities[1].id.clone(),
AccountSetMemberId::from(set_one.id())
);

let query_args = cala_ledger::query::PaginatedQueryArgs {
first: 2,
after: Some(AccountSetMemberCursor::from(ret.entities[1].created_at)),
};

let ret = cala
.account_sets()
.list_members(parent.id(), query_args)
.await?;
assert_eq!(ret.entities.len(), 1);
assert_eq!(ret.has_next_page, false);
assert_eq!(
ret.entities[0].id.clone(),
AccountSetMemberId::from(account_two.id())
);

Ok(())
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading