Skip to content

Commit f672bdf

Browse files
authored
Merge pull request #1458 from sanders41/webhooks
Add support for webhooks
2 parents 3c7fe80 + 3337c9b commit f672bdf

File tree

5 files changed

+369
-0
lines changed

5 files changed

+369
-0
lines changed

meilisearch_python_sdk/_client.py

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from meilisearch_python_sdk.models.settings import MeilisearchSettings
3737
from meilisearch_python_sdk.models.task import TaskInfo, TaskResult, TaskStatus
3838
from meilisearch_python_sdk.models.version import Version
39+
from meilisearch_python_sdk.models.webhook import Webhook, WebhookCreate, Webhooks, WebhookUpdate
3940
from meilisearch_python_sdk.plugins import AsyncIndexPlugins, IndexPlugins
4041
from meilisearch_python_sdk.types import JsonDict
4142

@@ -252,6 +253,124 @@ async def get_networks(self) -> Network:
252253

253254
return Network(**response.json())
254255

256+
async def get_webhooks(self) -> Webhooks:
257+
"""Get all webhooks.
258+
259+
Returns:
260+
An instance of Webhooks containing all configured webhooks.
261+
262+
Raises:
263+
MeilisearchCommunicationError: If there was an error communicating with the server.
264+
MeilisearchApiError: If the Meilisearch API returned an error.
265+
266+
Examples:
267+
>>> from meilisearch_python_sdk import AsyncClient
268+
>>> async with AsyncClient("http://localhost.com", "masterKey") as client:
269+
>>> webhooks = await client.get_webhooks()
270+
"""
271+
response = await self._http_requests.get("webhooks")
272+
273+
return Webhooks(**response.json())
274+
275+
async def get_webhook(self, uuid: str) -> Webhook:
276+
"""Get a specific webhook by UUID.
277+
278+
Args:
279+
uuid: The webhook's unique identifier.
280+
281+
Returns:
282+
An instance of Webhook containing the webhook information.
283+
284+
Raises:
285+
MeilisearchCommunicationError: If there was an error communicating with the server.
286+
MeilisearchApiError: If the Meilisearch API returned an error.
287+
288+
Examples:
289+
>>> from meilisearch_python_sdk import AsyncClient
290+
>>> async with AsyncClient("http://localhost.com", "masterKey") as client:
291+
>>> webhook = await client.get_webhook("abc-123")
292+
"""
293+
response = await self._http_requests.get(f"webhooks/{uuid}")
294+
295+
return Webhook(**response.json())
296+
297+
async def create_webhook(self, webhook: WebhookCreate) -> Webhook:
298+
"""Create a new webhook.
299+
300+
Args:
301+
webhook: The webhook configuration to create.
302+
303+
Returns:
304+
The created webhook.
305+
306+
Raises:
307+
MeilisearchCommunicationError: If there was an error communicating with the server.
308+
MeilisearchApiError: If the Meilisearch API returned an error.
309+
310+
Examples:
311+
>>> from meilisearch_python_sdk import AsyncClient
312+
>>> from meilisearch_python_sdk.models.webhook import WebhookCreate
313+
>>> async with AsyncClient("http://localhost.com", "masterKey") as client:
314+
>>> webhook_config = WebhookCreate(
315+
>>> url="https://example.com/webhook",
316+
>>> headers={"Authorization": "Bearer token"}
317+
>>> )
318+
>>> webhook = await client.create_webhook(webhook_config)
319+
"""
320+
response = await self._http_requests.post(
321+
"webhooks", webhook.model_dump(by_alias=True, exclude_none=True)
322+
)
323+
324+
return Webhook(**response.json())
325+
326+
async def update_webhook(self, *, uuid: str, webhook: WebhookUpdate) -> Webhook:
327+
"""Update an existing webhook.
328+
329+
Args:
330+
uuid: The webhook's unique identifier.
331+
webhook: The webhook configuration updates.
332+
333+
Returns:
334+
The updated webhook.
335+
336+
Raises:
337+
MeilisearchCommunicationError: If there was an error communicating with the server.
338+
MeilisearchApiError: If the Meilisearch API returned an error.
339+
340+
Examples:
341+
>>> from meilisearch_python_sdk import AsyncClient
342+
>>> from meilisearch_python_sdk.models.webhook import WebhookUpdate
343+
>>> async with AsyncClient("http://localhost.com", "masterKey") as client:
344+
>>> webhook_update = WebhookUpdate(url="https://example.com/new-webhook")
345+
>>> webhook = await client.update_webhook("abc-123", webhook_update)
346+
"""
347+
response = await self._http_requests.patch(
348+
f"webhooks/{uuid}", webhook.model_dump(by_alias=True, exclude_none=True)
349+
)
350+
351+
return Webhook(**response.json())
352+
353+
async def delete_webhook(self, uuid: str) -> int:
354+
"""Delete a webhook.
355+
356+
Args:
357+
uuid: The webhook's unique identifier.
358+
359+
Returns:
360+
The Response status code. 204 signifies a successful delete.
361+
362+
Raises:
363+
MeilisearchCommunicationError: If there was an error communicating with the server.
364+
MeilisearchApiError: If the Meilisearch API returned an error.
365+
366+
Examples:
367+
>>> from meilisearch_python_sdk import AsyncClient
368+
>>> async with AsyncClient("http://localhost.com", "masterKey") as client:
369+
>>> await client.delete_webhook("abc-123")
370+
"""
371+
response = await self._http_requests.delete(f"webhooks/{uuid}")
372+
return response.status_code
373+
255374
async def create_dump(self) -> TaskInfo:
256375
"""Trigger the creation of a Meilisearch dump.
257376
@@ -1229,6 +1348,124 @@ def get_networks(self) -> Network:
12291348

12301349
return Network(**response.json())
12311350

1351+
def get_webhooks(self) -> Webhooks:
1352+
"""Get all webhooks.
1353+
1354+
Returns:
1355+
An instance of Webhooks containing all configured webhooks.
1356+
1357+
Raises:
1358+
MeilisearchCommunicationError: If there was an error communicating with the server.
1359+
MeilisearchApiError: If the Meilisearch API returned an error.
1360+
1361+
Examples:
1362+
>>> from meilisearch_python_sdk import Client
1363+
>>> client = Client("http://localhost.com", "masterKey")
1364+
>>> webhooks = client.get_webhooks()
1365+
"""
1366+
response = self._http_requests.get("webhooks")
1367+
1368+
return Webhooks(**response.json())
1369+
1370+
def get_webhook(self, uuid: str) -> Webhook:
1371+
"""Get a specific webhook by UUID.
1372+
1373+
Args:
1374+
uuid: The webhook's unique identifier.
1375+
1376+
Returns:
1377+
An instance of Webhook containing the webhook information.
1378+
1379+
Raises:
1380+
MeilisearchCommunicationError: If there was an error communicating with the server.
1381+
MeilisearchApiError: If the Meilisearch API returned an error.
1382+
1383+
Examples:
1384+
>>> from meilisearch_python_sdk import Client
1385+
>>> client = Client("http://localhost.com", "masterKey")
1386+
>>> webhook = client.get_webhook("abc-123")
1387+
"""
1388+
response = self._http_requests.get(f"webhooks/{uuid}")
1389+
1390+
return Webhook(**response.json())
1391+
1392+
def create_webhook(self, webhook: WebhookCreate) -> Webhook:
1393+
"""Create a new webhook.
1394+
1395+
Args:
1396+
webhook: The webhook configuration to create.
1397+
1398+
Returns:
1399+
The created webhook.
1400+
1401+
Raises:
1402+
MeilisearchCommunicationError: If there was an error communicating with the server.
1403+
MeilisearchApiError: If the Meilisearch API returned an error.
1404+
1405+
Examples:
1406+
>>> from meilisearch_python_sdk import Client
1407+
>>> from meilisearch_python_sdk.models.webhook import WebhookCreate
1408+
>>> client = Client("http://localhost.com", "masterKey")
1409+
>>> webhook_config = WebhookCreate(
1410+
>>> url="https://example.com/webhook",
1411+
>>> headers={"Authorization": "Bearer token"}
1412+
>>> )
1413+
>>> webhook = client.create_webhook(webhook_config)
1414+
"""
1415+
response = self._http_requests.post(
1416+
"webhooks", webhook.model_dump(by_alias=True, exclude_none=True)
1417+
)
1418+
1419+
return Webhook(**response.json())
1420+
1421+
def update_webhook(self, *, uuid: str, webhook: WebhookUpdate) -> Webhook:
1422+
"""Update an existing webhook.
1423+
1424+
Args:
1425+
uuid: The webhook's unique identifier.
1426+
webhook: The webhook configuration updates.
1427+
1428+
Returns:
1429+
The updated webhook.
1430+
1431+
Raises:
1432+
MeilisearchCommunicationError: If there was an error communicating with the server.
1433+
MeilisearchApiError: If the Meilisearch API returned an error.
1434+
1435+
Examples:
1436+
>>> from meilisearch_python_sdk import Client
1437+
>>> from meilisearch_python_sdk.models.webhook import WebhookUpdate
1438+
>>> client = Client("http://localhost.com", "masterKey")
1439+
>>> webhook_update = WebhookUpdate(url="https://example.com/new-webhook")
1440+
>>> webhook = client.update_webhook("abc-123", webhook_update)
1441+
"""
1442+
response = self._http_requests.patch(
1443+
f"webhooks/{uuid}", webhook.model_dump(by_alias=True, exclude_none=True)
1444+
)
1445+
1446+
return Webhook(**response.json())
1447+
1448+
def delete_webhook(self, uuid: str) -> int:
1449+
"""Delete a webhook.
1450+
1451+
Args:
1452+
uuid: The webhook's unique identifier.
1453+
1454+
Returns:
1455+
The Response status code. 204 signifies a successful delete.
1456+
1457+
Raises:
1458+
MeilisearchCommunicationError: If there was an error communicating with the server.
1459+
MeilisearchApiError: If the Meilisearch API returned an error.
1460+
1461+
Examples:
1462+
>>> from meilisearch_python_sdk import Client
1463+
>>> client = Client("http://localhost.com", "masterKey")
1464+
>>> client.delete_webhook("abc-123")
1465+
"""
1466+
response = self._http_requests.delete(f"webhooks/{uuid}")
1467+
return response.status_code
1468+
12321469
def create_dump(self) -> TaskInfo:
12331470
"""Trigger the creation of a Meilisearch dump.
12341471
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from __future__ import annotations
2+
3+
from camel_converter.pydantic_base import CamelBase
4+
5+
6+
class Webhook(CamelBase):
7+
uuid: str
8+
url: str
9+
headers: dict[str, str] | None = None
10+
is_editable: bool
11+
12+
13+
class Webhooks(CamelBase):
14+
results: list[Webhook]
15+
16+
17+
class WebhookCreate(CamelBase):
18+
url: str
19+
headers: dict[str, str] | None = None
20+
21+
22+
class WebhookUpdate(CamelBase):
23+
url: str | None = None
24+
headers: dict[str, str] | None = None

