Skip to content

Commit

Permalink
Add fork Jupyter events
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbrochart committed Oct 25, 2024
1 parent d2d5360 commit 49f3ef1
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 14 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ jobs:
run: |
python -m pip install "jupyterlab>=4.0.0,<5"
python -m pip install -e ".[test]" codecov
python -m pip install -e projects/jupyter-collaboration-ui -e projects/jupyter-docprovider -e projects/jupyter-server-ydoc
python -m pip install -e projects/jupyter-collaboration-ui -e projects/jupyter-docprovider -e "projects/jupyter-server-ydoc[test]"
- name: List installed packages
run: |
Expand Down Expand Up @@ -163,7 +163,7 @@ jobs:
- name: Install the Python dependencies
run: |
pip install -e ".[test]"
pip install -e projects/jupyter-collaboration-ui -e projects/jupyter-docprovider -e projects/jupyter-server-ydoc
pip install -e projects/jupyter-collaboration-ui -e projects/jupyter-docprovider -e "projects/jupyter-server-ydoc[test]"
- name: Run the unit tests
run: |
Expand All @@ -184,7 +184,7 @@ jobs:
- name: Install the Python dependencies
run: |
pip install -e ".[test]"
pip install -e projects/jupyter-collaboration-ui -e projects/jupyter-docprovider -e projects/jupyter-server-ydoc
pip install -e projects/jupyter-collaboration-ui -e projects/jupyter-docprovider -e "projects/jupyter-server-ydoc[test]"
- name: List installed packages
run: |
Expand Down
2 changes: 2 additions & 0 deletions projects/jupyter-server-ydoc/jupyter_server_ydoc/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from .utils import (
AWARENESS_EVENTS_SCHEMA_PATH,
EVENTS_SCHEMA_PATH,
FORK_EVENTS_SCHEMA_PATH,
encode_file_path,
room_id_from_encoded_path,
)
Expand Down Expand Up @@ -86,6 +87,7 @@ def initialize(self):
super().initialize()
self.serverapp.event_logger.register_event_schema(EVENTS_SCHEMA_PATH)
self.serverapp.event_logger.register_event_schema(AWARENESS_EVENTS_SCHEMA_PATH)
self.serverapp.event_logger.register_event_schema(FORK_EVENTS_SCHEMA_PATH)

