Skip to content

Commit

Permalink
feat(*)!: add deposit and withdraw events
Browse files Browse the repository at this point in the history
Signed-off-by: Brooks Townsend <[email protected]>

ensure providers run on cosmonic

Signed-off-by: Brooks Townsend <[email protected]>
  • Loading branch information
brooksmtownsend committed Dec 20, 2023
1 parent 849510b commit fb89ab9
Show file tree
Hide file tree
Showing 17 changed files with 429 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ build:
(cd $dir && wash build); \
done

version := "0.1.0"
version := "0.2.0"
push:
# Push to GHCR
wash push ghcr.io/cosmonic/cosmonic-gitops/bankaccount_projector:{{version}} projector/build/bankaccount_projector_s.wasm
Expand Down
54 changes: 54 additions & 0 deletions aggregate/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,57 @@ pub(crate) fn handle_create_account(input: CreateAccount) -> Result<EventList> {
},
)])
}

pub(crate) fn handle_withdraw_funds(
input: WithdrawFunds,
state: Option<BankAccountAggregateState>,
) -> Result<EventList> {
let Some(state) = state else {
return Err(anyhow::anyhow!(
"Rejected command to withdraw funds. Account {} does not exist.",
input.account_number
));
};

if state.available_balance() < input.amount as u32 {
error!(
"Rejecting command to withdraw funds, account {} does not have sufficient funds. Available {}",
&input.account_number, state.available_balance()
);
Ok(vec![])
} else {
Ok(vec![Event::new(
FundsWithdrawn::TYPE,
STREAM,
&FundsWithdrawn {
note: input.note,
account_number: input.account_number.to_string(),
amount: input.amount,
customer_id: input.customer_id,
},
)])
}
}

pub(crate) fn handle_deposit_funds(
input: DepositFunds,
state: Option<BankAccountAggregateState>,
) -> Result<EventList> {
if state.is_none() {
return Err(anyhow::anyhow!(
"Rejected command to deposit funds. Account {} does not exist.",
input.account_number
));
};

Ok(vec![Event::new(
FundsDeposited::TYPE,
STREAM,
&FundsDeposited {
note: input.note,
account_number: input.account_number.to_string(),
amount: input.amount,
customer_id: input.customer_id,
},
)])
}
39 changes: 39 additions & 0 deletions aggregate/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,42 @@ impl From<AccountCreated> for BankAccountAggregateState {
pub(crate) fn apply_account_created(input: AccountCreated) -> Result<StateAck> {
Ok(StateAck::ok(Some(BankAccountAggregateState::from(input))))
}

pub(crate) fn apply_funds_deposited(
input: FundsDeposited,
state: Option<BankAccountAggregateState>,
) -> Result<StateAck> {
let Some(state) = state else {
error!(
"Rejecting funds deposited event. Account {} does not exist.",
input.account_number
);
return Ok(StateAck::error(
"Account does not exist",
None::<BankAccountAggregateState>,
));
};
let state = BankAccountAggregateState {
balance: state.balance + input.amount as u32,
..state
};
Ok(StateAck::ok(Some(state)))
}

pub(crate) fn apply_funds_withdrawn(
input: FundsWithdrawn,
state: Option<BankAccountAggregateState>,
) -> Result<StateAck> {
let Some(state) = state else {
error!(
"Rejecting funds withdrawn event. Account {} does not exist.",
input.account_number
);
return Ok(StateAck::error(
"Account does not exist",
None::<BankAccountAggregateState>,
));
};
let state = state.withdraw(input.amount as u32);
Ok(StateAck::ok(Some(state)))
}
34 changes: 34 additions & 0 deletions aggregate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anyhow::Result;
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use wasmcloud_interface_logging::error;

mod commands;
mod events;
Expand All @@ -25,14 +26,47 @@ impl BankAccountAggregate for BankAccountAggregateImpl {
commands::handle_create_account(input)
}

fn handle_withdraw_funds(
&self,
input: WithdrawFunds,
state: Option<BankAccountAggregateState>,
) -> anyhow::Result<EventList> {
commands::handle_withdraw_funds(input, state)
}

fn handle_deposit_funds(
&self,
input: DepositFunds,
state: Option<BankAccountAggregateState>,
) -> anyhow::Result<EventList> {
commands::handle_deposit_funds(input, state)
}

// -- Events --

fn apply_account_created(
&self,
input: AccountCreated,
_state: Option<BankAccountAggregateState>,
) -> anyhow::Result<StateAck> {
events::apply_account_created(input)
}

fn apply_funds_deposited(
&self,
input: FundsDeposited,
state: Option<BankAccountAggregateState>,
) -> anyhow::Result<StateAck> {
events::apply_funds_deposited(input, state)
}

fn apply_funds_withdrawn(
&self,
input: FundsWithdrawn,
state: Option<BankAccountAggregateState>,
) -> anyhow::Result<StateAck> {
events::apply_funds_withdrawn(input, state)
}
}

const STREAM: &str = "bankaccount";
Binary file modified eventcatalog/all_events.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions eventcatalog/events/DepositFunds/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
name: DepositFunds
summary: "A request to deposit funds into an account"
version: 0.0.1
consumers:
- 'Bank Account Aggregate'
tags:
- label: 'command'
externalLinks: []
badges: []
---
Requests the deposit of a specified amount into the account. This command can fail to process if the parameters are invalid.