tests/conftest.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from meilisearch_python_sdk import AsyncClient, Client
2020
from meilisearch_python_sdk._task import async_wait_for_task, wait_for_task
21+
from meilisearch_python_sdk.errors import MeilisearchApiError
2122
from meilisearch_python_sdk.json_handler import OrjsonHandler, UjsonHandler
2223
from meilisearch_python_sdk.models.settings import (
2324
Embedders,
@@ -29,6 +30,7 @@
2930
TypoTolerance,
3031
UserProvidedEmbedder,
3132
)
33+
from meilisearch_python_sdk.models.webhook import WebhookCreate
3234

3335
MASTER_KEY = "masterKey"
3436

@@ -353,3 +355,17 @@ def new_settings_localized():
353355
LocalizedAttributes(locales=["ita"], attribute_patterns=["*_it"]),
354356
],
355357
)
358+
359+
360+
@pytest.fixture
361+
def webhook(client):
362+
webhook_config = WebhookCreate(url="https://example.com/webhook")
363+
webhook = client.create_webhook(webhook_config)
364+
365+
yield webhook
366+
367+
try:
368+
client.delete_webhook(webhook.uuid)
369+
except MeilisearchApiError as e:
370+
if "webhook_not_found" in str(e):
371+
pass

tests/test_async_client.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from meilisearch_python_sdk.models.client import KeyCreate, KeyUpdate, Network
2626
from meilisearch_python_sdk.models.index import IndexInfo
2727
from meilisearch_python_sdk.models.version import Version
28+
from meilisearch_python_sdk.models.webhook import WebhookCreate, WebhookUpdate
2829
from meilisearch_python_sdk.types import JsonDict
2930

