datasette-events-forward 0.1a0__tar.gz → 0.1a2__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.

Potentially problematic release.


This version of datasette-events-forward might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: datasette-events-forward
3
- Version: 0.1a0
3
+ Version: 0.1a2
4
4
  Summary: Forward Datasette events to another instance
5
5
  Author: Simon Willison
6
6
  License: Apache-2.0
@@ -13,14 +13,16 @@ Classifier: License :: OSI Approved :: Apache Software License
13
13
  Requires-Python: >=3.8
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
- Requires-Dist: datasette>=1.0a10
16
+ Requires-Dist: datasette==1.0a19
17
17
  Requires-Dist: python-ulid
18
18
  Requires-Dist: aiolimiter
19
+ Requires-Dist: typing_extensions
19
20
  Provides-Extra: test
20
21
  Requires-Dist: pytest; extra == "test"
21
22
  Requires-Dist: pytest-asyncio; extra == "test"
22
23
  Requires-Dist: pytest-httpx; extra == "test"
23
24
  Requires-Dist: datasette-test; extra == "test"
25
+ Dynamic: license-file
24
26
 
25
27
  # datasette-events-forward
26
28
 
@@ -17,7 +17,6 @@ create table if not exists datasette_events_to_forward (
17
17
  database_name text,
18
18
  table_name text,
19
19
  properties text, -- JSON other properties
20
- sent_at text,
21
20
  failures integer default 0
22
21
  )
23
22
  """
@@ -25,6 +24,9 @@ create table if not exists datasette_events_to_forward (
25
24
  # Default is to allow 1 every 10s
26
25
  rate_limit = AsyncLimiter(max_rate=1, time_period=10)
27
26
 
27
+ # Lock to prevent concurrent send_events() calls
28
+ send_events_lock = asyncio.Lock()
29
+
28
30
  DEFAULT_BATCH_LIMIT = 10
29
31
 
30
32
 
@@ -43,7 +45,7 @@ async def send_events(datasette):
43
45
  await db.execute(
44
46
  """
45
47
  select * from datasette_events_to_forward
46
- where sent_at is null and failures < 3
48
+ where failures < 3
47
49
  order by id limit {}""".format(
48
50
  batch_limit + 1
49
51
  )
@@ -92,14 +94,14 @@ async def send_events(datasette):
92
94
  headers={"Authorization": "Bearer {}".format(api_token)},
93
95
  )
94
96
  if str(response.status_code).startswith("2"):
95
- # It worked! Mark the rows as sent
97
+ # It worked! Mark the rows as sent by deleting them
96
98
  await db.execute_write(
97
99
  """
98
- update datasette_events_to_forward
99
- set sent_at = ? where id in ({})""".format(
100
+ delete from datasette_events_to_forward
101
+ where id in ({})""".format(
100
102
  ",".join(["?"] * len(rows))
101
103
  ),
102
- [datetime.datetime.utcnow().isoformat()] + [row["id"] for row in rows],
104
+ [row["id"] for row in rows],
103
105
  )
104
106
  else:
105
107
  # Schedule a retry task
@@ -119,6 +121,7 @@ async def send_events(datasette):
119
121
  [row["id"] for row in rows],
120
122
  )
121
123
  should_run_again = True
124
+
122
125
  if should_run_again:
123
126
  asyncio.create_task(rate_limited_send_events(datasette))
124
127
 
@@ -128,7 +131,8 @@ async def rate_limited_send_events(datasette):
128
131
  # had been inserted before the send_events() function was called
129
132
  await asyncio.sleep(0.05)
130
133
  async with rate_limit:
131
- await send_events(datasette)
134
+ async with send_events_lock:
135
+ await send_events(datasette)
132
136
 
133
137
 
134
138
  @hookimpl
@@ -149,6 +153,10 @@ def startup(datasette):
149
153
 
150
154
  @hookimpl
151
155
  def track_event(datasette, event):
156
+ config = datasette.plugin_config("datasette-events-forward") or {}
157
+ if not config.get("api_url"):
158
+ return
159
+
152
160
  async def inner():
153
161
  db = datasette.get_internal_database()
154
162
  properties = event.properties()
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: datasette-events-forward
3
- Version: 0.1a0
3
+ Version: 0.1a2
4
4
  Summary: Forward Datasette events to another instance
5
5
  Author: Simon Willison
6
6
  License: Apache-2.0
@@ -13,14 +13,16 @@ Classifier: License :: OSI Approved :: Apache Software License
13
13
  Requires-Python: >=3.8
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
- Requires-Dist: datasette>=1.0a10
16
+ Requires-Dist: datasette==1.0a19
17
17
  Requires-Dist: python-ulid
18
18
  Requires-Dist: aiolimiter
19
+ Requires-Dist: typing_extensions
19
20
  Provides-Extra: test
