use derive_builder::Builder;
use serde::Serialize;
use sqlx::{postgres::types::Oid, query, query_as, types::Json};
use uuid::Uuid;
use crate::{client::AppDbClient, expression::PgExpressionAny};
/// A portal is a derivative representation of a Postgres relation.
#[derive(Clone, Debug, Serialize)]
pub struct Portal {
/// Primary key (defaults to UUIDv7).
pub id: Uuid,
/// Human friendly name for portal.
pub name: String,
/// Workspace to which this portal belongs.
pub workspace_id: Uuid,
/// OID of the underlying Postgres relation. Currently, this is expected
/// to be a normal table, not a view, etc.
pub class_oid: Oid,
/// JSONB-encoded expression to use for filtering rows in the web-based
/// table view.
pub table_filter: Json>,
}
impl Portal {
/// Build an insert statement to create a new portal.
pub fn insert() -> InsertablePortalBuilder {
InsertablePortalBuilder::default()
}
/// Build an update statement to alter an existing portal.
pub fn update() -> PortalUpdateBuilder {
PortalUpdateBuilder::default()
}
/// Build a single-field query by portal ID.
pub fn with_id(id: Uuid) -> WithIdQuery {
WithIdQuery { id }
}
/// Build a query by workspace ID and relation OID.
pub fn belonging_to_workspace(workspace_id: Uuid) -> BelongingToWorkspaceQuery {
BelongingToWorkspaceQuery { workspace_id }
}
}
#[derive(Clone, Debug)]
pub struct WithIdQuery {
id: Uuid,
}
impl WithIdQuery {
pub async fn fetch_optional(
self,
app_db: &mut AppDbClient,
) -> Result , sqlx::Error> {
query_as!(
Portal,
r#"
select
id,
name,
workspace_id,
class_oid,
table_filter as "table_filter: Json >"
from portals
where id = $1
"#,
self.id
)
.fetch_optional(&mut *app_db.conn)
.await
}
pub async fn fetch_one(self, app_db: &mut AppDbClient) -> Result {
query_as!(
Portal,
r#"
select
id,
name,
workspace_id,
class_oid,
table_filter as "table_filter: Json>"
from portals
where id = $1
"#,
self.id
)
.fetch_one(&mut *app_db.conn)
.await
}
}
#[derive(Clone, Debug)]
pub struct BelongingToWorkspaceQuery {
workspace_id: Uuid,
}
impl BelongingToWorkspaceQuery {
pub fn belonging_to_rel(self, rel_oid: Oid) -> BelongingToRelQuery {
BelongingToRelQuery {
workspace_id: self.workspace_id,
rel_oid,
}
}
}
#[derive(Clone, Debug)]
pub struct BelongingToRelQuery {
workspace_id: Uuid,
rel_oid: Oid,
}
impl BelongingToRelQuery {
pub async fn fetch_all(self, app_db: &mut AppDbClient) -> Result, sqlx::Error> {
query_as!(
Portal,
r#"
select
id,
name,
workspace_id,
class_oid,
table_filter as "table_filter: Json>"
from portals
where workspace_id = $1 and class_oid = $2
"#,
self.workspace_id,
self.rel_oid
)
.fetch_all(&mut *app_db.conn)
.await
}
}
#[derive(Clone, Debug, Serialize, sqlx::Type)]
#[sqlx(type_name = "lens_display_type", rename_all = "lowercase")]
pub enum LensDisplayType {
Table,
}
#[derive(Builder, Clone, Debug)]
pub struct InsertablePortal {
name: String,
workspace_id: Uuid,
class_oid: Oid,
}
impl InsertablePortal {
pub async fn execute(self, app_db: &mut AppDbClient) -> Result {
query_as!(
Portal,
r#"
insert into portals
(workspace_id, class_oid, name)
values ($1, $2, $3)
returning
id,
name,
workspace_id,
class_oid,
table_filter as "table_filter: Json>"
"#,
self.workspace_id,
self.class_oid,
self.name,
)
.fetch_one(&mut *app_db.conn)
.await
}
}
#[derive(Builder, Clone, Debug)]
pub struct PortalUpdate {
id: Uuid,
#[builder(setter(strip_option = true))]
filter: Option >,
}
impl PortalUpdate {
pub async fn execute(self, app_db: &mut AppDbClient) -> Result<(), sqlx::Error> {
if let Some(filter) = self.filter {
query!(
"update portals set table_filter = $1 where id = $2",
Json(filter) as Json >,
self.id
)
.execute(&mut *app_db.conn)
.await?;
}
Ok(())
}
}