Make sure room names are unique

This commit is contained in:
2024-09-25 13:13:18 +02:00
parent 9dec492b2f
commit 83857507ea
4 changed files with 74 additions and 23 deletions

View File

@@ -1,11 +1,9 @@
from logging.config import fileConfig
from reflector.settings import settings
from reflector.db import metadata
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
from reflector.db import metadata
from reflector.settings import settings
from sqlalchemy import engine_from_config, pool
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
@@ -45,6 +43,7 @@ def run_migrations_offline() -> None:
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
render_as_batch=True,
)
with context.begin_transaction():
@@ -67,7 +66,9 @@ def run_migrations_online() -> None:
)
with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=target_metadata)
context.configure(
connection=connection, target_metadata=target_metadata, render_as_batch=True
)
with context.begin_transaction():
context.run_migrations()

View File

@@ -0,0 +1,34 @@
"""Unique room names
Revision ID: 0925da921477
Revises: 764ce6db4388
Create Date: 2024-09-24 16:12:56.944133
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "0925da921477"
down_revision: Union[str, None] = "764ce6db4388"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("room", schema=None) as batch_op:
batch_op.create_unique_constraint("uq_room_name", ["name"])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("room", schema=None) as batch_op:
batch_op.drop_constraint("uq_room_name", type_="unique")
# ### end Alembic commands ###

View File

@@ -1,4 +1,5 @@
from datetime import datetime
from sqlite3 import IntegrityError
from typing import Literal
import sqlalchemy
@@ -12,7 +13,7 @@ rooms = sqlalchemy.Table(
"room",
metadata,
sqlalchemy.Column("id", sqlalchemy.String, primary_key=True),
sqlalchemy.Column("name", sqlalchemy.String, nullable=False),
sqlalchemy.Column("name", sqlalchemy.String, nullable=False, unique=True),
sqlalchemy.Column("user_id", sqlalchemy.String, nullable=False),
sqlalchemy.Column("created_at", sqlalchemy.DateTime, nullable=False),
sqlalchemy.Column(
@@ -113,7 +114,10 @@ class RoomController:
recording_trigger=recording_trigger,
)
query = rooms.insert().values(**room.model_dump())
await database.execute(query)
try:
await database.execute(query)
except IntegrityError:
raise HTTPException(status_code=400, detail="Room name is not unique")
return room
async def update(self, room: Room, values: dict, mutate=True):
@@ -121,7 +125,11 @@ class RoomController:
Update a room fields with key/values in values
"""
query = rooms.update().where(rooms.c.id == room.id).values(**values)
await database.execute(query)
try:
await database.execute(query)
except IntegrityError:
raise HTTPException(status_code=400, detail="Room name is not unique")
if mutate:
for key, value in values.items():
setattr(room, key, value)

View File

@@ -36,11 +36,7 @@ import { FaEllipsisVertical, FaTrash, FaPencil, FaLink } from "react-icons/fa6";
import useApi from "../../lib/useApi";
import useRoomList from "./useRoomList";
import { Select, Options, OptionBase } from "chakra-react-select";
interface Stream {
stream_id: number;
name: string;
}
import { ApiError } from "../../api";
interface SelectOption extends OptionBase {
label: string;
@@ -81,8 +77,7 @@ export default function RoomsList() {
const { loading, response, refetch } = useRoomList(page);
const [streams, setStreams] = useState<Stream[]>([]);
const [topics, setTopics] = useState<Topic[]>([]);
const [error, setError] = useState("");
const [nameError, setNameError] = useState("");
const [linkCopied, setLinkCopied] = useState("");
interface Stream {
stream_id: number;
@@ -151,7 +146,7 @@ export default function RoomsList() {
const handleSaveRoom = async () => {
try {
if (RESERVED_PATHS.includes(room.name)) {
setError("This room name is reserved. Please choose another name.");
setNameError("This room name is reserved. Please choose another name.");
return;
}
@@ -180,12 +175,24 @@ export default function RoomsList() {
setRoom(roomInitialState);
setIsEditing(false);
setEditRoomId("");
setError("");
setNameError("");
refetch();
onClose();
} catch (err) {
console.error(err);
if (err instanceof ApiError) {
const apiError = err as ApiError;
if (
apiError.status === 400 &&
(apiError.body as any).detail == "Room name is not unique"
) {
setNameError(
"This room name is already taken. Please choose a different name.",
);
} else {
setNameError("An error occurred. Please try again.");
}
}
}
onClose();
};
const handleEditRoom = (roomId, roomData) => {
@@ -201,6 +208,7 @@ export default function RoomsList() {
});
setEditRoomId(roomId);
setIsEditing(true);
setNameError("");
onOpen();
};
@@ -222,7 +230,7 @@ export default function RoomsList() {
.replace(/[^a-zA-Z0-9\s-]/g, "")
.replace(/\s+/g, "-")
.toLowerCase();
setError("");
setNameError("");
}
setRoom({
...room,
@@ -254,7 +262,7 @@ export default function RoomsList() {
onClick={() => {
setIsEditing(false);
setRoom(roomInitialState);
setError("");
setNameError("");
onOpen();
}}
>
@@ -277,7 +285,7 @@ export default function RoomsList() {
<FormHelperText>
No spaces or special characters allowed
</FormHelperText>
{error && <Text color="red.500">{error}</Text>}
{nameError && <Text color="red.500">{nameError}</Text>}
</FormControl>
<FormControl mt={4}>