Skip to content

Commit

Permalink
Merge pull request #39 from Jim-Hodapp-Coaching/add_organizations_by_…
Browse files Browse the repository at this point in the history
…user_id

Add organizations by user
  • Loading branch information
calebbourg committed May 12, 2024
2 parents 2a74efe + d5fb4f5 commit 168ee31
Show file tree
Hide file tree
Showing 22 changed files with 570 additions and 177 deletions.
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ name = "refactor_platform_rs"
version = "0.1.0"
edition = "2021"

default-run = "refactor_platform_rs"

[workspace]
members = [".", "entity_api", "entity", "migration", "service", "web"]

Expand All @@ -15,3 +17,7 @@ clap = { version = "4.4.6", features = ["cargo", "derive", "env"] }
log = "0.4"
simplelog = { version = "0.12", features = ["paris"] }
tokio = "1.33.0"

[[bin]]
name = "seed_db"
path = "src/bin/seed_db.rs"
2 changes: 1 addition & 1 deletion docs/db/refactor_platform_rs.dbml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Table refactor_platform.organizations {
id integer [primary key, unique, not null, increment]
external_id uuid [unique, not null, default: `gen_random_uuid()`, note: 'The publicly visible identifier for a record']
name varchar [note: 'The name of the organization that the coach <--> coachee belong to']
name varchar [not null, note: 'The name of the organization that the coach <--> coachee belong to']
logo varchar [note: 'A URI pointing to the organization\'s logo icon file']
created_at timestamptz [not null, default: `now()`]
updated_at timestamptz [not null, default: `now()`, note: 'The last date and time fields were changed']
Expand Down
5 changes: 4 additions & 1 deletion entity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ pub mod organizations;
pub mod overarching_goals;
pub mod users;

/// A type alias that represents any Entity's id field data type
/// A type alias that represents any Entity's internal id field data type
pub type Id = i32;

/// A type alias that represents any Entity's external id field data type
pub type ExternalId = String;
4 changes: 2 additions & 2 deletions entity/src/organizations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ use utoipa::ToSchema;
#[sea_orm(schema_name = "refactor_platform", table_name = "organizations")]
pub struct Model {
#[sea_orm(primary_key)]
#[serde(skip_deserializing)]
#[serde(skip_serializing, skip_deserializing)]
pub id: i32,
#[sea_orm(unique)]
pub external_id: Uuid,
pub name: Option<String>,
pub name: String,
pub logo: Option<String>,
#[schema(value_type = String, format = DateTime)] // Applies to OpenAPI schema
pub created_at: DateTimeWithTimeZone,
Expand Down
110 changes: 110 additions & 0 deletions entity_api/src/coaching_relationship.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use super::error::{EntityApiErrorCode, Error};
use chrono::Utc;
use entity::{
coaching_relationships,
coaching_relationships::{ActiveModel, Model},
Id,
};
use sea_orm::{entity::prelude::*, Condition, DatabaseConnection, Set};
use serde_json::from_str;

use log::*;

pub async fn create(
db: &DatabaseConnection,
coaching_relationship_model: Model,
) -> Result<Model, Error> {
debug!(
"New Coaching Relationship Model to be inserted: {:?}",
coaching_relationship_model
);

let now = Utc::now();

let coaching_relationship_active_model: ActiveModel = ActiveModel {
external_id: Set(Uuid::new_v4()),
organization_id: Set(coaching_relationship_model.organization_id),
coach_id: Set(coaching_relationship_model.coach_id),
coachee_id: Set(coaching_relationship_model.coachee_id),
created_at: Set(now.into()),
updated_at: Set(now.into()),
..Default::default()
};

Ok(coaching_relationship_active_model.insert(db).await?)
}

pub async fn find_by_user(db: &DatabaseConnection, user_id: Id) -> Result<Vec<Model>, Error> {
let coaching_relationships: Vec<coaching_relationships::Model> =
coaching_relationships::Entity::find()
.filter(
Condition::any()
.add(coaching_relationships::Column::CoachId.eq(user_id))
.add(coaching_relationships::Column::CoacheeId.eq(user_id)),
)
.all(db)
.await?;

Ok(coaching_relationships)
}

pub async fn find_by(
db: &DatabaseConnection,
params: std::collections::HashMap<String, String>,
) -> Result<Vec<Model>, Error> {
let mut query = coaching_relationships::Entity::find();

for (key, value) in params.iter() {
match key.as_str() {
"organization_id" => {
query = by_organization(query, from_str::<Id>(value).unwrap()).await;
}
_ => {
return Err(Error {
inner: None,
error_code: EntityApiErrorCode::InvalidQueryTerm,
});
}
}
}

Ok(query.all(db).await?)
}

async fn by_organization(
query: Select<coaching_relationships::Entity>,
organization_id: Id,
) -> Select<coaching_relationships::Entity> {
query.filter(coaching_relationships::Column::OrganizationId.eq(organization_id))
}

#[cfg(test)]
// We need to gate seaORM's mock feature behind conditional compilation because
// the feature removes the Clone trait implementation from seaORM's DatabaseConnection.
// see https://github.com/SeaQL/sea-orm/issues/830
#[cfg(feature = "mock")]
mod tests {
use super::*;
use sea_orm::{DatabaseBackend, MockDatabase, Transaction};

#[tokio::test]
async fn find_by_user_returns_all_records_associated_with_user() -> Result<(), Error> {
let db = MockDatabase::new(DatabaseBackend::Postgres)
// .append_query_results(coaching_relationships)
.into_connection();

let user_id = 1;
let _ = find_by_user(&db, user_id).await;

assert_eq!(
db.into_transaction_log(),
[Transaction::from_sql_and_values(
DatabaseBackend::Postgres,
r#"SELECT "coaching_relationships"."id", "coaching_relationships"."external_id", "coaching_relationships"."organization_id", "coaching_relationships"."coach_id", "coaching_relationships"."coachee_id", "coaching_relationships"."created_at", "coaching_relationships"."updated_at" FROM "refactor_platform"."coaching_relationships" WHERE "coaching_relationships"."coach_id" = $1 OR "coaching_relationships"."coachee_id" = $2"#,
[user_id.into(), user_id.into()]
)]
);

Ok(())
}
}
2 changes: 2 additions & 0 deletions entity_api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub struct Error {

#[derive(Debug, Serialize)]
pub enum EntityApiErrorCode {
// Invalid search term
InvalidQueryTerm,
// Record not found
RecordNotFound,
// Record not updated
Expand Down
111 changes: 108 additions & 3 deletions entity_api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,115 @@
use sea_orm::DatabaseConnection;
use chrono::Utc;
use password_auth::generate_hash;
use sea_orm::{prelude::Uuid, ActiveModelTrait, DatabaseConnection, Set};

use entity::coaching_relationships;
use entity::organizations;
use entity::users;

pub mod coaching_relationship;
pub mod error;
pub mod organization;
pub mod user;

pub async fn seed_database(db: &DatabaseConnection) {
organization::seed_database(db).await;
user::seed_database(db).await;
let now = Utc::now();

let jim_hodapp: users::ActiveModel = users::ActiveModel {
external_id: Set(Uuid::new_v4()),
email: Set("[email protected]".to_owned()),
first_name: Set(Some("Jim".to_owned())),
last_name: Set(Some("Hodapp".to_owned())),
display_name: Set(Some("Jim H".to_owned())),
password: Set(generate_hash("password")),
github_username: Set(Some("jhodapp".to_owned())),
github_profile_url: Set(Some("https://github.com/jhodapp".to_owned())),
created_at: Set(now.into()),
updated_at: Set(now.into()),
..Default::default()
}
.save(db)
.await
.unwrap();

let caleb_bourg: users::ActiveModel = users::ActiveModel {
external_id: Set(Uuid::new_v4()),
email: Set("[email protected]".to_owned()),
first_name: Set(Some("Caleb".to_owned())),
last_name: Set(Some("Bourg".to_owned())),
display_name: Set(Some("cbourg2".to_owned())),
password: Set(generate_hash("password")),
github_username: Set(Some("calebbourg".to_owned())),
github_profile_url: Set(Some("https://github.com/calebbourg".to_owned())),
created_at: Set(now.into()),
updated_at: Set(now.into()),
..Default::default()
}
.save(db)
.await
.unwrap();

let other_user: users::ActiveModel = users::ActiveModel {
external_id: Set(Uuid::new_v4()),
email: Set("[email protected]".to_owned()),
first_name: Set(Some("Other".to_owned())),
last_name: Set(Some("User".to_owned())),
display_name: Set(Some("Other U.".to_owned())),
password: Set(generate_hash("password")),
github_username: Set(None),
github_profile_url: Set(None),
created_at: Set(now.into()),
updated_at: Set(now.into()),
..Default::default()
}
.save(db)
.await
.unwrap();

let jim_hodapp_coaching = organizations::ActiveModel {
external_id: Set(Uuid::new_v4()),
name: Set("Jim Hodapp's Coaching".to_owned()),
created_at: Set(now.into()),
updated_at: Set(now.into()),
..Default::default()
}
.save(db)
.await
.unwrap();

let jim_hodapp_other_org = organizations::ActiveModel {
external_id: Set(Uuid::new_v4()),
name: Set("Jim Hodapp's Other Organization".to_owned()),
created_at: Set(now.into()),
updated_at: Set(now.into()),
..Default::default()
}
.save(db)
.await
.unwrap();

coaching_relationships::ActiveModel {
coach_id: Set(jim_hodapp.id.clone().unwrap()),
coachee_id: Set(caleb_bourg.id.clone().unwrap()),
organization_id: Set(jim_hodapp_coaching.id.unwrap()),
external_id: Set(Uuid::new_v4()),
created_at: Set(now.into()),
updated_at: Set(now.into()),
..Default::default()
}
.save(db)
.await
.unwrap();

coaching_relationships::ActiveModel {
coach_id: Set(jim_hodapp.id.clone().unwrap()),
coachee_id: Set(other_user.id.clone().unwrap()),
organization_id: Set(jim_hodapp_other_org.id.unwrap()),
external_id: Set(Uuid::new_v4()),
created_at: Set(now.into()),
updated_at: Set(now.into()),
..Default::default()
}
.save(db)
.await
.unwrap();
}
Loading

0 comments on commit 168ee31

Please sign in to comment.