Add a role column to the database and start using it

We cannot yet stop using `admin` because stable will continue writing
it.
This commit is contained in:
Conrad Irwin 2023-10-11 16:34:11 -06:00
parent be1800884e
commit 690d9fb971
10 changed files with 76 additions and 30 deletions

View file

@ -226,6 +226,7 @@ CREATE TABLE "channel_members" (
"channel_id" INTEGER NOT NULL REFERENCES channels (id) ON DELETE CASCADE,
"user_id" INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
"admin" BOOLEAN NOT NULL DEFAULT false,
"role" VARCHAR,
"accepted" BOOLEAN NOT NULL DEFAULT false,
"updated_at" TIMESTAMP NOT NULL DEFAULT now
);

View file

@ -0,0 +1,4 @@
-- Add migration script here
ALTER TABLE channel_members ADD COLUMN role TEXT;
UPDATE channel_members SET role = CASE WHEN admin THEN 'admin' ELSE 'member' END;

View file

@ -80,3 +80,14 @@ id_type!(SignupId);
id_type!(UserId);
id_type!(ChannelBufferCollaboratorId);
id_type!(FlagId);
#[derive(Eq, PartialEq, Copy, Clone, Debug, EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "String(None)")]
pub enum ChannelRole {
#[sea_orm(string_value = "admin")]
Admin,
#[sea_orm(string_value = "member")]
Member,
#[sea_orm(string_value = "guest")]
Guest,
}

View file