def initialize_settings(self):
self.settings.update(
Expand Down
34 changes: 34 additions & 0 deletions projects/jupyter-server-ydoc/jupyter_server_ydoc/events/fork.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"$id": https://schema.jupyter.org/jupyter_collaboration/fork/v1
"$schema": "http://json-schema.org/draft-07/schema"
version: 1
title: Collaborative fork events
personal-data: true
description: |
Fork events emitted from server-side during a collaborative session.
type: object
required:
- root_roomid
- fork_roomid
- username
- action
properties:
root_roomid:
type: string
description: |
Root room ID. Usually composed by the file type, format and ID.
fork_roomid:
type: string
description: |
Fork root room ID.
username:
type: string
description: |
The name of the user who created or deleted the fork.
action:
enum:
- create
- delete
description: |
Possible values:
1. create
2. delete
14 changes: 14 additions & 0 deletions projects/jupyter-server-ydoc/jupyter_server_ydoc/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from .utils import (
JUPYTER_COLLABORATION_AWARENESS_EVENTS_URI,
JUPYTER_COLLABORATION_EVENTS_URI,
JUPYTER_COLLABORATION_FORK_EVENTS_URI,
LogLevel,
MessageType,
decode_file_path,
Expand Down Expand Up @@ -654,6 +655,7 @@ async def put(self, root_roomid):
fork_room = YRoom(ydoc=fork_ydoc)
self._websocket_server.rooms[fork_roomid] = fork_room
await self._websocket_server.start_room(fork_room)
self._emit_fork_event(self.current_user.username, root_roomid, fork_roomid, "create")
data = json.dumps(
{
"sessionId": SERVER_SESSION,
Expand All @@ -679,4 +681,16 @@ async def delete(self, fork_roomid):
fork_update = fork_ydoc.get_update()
root_ydoc.apply_update(fork_update)
await self._websocket_server.delete_room(name=fork_roomid)
self._emit_fork_event(self.current_user.username, root_roomid, fork_roomid, "delete")
self.set_status(200)

def _emit_fork_event(
self, username: str, root_roomid: str, fork_roomid: str, action: str
) -> None:
data = {
"username": username,
"root_roomid": root_roomid,
"fork_roomid": fork_roomid,
"action": action,
}
self.event_logger.emit(schema_id=JUPYTER_COLLABORATION_FORK_EVENTS_URI, data=data)
2 changes: 2 additions & 0 deletions projects/jupyter-server-ydoc/jupyter_server_ydoc/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
JUPYTER_COLLABORATION_AWARENESS_EVENTS_URI = (
"https://schema.jupyter.org/jupyter_collaboration/awareness/v1"
)
JUPYTER_COLLABORATION_FORK_EVENTS_URI = "https://schema.jupyter.org/jupyter_collaboration/fork/v1"
AWARENESS_EVENTS_SCHEMA_PATH = EVENTS_FOLDER_PATH / "awareness.yaml"
FORK_EVENTS_SCHEMA_PATH = EVENTS_FOLDER_PATH / "fork.yaml"


class MessageType(IntEnum):
Expand Down
1 change: 1 addition & 0 deletions projects/jupyter-server-ydoc/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dynamic = ["version"]
[project.optional-dependencies]
test = [
"coverage",
"dirty-equals",
"jupyter_server[test]>=2.4.0",
"jupyter_server_fileid[test]",
"pytest>=7.0",
Expand Down
67 changes: 56 additions & 11 deletions tests/test_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from asyncio import Event, sleep
from typing import Any

from dirty_equals import IsStr
from jupyter_events.logger import EventLogger
from jupyter_ydoc import YUnicode
from pycrdt import Text
Expand Down Expand Up @@ -219,6 +220,7 @@ async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None:


async def test_fork_handler(
jp_serverapp,
rtc_create_file,
rtc_connect_doc_client,
rtc_connect_fork_client,
Expand All @@ -227,6 +229,17 @@ async def test_fork_handler(
rtc_delete_fork_client,
rtc_fetch_session,
):
collected_data = []

async def my_listener(logger: EventLogger, schema_id: str, data: dict) -> None:
collected_data.append(data)

event_logger = jp_serverapp.event_logger
event_logger.add_listener(
schema_id="https://schema.jupyter.org/jupyter_collaboration/fork/v1",
listener=my_listener,
)

path, _ = await rtc_create_file("test.txt", "Hello")

root_connect_event = Event()
Expand All @@ -241,27 +254,45 @@ def _on_root_change(topic: str, event: Any) -> None:
resp = await rtc_fetch_session("text", "file", path)
data = json.loads(resp.body.decode("utf-8"))
file_id = data["fileId"]
root_roomid = f"text:file:{file_id}"

async with await rtc_connect_doc_client("text", "file", path) as ws, WebsocketProvider(
root_ydoc.ydoc, ws
):
await root_connect_event.wait()

resp = await rtc_create_fork_client(f"text:file:{file_id}", False)
resp = await rtc_create_fork_client(root_roomid, False)
data = json.loads(resp.body.decode("utf-8"))
fork_roomid0 = data["roomId"]

resp = await rtc_get_forks_client(f"text:file:{file_id}")
resp = await rtc_get_forks_client(root_roomid)
data = json.loads(resp.body.decode("utf-8"))
assert data == {f"text:file:{file_id}": [fork_roomid0]}

resp = await rtc_create_fork_client(f"text:file:{file_id}", True)
assert data == {root_roomid: [fork_roomid0]}

assert collected_data == [
{
"username": IsStr(),
"root_roomid": root_roomid,
"fork_roomid": fork_roomid0,
"action": "create",
}
]

resp = await rtc_create_fork_client(root_roomid, True)
data = json.loads(resp.body.decode("utf-8"))
fork_roomid1 = data["roomId"]

resp = await rtc_get_forks_client(f"text:file:{file_id}")
resp = await rtc_get_forks_client(root_roomid)
data = json.loads(resp.body.decode("utf-8"))
assert data == {f"text:file:{file_id}": [fork_roomid0, fork_roomid1]}
assert data == {root_roomid: [fork_roomid0, fork_roomid1]}

assert len(collected_data) == 2
assert collected_data[1] == {
"username": IsStr(),
"root_roomid": root_roomid,
"fork_roomid": fork_roomid1,
"action": "create",
}

fork_ydoc = YUnicode()
fork_connect_event = Event()
Expand Down Expand Up @@ -289,13 +320,27 @@ def _on_fork_change(topic: str, event: Any) -> None:
await rtc_delete_fork_client(fork_roomid0, 1)
await sleep(0.1)
assert str(root_text) == "Hello, World!"
resp = await rtc_get_forks_client(f"text:file:{file_id}")
resp = await rtc_get_forks_client(root_roomid)
data = json.loads(resp.body.decode("utf-8"))
assert data == {f"text:file:{file_id}": [fork_roomid1]}
assert data == {root_roomid: [fork_roomid1]}
assert len(collected_data) == 3
assert collected_data[2] == {
"username": IsStr(),
"root_roomid": root_roomid,
"fork_roomid": fork_roomid0,
"action": "delete",
}

await rtc_delete_fork_client(fork_roomid1, 1)
await sleep(0.1)
assert str(root_text) == "Hello, World! Hi!"
resp = await rtc_get_forks_client(f"text:file:{file_id}")
resp = await rtc_get_forks_client(root_roomid)
data = json.loads(resp.body.decode("utf-8"))
assert data == {f"text:file:{file_id}": []}
assert data == {root_roomid: []}
assert len(collected_data) == 4
assert collected_data[3] == {
"username": IsStr(),
"root_roomid": root_roomid,
"fork_roomid": fork_roomid1,
"action": "delete",
}

0 comments on commit 49f3ef1

Please sign in to comment.