registry: extract storage domain modules
This commit is contained in:
@@ -0,0 +1,319 @@
|
||||
use super::*;
|
||||
|
||||
impl PostgresRegistry {
|
||||
pub async fn list_workspaces(&self) -> Result<Vec<WorkspaceRecord>, RegistryError> {
|
||||
let rows = sqlx::query(
|
||||
"select
|
||||
id,
|
||||
slug,
|
||||
display_name,
|
||||
status,
|
||||
settings_json,
|
||||
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
|
||||
to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as updated_at
|
||||
from workspaces
|
||||
order by slug asc",
|
||||
)
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
|
||||
rows.iter().map(map_workspace_record).collect()
|
||||
}
|
||||
|
||||
pub async fn list_workspaces_for_user(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
) -> Result<Vec<WorkspaceMembershipRecord>, RegistryError> {
|
||||
let rows = sqlx::query(
|
||||
"select
|
||||
w.id,
|
||||
w.slug,
|
||||
w.display_name,
|
||||
w.status,
|
||||
w.settings_json,
|
||||
to_char(w.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
|
||||
to_char(w.updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as updated_at,
|
||||
m.role
|
||||
from memberships m
|
||||
join workspaces w on w.id = m.workspace_id
|
||||
where m.user_id = $1
|
||||
order by w.slug asc",
|
||||
)
|
||||
.bind(user_id.as_str())
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
|
||||
rows.iter().map(map_workspace_membership_record).collect()
|
||||
}
|
||||
|
||||
pub async fn list_memberships(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
) -> Result<Vec<MembershipRecord>, RegistryError> {
|
||||
let rows = sqlx::query(
|
||||
"select
|
||||
m.workspace_id,
|
||||
m.user_id,
|
||||
m.role,
|
||||
to_char(m.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
|
||||
u.email,
|
||||
u.display_name,
|
||||
u.status,
|
||||
to_char(u.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as user_created_at
|
||||
from memberships m
|
||||
join users u on u.id = m.user_id
|
||||
where m.workspace_id = $1
|
||||
order by u.email asc",
|
||||
)
|
||||
.bind(workspace_id.as_str())
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
|
||||
rows.iter().map(map_membership_record).collect()
|
||||
}
|
||||
|
||||
pub async fn update_membership_role(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
user_id: &UserId,
|
||||
role: MembershipRole,
|
||||
) -> Result<(), RegistryError> {
|
||||
let result = sqlx::query(
|
||||
"update memberships
|
||||
set role = $3
|
||||
where workspace_id = $1 and user_id = $2",
|
||||
)
|
||||
.bind(workspace_id.as_str())
|
||||
.bind(user_id.as_str())
|
||||
.bind(serialize_enum_text(&role, "role")?)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
if result.rows_affected() == 0 {
|
||||
return Err(RegistryError::MembershipNotFound {
|
||||
workspace_id: workspace_id.as_str().to_owned(),
|
||||
user_id: user_id.as_str().to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_membership(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
user_id: &UserId,
|
||||
) -> Result<(), RegistryError> {
|
||||
let result = sqlx::query(
|
||||
"delete from memberships
|
||||
where workspace_id = $1 and user_id = $2",
|
||||
)
|
||||
.bind(workspace_id.as_str())
|
||||
.bind(user_id.as_str())
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
if result.rows_affected() == 0 {
|
||||
return Err(RegistryError::MembershipNotFound {
|
||||
workspace_id: workspace_id.as_str().to_owned(),
|
||||
user_id: user_id.as_str().to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list_invitations(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
) -> Result<Vec<InvitationRecord>, RegistryError> {
|
||||
let rows = sqlx::query(
|
||||
"select
|
||||
id,
|
||||
workspace_id,
|
||||
email,
|
||||
role,
|
||||
status,
|
||||
token_hash,
|
||||
to_char(expires_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as expires_at,
|
||||
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at
|
||||
from invitation_tokens
|
||||
where workspace_id = $1
|
||||
order by created_at desc",
|
||||
)
|
||||
.bind(workspace_id.as_str())
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
|
||||
rows.iter().map(map_invitation_record).collect()
|
||||
}
|
||||
|
||||
pub async fn create_invitation(
|
||||
&self,
|
||||
request: CreateInvitationRequest<'_>,
|
||||
) -> Result<(), RegistryError> {
|
||||
sqlx::query(
|
||||
"insert into invitation_tokens (
|
||||
id,
|
||||
workspace_id,
|
||||
email,
|
||||
role,
|
||||
status,
|
||||
token_hash,
|
||||
expires_at,
|
||||
created_at
|
||||
) values (
|
||||
$1, $2, $3, $4, $5, $6, $7::timestamptz, $8::timestamptz
|
||||
)",
|
||||
)
|
||||
.bind(request.invitation.id.as_str())
|
||||
.bind(request.invitation.workspace_id.as_str())
|
||||
.bind(&request.invitation.email)
|
||||
.bind(serialize_enum_text(&request.invitation.role, "role")?)
|
||||
.bind(serialize_enum_text(&request.invitation.status, "status")?)
|
||||
.bind(&request.invitation.token_hash)
|
||||
.bind(&request.invitation.expires_at)
|
||||
.bind(&request.invitation.created_at)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_invitation(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
invitation_id: &InvitationId,
|
||||
) -> Result<(), RegistryError> {
|
||||
let result =
|
||||
sqlx::query("delete from invitation_tokens where workspace_id = $1 and id = $2")
|
||||
.bind(workspace_id.as_str())
|
||||
.bind(invitation_id.as_str())
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
if result.rows_affected() == 0 {
|
||||
return Err(RegistryError::InvitationNotFound {
|
||||
invitation_id: invitation_id.as_str().to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_workspace(&self, workspace_id: &WorkspaceId) -> Result<(), RegistryError> {
|
||||
let result = sqlx::query("delete from workspaces where id = $1")
|
||||
.bind(workspace_id.as_str())
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
|
||||
if result.rows_affected() == 0 {
|
||||
return Err(RegistryError::WorkspaceNotFound {
|
||||
workspace_id: workspace_id.as_str().to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_workspace(
|
||||
&self,
|
||||
workspace_id: &WorkspaceId,
|
||||
) -> Result<Option<WorkspaceRecord>, RegistryError> {
|
||||
let row = sqlx::query(
|
||||
"select
|
||||
id,
|
||||
slug,
|
||||
display_name,
|
||||
status,
|
||||
settings_json,
|
||||
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
|
||||
to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as updated_at
|
||||
from workspaces
|
||||
where id = $1",
|
||||
)
|
||||
.bind(workspace_id.as_str())
|
||||
.fetch_optional(&self.pool)
|
||||
.await?;
|
||||
|
||||
row.as_ref().map(map_workspace_record).transpose()
|
||||
}
|
||||
|
||||
pub async fn create_workspace(
|
||||
&self,
|
||||
request: CreateWorkspaceRequest<'_>,
|
||||
) -> Result<(), RegistryError> {
|
||||
let result = sqlx::query(
|
||||
"insert into workspaces (
|
||||
id,
|
||||
slug,
|
||||
display_name,
|
||||
status,
|
||||
settings_json,
|
||||
created_at,
|
||||
updated_at
|
||||
) values (
|
||||
$1, $2, $3, $4, $5, $6::timestamptz, $7::timestamptz
|
||||
)",
|
||||
)
|
||||
.bind(request.workspace.id.as_str())
|
||||
.bind(&request.workspace.slug)
|
||||
.bind(&request.workspace.display_name)
|
||||
.bind(serialize_enum_text(&request.workspace.status, "status")?)
|
||||
.bind(Json(request.workspace.settings.clone()))
|
||||
.bind(&request.workspace.created_at)
|
||||
.bind(&request.workspace.updated_at)
|
||||
.execute(&self.pool)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(sqlx::Error::Database(error))
|
||||
if error.constraint() == Some("workspaces_slug_key") =>
|
||||
{
|
||||
Err(RegistryError::WorkspaceSlugAlreadyExists {
|
||||
slug: request.workspace.slug.clone(),
|
||||
})
|
||||
}
|
||||
Err(error) => Err(RegistryError::Storage(error)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_workspace(
|
||||
&self,
|
||||
request: UpdateWorkspaceRequest<'_>,
|
||||
) -> Result<(), RegistryError> {
|
||||
let result = sqlx::query(
|
||||
"update workspaces
|
||||
set slug = $1,
|
||||
display_name = $2,
|
||||
status = $3,
|
||||
settings_json = $4,
|
||||
updated_at = $5::timestamptz
|
||||
where id = $6",
|
||||
)
|
||||
.bind(&request.workspace.slug)
|
||||
.bind(&request.workspace.display_name)
|
||||
.bind(serialize_enum_text(&request.workspace.status, "status")?)
|
||||
.bind(Json(request.workspace.settings.clone()))
|
||||
.bind(&request.workspace.updated_at)
|
||||
.bind(request.workspace.id.as_str())
|
||||
.execute(&self.pool)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(result) if result.rows_affected() == 0 => Err(RegistryError::WorkspaceNotFound {
|
||||
workspace_id: request.workspace.id.as_str().to_owned(),
|
||||
}),
|
||||
Ok(_) => Ok(()),
|
||||
Err(sqlx::Error::Database(error))
|
||||
if error.constraint() == Some("workspaces_slug_key") =>
|
||||
{
|
||||
Err(RegistryError::WorkspaceSlugAlreadyExists {
|
||||
slug: request.workspace.slug.clone(),
|
||||
})
|
||||
}
|
||||
Err(error) => Err(RegistryError::Storage(error)),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user