Skip to content

Addition related to API keys #382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 30, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions meilisearch/_httprequests.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ def post(
) -> Any:
return self.send_request(requests.post, path, body, content_type)

def patch(
self,
path: str,
body: Optional[Union[Dict[str, Any], List[Dict[str, Any]], List[str], str]] = None,
content_type: Optional[str] = 'application/json',
) -> Any:
return self.send_request(requests.patch, path, body, content_type)

def put(
self,
path: str,
Expand Down
111 changes: 107 additions & 4 deletions meilisearch/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,15 +217,34 @@ def is_healthy(self) -> bool:
return False
return True

def get_keys(self) -> Dict[str, str]:
"""Get all keys.
def get_key(self, key: str) -> Dict[str, str]:
"""Gets information about a specific API key.

Get the public and private keys.
Parameters
----------
key:
The key for which to retrieve the information.

Returns
-------
key:
The API key.
https://docs.meilisearch.com/reference/api/keys.html#get-key

Raises
------
MeiliSearchApiError
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
"""
return self.http.get(f'{self.config.paths.keys}/{key}')

def get_keys(self) -> Dict[str, str]:
"""Gets the MeiliSearch API keys.

Returns
-------
keys:
Dictionary of keys and their information.
API keys.
https://docs.meilisearch.com/reference/api/keys.html#get-keys

Raises
Expand All @@ -235,6 +254,90 @@ def get_keys(self) -> Dict[str, str]:
"""
return self.http.get(self.config.paths.keys)

def create_key(
self,
options: Optional[Dict[str, Any]] = None
) -> Dict[str, int]:
"""Creates a new API key.

Parameters
----------
options:
Options, the information to use in creating the key (ex: { 'actions': ['*'], 'indexes': ['movies'], 'description': 'Search Key', 'expiresAt': '22-01-01' }).
An `actions`, an `indexes` and a `expiresAt` fields are mandatory,`None` should be specified for no expiration date.
`actions`: A list of actions permitted for the key. ["*"] for all actions.
`indexes`: A list of indexes permitted for the key. ["*"] for all indexes.
Note that if an expires_at value is included it should be in UTC time.

Returns
-------
keys:
The new API key.
https://docs.meilisearch.com/reference/api/keys.html#get-keys

Raises
------
MeiliSearchApiError
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
"""
if options is None:
options= {}
payload = {**options}
return self.http.post(f'{self.config.paths.keys}', payload)

def udpate_key(
self,
key: str,
options: Optional[Dict[str, Any]] = None
) -> Dict[str, int]:
"""Update an API key.

Parameters
----------
key:
The information to use in updating the key. Note that if an expires_at value
is included it should be in UTC time.
options:
Options, the information to use in creating the key (ex: { 'description': 'Search Key', 'expiresAt': '22-01-01' }).

Returns
-------
key:
The updated API key.
https://docs.meilisearch.com/reference/api/keys.html#get-keys

Raises
------
MeiliSearchApiError
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
"""
if options is None:
options= {}
payload = {**options}
url = f'{self.config.paths.keys}/{key}'
return self.http.patch(url, payload)

def delete_key(self, key: str) -> Dict[str, int]:
"""Deletes an API key.

Parameters
----------
key:
The key to delete.

Returns
-------
keys:
The Response status code. 204 signifies a successful delete.
https://docs.meilisearch.com/reference/api/keys.html#get-keys

Raises
------
MeiliSearchApiError
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
"""
return self.http.delete(f'{self.config.paths.keys}/{key}')

def get_version(self) -> Dict[str, str]:
"""Get version MeiliSearch

Expand Down
79 changes: 79 additions & 0 deletions tests/client/test_client_key_meilisearch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import pytest
from tests import common
from datetime import datetime, timedelta

def test_get_keys_default(client):
"""Tests if public and private keys have been generated and can be retrieved."""
key = client.get_keys()
assert isinstance(key, list)
assert len(key) >= 2
assert 'actions' in key[0]
assert 'indexes' in key[0]
assert key[0]['key'] is not None
assert key[1]['key'] is not None

def test_get_key(client):
"""Tests if a key can be retrieved."""
keys = client.get_keys()
key = client.get_key(keys[0]['key'])
assert isinstance(key, dict)
assert 'actions' in key
assert 'indexes' in key
assert key['createdAt'] is not None

def test_get_key_inexistent(client):
"""Tests getting a key that does not exists."""
with pytest.raises(Exception):
client.get_key('No existing key')

def test_create_keys_default(client):
"""Tests the creation of a key with no optional argument."""
key = client.create_key(options={ 'actions': ['*'], 'indexes': [common.INDEX_UID], 'expiresAt': None })
assert isinstance(key, dict)
assert 'key' in key
assert 'actions' in key
assert 'indexes' in key
assert key['key'] is not None
assert key['expiresAt'] is None
assert key['createdAt'] is not None
assert key['updatedAt'] is not None
assert key['key'] is not None
assert key['actions'] == ['*']
assert key['indexes'] == [common.INDEX_UID]

def test_create_keys_with_options(client):
"""Tests the creation of a key with arguments."""
key = client.create_key(options={ 'actions': ['*'], 'indexes': [common.INDEX_UID], 'description': 'Test key', 'expiresAt': datetime(2030, 6, 4, 21, 8, 12, 32).isoformat()[:-3]+'Z' })
assert isinstance(key, dict)
assert key['key'] is not None
assert key['description'] == 'Test key'
assert key['expiresAt'] is not None
assert key['createdAt'] is not None
assert key['updatedAt'] is not None
assert key['actions'] == ['*']
assert key['indexes'] == [common.INDEX_UID]

def test_create_keys_without_actions(client):
"""Tests the creation of a key with missing arguments."""
with pytest.raises(Exception):
client.create_key(options={'indexes': [common.INDEX_UID]})

def test_update_keys(client):
"""Tests updating a key."""
key = client.create_key(options={ 'actions': ['*'], 'indexes': ['*'], 'expiresAt': None })
assert key['actions'] == ['*']
update_key = client.udpate_key(key=key['key'], options={ 'actions': ['search'] })
assert update_key['key'] is not None
assert update_key['expiresAt'] is None
assert update_key['actions'] == ['search']

def test_delete_key(client):
"""Tests deleting a key."""
key = client.create_key(options={ 'actions': ['*'], 'indexes': ['*'], 'expiresAt': None })
resp = client.delete_key(key['key'])
assert resp.status_code == 204

def test_delete_key_inexisting(client):
"""Tests deleting a key that does not exists."""
with pytest.raises(Exception):
client.delete_key('No existing key')