Set up some routes using new Rust API

This commit is contained in:
Ben Grant 2023-05-13 13:46:23 +10:00
parent fdc58b428b
commit fc8e2a4360
26 changed files with 703 additions and 134 deletions

View file

@ -1,7 +1,5 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
use chrono::{DateTime as ChronoDateTime, Utc};
use data::event::Event;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
@ -11,6 +9,7 @@ pub struct Model {
pub id: String,
pub name: String,
pub created_at: DateTime,
pub visited_at: DateTime,
pub times: Json,
pub timezone: String,
}
@ -28,15 +27,3 @@ impl Related<super::person::Entity> for Entity {
}
impl ActiveModelBehavior for ActiveModel {}
impl From<Model> for Event {
fn from(value: Model) -> Self {
Self {
id: value.id,
name: value.name,
created_at: ChronoDateTime::<Utc>::from_utc(value.created_at, Utc),
times: serde_json::from_value(value.times).unwrap_or(vec![]),
timezone: value.timezone,
}
}
}

View file

@ -1,7 +1,5 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
use chrono::{DateTime as ChronoDateTime, Utc};
use data::person::Person;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
@ -35,14 +33,3 @@ impl Related<super::event::Entity> for Entity {
}
impl ActiveModelBehavior for ActiveModel {}
impl From<Model> for Person {
fn from(value: Model) -> Self {
Self {
name: value.name,
password_hash: value.password_hash,
created_at: ChronoDateTime::<Utc>::from_utc(value.created_at, Utc),
availability: serde_json::from_value(value.availability).unwrap_or(vec![]),
}
}
}

View file

