summaryrefslogtreecommitdiff
path: root/backend/src
diff options
context:
space:
mode:
Diffstat (limited to 'backend/src')
-rw-r--r--backend/src/databases.rs4
-rw-r--r--backend/src/main.rs41
-rw-r--r--backend/src/models/account.rs13
-rw-r--r--backend/src/models/budget_update.rs14
-rw-r--r--backend/src/models/category.rs10
-rw-r--r--backend/src/models/category_transaction.rs10
-rw-r--r--backend/src/models/category_transfer.rs11
-rw-r--r--backend/src/models/mod.rs6
-rw-r--r--backend/src/models/transaction.rs14
-rw-r--r--backend/src/routes/account.rs68
-rw-r--r--backend/src/routes/mod.rs1
-rw-r--r--backend/src/schema.rs71
12 files changed, 263 insertions, 0 deletions
diff --git a/backend/src/databases.rs b/backend/src/databases.rs
new file mode 100644
index 0000000..461fc44
--- /dev/null
+++ b/backend/src/databases.rs
@@ -0,0 +1,4 @@
+use rocket_sync_db_pools::{database, diesel};
+
+#[database("user_data")]
+pub struct UserDataDb(diesel::SqliteConnection);
diff --git a/backend/src/main.rs b/backend/src/main.rs
new file mode 100644
index 0000000..db47ceb
--- /dev/null
+++ b/backend/src/main.rs
@@ -0,0 +1,41 @@
+use diesel::SqliteConnection;
+use diesel_migrations::MigrationHarness;
+use diesel_migrations::{embed_migrations, EmbeddedMigrations};
+use rocket::fairing::AdHoc;
+use rocket::{launch, routes, Build, Rocket};
+
+use crate::databases::UserDataDb;
+
+pub mod databases;
+pub mod models;
+pub mod routes;
+pub mod schema;
+
+pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
+
+fn run_database_migrations(connection: &mut SqliteConnection) {
+ connection
+ .run_pending_migrations(MIGRATIONS)
+ .expect("Error running database migrations");
+}
+
+#[launch]
+async fn rocket() -> Rocket<Build> {
+ rocket::build()
+ .attach(UserDataDb::fairing())
+ .attach(AdHoc::on_liftoff("Database Migration", |rocket| {
+ Box::pin(async move {
+ let user_data_db = UserDataDb::get_one(rocket).await.expect(
+ "Failed to get connection to user data database in order to run migrations",
+ );
+ user_data_db.run(run_database_migrations).await;
+ })
+ }))
+ .mount(
+ "/",
+ routes![
+ routes::account::get_all_accounts,
+ routes::account::create_account
+ ],
+ )
+}
diff --git a/backend/src/models/account.rs b/backend/src/models/account.rs
new file mode 100644
index 0000000..1a2aa41
--- /dev/null
+++ b/backend/src/models/account.rs
@@ -0,0 +1,13 @@
+use diesel::prelude::*;
+use serde::{Deserialize, Serialize};
+
+#[derive(
+ Insertable, Queryable, Identifiable, Selectable, Debug, PartialEq, Clone, Serialize, Deserialize,
+)]
+#[diesel(table_name = crate::schema::accounts)]
+pub struct Account {
+ pub id: i32,
+ pub name: String,
+ pub opening_balance: i32,
+ pub opening_date: String,
+}
diff --git a/backend/src/models/budget_update.rs b/backend/src/models/budget_update.rs
new file mode 100644
index 0000000..3843455
--- /dev/null
+++ b/backend/src/models/budget_update.rs
@@ -0,0 +1,14 @@
+use diesel::prelude::*;
+
+use crate::models::category::Category;
+
+#[derive(Queryable, Identifiable, Selectable, Associations, Debug, PartialEq)]
+#[diesel(table_name = crate::schema::budget_updates)]
+#[diesel(belongs_to(Category))]
+pub struct BudgetUpdate {
+ pub id: i32,
+ pub category_id: i32,
+ pub date: String,
+ pub new_budget: i32,
+ pub new_period: i32,
+}
diff --git a/backend/src/models/category.rs b/backend/src/models/category.rs
new file mode 100644
index 0000000..d7e25aa
--- /dev/null
+++ b/backend/src/models/category.rs
@@ -0,0 +1,10 @@
+use diesel::prelude::*;
+
+#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)]
+#[diesel(table_name = crate::schema::categories)]
+pub struct Category {
+ pub id: i32,
+ pub name: String,
+ pub initial_budget: i32,
+ pub initial_period: i32,
+}
diff --git a/backend/src/models/category_transaction.rs b/backend/src/models/category_transaction.rs
new file mode 100644
index 0000000..2163860
--- /dev/null
+++ b/backend/src/models/category_transaction.rs
@@ -0,0 +1,10 @@
+use diesel::prelude::*;
+
+#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)]
+#[diesel(table_name = crate::schema::category_transactions)]
+pub struct CategoryTransaction {
+ pub id: i32,
+ pub description: String,
+ pub quantity: i32,
+ pub category_id: i32,
+}
diff --git a/backend/src/models/category_transfer.rs b/backend/src/models/category_transfer.rs
new file mode 100644
index 0000000..6cbda0e
--- /dev/null
+++ b/backend/src/models/category_transfer.rs
@@ -0,0 +1,11 @@
+use diesel::prelude::*;
+
+#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)]
+#[diesel(table_name = crate::schema::category_transfers)]
+pub struct CategoryTransfer {
+ pub id: i32,
+ pub description: String,
+ pub quantity: i32,
+ pub from_category_id: i32,
+ pub to_category_id: i32,
+}
diff --git a/backend/src/models/mod.rs b/backend/src/models/mod.rs
new file mode 100644
index 0000000..f70212a
--- /dev/null
+++ b/backend/src/models/mod.rs
@@ -0,0 +1,6 @@
+pub mod account;
+pub mod budget_update;
+pub mod category;
+pub mod category_transaction;
+pub mod category_transfer;
+pub mod transaction;
diff --git a/backend/src/models/transaction.rs b/backend/src/models/transaction.rs
new file mode 100644
index 0000000..a83a0ad
--- /dev/null
+++ b/backend/src/models/transaction.rs
@@ -0,0 +1,14 @@
+use diesel::prelude::*;
+
+use crate::models::account::Account;
+
+#[derive(Queryable, Identifiable, Selectable, Associations, Debug, PartialEq)]
+#[diesel(table_name = crate::schema::transactions)]
+#[diesel(belongs_to(Account))]
+pub struct Transaction {
+ pub id: i32,
+ pub description: String,
+ pub payee: String,
+ pub quantity: i32,
+ pub account_id: i32,
+}
diff --git a/backend/src/routes/account.rs b/backend/src/routes/account.rs
new file mode 100644
index 0000000..e7f000c
--- /dev/null
+++ b/backend/src/routes/account.rs
@@ -0,0 +1,68 @@
+use diesel::prelude::*;
+use diesel::result::DatabaseErrorKind;
+use rocket::error::ErrorKind;
+use rocket::serde::json::Json;
+use rocket::tokio::sync::oneshot::error::RecvError;
+use rocket::{get, post, Responder};
+
+use crate::databases::UserDataDb;
+use crate::models::account::Account;
+use crate::schema::accounts::dsl::accounts;
+
+#[get("/account")]
+pub async fn get_all_accounts(user_data_db: UserDataDb) -> Json<Vec<Account>> {
+ user_data_db
+ .run(|connection| {
+ Json(
+ accounts
+ .select(Account::as_select())
+ .load(connection)
+ .expect("Error getting accounts"),
+ )
+ })
+ .await
+}
+
+#[derive(Responder)]
+pub enum CreateAccountResponder {
+ #[response(status = 500, content_type = "text")]
+ InternalServerError(String),
+
+ #[response(status = 202, content_type = "json")]
+ Created(Json<Account>),
+
+ #[response(status = 400, content_type = "text")]
+ BadRequest(String),
+}
+
+#[post("/account", data = "<account>", format = "json")]
+pub async fn create_account(
+ account: Json<Account>,
+ user_data_db: UserDataDb,
+) -> CreateAccountResponder {
+ let account = account.into_inner();
+ let account_clone = account.clone();
+
+ let num_rows_inserted = user_data_db
+ .run(|connection| {
+ diesel::insert_into(accounts)
+ .values(vec![account_clone])
+ .execute(connection)
+ })
+ .await;
+
+ match num_rows_inserted {
+ Ok(_) => CreateAccountResponder::Created(Json(account)),
+ Err(err) => match err {
+ diesel::result::Error::DatabaseError(DatabaseErrorKind::UniqueViolation, info) => {
+ let column = info.column_name().unwrap_or("account");
+ CreateAccountResponder::BadRequest(format!(
+ "Error creating account: {column} was not unique."
+ ))
+ }
+ _ => CreateAccountResponder::InternalServerError(format!(
+ "Error creating account: {err}"
+ )),
+ },
+ }
+}
diff --git a/backend/src/routes/mod.rs b/backend/src/routes/mod.rs
new file mode 100644
index 0000000..b0edc6c
--- /dev/null
+++ b/backend/src/routes/mod.rs
@@ -0,0 +1 @@
+pub mod account;
diff --git a/backend/src/schema.rs b/backend/src/schema.rs
new file mode 100644
index 0000000..d98a629
--- /dev/null
+++ b/backend/src/schema.rs
@@ -0,0 +1,71 @@
+// @generated automatically by Diesel CLI.
+
+diesel::table! {
+ accounts (id) {
+ id -> Integer,
+ name -> Text,
+ opening_balance -> Integer,
+ opening_date -> Text,
+ }
+}
+
+diesel::table! {
+ budget_updates (id) {
+ id -> Integer,
+ category_id -> Integer,
+ date -> Text,
+ new_budget -> Integer,
+ new_period -> Integer,
+ }
+}
+
+diesel::table! {
+ categories (id) {
+ id -> Integer,
+ name -> Text,
+ initial_budget -> Integer,
+ initial_period -> Integer,
+ }
+}
+
+diesel::table! {
+ category_transactions (id) {
+ id -> Integer,
+ description -> Text,
+ quantity -> Integer,
+ category_id -> Integer,
+ }
+}
+
+diesel::table! {
+ category_transfers (id) {
+ id -> Integer,
+ description -> Text,
+ quantity -> Integer,
+ from_category_id -> Integer,
+ to_category_id -> Integer,
+ }
+}
+
+diesel::table! {
+ transactions (id) {
+ id -> Integer,
+ description -> Text,
+ payee -> Text,
+ quantity -> Integer,
+ account_id -> Integer,
+ }
+}
+
+diesel::joinable!(budget_updates -> categories (category_id));
+diesel::joinable!(category_transactions -> categories (category_id));
+diesel::joinable!(transactions -> accounts (account_id));
+
+diesel::allow_tables_to_appear_in_same_query!(
+ accounts,
+ budget_updates,
+ categories,
+ category_transactions,
+ category_transfers,
+ transactions,
+);