diff --git a/.travis.yml b/.travis.yml index 5beb7a1..8bc8f11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,4 +6,5 @@ before_script: script: - cargo fmt --all -- --check - cargo build -- cargo test \ No newline at end of file +- cargo test -- --test-threads=1 + diff --git a/docs/database-schema.png b/docs/database-schema.png index 4e3debf..913df2e 100644 Binary files a/docs/database-schema.png and b/docs/database-schema.png differ diff --git a/docs/database-schema.xml b/docs/database-schema.xml index 31060ed..e564c81 100644 --- a/docs/database-schema.xml +++ b/docs/database-schema.xml @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/docs/extra-docker-info.md b/docs/extra-docker-info.md index d840edf..f96809f 100644 --- a/docs/extra-docker-info.md +++ b/docs/extra-docker-info.md @@ -41,8 +41,9 @@ docker-compose exec backend bash ``` From there all normal Diesel or Rust maintenance commands can be run. Please go to the official documentation for details, a list of commonly used commands are below. -- `cargo upgrade` - `cargo fmt` +- `cargo test -- --test-threads=1` +- `cargo upgrade` - `cargo clippy` - `diesel setup` - `diesel migration generate {name}` diff --git a/migrations/2018-12-19-163802_create_students_and_events/down.sql b/migrations/2018-12-19-163802_create_students_and_events/down.sql new file mode 100644 index 0000000..468999e --- /dev/null +++ b/migrations/2018-12-19-163802_create_students_and_events/down.sql @@ -0,0 +1,4 @@ +-- Remove the member, event, and attendance table +DROP TABLE attendance; -- Remove this first, because it depends on the other tables +DROP TABLE member; +DROP TABLE event; diff --git a/migrations/2018-12-19-163802_create_students_and_events/up.sql b/migrations/2018-12-19-163802_create_students_and_events/up.sql new file mode 100644 index 0000000..ecba77d --- /dev/null +++ b/migrations/2018-12-19-163802_create_students_and_events/up.sql @@ -0,0 +1,30 @@ +-- Create a member table +CREATE TABLE member ( + ufl_username TEXT PRIMARY KEY, + is_info_filled_out BOOLEAN NOT NULL DEFAULT FALSE, + first_name TEXT NOT NULL DEFAULT '', + last_name TEXT NOT NULL DEFAULT '', + discord_username TEXT NOT NULL DEFAULT '', + github_username TEXT NOT NULL DEFAULT '', + server_username TEXT NOT NULL DEFAULT '', + server_key TEXT NOT NULL DEFAULT '', + is_acm_shareable BOOLEAN NOT NULL DEFAULT FALSE, + is_in_email_list BOOLEAN NOT NULL DEFAULT FALSE +); + +-- Create an event table +CREATE TABLE event ( + start_timestamp TIMESTAMPTZ PRIMARY KEY, -- Events can not start at the same time and day + title TEXT NOT NULL, -- Must have title + location TEXT NOT NULL, -- Must have location + description TEXT NOT NULL DEFAULT '', + end_timestamp TIMESTAMPTZ NOT NULL, -- Events must have an end time + image BYTEA NOT NULL DEFAULT '\000'-- Image binary defaults to 0 +); + +-- Create a many to many attendance table +CREATE TABLE attendance ( + ufl_username TEXT REFERENCES member, + start_timestamp TIMESTAMPTZ REFERENCES event, + PRIMARY KEY (ufl_username, start_timestamp) +); diff --git a/src/attendance.rs b/src/attendance.rs new file mode 100644 index 0000000..7b1ce90 --- /dev/null +++ b/src/attendance.rs @@ -0,0 +1,11 @@ +use diesel::prelude::*; +use diesel::sql_types::Timestamptz; + +use super::database; +use super::schema::attendance; + +#[derive(Queryable)] +pub struct Attendance { + pub ufl_username: String, + pub start_timestamp: Timestamptz, +} diff --git a/src/database.rs b/src/database.rs new file mode 100644 index 0000000..d2d3937 --- /dev/null +++ b/src/database.rs @@ -0,0 +1,15 @@ +extern crate diesel; + +use diesel::pg::PgConnection; +use diesel::prelude::*; +use std::env; + +// club_data data is defined in Rocket.toml +#[database("club_data")] +pub struct ClubDbConn(diesel::PgConnection); + +// Create connection for other functions to use +pub fn establish_connection() -> PgConnection { + let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url)) +} diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..47f2270 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,16 @@ +use diesel::prelude::*; +use diesel::sql_types::Bytea; +use diesel::sql_types::Timestamptz; + +use super::database; +use super::schema::event; + +#[derive(Queryable)] +pub struct Event { + pub start_timestamp: Timestamptz, + pub title: String, + pub location: String, + pub description: String, + pub end_timestamp: Timestamptz, + pub image: Bytea, +} diff --git a/src/main.rs b/src/main.rs index 291e396..48568f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,34 @@ #![feature(proc_macro_hygiene, decl_macro)] +// Temporarily silence warnings caused by Diesel (https://github.com/diesel-rs/diesel/issues/1785) +#![allow(proc_macro_derive_resolution_fallback)] #[macro_use] extern crate rocket; #[macro_use] extern crate rocket_contrib; +#[macro_use] +extern crate diesel; -use diesel::pg::PgConnection; -use diesel::prelude::*; -use rocket_contrib::databases::diesel; -use std::env; - -#[database("club_data")] -struct ClubDbConn(diesel::PgConnection); +// Utility local dependencies +mod database; +mod schema; -pub fn establish_connection() -> PgConnection { - let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); - PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url)) -} +// Table specifc local dependencies +mod attendance; +mod event; +mod member; +// Check to see if the server is working #[get("/")] fn index() -> &'static str { "Hello, world!" } +// Launch the REST server with the database connection fn main() { rocket::ignite() - .attach(ClubDbConn::fairing()) + .attach(database::ClubDbConn::fairing()) + // Note: Be sure to mount all the routes from differnt modules .mount("/", routes![index]) .launch(); } diff --git a/src/member.rs b/src/member.rs new file mode 100644 index 0000000..ce11f23 --- /dev/null +++ b/src/member.rs @@ -0,0 +1,137 @@ +use diesel::prelude::*; + +use super::database; +use super::schema::member; + +/* Struct Setup */ + +// Struct for interacting with the member table +#[derive(Insertable, Queryable)] +#[table_name = "member"] +pub struct Member { + pub ufl_username: String, + pub is_info_filled_out: bool, + pub first_name: String, + pub last_name: String, + pub discord_username: String, + pub github_username: String, + pub server_username: String, + pub server_key: String, + pub is_acm_shareable: bool, + pub is_in_email_list: bool, +} + +// Support creating new members that supply just a UFL username +impl Member { + fn new(ufl_username: &str) -> Self { + Member { + ufl_username: ufl_username.to_string(), + ..Default::default() + } + } +} + +// Set default values for Member +// Note: the UFL username should never be not set (Rust requires all values to have a default) +impl Default for Member { + fn default() -> Member { + Member { + ufl_username: "".to_string(), + is_info_filled_out: false, + first_name: "".to_string(), + last_name: "".to_string(), + github_username: "".to_string(), + discord_username: "".to_string(), + server_username: "".to_string(), + server_key: "".to_string(), + is_acm_shareable: false, + is_in_email_list: false, + } + } +} + +/* CRUD and other functions */ + +// Return all members +pub fn list_members() -> Vec { + let connection = database::establish_connection(); + let results = member::table + .load::(&connection) + .expect("Error loading members"); + results +} + +// Add a member with a UFL username +pub fn add_member(ufl_username: &str) { + let connection = database::establish_connection(); + + let new_member = Member::new(&ufl_username); + + diesel::insert_into(member::table) + .values(&new_member) + .get_result::(&connection) + .expect("Error saving new member"); +} + +// Remove a member by their username +pub fn remove_member(ufl_username: &str) { + let connection = database::establish_connection(); + + let num_deleted = + diesel::delete(member::table.filter(member::columns::ufl_username.eq(ufl_username))) //.like(ufl_username))) + .execute(&connection) + .expect("Error deleting members"); + + println!("Deleted {} members", num_deleted); +} + +/* Unit testing */ + +// Note: Do run the test as `cargo test -- --test-threads=1` to run the database calls in order +#[cfg(test)] +mod tests { + use super::*; + + // Utility function to clear out the whole member table + fn clear_table() { + let connection = database::establish_connection(); + + diesel::delete(member::table) + .execute(&connection) + .expect("Error deleting all members"); + } + + // Check to make sure no members exist by default + #[test] + fn no_members() { + clear_table(); + assert_eq!(Vec::len(&list_members()), 0); + } + + // Check that one member exists after they are created + #[test] + fn one_member() { + clear_table(); + add_member("one_member_test@email.com"); + assert_eq!(Vec::len(&list_members()), 1); + } + + // Check that two members exist agter they are both created + #[test] + fn two_member() { + clear_table(); + add_member("two_member_test_one@email.com"); + add_member("two_member_test_two@email.com"); + assert_eq!(Vec::len(&list_members()), 2); + } + + // Checl that a single member can be deleted after being created + #[test] + fn delete_member() { + clear_table(); + add_member("delete_member_test@email.com"); + remove_member("delete_member_test@email.com"); + assert_eq!(Vec::len(&list_members()), 0); + } + +} diff --git a/src/schema.rs b/src/schema.rs new file mode 100644 index 0000000..e22fe1a --- /dev/null +++ b/src/schema.rs @@ -0,0 +1,37 @@ +table! { + attendance (ufl_username, start_timestamp) { + ufl_username -> Text, + start_timestamp -> Timestamptz, + } +} + +table! { + event (start_timestamp) { + start_timestamp -> Timestamptz, + title -> Text, + location -> Text, + description -> Text, + end_timestamp -> Timestamptz, + image -> Bytea, + } +} + +table! { + member (ufl_username) { + ufl_username -> Text, + is_info_filled_out -> Bool, + first_name -> Text, + last_name -> Text, + discord_username -> Text, + github_username -> Text, + server_username -> Text, + server_key -> Text, + is_acm_shareable -> Bool, + is_in_email_list -> Bool, + } +} + +joinable!(attendance -> event (start_timestamp)); +joinable!(attendance -> member (ufl_username)); + +allow_tables_to_appear_in_same_query!(attendance, event, member,);