mirror of
https://github.com/livekit/livekit.git
synced 2026-08-27 22:34:25 +00:00
Enable Room.List to filter by specific names (#290)
This commit is contained in:
@@ -14,7 +14,7 @@ require (
|
||||
github.com/google/wire v0.5.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/livekit/protocol v0.11.3
|
||||
github.com/livekit/protocol v0.11.4
|
||||
github.com/magefile/mage v1.11.0
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
|
||||
@@ -132,8 +132,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lithammer/shortuuid/v3 v3.0.6 h1:pr15YQyvhiSX/qPxncFtqk+v4xLEpOZObbsY/mKrcvA=
|
||||
github.com/lithammer/shortuuid/v3 v3.0.6/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaKlRuDIi8tWWmAts=
|
||||
github.com/livekit/protocol v0.11.3 h1:Al2oOrRwFNmgpw7dUvvc0s+oju9DoRUWi7g7GwrDiZc=
|
||||
github.com/livekit/protocol v0.11.3/go.mod h1:YoHW9YbWbPnuVsgwBB4hAINKT+V68jmfh9zXBSSn6Wg=
|
||||
github.com/livekit/protocol v0.11.4 h1:p4ZA/OW+Wuc3q48DdeSFUAFaTHmqz62/C/LXM3D0/Z4=
|
||||
github.com/livekit/protocol v0.11.4/go.mod h1:YoHW9YbWbPnuVsgwBB4hAINKT+V68jmfh9zXBSSn6Wg=
|
||||
github.com/magefile/mage v1.11.0 h1:C/55Ywp9BpgVVclD3lRnSYCwXTYxmSppIgLeDYlNuls=
|
||||
github.com/magefile/mage v1.11.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
|
||||
@@ -29,7 +29,9 @@ type RoomStore interface {
|
||||
//counterfeiter:generate . RORoomStore
|
||||
type RORoomStore interface {
|
||||
LoadRoom(ctx context.Context, name string) (*livekit.Room, error)
|
||||
ListRooms(ctx context.Context) ([]*livekit.Room, error)
|
||||
// ListRooms returns currently active rooms. if names is not nil, it'll filter and return
|
||||
// only rooms that match
|
||||
ListRooms(ctx context.Context, names []string) ([]*livekit.Room, error)
|
||||
|
||||
LoadParticipant(ctx context.Context, roomName, identity string) (*livekit.ParticipantInfo, error)
|
||||
ListParticipants(ctx context.Context, roomName string) ([]*livekit.ParticipantInfo, error)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/livekit/protocol/livekit"
|
||||
"github.com/thoas/go-funk"
|
||||
)
|
||||
|
||||
// encapsulates CRUD operations for room settings
|
||||
@@ -47,12 +48,14 @@ func (p *LocalRoomStore) LoadRoom(_ context.Context, name string) (*livekit.Room
|
||||
return room, nil
|
||||
}
|
||||
|
||||
func (p *LocalRoomStore) ListRooms(_ context.Context) ([]*livekit.Room, error) {
|
||||
func (p *LocalRoomStore) ListRooms(_ context.Context, names []string) ([]*livekit.Room, error) {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
rooms := make([]*livekit.Room, 0, len(p.rooms))
|
||||
for _, r := range p.rooms {
|
||||
rooms = append(rooms, r)
|
||||
if names == nil || funk.Contains(names, r.Name) {
|
||||
rooms = append(rooms, r)
|
||||
}
|
||||
}
|
||||
return rooms, nil
|
||||
}
|
||||
|
||||
@@ -72,10 +72,25 @@ func (p *RedisRoomStore) LoadRoom(_ context.Context, name string) (*livekit.Room
|
||||
return &room, nil
|
||||
}
|
||||
|
||||
func (p *RedisRoomStore) ListRooms(_ context.Context) ([]*livekit.Room, error) {
|
||||
items, err := p.rc.HVals(p.ctx, RoomsKey).Result()
|
||||
if err != nil && err != redis.Nil {
|
||||
return nil, errors.Wrap(err, "could not get rooms")
|
||||
func (p *RedisRoomStore) ListRooms(_ context.Context, names []string) ([]*livekit.Room, error) {
|
||||
var items []string
|
||||
var err error
|
||||
if names == nil {
|
||||
items, err = p.rc.HVals(p.ctx, RoomsKey).Result()
|
||||
if err != nil && err != redis.Nil {
|
||||
return nil, errors.Wrap(err, "could not get rooms")
|
||||
}
|
||||
} else {
|
||||
var results []interface{}
|
||||
results, err = p.rc.HMGet(p.ctx, RoomsKey, names...).Result()
|
||||
if err != nil && err != redis.Nil {
|
||||
return nil, errors.Wrap(err, "could not get rooms by names")
|
||||
}
|
||||
for _, r := range results {
|
||||
if item, ok := r.(string); ok {
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rooms := make([]*livekit.Room, 0, len(items))
|
||||
|
||||
@@ -105,7 +105,7 @@ func (r *RoomManager) DeleteRoom(ctx context.Context, roomName string) error {
|
||||
func (r *RoomManager) CleanupRooms() error {
|
||||
// cleanup rooms that have been left for over a day
|
||||
ctx := context.Background()
|
||||
rooms, err := r.roomStore.ListRooms(ctx)
|
||||
rooms, err := r.roomStore.ListRooms(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -40,13 +40,17 @@ func (s *RoomService) CreateRoom(ctx context.Context, req *livekit.CreateRoomReq
|
||||
return
|
||||
}
|
||||
|
||||
func (s *RoomService) ListRooms(ctx context.Context, _ *livekit.ListRoomsRequest) (res *livekit.ListRoomsResponse, err error) {
|
||||
func (s *RoomService) ListRooms(ctx context.Context, req *livekit.ListRoomsRequest) (res *livekit.ListRoomsResponse, err error) {
|
||||
err = EnsureListPermission(ctx)
|
||||
if err != nil {
|
||||
return nil, twirpAuthError(err)
|
||||
}
|
||||
|
||||
rooms, err := s.roomStore.ListRooms(ctx)
|
||||
var names []string
|
||||
if len(req.Names) > 0 {
|
||||
names = req.Names
|
||||
}
|
||||
rooms, err := s.roomStore.ListRooms(ctx, names)
|
||||
if err != nil {
|
||||
// TODO: translate error codes to twirp
|
||||
return
|
||||
|
||||
@@ -50,10 +50,11 @@ type FakeRoomStore struct {
|
||||
result1 []*livekit.ParticipantInfo
|
||||
result2 error
|
||||
}
|
||||
ListRoomsStub func(context.Context) ([]*livekit.Room, error)
|
||||
ListRoomsStub func(context.Context, []string) ([]*livekit.Room, error)
|
||||
listRoomsMutex sync.RWMutex
|
||||
listRoomsArgsForCall []struct {
|
||||
arg1 context.Context
|
||||
arg2 []string
|
||||
}
|
||||
listRoomsReturns struct {
|
||||
result1 []*livekit.Room
|
||||
@@ -339,18 +340,24 @@ func (fake *FakeRoomStore) ListParticipantsReturnsOnCall(i int, result1 []*livek
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeRoomStore) ListRooms(arg1 context.Context) ([]*livekit.Room, error) {
|
||||
func (fake *FakeRoomStore) ListRooms(arg1 context.Context, arg2 []string) ([]*livekit.Room, error) {
|
||||
var arg2Copy []string
|
||||
if arg2 != nil {
|
||||
arg2Copy = make([]string, len(arg2))
|
||||
copy(arg2Copy, arg2)
|
||||
}
|
||||
fake.listRoomsMutex.Lock()
|
||||
ret, specificReturn := fake.listRoomsReturnsOnCall[len(fake.listRoomsArgsForCall)]
|
||||
fake.listRoomsArgsForCall = append(fake.listRoomsArgsForCall, struct {
|
||||
arg1 context.Context
|
||||
}{arg1})
|
||||
arg2 []string
|
||||
}{arg1, arg2Copy})
|
||||
stub := fake.ListRoomsStub
|
||||
fakeReturns := fake.listRoomsReturns
|
||||
fake.recordInvocation("ListRooms", []interface{}{arg1})
|
||||
fake.recordInvocation("ListRooms", []interface{}{arg1, arg2Copy})
|
||||
fake.listRoomsMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
@@ -364,17 +371,17 @@ func (fake *FakeRoomStore) ListRoomsCallCount() int {
|
||||
return len(fake.listRoomsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeRoomStore) ListRoomsCalls(stub func(context.Context) ([]*livekit.Room, error)) {
|
||||
func (fake *FakeRoomStore) ListRoomsCalls(stub func(context.Context, []string) ([]*livekit.Room, error)) {
|
||||
fake.listRoomsMutex.Lock()
|
||||
defer fake.listRoomsMutex.Unlock()
|
||||
fake.ListRoomsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeRoomStore) ListRoomsArgsForCall(i int) context.Context {
|
||||
func (fake *FakeRoomStore) ListRoomsArgsForCall(i int) (context.Context, []string) {
|
||||
fake.listRoomsMutex.RLock()
|
||||
defer fake.listRoomsMutex.RUnlock()
|
||||
argsForCall := fake.listRoomsArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeRoomStore) ListRoomsReturns(result1 []*livekit.Room, result2 error) {
|
||||
|
||||
@@ -24,10 +24,11 @@ type FakeRORoomStore struct {
|
||||
result1 []*livekit.ParticipantInfo
|
||||
result2 error
|
||||
}
|
||||
ListRoomsStub func(context.Context) ([]*livekit.Room, error)
|
||||
ListRoomsStub func(context.Context, []string) ([]*livekit.Room, error)
|
||||
listRoomsMutex sync.RWMutex
|
||||
listRoomsArgsForCall []struct {
|
||||
arg1 context.Context
|
||||
arg2 []string
|
||||
}
|
||||
listRoomsReturns struct {
|
||||
result1 []*livekit.Room
|
||||
@@ -135,18 +136,24 @@ func (fake *FakeRORoomStore) ListParticipantsReturnsOnCall(i int, result1 []*liv
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *FakeRORoomStore) ListRooms(arg1 context.Context) ([]*livekit.Room, error) {
|
||||
func (fake *FakeRORoomStore) ListRooms(arg1 context.Context, arg2 []string) ([]*livekit.Room, error) {
|
||||
var arg2Copy []string
|
||||
if arg2 != nil {
|
||||
arg2Copy = make([]string, len(arg2))
|
||||
copy(arg2Copy, arg2)
|
||||
}
|
||||
fake.listRoomsMutex.Lock()
|
||||
ret, specificReturn := fake.listRoomsReturnsOnCall[len(fake.listRoomsArgsForCall)]
|
||||
fake.listRoomsArgsForCall = append(fake.listRoomsArgsForCall, struct {
|
||||
arg1 context.Context
|
||||
}{arg1})
|
||||
arg2 []string
|
||||
}{arg1, arg2Copy})
|
||||
stub := fake.ListRoomsStub
|
||||
fakeReturns := fake.listRoomsReturns
|
||||
fake.recordInvocation("ListRooms", []interface{}{arg1})
|
||||
fake.recordInvocation("ListRooms", []interface{}{arg1, arg2Copy})
|
||||
fake.listRoomsMutex.Unlock()
|
||||
if stub != nil {
|
||||
return stub(arg1)
|
||||
return stub(arg1, arg2)
|
||||
}
|
||||
if specificReturn {
|
||||
return ret.result1, ret.result2
|
||||
@@ -160,17 +167,17 @@ func (fake *FakeRORoomStore) ListRoomsCallCount() int {
|
||||
return len(fake.listRoomsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *FakeRORoomStore) ListRoomsCalls(stub func(context.Context) ([]*livekit.Room, error)) {
|
||||
func (fake *FakeRORoomStore) ListRoomsCalls(stub func(context.Context, []string) ([]*livekit.Room, error)) {
|
||||
fake.listRoomsMutex.Lock()
|
||||
defer fake.listRoomsMutex.Unlock()
|
||||
fake.ListRoomsStub = stub
|
||||
}
|
||||
|
||||
func (fake *FakeRORoomStore) ListRoomsArgsForCall(i int) context.Context {
|
||||
func (fake *FakeRORoomStore) ListRoomsArgsForCall(i int) (context.Context, []string) {
|
||||
fake.listRoomsMutex.RLock()
|
||||
defer fake.listRoomsMutex.RUnlock()
|
||||
argsForCall := fake.listRoomsArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *FakeRORoomStore) ListRoomsReturns(result1 []*livekit.Room, result2 error) {
|
||||
|
||||
@@ -58,7 +58,7 @@ func setupSingleNodeTest(name string, roomName string) (*service.LivekitServer,
|
||||
waitForServerToStart(s)
|
||||
|
||||
// create test room
|
||||
_, err := roomClient.CreateRoom(contextWithCreateRoomToken(), &livekit.CreateRoomRequest{Name: roomName})
|
||||
_, err := roomClient.CreateRoom(contextWithToken(createRoomToken()), &livekit.CreateRoomRequest{Name: roomName})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -86,9 +86,9 @@ func setupMultiNodeTest(name string) (*service.LivekitServer, *service.LivekitSe
|
||||
}
|
||||
}
|
||||
|
||||
func contextWithCreateRoomToken() context.Context {
|
||||
func contextWithToken(token string) context.Context {
|
||||
header := make(http.Header)
|
||||
testclient.SetAuthorizationToken(header, createRoomToken())
|
||||
testclient.SetAuthorizationToken(header, token)
|
||||
tctx, err := twirp.WithHTTPRequestHeaders(context.Background(), header)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -258,6 +258,16 @@ func createRoomToken() string {
|
||||
return t
|
||||
}
|
||||
|
||||
func listRoomToken() string {
|
||||
at := auth.NewAccessToken(testApiKey, testApiSecret).
|
||||
AddGrant(&auth.VideoGrant{RoomList: true})
|
||||
t, err := at.ToJWT()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func stopWriters(writers ...*testclient.TrackWriter) {
|
||||
for _, w := range writers {
|
||||
w.Stop()
|
||||
|
||||
+13
-2
@@ -20,7 +20,7 @@ func TestMultiNodeRouting(t *testing.T) {
|
||||
defer finish()
|
||||
|
||||
// creating room on node 1
|
||||
_, err := roomClient.CreateRoom(contextWithCreateRoomToken(), &livekit.CreateRoomRequest{
|
||||
_, err := roomClient.CreateRoom(contextWithToken(createRoomToken()), &livekit.CreateRoomRequest{
|
||||
Name: testRoom,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -103,7 +103,7 @@ func TestMultinodeReconnectAfterNodeShutdown(t *testing.T) {
|
||||
defer finish()
|
||||
|
||||
// creating room on node 1
|
||||
_, err := roomClient.CreateRoom(contextWithCreateRoomToken(), &livekit.CreateRoomRequest{
|
||||
_, err := roomClient.CreateRoom(contextWithToken(createRoomToken()), &livekit.CreateRoomRequest{
|
||||
Name: testRoom,
|
||||
NodeId: s2.Node().Id,
|
||||
})
|
||||
@@ -136,3 +136,14 @@ func TestMultinodeDataPublishing(t *testing.T) {
|
||||
|
||||
scenarioDataPublish(t)
|
||||
}
|
||||
|
||||
func TestMultiNodeRoomList(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
_, _, finish := setupMultiNodeTest("TestMultiNodeRoomList")
|
||||
defer finish()
|
||||
|
||||
roomServiceListRoom(t)
|
||||
}
|
||||
|
||||
+30
-2
@@ -141,8 +141,6 @@ func scenarioDataPublish(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// websocket reconnects
|
||||
|
||||
func publishTracksForClients(t *testing.T, clients ...*testclient.RTCClient) []*testclient.TrackWriter {
|
||||
logger.Infow("publishing tracks for clients")
|
||||
var writers []*testclient.TrackWriter
|
||||
@@ -158,3 +156,33 @@ func publishTracksForClients(t *testing.T, clients ...*testclient.RTCClient) []*
|
||||
}
|
||||
return writers
|
||||
}
|
||||
|
||||
// Room service tests
|
||||
|
||||
func roomServiceListRoom(t *testing.T) {
|
||||
createCtx := contextWithToken(createRoomToken())
|
||||
listCtx := contextWithToken(listRoomToken())
|
||||
// create rooms
|
||||
_, err := roomClient.CreateRoom(createCtx, &livekit.CreateRoomRequest{
|
||||
Name: testRoom,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = roomClient.CreateRoom(contextWithToken(createRoomToken()), &livekit.CreateRoomRequest{
|
||||
Name: "yourroom",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("list all rooms", func(t *testing.T) {
|
||||
res, err := roomClient.ListRooms(listCtx, &livekit.ListRoomsRequest{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res.Rooms, 2)
|
||||
})
|
||||
t.Run("list specific rooms", func(t *testing.T) {
|
||||
res, err := roomClient.ListRooms(listCtx, &livekit.ListRoomsRequest{
|
||||
Names: []string{"yourroom"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, res.Rooms, 1)
|
||||
require.Equal(t, "yourroom", res.Rooms[0].Name)
|
||||
})
|
||||
}
|
||||
|
||||
+11
-1
@@ -291,5 +291,15 @@ func Test_RenegotiationWithDifferentCodecs(t *testing.T) {
|
||||
if !success {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSingleNodeRoomList(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.SkipNow()
|
||||
return
|
||||
}
|
||||
_, finish := setupSingleNodeTest("TestSingleNodeRoomList", testRoom)
|
||||
defer finish()
|
||||
|
||||
roomServiceListRoom(t)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user