@ -1,6 +1,5 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
use data::stats::Stats;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
@ -16,12 +15,3 @@ pub struct Model {
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
impl From<Model> for Stats {
fn from(value: Model) -> Self {
Self {
event_count: value.event_count,
person_count: value.person_count,
}
}
}

View file

@ -1,7 +1,8 @@
use std::{env, error::Error};
use async_trait::async_trait;
use data::{
use chrono::{DateTime as ChronoDateTime, Utc};
use common::{
adaptor::Adaptor,
event::{Event, EventDeletion},
person::Person,
@ -10,54 +11,48 @@ use data::{
use entity::{event, person, stats};
use migration::{Migrator, MigratorTrait};
use sea_orm::{
strum::Display,
ActiveModelTrait,
ActiveValue::{NotSet, Set},
ColumnTrait, Database, DatabaseConnection, DbErr, EntityTrait, ModelTrait, QueryFilter,
TransactionTrait, TryIntoModel,
TransactionError, TransactionTrait, TryIntoModel,
};
use serde_json::json;
mod entity;
mod migration;
pub struct PostgresAdaptor {
pub struct SqlAdaptor {
db: DatabaseConnection,
}
#[async_trait]
impl Adaptor for PostgresAdaptor {
async fn new() -> Self {
let connection_string = env::var("DATABASE_URL").unwrap();
impl Adaptor for SqlAdaptor {
type Error = SqlAdaptorError;
// Connect to the database
let db = Database::connect(&connection_string).await.unwrap();
println!("Connected to database at {}", connection_string);
// Setup tables
Migrator::up(&db, None).await.unwrap();
Self { db }
async fn get_stats(&self) -> Result<Stats, Self::Error> {
let stats_row = get_stats_row(&self.db).await?;
Ok(Stats {
event_count: stats_row.event_count.unwrap(),
person_count: stats_row.person_count.unwrap(),
})
}
async fn get_stats(&self) -> Result<Stats, Box<dyn Error>> {
Ok(get_stats_row(&self.db).await?.try_into_model()?.into())
}
async fn increment_stat_event_count(&self) -> Result<i32, Box<dyn Error>> {
async fn increment_stat_event_count(&self) -> Result<i32, Self::Error> {
let mut current_stats = get_stats_row(&self.db).await?;
current_stats.event_count = Set(current_stats.event_count.unwrap() + 1);
Ok(current_stats.save(&self.db).await?.event_count.unwrap())
}
async fn increment_stat_person_count(&self) -> Result<i32, Box<dyn Error>> {
async fn increment_stat_person_count(&self) -> Result<i32, Self::Error> {
let mut current_stats = get_stats_row(&self.db).await?;
current_stats.person_count = Set(current_stats.person_count.unwrap() + 1);
Ok(current_stats.save(&self.db).await?.person_count.unwrap())
}
async fn get_people(&self, event_id: String) -> Result<Option<Vec<Person>>, Box<dyn Error>> {
async fn get_people(&self, event_id: String) -> Result<Option<Vec<Person>>, Self::Error> {
// TODO: optimize into one query
let event_row = event::Entity::find_by_id(event_id).one(&self.db).await?;
@ -75,11 +70,7 @@ impl Adaptor for PostgresAdaptor {
})
}
async fn upsert_person(
&self,
event_id: String,
person: Person,
) -> Result<Person, Box<dyn Error>> {
async fn upsert_person(&self, event_id: String, person: Person) -> Result<Person, Self::Error> {
Ok(person::ActiveModel {
name: Set(person.name),
password_hash: Set(person.password_hash),
@ -93,28 +84,29 @@ impl Adaptor for PostgresAdaptor {
.into())
}
async fn get_event(&self, id: String) -> Result<Option<Event>, Box<dyn Error>> {
async fn get_event(&self, id: String) -> Result<Option<Event>, Self::Error> {
Ok(event::Entity::find_by_id(id)
.one(&self.db)
.await?
.map(|model| model.into()))
}
async fn create_event(&self, event: Event) -> Result<Event, Box<dyn Error>> {
async fn create_event(&self, event: Event) -> Result<Event, Self::Error> {
Ok(event::ActiveModel {
id: Set(event.id),
name: Set(event.name),
created_at: Set(event.created_at.naive_utc()),
visited_at: Set(event.visited_at.naive_utc()),
times: Set(serde_json::to_value(event.times).unwrap_or(json!([]))),
timezone: Set(event.timezone),
}
.save(&self.db)
.insert(&self.db)
.await?
.try_into_model()?
.into())
}
async fn delete_event(&self, id: String) -> Result<EventDeletion, Box<dyn Error>> {
async fn delete_event(&self, id: String) -> Result<EventDeletion, Self::Error> {
let event_id = id.clone();
let person_count = self
.db
@ -141,6 +133,7 @@ impl Adaptor for PostgresAdaptor {
// Get the current stats as an ActiveModel
async fn get_stats_row(db: &DatabaseConnection) -> Result<stats::ActiveModel, DbErr> {
let current_stats = stats::Entity::find().one(db).await?;
Ok(match current_stats {
Some(model) => model.into(),
None => stats::ActiveModel {
@ -150,3 +143,61 @@ async fn get_stats_row(db: &DatabaseConnection) -> Result<stats::ActiveModel, Db
},
})
}
impl SqlAdaptor {
pub async fn new() -> Self {
let connection_string = env::var("DATABASE_URL").unwrap();
// Connect to the database
let db = Database::connect(&connection_string).await.unwrap();
println!("Connected to database at {}", connection_string);
// Setup tables
Migrator::up(&db, None).await.unwrap();
Self { db }
}
}
impl From<event::Model> for Event {
fn from(value: event::Model) -> Self {
Self {
id: value.id,
name: value.name,
created_at: ChronoDateTime::<Utc>::from_utc(value.created_at, Utc),
visited_at: ChronoDateTime::<Utc>::from_utc(value.visited_at, Utc),
times: serde_json::from_value(value.times).unwrap_or(vec![]),
timezone: value.timezone,
}
}
}
impl From<person::Model> for Person {
fn from(value: person::Model) -> Self {
Self {
name: value.name,
password_hash: value.password_hash,
created_at: ChronoDateTime::<Utc>::from_utc(value.created_at, Utc),
availability: serde_json::from_value(value.availability).unwrap_or(vec![]),
}
}
}
#[derive(Display, Debug)]
pub enum SqlAdaptorError {
DbErr(DbErr),
TransactionError(TransactionError<DbErr>),
}
impl Error for SqlAdaptorError {}
impl From<DbErr> for SqlAdaptorError {
fn from(value: DbErr) -> Self {
Self::DbErr(value)
}
}
impl From<TransactionError<DbErr>> for SqlAdaptorError {
fn from(value: TransactionError<DbErr>) -> Self {
Self::TransactionError(value)
}
}

View file

@ -36,6 +36,7 @@ impl MigrationTrait for Migration {
.col(ColumnDef::new(Event::Id).string().not_null().primary_key())
.col(ColumnDef::new(Event::Name).string().not_null())
.col(ColumnDef::new(Event::CreatedAt).timestamp().not_null())
.col(ColumnDef::new(Event::VisitedAt).timestamp().not_null())
.col(ColumnDef::new(Event::Times).json().not_null())
.col(ColumnDef::new(Event::Timezone).string().not_null())
.to_owned(),
@ -105,6 +106,7 @@ enum Event {
Id,
Name,
CreatedAt,
VisitedAt,
Times,
Timezone,
}

View file

@ -1,12 +1,12 @@
pub use sea_orm_migration::prelude::*;
mod setup_tables;
mod m01_setup_tables;
pub struct Migrator;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![Box::new(setup_tables::Migration)]
vec![Box::new(m01_setup_tables::Migration)]
}
}