eduskit 0.1.4__tar.gz → 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {eduskit-0.1.4 → eduskit-0.1.5}/PKG-INFO +1 -1
- {eduskit-0.1.4 → eduskit-0.1.5}/eduskit/whiteboard.py +43 -1
- {eduskit-0.1.4 → eduskit-0.1.5}/eduskit.egg-info/PKG-INFO +1 -1
- {eduskit-0.1.4 → eduskit-0.1.5}/pyproject.toml +1 -1
- {eduskit-0.1.4 → eduskit-0.1.5}/tests/test_eduskit.py +38 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/LICENSE +0 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/README.md +0 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/eduskit/__init__.py +0 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/eduskit/classroom.py +0 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/eduskit/errors.py +0 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/eduskit/http.py +0 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/eduskit/token.py +0 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/eduskit.egg-info/SOURCES.txt +0 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/eduskit.egg-info/dependency_links.txt +0 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/eduskit.egg-info/top_level.txt +0 -0
- {eduskit-0.1.4 → eduskit-0.1.5}/setup.cfg +0 -0
|
@@ -21,7 +21,7 @@ class _Auth:
|
|
|
21
21
|
expires_in = input.get("expiresIn", 3600)
|
|
22
22
|
token, expires_at = sign_hs256(self._app_id, self._app_secret, user_id,
|
|
23
23
|
"eduskit", "eduskit-room", expires_in,
|
|
24
|
-
{"app_id": self._app_id, "room_id": room_id, "role": role, "source": "server_sdk"},
|
|
24
|
+
{"app_id": self._app_id, "room_id": room_id, "role": role, "source": "server_sdk", "access_generation": None},
|
|
25
25
|
"whiteboard")
|
|
26
26
|
return {"token": token, "appId": self._app_id, "roomId": room_id, "userId": user_id,
|
|
27
27
|
"role": role, "expiresIn": expires_in, "expiresAt": iso_time(expires_at)}
|
|
@@ -93,12 +93,54 @@ class _Files:
|
|
|
93
93
|
return self._http.request("GET", '/v1/files/convert' + "?" + urlencode({'jobId': job_id}))
|
|
94
94
|
|
|
95
95
|
|
|
96
|
+
class _Rooms:
|
|
97
|
+
def __init__(self, http: HttpTransport) -> None:
|
|
98
|
+
self._http = http
|
|
99
|
+
|
|
100
|
+
def schedule_private_room_writes(self, room_id: str, request_id: str, opens_at: str, closes_at: str) -> Any:
|
|
101
|
+
return self._http.request("POST", "/v1/rooms/private/write-window", {"roomId": room_id, "requestId": request_id, "opensAt": opens_at, "closesAt": closes_at})
|
|
102
|
+
|
|
103
|
+
def provision_private_room(self, room_id: str, assignment_id: str) -> Any:
|
|
104
|
+
return self._http.request("POST", "/v1/rooms/private", {"roomId": room_id, "assignmentId": assignment_id})
|
|
105
|
+
|
|
106
|
+
def initialize_private_workspace(self, room_id: str, assignment_id: str, source_snapshot_id: str | None) -> Any:
|
|
107
|
+
return self._http.request("POST", "/v1/rooms/private/initializations", {"roomId": room_id, "assignmentId": assignment_id, "sourceSnapshotId": source_snapshot_id})
|
|
108
|
+
|
|
109
|
+
def get_private_workspace_initialization(self, room_id: str) -> Any:
|
|
110
|
+
return self._http.request("POST", "/v1/rooms/private/initializations/query", {"roomId": room_id})
|
|
111
|
+
|
|
112
|
+
def change_private_room_grant(self, room_id: str, **input: Any) -> Any:
|
|
113
|
+
return self._http.request("POST", "/v1/rooms/private/grants", {**input, "roomId": room_id})
|
|
114
|
+
|
|
115
|
+
def get_private_room_access(self, room_id: str, user_id: str) -> Any:
|
|
116
|
+
return self._http.request("POST", "/v1/rooms/private/access/query", {"roomId": room_id, "userId": user_id})
|
|
117
|
+
|
|
118
|
+
def issue_private_room_token(self, room_id: str, **input: Any) -> Any:
|
|
119
|
+
return self._http.request("POST", "/v1/rooms/private/token", {**input, "roomId": room_id})
|
|
120
|
+
|
|
121
|
+
def seal_private_room(self, room_id: str) -> Any:
|
|
122
|
+
return self._http.request("POST", "/v1/rooms/private/seal", {"roomId": room_id})
|
|
123
|
+
|
|
124
|
+
def create_frozen_snapshot(self, room_id: str, snapshot_id: str) -> Any:
|
|
125
|
+
return self._http.request("POST", "/v1/rooms/private/snapshots", {"roomId": room_id, "snapshotId": snapshot_id})
|
|
126
|
+
|
|
127
|
+
def get_frozen_snapshot(self, room_id: str, snapshot_id: str) -> Any:
|
|
128
|
+
return self._http.request("POST", "/v1/rooms/private/snapshots/query", {"roomId": room_id, "snapshotId": snapshot_id})
|
|
129
|
+
|
|
130
|
+
def get_frozen_snapshot_download(self, room_id: str, snapshot_id: str) -> Any:
|
|
131
|
+
return self._http.request("POST", "/v1/rooms/private/snapshots/download", {"roomId": room_id, "snapshotId": snapshot_id})
|
|
132
|
+
|
|
133
|
+
def attach_courseware(self, room_id: str, **input: Any) -> Any:
|
|
134
|
+
return self._http.request("POST", "/v1/rooms/coursewares", {**input, "roomId": room_id})
|
|
135
|
+
|
|
136
|
+
|
|
96
137
|
class WhiteboardClient:
|
|
97
138
|
def __init__(self, http: HttpTransport, *, app_id: str, app_secret: str) -> None:
|
|
98
139
|
self.auth = _Auth(app_id, app_secret)
|
|
99
140
|
self.recordings = _Recordings(http)
|
|
100
141
|
self.captures = _Captures(http)
|
|
101
142
|
self.files = _Files(http)
|
|
143
|
+
self.rooms = _Rooms(http)
|
|
102
144
|
self._http = http
|
|
103
145
|
|
|
104
146
|
@property
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import base64
|
|
2
3
|
import unittest
|
|
3
4
|
|
|
4
5
|
from eduskit import Eduskit, EduskitError
|
|
@@ -22,6 +23,39 @@ def mock_fetch(body, status=200, headers=None):
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class EduskitTest(unittest.TestCase):
|
|
26
|
+
def test_private_room_server_routes(self):
|
|
27
|
+
fetch, calls = mock_fetch({"code": 0, "data": {"marker": "server-result"}})
|
|
28
|
+
sdk = Eduskit(whiteboard_client={"base_url": "http://wb.test", "app_id": "app_wb", "app_key": "wk", "app_secret": "ws"}, fetch=fetch)
|
|
29
|
+
rooms = sdk.whiteboard_client.rooms
|
|
30
|
+
operations = [
|
|
31
|
+
lambda: rooms.provision_private_room("room_a", "assignment_a"),
|
|
32
|
+
lambda: rooms.change_private_room_grant("room_a", userId="student_a", requestId="grant_a", expectedGeneration="9223372036854775806", action="grant", role="participant"),
|
|
33
|
+
lambda: rooms.get_private_room_access("room_a", "student_a"),
|
|
34
|
+
lambda: rooms.issue_private_room_token("room_a", userId="student_a", role="participant", expiresIn=600),
|
|
35
|
+
lambda: rooms.seal_private_room("room_a"),
|
|
36
|
+
lambda: rooms.create_frozen_snapshot("room_a", "snapshot_a"),
|
|
37
|
+
lambda: rooms.get_frozen_snapshot("room_a", "snapshot_a"),
|
|
38
|
+
lambda: rooms.get_frozen_snapshot_download("room_a", "snapshot_a"),
|
|
39
|
+
lambda: rooms.initialize_private_workspace("room_a", "assignment_a", None),
|
|
40
|
+
lambda: rooms.initialize_private_workspace("room_a", "assignment_a", "snapshot_a"),
|
|
41
|
+
lambda: rooms.get_private_workspace_initialization("room_a"),
|
|
42
|
+
lambda: rooms.schedule_private_room_writes("room_a", "window_a", "2026-10-02T00:00:00.000Z", "2026-10-02T00:10:00.000Z"),
|
|
43
|
+
]
|
|
44
|
+
for operation in operations:
|
|
45
|
+
self.assertEqual(operation(), {"marker": "server-result"})
|
|
46
|
+
expected = ["", "/grants", "/access/query", "/token", "/seal", "/snapshots", "/snapshots/query", "/snapshots/download", "/initializations", "/initializations", "/initializations/query", "/write-window"]
|
|
47
|
+
self.assertEqual([call["url"] for call in calls], ["http://wb.test/v1/rooms/private" + suffix for suffix in expected])
|
|
48
|
+
for call in calls:
|
|
49
|
+
self.assertEqual(call["method"], "POST")
|
|
50
|
+
self.assertEqual(call["body"]["roomId"], "room_a")
|
|
51
|
+
self.assertEqual(call["headers"]["x-app-key"], "wk")
|
|
52
|
+
self.assertEqual(calls[1]["body"]["expectedGeneration"], "9223372036854775806")
|
|
53
|
+
self.assertNotIn("accessGeneration", calls[3]["body"])
|
|
54
|
+
self.assertEqual(calls[8]["body"], {"roomId": "room_a", "assignmentId": "assignment_a", "sourceSnapshotId": None})
|
|
55
|
+
self.assertEqual(calls[9]["body"]["sourceSnapshotId"], "snapshot_a")
|
|
56
|
+
self.assertEqual(calls[10]["body"], {"roomId": "room_a"})
|
|
57
|
+
self.assertEqual(calls[11]["body"], {"roomId":"room_a", "requestId":"window_a", "opensAt":"2026-10-02T00:00:00.000Z", "closesAt":"2026-10-02T00:10:00.000Z"})
|
|
58
|
+
|
|
25
59
|
def test_classroom_register(self):
|
|
26
60
|
fetch, calls = mock_fetch(
|
|
27
61
|
{
|
|
@@ -56,6 +90,10 @@ class EduskitTest(unittest.TestCase):
|
|
|
56
90
|
)
|
|
57
91
|
self.assertEqual(token["appId"], "app_wb")
|
|
58
92
|
self.assertEqual(len(token["token"].split(".")), 3)
|
|
93
|
+
segment = token["token"].split(".")[1]
|
|
94
|
+
claims = json.loads(base64.urlsafe_b64decode(segment + "=" * (-len(segment) % 4)))
|
|
95
|
+
self.assertIn("access_generation", claims)
|
|
96
|
+
self.assertIsNone(claims["access_generation"])
|
|
59
97
|
self.assertEqual(calls, [])
|
|
60
98
|
|
|
61
99
|
def test_error_envelope(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|