Set up the database table with users, invitations, and sessions

main
Savanni D'Gerinel 2022-11-17 15:55:10 -05:00
parent 57b438c6e6
commit 6e1e4d7811
7 changed files with 133 additions and 1 deletions

View File

@ -1,9 +1,16 @@
.PHONY: server-dev client-dev client-test
.PHONY: server-dev server-test client-dev client-test
test:
cd server && make test-oneshot
cd v-client && make test
server-dev:
cd server && make dev
server-test:
cd server && make test
client-dev:
cd v-client && make dev

1
server/Cargo.lock generated
View File

@ -705,6 +705,7 @@ dependencies = [
"rand",
"rusqlite",
"sha2",
"tempfile",
"thiserror",
"tokio",
"uuid",

View File

@ -15,3 +15,5 @@ tokio = { version = "1", features = ["full"] }
uuid = { version = "0.8", features = ["v4"] }
warp = { version = "0.3" }
[dev-dependencies]
tempfile = { version = "3" }

View File

@ -3,3 +3,9 @@
dev:
cargo watch -x run
test:
cargo watch -x test
test-oneshot:
cargo test

View File

@ -0,0 +1,3 @@
fn main() {
println!("There is a tool");
}

111
server/src/database.rs Normal file
View File

@ -0,0 +1,111 @@
use rusqlite::{params, Connection};
use std::{
ops::{Deref, DerefMut},
path::PathBuf,
sync::{Arc, Mutex},
};
pub struct ManagedConnection<'a> {
pool: &'a Database,
connection: Option<Connection>,
}
pub struct Database {
file_path: PathBuf,
pool: Arc<Mutex<Vec<Connection>>>,
}
impl Database {
pub fn new(file_path: PathBuf) -> Result<Database, anyhow::Error> {
let mut connection = Connection::open(file_path.clone())?;
let tx = connection.transaction()?;
let version: i32 = tx.pragma_query_value(None, "user_version", |r| r.get(0))?;
if version == 0 {
tx.execute_batch(
"CREATE TABLE users (id STRING PRIMARY KEY, name TEXT);
CREATE TABLE invitations (token STRING PRIMARY KEY, user_id STRING, FOREIGN KEY(user_id) REFERENCES users(id));
CREATE TABLE sessions (token STRING PRIMARY KEY, user_id STRING, FOREIGN KEY(user_id) REFERENCES users(id));
PRAGMA user_version = 1;",
)?;
}
tx.commit()?;
Ok(Database {
file_path,
pool: Arc::new(Mutex::new(vec![connection])),
})
}
pub fn connect<'a>(&'a self) -> Result<ManagedConnection<'a>, anyhow::Error> {
let mut pool = self.pool.lock().unwrap();
match pool.pop() {
Some(connection) => Ok(ManagedConnection {
pool: &self,
connection: Some(connection),
}),
None => {
let connection = Connection::open(self.file_path.clone())?;
Ok(ManagedConnection {
pool: &self,
connection: Some(connection),
})
}
}
}
pub fn release(&self, connection: Connection) {
let mut pool = self.pool.lock().unwrap();
pool.push(connection);
}
}
impl Deref for ManagedConnection<'_> {
type Target = Connection;
fn deref(&self) -> &Connection {
self.connection.as_ref().unwrap()
}
}
impl DerefMut for ManagedConnection<'_> {
fn deref_mut(&mut self) -> &mut Connection {
self.connection.as_mut().unwrap()
}
}
impl Drop for ManagedConnection<'_> {
fn drop(&mut self) {
self.pool.release(self.connection.take().unwrap());
}
}
#[cfg(test)]
mod test {
use super::*;
use tempfile::NamedTempFile;
#[test]
fn it_can_create_users() {
let path = NamedTempFile::new().unwrap().into_temp_path();
let database = Database::new(path.to_path_buf()).unwrap();
let mut connection = database.connect().unwrap();
let tr = connection.transaction().unwrap();
tr.execute(
"INSERT INTO users VALUES(?, ?)",
params!["abcdefg", "mercer"],
)
.unwrap();
tr.commit().unwrap();
let connection = database.connect().unwrap();
let id: Option<String> = connection
.query_row(
"SELECT id FROM users WHERE name = ?",
[String::from("mercer")],
|row| row.get("id"),
)
.unwrap();
assert_eq!(id, Some(String::from("abcdefg")));
}
}

View File

@ -1,6 +1,8 @@
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use warp::Filter;
mod database;
#[tokio::main]
pub async fn main() {
let echo_unauthenticated = warp::path!("api" / "v1" / "echo" / String).map(|param: String| {