3031

@@ -1085,3 +1086,48 @@ async def test_add_or_update_networks(async_client):
10851086
assert len(response.remotes) >= 2
10861087
assert "remote_1" in response.remotes
10871088
assert "remote_2" in response.remotes
1089+
1090+
1091+
async def test_get_webhooks(async_client, webhook):
1092+
response = await async_client.get_webhooks()
1093+
1094+
assert response.results is not None
1095+
assert webhook in response.results
1096+
1097+
1098+
async def test_create_webhook(async_client):
1099+
webhook_config = WebhookCreate(
1100+
url="https://example.com/webhook", headers={"Authorization": "Bearer token"}
1101+
)
1102+
webhook = await async_client.create_webhook(webhook_config)
1103+
1104+
try:
1105+
assert webhook.uuid is not None
1106+
assert webhook.url == "https://example.com/webhook"
1107+
assert webhook.headers == {"Authorization": "Bearer token"}
1108+
assert webhook.is_editable is True
1109+
finally:
1110+
await async_client.delete_webhook(webhook.uuid)
1111+
1112+
1113+
async def test_get_webhook(async_client, webhook):
1114+
result = await async_client.get_webhook(webhook.uuid)
1115+
1116+
assert result.uuid == webhook.uuid
1117+
assert result.url == webhook.url
1118+
assert result.is_editable == webhook.is_editable
1119+
1120+
1121+
async def test_update_webhook(async_client, webhook):
1122+
update = WebhookUpdate(url="https://example.com/new-webhook", headers={"X-Custom": "value"})
1123+
updated = await async_client.update_webhook(uuid=webhook.uuid, webhook=update)
1124+
1125+
assert updated.uuid == webhook.uuid
1126+
assert updated.url == "https://example.com/new-webhook"
1127+
assert updated.headers == {"X-Custom": "value"}
1128+
1129+
1130+
async def test_delete_webhook(async_client, webhook):
1131+
status_code = await async_client.delete_webhook(webhook.uuid)
1132+
1133+
assert status_code == 204

0 commit comments

Comments
 (0)