1use chrono::{DateTime, Utc};
2use es_entity::*;
3use sqlx::{Executor, PgPool, Postgres, Transaction};
4
5use std::collections::HashMap;
6
7use crate::primitives::{AccountId, DataSourceId, JournalId};
8
9use super::{entity::*, error::*};
10
11const ADDVISORY_LOCK_ID: i64 = 123456;
12
13pub mod members_cursor {
14 use cala_types::account_set::AccountSetMember;
15 use serde::{Deserialize, Serialize};
16
17 #[derive(Debug, Serialize, Deserialize)]
18 pub struct AccountSetMembersCursor {
19 pub member_created_at: chrono::DateTime<chrono::Utc>,
20 }
21
22 impl From<&AccountSetMember> for AccountSetMembersCursor {
23 fn from(member: &AccountSetMember) -> Self {
24 Self {
25 member_created_at: member.created_at,
26 }
27 }
28 }
29
30 #[cfg(feature = "graphql")]
31 impl async_graphql::connection::CursorType for AccountSetMembersCursor {
32 type Error = String;
33
34 fn encode_cursor(&self) -> String {
35 use base64::{engine::general_purpose, Engine as _};
36 let json = serde_json::to_string(&self).expect("could not serialize token");
37 general_purpose::STANDARD_NO_PAD.encode(json.as_bytes())
38 }
39
40 fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
41 use base64::{engine::general_purpose, Engine as _};
42 let bytes = general_purpose::STANDARD_NO_PAD
43 .decode(s.as_bytes())
44 .map_err(|e| e.to_string())?;
45 let json = String::from_utf8(bytes).map_err(|e| e.to_string())?;
46 serde_json::from_str(&json).map_err(|e| e.to_string())
47 }
48 }
49}
50
51use account_set_cursor::*;
52use members_cursor::*;
53
54#[derive(EsRepo, Debug, Clone)]
55#[es_repo(
56 entity = "AccountSet",
57 err = "AccountSetError",
58 columns(
59 name(ty = "String", update(accessor = "values().name"), list_by, list_for),
60 journal_id(ty = "JournalId", update(persist = false)),
61 external_id(
62 ty = "Option<String>",
63 update(accessor = "values().external_id"),
64 list_by
65 ),
66 data_source_id(
67 ty = "DataSourceId",
68 create(accessor = "data_source().into()"),
69 update(persist = false)
70 ),
71 ),
72 tbl_prefix = "cala"
73)]
74pub(super) struct AccountSetRepo {
75 pool: PgPool,
76}
77
78impl AccountSetRepo {
79 pub fn new(pool: &PgPool) -> Self {
80 Self { pool: pool.clone() }
81 }
82
83 pub async fn list_children(
84 &self,
85 id: AccountSetId,
86 args: es_entity::PaginatedQueryArgs<AccountSetMembersCursor>,
87 ) -> Result<
88 es_entity::PaginatedQueryRet<AccountSetMember, AccountSetMembersCursor>,
89 AccountSetError,
90 > {
91 self.list_children_in_executor(&self.pool, id, args).await
92 }
93
94 pub async fn list_children_in_tx(
95 &self,
96 db: &mut Transaction<'_, Postgres>,
97 id: AccountSetId,
98 args: es_entity::PaginatedQueryArgs<AccountSetMembersCursor>,
99 ) -> Result<
100 es_entity::PaginatedQueryRet<AccountSetMember, AccountSetMembersCursor>,
101 AccountSetError,
102 > {
103 self.list_children_in_executor(&mut **db, id, args).await
104 }
105
106 async fn list_children_in_executor(
107 &self,
108 executor: impl Executor<'_, Database = Postgres>,
109 id: AccountSetId,
110 args: es_entity::PaginatedQueryArgs<AccountSetMembersCursor>,
111 ) -> Result<
112 es_entity::PaginatedQueryRet<AccountSetMember, AccountSetMembersCursor>,
113 AccountSetError,
114 > {
115 let after = args.after.map(|c| c.member_created_at) as Option<DateTime<Utc>>;
116 let rows = sqlx::query!(
117 r#"
118 WITH member_accounts AS (
119 SELECT
120 member_account_id AS member_id,
121 member_account_id,
122 NULL::uuid AS member_account_set_id,
123 created_at
124 FROM cala_account_set_member_accounts
125 WHERE
126 transitive IS FALSE
127 AND account_set_id = $1
128 AND (created_at < $2 OR $2 IS NULL)
129 ORDER BY created_at DESC
130 LIMIT $3
131 ), member_sets AS (
132 SELECT
133 member_account_set_id AS member_id,
134 NULL::uuid AS member_account_id,
135 member_account_set_id,
136 created_at
137 FROM cala_account_set_member_account_sets
138 WHERE
139 account_set_id = $1
140 AND (created_at < $2 OR $2 IS NULL)
141 ORDER BY created_at DESC
142 LIMIT $3
143 ), all_members AS (
144 SELECT * FROM member_accounts
145 UNION ALL
146 SELECT * FROM member_sets
147 )
148 SELECT * FROM all_members
149 ORDER BY created_at DESC
150 LIMIT $3
151 "#,
152 id as AccountSetId,
153 after,
154 args.first as i64 + 1,
155 )
156 .fetch_all(executor)
157 .await?;
158 let has_next_page = rows.len() > args.first;
159 let mut end_cursor = None;
160 if let Some(last) = rows.last() {
161 end_cursor = Some(AccountSetMembersCursor {
162 member_created_at: last.created_at.expect("created_at not set"),
163 });
164 }
165
166 let account_set_members = rows
167 .into_iter()
168 .take(args.first)
169 .map(
170 |row| match (row.member_account_id, row.member_account_set_id) {
171 (Some(member_account_id), _) => AccountSetMember::from((
172 AccountSetMemberId::Account(AccountId::from(member_account_id)),
173 row.created_at.expect("created at should always be present"),
174 )),
175 (_, Some(member_account_set_id)) => AccountSetMember::from((
176 AccountSetMemberId::AccountSet(AccountSetId::from(member_account_set_id)),
177 row.created_at.expect("created at should always be present"),
178 )),
179 _ => unreachable!(),
180 },
181 )
182 .collect::<Vec<AccountSetMember>>();
183
184 Ok(es_entity::PaginatedQueryRet {
185 entities: account_set_members,
186 has_next_page,
187 end_cursor,
188 })
189 }
190
191 pub async fn add_member_account_and_return_parents(
192 &self,
193 db: &mut Transaction<'_, Postgres>,
194 account_set_id: AccountSetId,
195 account_id: AccountId,
196 ) -> Result<(DateTime<Utc>, Vec<AccountSetId>), AccountSetError> {
197 sqlx::query!("SELECT pg_advisory_xact_lock($1)", ADDVISORY_LOCK_ID)
198 .execute(&mut **db)
199 .await?;
200 let rows = sqlx::query!(r#"
201 WITH RECURSIVE parents AS (
202 SELECT m.member_account_set_id, m.account_set_id
203 FROM cala_account_set_member_account_sets m
204 JOIN cala_account_sets s
205 ON s.id = m.account_set_id
206 WHERE m.member_account_set_id = $1
207
208 UNION ALL
209 SELECT p.member_account_set_id, m.account_set_id
210 FROM parents p
211 JOIN cala_account_set_member_account_sets m
212 ON p.account_set_id = m.member_account_set_id
213 ),
214 non_transitive_insert AS (
215 INSERT INTO cala_account_set_member_accounts (account_set_id, member_account_id)
216 VALUES ($1, $2)
217 ),
218 transitive_insert AS (
219 INSERT INTO cala_account_set_member_accounts (account_set_id, member_account_id, transitive)
220 SELECT p.account_set_id, $2, TRUE
221 FROM parents p
222 )
223 SELECT account_set_id, NULL AS now
224 FROM parents
225 UNION ALL
226 SELECT NULL AS account_set_id, NOW() AS now
227 "#,
228 account_set_id as AccountSetId,
229 account_id as AccountId,
230 )
231 .fetch_all(&mut **db)
232 .await?;
233 let mut time = None;
234 let ret = rows
235 .into_iter()
236 .filter_map(|row| {
237 if let Some(t) = row.now {
238 time = Some(t);
239 None
240 } else {
241 Some(AccountSetId::from(
242 row.account_set_id.expect("account_set_id not set"),
243 ))
244 }
245 })
246 .collect();
247 Ok((time.expect("time not set"), ret))
248 }
249
250 pub async fn remove_member_account_and_return_parents(
251 &self,
252 db: &mut Transaction<'_, Postgres>,
253 account_set_id: AccountSetId,
254 account_id: AccountId,
255 ) -> Result<(DateTime<Utc>, Vec<AccountSetId>), AccountSetError> {
256 sqlx::query!("SELECT pg_advisory_xact_lock($1)", ADDVISORY_LOCK_ID)
257 .execute(&mut **db)
258 .await?;
259 let rows = sqlx::query!(
260 r#"
261 WITH RECURSIVE parents AS (
262 SELECT m.member_account_set_id, m.account_set_id
263 FROM cala_account_set_member_account_sets m
264 JOIN cala_account_sets s
265 ON s.id = m.account_set_id
266 WHERE m.member_account_set_id = $1
267
268 UNION ALL
269 SELECT p.member_account_set_id, m.account_set_id
270 FROM parents p
271 JOIN cala_account_set_member_account_sets m
272 ON p.account_set_id = m.member_account_set_id
273 ),
274 deletions as (
275 DELETE FROM cala_account_set_member_accounts
276 WHERE account_set_id IN (SELECT account_set_id FROM parents UNION SELECT $1)
277 AND member_account_id = $2
278 )
279 SELECT account_set_id, NULL AS now
280 FROM parents
281 UNION ALL
282 SELECT NULL AS account_set_id, NOW() AS now
283 "#,
284 account_set_id as AccountSetId,
285 account_id as AccountId,
286 )
287 .fetch_all(&mut **db)
288 .await?;
289 let mut time = None;
290 let ret = rows
291 .into_iter()
292 .filter_map(|row| {
293 if let Some(t) = row.now {
294 time = Some(t);
295 None
296 } else {
297 Some(AccountSetId::from(
298 row.account_set_id.expect("account_set_id not set"),
299 ))
300 }
301 })
302 .collect();
303 Ok((time.expect("time not set"), ret))
304 }
305
306 pub async fn add_member_set_and_return_parents(
307 &self,
308 db: &mut Transaction<'_, Postgres>,
309 account_set_id: AccountSetId,
310 member_account_set_id: AccountSetId,
311 ) -> Result<(DateTime<Utc>, Vec<AccountSetId>), AccountSetError> {
312 sqlx::query!("SELECT pg_advisory_xact_lock($1)", ADDVISORY_LOCK_ID)
313 .execute(&mut **db)
314 .await?;
315 let rows = sqlx::query!(r#"
316 WITH RECURSIVE parents AS (
317 SELECT m.member_account_set_id, m.account_set_id
318 FROM cala_account_set_member_account_sets m
319 JOIN cala_account_sets s
320 ON s.id = m.account_set_id
321 WHERE m.member_account_set_id = $1
322
323 UNION ALL
324 SELECT p.member_account_set_id, m.account_set_id
325 FROM parents p
326 JOIN cala_account_set_member_account_sets m
327 ON p.account_set_id = m.member_account_set_id
328 ),
329 set_insert AS (
330 INSERT INTO cala_account_set_member_account_sets (account_set_id, member_account_set_id)
331 VALUES ($1, $2)
332 ),
333 new_members AS (
334 INSERT INTO cala_account_set_member_accounts (account_set_id, member_account_id, transitive)
335 SELECT $1, m.member_account_id, TRUE
336 FROM cala_account_set_member_accounts m
337 WHERE m.account_set_id = $2
338 RETURNING member_account_id
339 ),
340 transitive_inserts AS (
341 INSERT INTO cala_account_set_member_accounts (account_set_id, member_account_id, transitive)
342 SELECT p.account_set_id, n.member_account_id, TRUE
343 FROM parents p
344 CROSS JOIN new_members n
345 )
346 SELECT account_set_id, NULL AS now
347 FROM parents
348 UNION ALL
349 SELECT NULL AS account_set_id, NOW() AS now
350 "#,
351 account_set_id as AccountSetId,
352 member_account_set_id as AccountSetId,
353 )
354 .fetch_all(&mut **db)
355 .await?;
356 let mut time = None;
357 let ret = rows
358 .into_iter()
359 .filter_map(|row| {
360 if let Some(t) = row.now {
361 time = Some(t);
362 None
363 } else {
364 Some(AccountSetId::from(
365 row.account_set_id.expect("account_set_id not set"),
366 ))
367 }
368 })
369 .collect();
370 Ok((time.expect("time not set"), ret))
371 }
372
373 pub async fn remove_member_set_and_return_parents(
374 &self,
375 db: &mut Transaction<'_, Postgres>,
376 account_set_id: AccountSetId,
377 member_account_set_id: AccountSetId,
378 ) -> Result<(DateTime<Utc>, Vec<AccountSetId>), AccountSetError> {
379 sqlx::query!("SELECT pg_advisory_xact_lock($1)", ADDVISORY_LOCK_ID)
380 .execute(&mut **db)
381 .await?;
382 let rows = sqlx::query!(
383 r#"
384 WITH RECURSIVE parents AS (
385 SELECT m.member_account_set_id, m.account_set_id
386 FROM cala_account_set_member_account_sets m
387 JOIN cala_account_sets s
388 ON s.id = m.account_set_id
389 WHERE m.member_account_set_id = $1
390
391 UNION ALL
392 SELECT p.member_account_set_id, m.account_set_id
393 FROM parents p
394 JOIN cala_account_set_member_account_sets m
395 ON p.account_set_id = m.member_account_set_id
396 ),
397 member_accounts_deletion AS (
398 DELETE FROM cala_account_set_member_accounts
399 WHERE account_set_id IN (SELECT account_set_id FROM parents UNION SELECT $1)
400 AND member_account_id IN (SELECT member_account_id FROM cala_account_set_member_accounts
401 WHERE account_set_id = $2)
402 ),
403 member_account_set_deletion AS (
404 DELETE FROM cala_account_set_member_account_sets
405 WHERE account_set_id IN (SELECT account_set_id FROM parents UNION SELECT $1)
406 AND member_account_set_id = $2
407 )
408 SELECT account_set_id, NULL AS now
409 FROM parents
410 UNION ALL
411 SELECT NULL AS account_set_id, NOW() AS now
412 "#,
413 account_set_id as AccountSetId,
414 member_account_set_id as AccountSetId,
415 )
416 .fetch_all(&mut **db)
417 .await?;
418 let mut time = None;
419 let ret = rows
420 .into_iter()
421 .filter_map(|row| {
422 if let Some(t) = row.now {
423 time = Some(t);
424 None
425 } else {
426 Some(AccountSetId::from(
427 row.account_set_id.expect("account_set_id not set"),
428 ))
429 }
430 })
431 .collect();
432 Ok((time.expect("time not set"), ret))
433 }
434
435 pub async fn find_where_account_is_member(
436 &self,
437 account_id: AccountId,
438 query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
439 ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
440 {
441 self.find_where_account_is_member_in_executor(&self.pool, account_id, query)
442 .await
443 }
444
445 pub async fn find_where_account_is_member_in_tx(
446 &self,
447 tx: &mut Transaction<'_, Postgres>,
448 account_id: AccountId,
449 query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
450 ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
451 {
452 self.find_where_account_is_member_in_executor(&mut **tx, account_id, query)
453 .await
454 }
455
456 async fn find_where_account_is_member_in_executor(
457 &self,
458 executor: impl Executor<'_, Database = Postgres>,
459 account_id: AccountId,
460 query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
461 ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
462 {
463 let rows = sqlx::query_as!(
464 account_set_repo_types::Repo__DbEvent,
465 r#"
466 WITH member_account_sets AS (
467 SELECT a.id, a.name, a.created_at
468 FROM cala_account_set_member_accounts asm
469 JOIN cala_account_sets a ON asm.account_set_id = a.id
470 WHERE asm.member_account_id = $1 AND transitive IS FALSE
471 AND ((a.name, a.id) > ($3, $2) OR ($3 IS NULL AND $2 IS NULL))
472 ORDER BY a.name, a.id
473 LIMIT $4
474 )
475 SELECT mas.id AS "entity_id!: AccountSetId", e.sequence, e.event, e.recorded_at
476 FROM member_account_sets mas
477 JOIN cala_account_set_events e ON mas.id = e.id
478 ORDER BY mas.name, mas.id, e.sequence
479 "#,
480 account_id as AccountId,
481 query.after.as_ref().map(|c| c.id) as Option<AccountSetId>,
482 query.after.map(|c| c.name),
483 query.first as i64 + 1
484 )
485 .fetch_all(executor)
486 .await?;
487
488 let (entities, has_next_page) = EntityEvents::load_n::<AccountSet>(rows, query.first)?;
489 let mut end_cursor = None;
490 if let Some(last) = entities.last() {
491 end_cursor = Some(AccountSetsByNameCursor {
492 id: last.values().id,
493 name: last.values().name.clone(),
494 });
495 }
496 Ok(es_entity::PaginatedQueryRet {
497 entities,
498 has_next_page,
499 end_cursor,
500 })
501 }
502
503 pub async fn find_where_account_set_is_member(
504 &self,
505 account_set_id: AccountSetId,
506 query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
507 ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
508 {
509 self.find_where_account_set_is_member_in_executor(&self.pool, account_set_id, query)
510 .await
511 }
512
513 pub async fn find_where_account_set_is_member_in_tx(
514 &self,
515 tx: &mut Transaction<'_, Postgres>,
516 account_set_id: AccountSetId,
517 query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
518 ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
519 {
520 self.find_where_account_set_is_member_in_executor(&mut **tx, account_set_id, query)
521 .await
522 }
523
524 async fn find_where_account_set_is_member_in_executor(
525 &self,
526 executor: impl Executor<'_, Database = Postgres>,
527 account_set_id: AccountSetId,
528 query: es_entity::PaginatedQueryArgs<AccountSetsByNameCursor>,
529 ) -> Result<es_entity::PaginatedQueryRet<AccountSet, AccountSetsByNameCursor>, AccountSetError>
530 {
531 let rows = sqlx::query_as!(
532 account_set_repo_types::Repo__DbEvent,
533 r#"
534 WITH member_account_sets AS (
535 SELECT a.id, a.name, a.created_at
536 FROM cala_account_set_member_account_sets asm
537 JOIN cala_account_sets a ON asm.account_set_id = a.id
538 WHERE asm.member_account_set_id = $1
539 AND ((a.name, a.id) > ($3, $2) OR ($3 IS NULL AND $2 IS NULL))
540 ORDER BY a.name, a.id
541 LIMIT $4
542 )
543 SELECT mas.id AS "entity_id!: AccountSetId", e.sequence, e.event, e.recorded_at
544 FROM member_account_sets mas
545 JOIN cala_account_set_events e ON mas.id = e.id
546 ORDER BY mas.name, mas.id, e.sequence
547 "#,
548 account_set_id as AccountSetId,
549 query.after.as_ref().map(|c| c.id) as Option<AccountSetId>,
550 query.after.map(|c| c.name),
551 query.first as i64 + 1
552 )
553 .fetch_all(executor)
554 .await?;
555
556 let (entities, has_next_page) = EntityEvents::load_n::<AccountSet>(rows, query.first)?;
557 let mut end_cursor = None;
558 if let Some(last) = entities.last() {
559 end_cursor = Some(AccountSetsByNameCursor {
560 id: last.values().id,
561 name: last.values().name.clone(),
562 });
563 }
564 Ok(es_entity::PaginatedQueryRet {
565 entities,
566 has_next_page,
567 end_cursor,
568 })
569 }
570
571 #[cfg(feature = "import")]
572 pub async fn import_in_op(
573 &self,
574 op: &mut DbOp<'_>,
575 origin: DataSourceId,
576 account_set: &mut AccountSet,
577 ) -> Result<(), AccountSetError> {
578 let recorded_at = op.now();
579 sqlx::query!(
580 r#"INSERT INTO cala_account_sets (data_source_id, id, journal_id, name, external_id, created_at)
581 VALUES ($1, $2, $3, $4, $5, $6)"#,
582 origin as DataSourceId,
583 account_set.values().id as AccountSetId,
584 account_set.values().journal_id as JournalId,
585 account_set.values().name,
586 account_set.values().external_id,
587 recorded_at
588 )
589 .execute(&mut **op.tx())
590 .await?;
591 self.persist_events(op, &mut account_set.events).await?;
592 Ok(())
593 }
594
595 pub async fn fetch_mappings(
596 &self,
597 journal_id: JournalId,
598 account_ids: &[AccountId],
599 ) -> Result<HashMap<AccountId, Vec<AccountSetId>>, AccountSetError> {
600 let rows = sqlx::query!(
601 r#"
602 SELECT m.account_set_id AS "set_id!: AccountSetId", m.member_account_id AS "account_id!: AccountId"
603 FROM cala_account_set_member_accounts m
604 JOIN cala_account_sets s
605 ON m.account_set_id = s.id AND s.journal_id = $1
606 WHERE m.member_account_id = ANY($2)
607 "#,
608 journal_id as JournalId,
609 account_ids as &[AccountId]
610 )
611 .fetch_all(&self.pool)
612 .await?;
613 let mut mappings = HashMap::new();
614 for row in rows {
615 mappings
616 .entry(row.account_id)
617 .or_insert_with(Vec::new)
618 .push(row.set_id);
619 }
620 Ok(mappings)
621 }
622
623 #[cfg(feature = "import")]
624 pub async fn import_member_account_in_op(
625 &self,
626 op: &mut DbOp<'_>,
627 account_set_id: AccountSetId,
628 account_id: AccountId,
629 ) -> Result<(), AccountSetError> {
630 let recorded_at = op.now();
631 sqlx::query!(
632 r#"INSERT INTO cala_account_set_member_accounts (account_set_id, member_account_id, created_at)
633 VALUES ($1, $2, $3)"#,
634 account_set_id as AccountSetId,
635 account_id as AccountId,
636 recorded_at
637 )
638 .execute(&mut **op.tx())
639 .await?;
640 Ok(())
641 }
642
643 #[cfg(feature = "import")]
644 pub async fn import_remove_member_account(
645 &self,
646 db: &mut Transaction<'_, Postgres>,
647 account_set_id: AccountSetId,
648 account_id: AccountId,
649 ) -> Result<(), AccountSetError> {
650 sqlx::query!(
651 r#"DELETE FROM cala_account_set_member_accounts
652 WHERE account_set_id = $1 AND member_account_id = $2"#,
653 account_set_id as AccountSetId,
654 account_id as AccountId,
655 )
656 .execute(&mut **db)
657 .await?;
658 Ok(())
659 }
660
661 #[cfg(feature = "import")]
662 pub async fn import_member_set_in_op(
663 &self,
664 op: &mut DbOp<'_>,
665 account_set_id: AccountSetId,
666 member_account_set_id: AccountSetId,
667 ) -> Result<(), AccountSetError> {
668 let recorded_at = op.now();
669 sqlx::query!(
670 r#"INSERT INTO cala_account_set_member_account_sets (account_set_id, member_account_set_id, created_at)
671 VALUES ($1, $2, $3)"#,
672 account_set_id as AccountSetId,
673 member_account_set_id as AccountSetId,
674 recorded_at
675 )
676 .execute(&mut **op.tx())
677 .await?;
678 Ok(())
679 }
680
681 #[cfg(feature = "import")]
682 pub async fn import_remove_member_set(
683 &self,
684 db: &mut Transaction<'_, Postgres>,
685 account_set_id: AccountSetId,
686 member_account_set_id: AccountSetId,
687 ) -> Result<(), AccountSetError> {
688 sqlx::query!(
689 r#"DELETE FROM cala_account_set_member_account_sets
690 WHERE account_set_id = $1 AND member_account_set_id = $2"#,
691 account_set_id as AccountSetId,
692 member_account_set_id as AccountSetId,
693 )
694 .execute(&mut **db)
695 .await?;
696 Ok(())
697 }
698}