Skip to content

Commit ba9ae2e

Browse files
authored
Merge pull request #382 from meilisearch/keys
Addition related to API keys
2 parents e25c1de + 4af8247 commit ba9ae2e

File tree

4 files changed

+218
-4
lines changed

4 files changed

+218
-4
lines changed

meilisearch/_httprequests.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ def post(
6060
) -> Any:
6161
return self.send_request(requests.post, path, body, content_type)
6262

63+
def patch(
64+
self,
65+
path: str,
66+
body: Optional[Union[Dict[str, Any], List[Dict[str, Any]], List[str], str]] = None,
67+
content_type: Optional[str] = 'application/json',
68+
) -> Any:
69+
return self.send_request(requests.patch, path, body, content_type)
70+
6371
def put(
6472
self,
6573
path: str,

meilisearch/client.py

Lines changed: 102 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,34 @@ def is_healthy(self) -> bool:
217217
return False
218218
return True
219219

220-
def get_keys(self) -> Dict[str, str]:
221-
"""Get all keys.
220+
def get_key(self, key: str) -> Dict[str, Any]:
221+
"""Gets information about a specific API key.
222222
223-
Get the public and private keys.
223+
Parameters
224+
----------
225+
key:
226+
The key for which to retrieve the information.
227+
228+
Returns
229+
-------
230+
key:
231+
The API key.
232+
https://docs.meilisearch.com/reference/api/keys.html#get-key
233+
234+
Raises
235+
------
236+
MeiliSearchApiError
237+
An error containing details about why MeiliSearch can't process your request. MeiliSearch error codes are described here: https://docs.meilisearch.com/errors/#meilisearch-errors
238+
"""
239+
return self.http.get(f'{self.config.paths.keys}/{key}')
240+
241+
def get_keys(self) -> List[Dict[str, Any]]:
242+
"""Gets the MeiliSearch API keys.
224243
225244
Returns
226245
-------
227246
keys:
228-
Dictionary of keys and their information.
247+
API keys.
229248
https://docs.meilisearch.com/reference/api/keys.html#get-keys
230249
231250
Raises
@@ -235,6 +254,85 @@ def get_keys(self) -> Dict[str, str]:
235254
"""
236255
return self.http.get(self.config.paths.keys)
237256

257+
def create_key(
258+
self,
259+
options: Dict[str, Any]
260+
) -> Dict[str, Any]:
261+
"""Creates a new API key.
262+
263+
Parameters
264+
----------
265+
options:
266+
Options, the information to use in creating the key (ex: { 'actions': ['*'], 'indexes': ['movies'], 'description': 'Search Key', 'expiresAt': '22-01-01' }).
267+
An `actions`, an `indexes` and a `expiresAt` fields are mandatory,`None` should be specified for no expiration date.
268+
`actions`: A list of actions permitted for the key. ["*"] for all actions.
269+
`indexes`: A list of indexes permitted for the key. ["*"] for all indexes.
270+
Note that if an expires_at value is included it should be in UTC time.
271+
272+
Returns
273+
-------
274+
keys:
275+
The new API key.
276+
https://docs.meilisearch.com/reference/api/keys.html#get-keys
277+
278+
Raises
279+
------
280+
MeiliSearchApiError
281+
An error containing details about why MeiliSearch can't process your request. MeiliSearch error codes are described here: https://docs.meilisearch.com/errors/#meilisearch-errors
282+
"""
283+
return self.http.post(f'{self.config.paths.keys}', options)
284+
285+
def udpate_key(
286+
self,
287+
key: str,
288+
options: Dict[str, Any]
289+
) -> Dict[str, Any]:
290+
"""Update an API key.
291+
292+
Parameters
293+
294+
----------
295+
key:
296+
The key for which to update the information.
297+
options:
298+
The information to use in creating the key (ex: { 'description': 'Search Key', 'expiresAt': '22-01-01' }). Note that if an
299+
expires_at value is included it should be in UTC time.
300+
301+
Returns
302+
-------
303+
key:
304+
The updated API key.
305+
https://docs.meilisearch.com/reference/api/keys.html#get-keys
306+
307+
Raises
308+
------
309+
MeiliSearchApiError
310+
An error containing details about why MeiliSearch can't process your request. MeiliSearch error codes are described here: https://docs.meilisearch.com/errors/#meilisearch-errors
311+
"""
312+
url = f'{self.config.paths.keys}/{key}'
313+
return self.http.patch(url, options)
314+
315+
def delete_key(self, key: str) -> Dict[str, int]:
316+
"""Deletes an API key.
317+
318+
Parameters
319+
----------
320+
key:
321+
The key to delete.
322+
323+
Returns
324+
-------
325+
keys:
326+
The Response status code. 204 signifies a successful delete.
327+
https://docs.meilisearch.com/reference/api/keys.html#get-keys
328+
329+
Raises
330+
------
331+
MeiliSearchApiError
332+
An error containing details about why MeiliSearch can't process your request. MeiliSearch error codes are described here: https://docs.meilisearch.com/errors/#meilisearch-errors
333+
"""
334+
return self.http.delete(f'{self.config.paths.keys}/{key}')
335+
238336
def get_version(self) -> Dict[str, str]:
239337
"""Get version MeiliSearch
240338
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import pytest
2+
from tests import common
3+
from datetime import datetime
4+
from meilisearch.errors import MeiliSearchApiError
5+
6+
def test_get_keys_default(client):
7+
"""Tests if public and private keys have been generated and can be retrieved."""
8+
keys = client.get_keys()
9+
assert isinstance(keys, list)
10+
assert len(keys) == 2
11+
assert 'actions' in keys[0]
12+
assert 'indexes' in keys[0]
13+
assert keys[0]['key'] is not None
14+
assert keys[1]['key'] is not None
15+
16+
def test_get_key(client, test_key):
17+
"""Tests if a key can be retrieved."""
18+
key = client.get_key(test_key['key'])
19+
assert isinstance(key, dict)
20+
assert 'actions' in key
21+
assert 'indexes' in key
22+
assert key['createdAt'] is not None
23+
24+
def test_get_key_inexistent(client):
25+
"""Tests getting a key that does not exists."""
26+
with pytest.raises(Exception):
27+
client.get_key('No existing key')
28+
29+
def test_create_keys_default(client, test_key_info):
30+
"""Tests the creation of a key with no optional argument."""
31+
key = client.create_key(test_key_info)
32+
assert isinstance(key, dict)
33+
assert 'key' in key
34+
assert 'actions' in key
35+
assert 'indexes' in key
36+
assert key['key'] is not None
37+
assert key['expiresAt'] is None
38+
assert key['createdAt'] is not None
39+
assert key['updatedAt'] is not None
40+
assert key['key'] is not None
41+
assert key['actions'] == test_key_info['actions']
42+
assert key['indexes'] == test_key_info['indexes']
43+
44+
def test_create_keys_with_options(client, test_key_info):
45+
"""Tests the creation of a key with arguments."""
46+
key = client.create_key(options={'description': test_key_info['description'], 'actions': test_key_info['actions'], 'indexes': test_key_info['indexes'], 'expiresAt': datetime(2030, 6, 4, 21, 8, 12, 32).isoformat()[:-3]+'Z' })
47+
assert isinstance(key, dict)
48+
assert key['key'] is not None
49+
assert key['description'] == test_key_info['description']
50+
assert key['expiresAt'] is not None
51+
assert key['createdAt'] is not None
52+
assert key['updatedAt'] is not None
53+
assert key['actions'] == test_key_info['actions']
54+
assert key['indexes'] == test_key_info['indexes']
55+
56+
def test_create_keys_without_actions(client):
57+
"""Tests the creation of a key with missing arguments."""
58+
with pytest.raises(MeiliSearchApiError):
59+
client.create_key(options={'indexes': [common.INDEX_UID]})
60+
61+
def test_update_keys(client, test_key_info):
62+
"""Tests updating a key."""
63+
key = client.create_key(test_key_info)
64+
assert key['actions'] == test_key_info['actions']
65+
update_key = client.udpate_key(key=key['key'], options={ 'actions': ['search'] })
66+
assert update_key['key'] is not None
67+
assert update_key['expiresAt'] is None
68+
assert update_key['actions'] == ['search']
69+
70+
def test_delete_key(client, test_key):
71+
"""Tests deleting a key."""
72+
resp = client.delete_key(test_key['key'])
73+
assert resp.status_code == 204
74+
with pytest.raises(MeiliSearchApiError):
75+
client.get_key(test_key['key'])
76+
77+
def test_delete_key_inexisting(client):
78+
"""Tests deleting a key that does not exists."""
79+
with pytest.raises(MeiliSearchApiError):
80+
client.delete_key('No existing key')

tests/conftest.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from tests import common
66
import meilisearch
7+
from meilisearch.errors import MeiliSearchApiError
78

89
@fixture(scope='session')
910
def client():
@@ -81,3 +82,30 @@ def index_maker(index_name=common.INDEX_UID, documents=small_movies):
8182
index.wait_for_task(task['uid'])
8283
return index
8384
return index_maker
85+
86+
@fixture(scope='function')
87+
def test_key(client):
88+
key_info = {'description': 'test', 'actions': ['search'], 'indexes': ['movies'], 'expiresAt': None}
89+
90+
key = client.create_key(key_info)
91+
92+
yield key
93+
94+
try:
95+
client.delete_key(key['key'])
96+
except MeiliSearchApiError:
97+
pass
98+
99+
100+
@fixture(scope='function')
101+
def test_key_info(client):
102+
key_info = {'description': 'test', 'actions': ['search'], 'indexes': [common.INDEX_UID], 'expiresAt': None}
103+
104+
yield key_info
105+
106+
try:
107+
keys = client.get_keys()
108+
key = next(x for x in keys if x['description'] == key_info['description'])
109+
client.delete_key(key['key'])
110+
except MeiliSearchApiError:
111+
pass

0 commit comments

Comments
 (0)