20
21
  Requires-Dist: pytest; extra == "test"
21
22
  Requires-Dist: pytest-asyncio; extra == "test"
22
23
  Requires-Dist: pytest-httpx; extra == "test"
23
24
  Requires-Dist: datasette-test; extra == "test"
25
+ Dynamic: license-file
24
26
 
25
27
  # datasette-events-forward
26
28
 
@@ -1,6 +1,7 @@
1
- datasette>=1.0a10
1
+ datasette==1.0a19
2
2
  python-ulid
3
3
  aiolimiter
4
+ typing_extensions
4
5
 
5
6
  [test]
6
7
  pytest
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datasette-events-forward"
3
- version = "0.1a0"
3
+ version = "0.1a2"
4
4
  description = "Forward Datasette events to another instance"
5
5
  readme = "README.md"
6
6
  authors = [{name = "Simon Willison"}]
@@ -11,9 +11,10 @@ classifiers=[
11
11
  ]
12
12
  requires-python = ">=3.8"
13
13
  dependencies = [
14
- "datasette>=1.0a10",
14
+ "datasette==1.0a19",
15
15
  "python-ulid",
16
- "aiolimiter"
16
+ "aiolimiter",
17
+ "typing_extensions"
17
18
  ]
18
19
 
19
20
  [project.urls]
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
- from datasette_test import Datasette
2
+ from datasette.app import Datasette
3
+ import httpx
3
4
  import json
4
5
  from sqlite_utils import Database
5
6
  import pytest
@@ -11,20 +12,32 @@ def non_mocked_hosts():
11
12
 
12
13
 
13
14
  @pytest.mark.asyncio
14
- async def test_events_forward(tmpdir, httpx_mock):
15
- httpx_mock.add_response(url="https://example.com/data/-/create", json={"ok": True})
15
+ @pytest.mark.parametrize("configured", (True, False))
16
+ async def test_events_forward(tmpdir, configured, httpx_mock):
17
+ if configured:
18
+ # Allow multiple POST requests to the same URL
19
+ def custom_response(request):
20
+ return httpx.Response(200, json={"ok": True})
21
+
22
+ httpx_mock.add_callback(
23
+ custom_response, url="https://example.com/data/-/create"
24
+ )
16
25
 
17
26
  db_path = str(tmpdir / "data.db")
18
27
  db = Database(db_path)
19
28
  db["foo"].insert({"id": 1}, pk="id")
20
29
  datasette = Datasette(
21
30
  [db_path],
22
- plugin_config={
23
- "datasette-events-forward": {
24
- "api_url": "https://example.com/data/-/create",
25
- "api_token": "xxx",
26
- "rate_limit": 1,
27
- "time_period": 0.2,
31
+ config={
32
+ "plugins": {
33
+ "datasette-events-forward": {
34
+ "api_url": (
35
+ "https://example.com/data/-/create" if configured else ""
36
+ ),
37
+ "api_token": "xxx",
38
+ "max_rate": 5,
39
+ "time_period": 0.2,
40
+ }
28
41
  }
29
42
  },
30
43
  )
@@ -40,6 +53,16 @@ async def test_events_forward(tmpdir, httpx_mock):
40
53
  headers={"Authorization": "Bearer {}".format(token)},
41
54
  )
42
55
  assert response.status_code == 201
56
+ if not configured:
57
+ # Should NOT have added row to table
58
+ count = (
59
+ await internal_db.execute(
60
+ "select count(*) from datasette_events_to_forward"
61
+ )
62
+ ).rows[0][0]
63
+ assert not count
64
+ return
65
+
43
66
  # Should have added a row to that table
44
67
  to_forward = dict(
45
68
  (await internal_db.execute("select * from datasette_events_to_forward")).rows[0]
@@ -48,14 +71,13 @@ async def test_events_forward(tmpdir, httpx_mock):
48
71
  assert to_forward["event"] == "create-table"
49
72
  assert to_forward["database_name"] == "data"
50
73
  assert to_forward["table_name"] == "hello"
51
- assert to_forward["sent_at"] is None
52
- # Wait 0.6s to give things time to be delivered
53
- await asyncio.sleep(0.6)
54
- to_forward2 = dict(
55
- (await internal_db.execute("select * from datasette_events_to_forward")).rows[0]
56
- )
57
- assert to_forward2["event"] == "create-table"
58
- assert to_forward2["sent_at"] is not None
74
+ # Wait 0.3s to give things time to be delivered
75
+ await asyncio.sleep(0.3)
76
+ # Table should be empty now
77
+ to_forward_count = (
78
+ await internal_db.execute("select count(*) from datasette_events_to_forward")
79
+ ).rows[0][0]
80
+ assert to_forward_count == 0
59
81
  # And the request should have been caught
60
82
  request = httpx_mock.get_request()
61
83
  assert request.url == "https://example.com/data/-/create"