<Mermaid />

## Schema
<SchemaViewer />
30 changes: 30 additions & 0 deletions eventcatalog/events/DepositFunds/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$id": "https://cosmonic.com/concordance/bankaccount/DepositFunds.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "DepositFunds",
"type": "object",
"properties": {
"accountNumber": {
"type": "string",
"description": "The account number"
},
"amount": {
"type": "integer",
"description": "The amount to deposit"
},
"note": {
"type": "string",
"description": "An optional note to be associated with the deposit"
},
"customerId": {
"type": "string",
"description": "The ID of the customer performing the deposit"
},
"transferId": {
"type": "string",
"description": "A unique ID identifying the transfer transaction if applicable"
}
},
"required": ["accountNumber", "amount", "customerId"]
}

20 changes: 20 additions & 0 deletions eventcatalog/events/FundsDeposited/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: FundsDeposited
summary: "Indicates funds have been deposited into an account"
version: 0.0.1
consumers:
- 'Bank Account Aggregate'
- 'Bank Account Projector'
producers:
- 'Bank Account Aggregate'
tags:
- label: 'event'
externalLinks: []
badges: []
---
Indicates that funds have been deposited into an account.

<Mermaid />

## Schema
<SchemaViewer />
26 changes: 26 additions & 0 deletions eventcatalog/events/FundsDeposited/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$id": "https://cosmonic.com/concordance/bankaccount/FundsDeposited.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "FundsDeposited",
"type": "object",
"properties": {
"accountNumber": {
"type": "string",
"description": "The account number"
},
"amount": {
"type": "integer",
"description": "The amount deposited"
},
"note": {
"type": "string",
"description": "An optional note to associated with the deposit"
},
"customerId": {
"type": "string",
"description": "The ID of the customer that performed the deposit"
}
},
"required": ["accountNumber", "amount", "customerId"]
}

20 changes: 20 additions & 0 deletions eventcatalog/events/FundsWithdrawn/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: FundsWithdrawn
summary: "Indicates a successful withdrawal of funds"
version: 0.0.1
consumers:
- 'Bank Account Aggregate'
- 'Bank Account Projector'
producers:
- 'Bank Account Aggregate'
tags:
- label: 'event'
externalLinks: []
badges: []
---
Indicates funds have been withdrawn from the account

<Mermaid />

## Schema
<SchemaViewer />
26 changes: 26 additions & 0 deletions eventcatalog/events/FundsWithdrawn/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$id": "https://cosmonic.com/concordance/bankaccount/FundsWithdrawn.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "FundsWithdrawn",
"type": "object",
"properties": {
"accountNumber": {
"type": "string",
"description": "The account number"
},
"amount": {
"type": "integer",
"description": "The amount withdrawn"
},
"note": {
"type": "string",
"description": "An optional note associated with the withdrawal"
},
"customerId": {
"type": "string",
"description": "The ID of the customer that performed the withdrawal"
}
},
"required": ["accountNumber", "amount", "customerId"]
}

20 changes: 20 additions & 0 deletions eventcatalog/events/WithdrawFunds/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: WithdrawFunds
summary: "A request to withdraw funds from an account"
version: 0.0.1
consumers:
- 'Bank Account Aggregate'
tags:
- label: 'command'
externalLinks: []
badges: []
---
Requests the withdrawal of a specified amount from the account. This command can fail to process if the parameters are invalid or if the account does not have sufficient funds.

Note that there is a design decision here. You can allow the withdrawal to go through even if there is insufficient funds, and then also emit an overdraft event. Or all commands attempting to withdraw below the minimum (or 0 if omitted) are rejected. This is a domain/application decision and
not really something that can be decided by the framework.

<Mermaid />

## Schema
<SchemaViewer />
26 changes: 26 additions & 0 deletions eventcatalog/events/WithdrawFunds/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$id": "https://cosmonic.com/concordance/bankaccount/WithdrawFunds.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "WithdrawFunds",
"type": "object",
"properties": {
"accountNumber": {
"type": "string",
"description": "The account number"
},
"amount": {
"type": "integer",
"description": "The amount to withdraw"
},
"note": {
"type": "string",
"description": "An optional note to be associated with the withdrawal"
},
"customerId": {
"type": "string",
"description": "The ID of the customer performing the withdrawal"
}
},
"required": ["accountNumber", "amount", "customerId"]
}

2 changes: 1 addition & 1 deletion eventcatalog/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eventcatalog",
"version": "0.1.0",
"version": "0.2.0",
"private": true,
"scripts": {
"start": "eventcatalog start",
Expand Down
8 changes: 8 additions & 0 deletions projector/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,12 @@ impl BankAccountProjector for BankAccountProjectorImpl {
async fn handle_account_created(&self, input: AccountCreated) -> Result<()> {
store::initialize_account(input).await
}

async fn handle_funds_deposited(&self, input: FundsDeposited) -> Result<()> {
store::record_funds_deposited(input).await
}

async fn handle_funds_withdrawn(&self, input: FundsWithdrawn) -> Result<()> {
store::record_funds_withdrawn(input).await
}
}
Loading

0 comments on commit fb89ab9

Please sign in to comment.