118 lines
3.7 KiB
Rust
118 lines
3.7 KiB
Rust
use anyhow::{Context as _, Result};
|
|
use derive_builder::Builder;
|
|
use diesel::{
|
|
dsl::{auto_type, insert_into, AsSelect, Eq},
|
|
pg::Pg,
|
|
prelude::*,
|
|
};
|
|
use uuid::Uuid;
|
|
|
|
use crate::{
|
|
channel_selections::{self, ChannelSelection},
|
|
channels::{self, Channel},
|
|
schema::projects,
|
|
};
|
|
|
|
pub use crate::schema::projects::{dsl, table};
|
|
|
|
pub const DEFAULT_PROJECT_NAME: &str = "default";
|
|
|
|
/// A project maps approximately to an application service, and allows messages
|
|
/// to be directed to an adjustable set of output channels.
|
|
#[derive(Clone, Debug, Identifiable, Insertable, Queryable, Selectable)]
|
|
#[diesel(table_name = projects)]
|
|
pub struct Project {
|
|
pub id: Uuid,
|
|
pub team_id: Uuid,
|
|
pub name: String,
|
|
}
|
|
|
|
impl Project {
|
|
#[auto_type(no_type_alias)]
|
|
pub fn all() -> _ {
|
|
let select: AsSelect<Project, Pg> = Project::as_select();
|
|
table.select(select)
|
|
}
|
|
|
|
#[auto_type(no_type_alias)]
|
|
pub fn with_id<'a>(project_id: &'a Uuid) -> _ {
|
|
dsl::id.eq(project_id)
|
|
}
|
|
|
|
#[auto_type(no_type_alias)]
|
|
pub fn with_team<'a>(team_id: &'a Uuid) -> _ {
|
|
dsl::team_id.eq(team_id)
|
|
}
|
|
|
|
#[auto_type(no_type_alias)]
|
|
pub fn with_name<'a>(name: &'a str) -> _ {
|
|
dsl::name.eq(name)
|
|
}
|
|
|
|
#[auto_type(no_type_alias)]
|
|
pub fn selected_channels(&self) -> _ {
|
|
let select: AsSelect<Channel, Pg> = Channel::as_select();
|
|
let project_filter: Eq<channel_selections::dsl::project_id, &Uuid> =
|
|
ChannelSelection::with_project(&self.id);
|
|
channels::table
|
|
.inner_join(channel_selections::table)
|
|
.filter(project_filter)
|
|
.select(select)
|
|
}
|
|
|
|
/// Lazily fetch a project from the database. That is, query for it, and
|
|
/// insert if it does not exist yet.
|
|
pub fn lazy_getter() -> LazyGetterBuilder {
|
|
LazyGetterBuilder::default()
|
|
}
|
|
}
|
|
|
|
#[derive(Builder)]
|
|
#[builder(pattern = "owned", setter(prefix = "with"))]
|
|
pub struct LazyGetter {
|
|
team_id: Uuid,
|
|
name: String,
|
|
}
|
|
|
|
impl LazyGetter {
|
|
pub fn execute(self, db_conn: &mut PgConnection) -> Result<Project> {
|
|
db_conn.transaction(move |conn| {
|
|
Ok(
|
|
if let Some(project) = Project::all()
|
|
.filter(Project::with_team(&self.team_id))
|
|
.filter(Project::with_name(&self.name))
|
|
.first(conn)
|
|
.optional()
|
|
.context("failed to load project")?
|
|
{
|
|
project
|
|
} else {
|
|
let default_channels = Channel::all()
|
|
.filter(Channel::with_team(&self.team_id))
|
|
.filter(Channel::where_enabled_by_default())
|
|
.load(conn)
|
|
.context("failed to load default channels")?;
|
|
let id: Uuid = Uuid::now_v7();
|
|
let project: Project = insert_into(table)
|
|
.values(Project {
|
|
id,
|
|
team_id: self.team_id,
|
|
name: self.name,
|
|
})
|
|
.get_result(conn)
|
|
.context("failed to insert project")?;
|
|
for channel in default_channels {
|
|
insert_into(channel_selections::table)
|
|
.values(ChannelSelection {
|
|
project_id: project.id,
|
|
channel_id: channel.id,
|
|
})
|
|
.execute(conn)
|
|
.context("failed to insert channel selection")?;
|
|
}
|
|
project
|
|
},
|
|
)
|
|
})
|
|
}
|
|
}
|