Skip to content

Commit 4649536

Browse files
committed
Add room merging
1 parent 71487f6 commit 4649536

File tree

4 files changed

+61
-11
lines changed

4 files changed

+61
-11
lines changed

jupyverse_api/jupyverse_api/yjs/__init__.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
from __future__ import annotations
2+
13
from abc import ABC, abstractmethod
24
from typing import Any
35

46
from fastapi import APIRouter, Depends, Request, Response
57

68
from jupyverse_api import Router
79

10+
from .models import MergeRoom
811
from ..app import App
912
from ..auth import Auth, User
1013

@@ -35,13 +38,20 @@ async def create_roomid(
3538
):
3639
return await self.create_roomid(path, request, response, user)
3740

38-
@router.put("/api/collaboration/room/{roomid}", status_code=201)
41+
@router.put("/api/collaboration/fork_room/{roomid}", status_code=201)
3942
async def fork_room(
4043
roomid,
4144
user: User = Depends(auth.current_user(permissions={"contents": ["read"]})),
4245
):
4346
return await self.fork_room(roomid, user)
4447

48+
@router.put("/api/collaboration/merge_room", status_code=200)
49+
async def merge_room(
50+
merge_room: MergeRoom,
51+
user: User = Depends(auth.current_user(permissions={"contents": ["read"]})),
52+
):
53+
return await self.merge_room(merge_room, user)
54+
4555
self.include_router(router)
4656

4757
@abstractmethod
@@ -70,6 +80,14 @@ async def fork_room(
7080
):
7181
...
7282

83+
@abstractmethod
84+
async def merge_room(
85+
self,
86+
merge_room: MergeRoom,
87+
user: User,
88+
):
89+
...
90+
7391
@abstractmethod
7492
def get_document(
7593
self,

jupyverse_api/jupyverse_api/yjs/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@
44
class CreateDocumentSession(BaseModel):
55
format: str
66
type: str
7+
8+
9+
class MergeRoom(BaseModel):
10+
fork_roomid: str
11+
root_roomid: str

plugins/yjs/fps_yjs/routes.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from jupyverse_api.auth import Auth, User
2222
from jupyverse_api.contents import Contents
2323
from jupyverse_api.yjs import Yjs
24-
from jupyverse_api.yjs.models import CreateDocumentSession
24+
from jupyverse_api.yjs.models import CreateDocumentSession, MergeRoom
2525

2626
from .ydocs import ydocs as YDOCS
2727
from .ydocs.ybasedoc import YBaseDoc
@@ -104,17 +104,27 @@ async def fork_room(
104104

105105
root_room = await self.room_manager.websocket_server.get_room(roomid)
106106
update = root_room.ydoc.get_update()
107-
new_ydoc = Doc()
108-
new_ydoc.apply_update(update)
109-
new_room = await self.room_manager.websocket_server.get_room(idx, new_ydoc)
110-
root_room.local_clients.add(new_room)
107+
fork_ydoc = Doc()
108+
fork_ydoc.apply_update(update)
109+
fork_room = await self.room_manager.websocket_server.get_room(idx, fork_ydoc)
110+
root_room.local_clients.add(fork_room)
111111

112112
res = {
113113
"sessionId": SERVER_SESSION,
114114
"roomId": idx,
115115
}
116116
return res
117117

118+
async def merge_room(
119+
self,
120+
merge_room: MergeRoom,
121+
user: User,
122+
):
123+
fork_room = await self.room_manager.websocket_server.get_room(merge_room.fork_roomid)
124+
root_room = await self.room_manager.websocket_server.get_room(merge_room.root_roomid)
125+
update = fork_room.ydoc.get_update()
126+
root_room.ydoc.apply_update(update)
127+
118128
def get_document(self, document_id: str) -> YBaseDoc:
119129
return self.room_manager.documents[document_id]
120130

tests/test_yjs.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from pycrdt import Doc, Text
1111

1212
from jupyverse_api.main import JupyverseComponent
13-
from jupyverse_api.yjs.models import CreateDocumentSession
13+
from jupyverse_api.yjs.models import CreateDocumentSession, MergeRoom
1414

1515

1616
@pytest.mark.asyncio
@@ -52,25 +52,42 @@ async def test_fork_room(tmp_path, unused_tcp_port):
5252
await sleep(0.1)
5353
assert str(root_ytext) == "Hello"
5454
# fork room
55+
root_roomid = f"text:file:{file_id}"
5556
response = await http.put(
56-
f"http://127.0.0.1:{unused_tcp_port}/api/collaboration/room/text:file:{file_id}"
57+
f"http://127.0.0.1:{unused_tcp_port}/api/collaboration/fork_room/{root_roomid}"
5758
)
5859
r = response.json()
59-
fork_room_id = r["roomId"]
60+
fork_roomid = r["roomId"]
6061
# connect to forked room
6162
async with aconnect_ws(
62-
f"http://127.0.0.1:{unused_tcp_port}/api/collaboration/room/{fork_room_id}"
63+
f"http://127.0.0.1:{unused_tcp_port}/api/collaboration/room/{fork_roomid}"
6364
) as fork_ws:
6465
# create a forked room client
6566
fork_ydoc = Doc()
6667
fork_ydoc["source"] = fork_ytext = Text()
67-
async with WebsocketProvider(fork_ydoc, Websocket(fork_ws, fork_room_id)):
68+
async with WebsocketProvider(fork_ydoc, Websocket(fork_ws, fork_roomid)):
6869
await sleep(0.1)
6970
assert str(fork_ytext) == "Hello"
71+
# check that the forked room is synced with the root room
7072
root_ytext += ", World!"
7173
await sleep(0.1)
7274
assert str(root_ytext) == "Hello, World!"
7375
assert str(fork_ytext) == "Hello, World!"
76+
# check that the root room is not synced with the forked room
77+
fork_ytext += " Bye!"
78+
await sleep(0.1)
79+
assert str(root_ytext) == "Hello, World!"
80+
assert str(fork_ytext) == "Hello, World! Bye!"
81+
# merge forked room into root room
82+
merge_room = MergeRoom(fork_roomid=fork_roomid, root_roomid=root_roomid)
83+
response = await http.put(
84+
f"http://127.0.0.1:{unused_tcp_port}/api/collaboration/merge_room",
85+
json=merge_room.model_dump(),
86+
)
87+
# check that the root room is synced with the forked room
88+
await sleep(0.1)
89+
assert str(root_ytext) == "Hello, World! Bye!"
90+
assert str(fork_ytext) == "Hello, World! Bye!"
7491

7592

7693
class Websocket:

0 commit comments

Comments
 (0)