@ -74,11 +74,12 @@ impl Database {
}
channel_member::ActiveModel {
id: ActiveValue::NotSet,
channel_id: ActiveValue::Set(channel.id),
user_id: ActiveValue::Set(creator_id),
accepted: ActiveValue::Set(true),
admin: ActiveValue::Set(true),
..Default::default()
role: ActiveValue::Set(Some(ChannelRole::Admin)),
}
.insert(&*tx)
.await?;
@ -160,18 +161,19 @@ impl Database {
channel_id: ChannelId,
invitee_id: UserId,
inviter_id: UserId,
is_admin: bool,
role: ChannelRole,
) -> Result<()> {
self.transaction(move |tx| async move {
self.check_user_is_channel_admin(channel_id, inviter_id, &*tx)
.await?;
channel_member::ActiveModel {
id: ActiveValue::NotSet,
channel_id: ActiveValue::Set(channel_id),
user_id: ActiveValue::Set(invitee_id),
accepted: ActiveValue::Set(false),
admin: ActiveValue::Set(is_admin),
..Default::default()
admin: ActiveValue::Set(role == ChannelRole::Admin),
role: ActiveValue::Set(Some(role)),
}
.insert(&*tx)
.await?;
@ -417,7 +419,13 @@ impl Database {
let channels_with_admin_privileges = channel_memberships
.iter()
.filter_map(|membership| membership.admin.then_some(membership.channel_id))
.filter_map(|membership| {
if membership.role == Some(ChannelRole::Admin) || membership.admin {
Some(membership.channel_id)
} else {
None
}
})
.collect();
let graph = self
@ -470,12 +478,12 @@ impl Database {
.await
}
pub async fn set_channel_member_admin(
pub async fn set_channel_member_role(
&self,
channel_id: ChannelId,
from: UserId,
for_user: UserId,
admin: bool,
role: ChannelRole,
) -> Result<()> {
self.transaction(|tx| async move {
self.check_user_is_channel_admin(channel_id, from, &*tx)
@ -488,7 +496,8 @@ impl Database {
.and(channel_member::Column::UserId.eq(for_user)),
)
.set(channel_member::ActiveModel {
admin: ActiveValue::set(admin),
admin: ActiveValue::set(role == ChannelRole::Admin),
role: ActiveValue::set(Some(role)),
..Default::default()
})
.exec(&*tx)
@ -516,6 +525,7 @@ impl Database {
enum QueryMemberDetails {
UserId,
Admin,
Role,
IsDirectMember,
Accepted,
}
@ -528,6 +538,7 @@ impl Database {
.select_only()
.column(channel_member::Column::UserId)
.column(channel_member::Column::Admin)
.column(channel_member::Column::Role)
.column_as(
channel_member::Column::ChannelId.eq(channel_id),
QueryMemberDetails::IsDirectMember,
@ -540,9 +551,10 @@ impl Database {
let mut rows = Vec::<proto::ChannelMember>::new();
while let Some(row) = stream.next().await {
let (user_id, is_admin, is_direct_member, is_invite_accepted): (
let (user_id, is_admin, channel_role, is_direct_member, is_invite_accepted): (
UserId,
bool,
Option<ChannelRole>,
bool,
bool,
) = row?;
@ -558,7 +570,7 @@ impl Database {
if last_row.user_id == user_id {
if is_direct_member {
last_row.kind = kind;
last_row.admin = is_admin;
last_row.admin = channel_role == Some(ChannelRole::Admin) || is_admin;
}
continue;
}
@ -566,7 +578,7 @@ impl Database {
rows.push(proto::ChannelMember {
user_id,
kind,
admin: is_admin,
admin: channel_role == Some(ChannelRole::Admin) || is_admin,
});
}

View file

@ -1,7 +1,7 @@
use crate::db::{channel_member, ChannelId, ChannelMemberId, UserId};
use crate::db::{channel_member, ChannelId, ChannelMemberId, ChannelRole, UserId};
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, Default, PartialEq, Eq, DeriveEntityModel)]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "channel_members")]
pub struct Model {
#[sea_orm(primary_key)]
@ -10,6 +10,8 @@ pub struct Model {
pub user_id: UserId,
pub accepted: bool,
pub admin: bool,
// only optional while migrating
pub role: Option<ChannelRole>,
}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -56,7 +56,7 @@ async fn test_channel_buffers(db: &Arc<Database>) {
let zed_id = db.create_root_channel("zed", a_id).await.unwrap();
db.invite_channel_member(zed_id, b_id, a_id, false)
db.invite_channel_member(zed_id, b_id, a_id, ChannelRole::Member)
.await
.unwrap();
@ -211,7 +211,7 @@ async fn test_channel_buffers_last_operations(db: &Database) {
.await
.unwrap();
db.invite_channel_member(channel, observer_id, user_id, false)
db.invite_channel_member(channel, observer_id, user_id, ChannelRole::Member)
.await
.unwrap();
db.respond_to_channel_invite(channel, observer_id, true)

View file

@ -8,7 +8,7 @@ use crate::{
db::{
queries::channels::ChannelGraph,
tests::{graph, TEST_RELEASE_CHANNEL},
ChannelId, Database, NewUserParams,
ChannelId, ChannelRole, Database, NewUserParams,
},
test_both_dbs,
};
@ -50,7 +50,7 @@ async fn test_channels(db: &Arc<Database>) {
// Make sure that people cannot read channels they haven't been invited to
assert!(db.get_channel(zed_id, b_id).await.unwrap().is_none());
db.invite_channel_member(zed_id, b_id, a_id, false)
db.invite_channel_member(zed_id, b_id, a_id, ChannelRole::Member)
.await
.unwrap();
@ -125,9 +125,13 @@ async fn test_channels(db: &Arc<Database>) {
);
// Update member permissions
let set_subchannel_admin = db.set_channel_member_admin(crdb_id, a_id, b_id, true).await;
let set_subchannel_admin = db
.set_channel_member_role(crdb_id, a_id, b_id, ChannelRole::Admin)
.await;
assert!(set_subchannel_admin.is_err());
let set_channel_admin = db.set_channel_member_admin(zed_id, a_id, b_id, true).await;
let set_channel_admin = db
.set_channel_member_role(zed_id, a_id, b_id, ChannelRole::Admin)
.await;
assert!(set_channel_admin.is_ok());
let result = db.get_channels_for_user(b_id).await.unwrap();
@ -284,13 +288,13 @@ async fn test_channel_invites(db: &Arc<Database>) {
let channel_1_2 = db.create_root_channel("channel_2", user_1).await.unwrap();
db.invite_channel_member(channel_1_1, user_2, user_1, false)
db.invite_channel_member(channel_1_1, user_2, user_1, ChannelRole::Member)
.await
.unwrap();
db.invite_channel_member(channel_1_2, user_2, user_1, false)
db.invite_channel_member(channel_1_2, user_2, user_1, ChannelRole::Member)
.await
.unwrap();
db.invite_channel_member(channel_1_1, user_3, user_1, true)
db.invite_channel_member(channel_1_1, user_3, user_1, ChannelRole::Admin)
.await
.unwrap();

View file

@ -1,5 +1,5 @@
use crate::{
db::{Database, MessageId, NewUserParams},
db::{ChannelRole, Database, MessageId, NewUserParams},
test_both_dbs,
};
use std::sync::Arc;
@ -155,7 +155,7 @@ async fn test_channel_message_new_notification(db: &Arc<Database>) {
let channel_2 = db.create_channel("channel-2", None, user).await.unwrap();
db.invite_channel_member(channel_1, observer, user, false)
db.invite_channel_member(channel_1, observer, user, ChannelRole::Member)
.await
.unwrap();
@ -163,7 +163,7 @@ async fn test_channel_message_new_notification(db: &Arc<Database>) {
.await
.unwrap();
db.invite_channel_member(channel_2, observer, user, false)
db.invite_channel_member(channel_2, observer, user, ChannelRole::Member)
.await
.unwrap();

View file

@ -3,8 +3,8 @@ mod connection_pool;
use crate::{
auth,
db::{
self, BufferId, ChannelId, ChannelsForUser, Database, MessageId, ProjectId, RoomId,
ServerId, User, UserId,
self, BufferId, ChannelId, ChannelRole, ChannelsForUser, Database, MessageId, ProjectId,
RoomId, ServerId, User, UserId,
},
executor::Executor,
AppState, Result,
@ -2282,7 +2282,12 @@ async fn invite_channel_member(
let db = session.db().await;
let channel_id = ChannelId::from_proto(request.channel_id);
let invitee_id = UserId::from_proto(request.user_id);
db.invite_channel_member(channel_id, invitee_id, session.user_id, request.admin)
let role = if request.admin {
ChannelRole::Admin
} else {
ChannelRole::Member
};
db.invite_channel_member(channel_id, invitee_id, session.user_id, role)
.await?;
let (channel, _) = db
@ -2342,7 +2347,12 @@ async fn set_channel_member_admin(
let db = session.db().await;
let channel_id = ChannelId::from_proto(request.channel_id);
let member_id = UserId::from_proto(request.user_id);
db.set_channel_member_admin(channel_id, session.user_id, member_id, request.admin)
let role = if request.admin {
ChannelRole::Admin
} else {
ChannelRole::Member
};
db.set_channel_member_role(channel_id, session.user_id, member_id, role)
.await?;
let (channel, has_accepted) = db

View file

@ -1,3 +1,5 @@
use crate::db::ChannelRole;
use super::{run_randomized_test, RandomizedTest, TestClient, TestError, TestServer, UserTestPlan};
use anyhow::Result;
use async_trait::async_trait;
@ -50,7 +52,7 @@ impl RandomizedTest for RandomChannelBufferTest {
.await
.unwrap();
for user in &users[1..] {
db.invite_channel_member(id, user.user_id, users[0].user_id, false)
db.invite_channel_member(id, user.user_id, users[0].user_id, ChannelRole::Member)
.await
.unwrap();
db.respond_to_channel_invite(id, user.user_id, true)