Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
22 changes: 20 additions & 2 deletions sdk/python/scilog/scicat.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ def authenticate(self, username, password):
return token


class SciCat:
class SciCatLegacy:
def __init__(self, *args, **kwargs):
self.http_client = SciCatRestAPI(*args, **kwargs)

@property
def proposals(self):
url = self.http_client.address + "/proposals"
return self.http_client.get_request(url, headers=HEADER_JSON)


class SciCatNew:
max_iterations = 1000

def __init__(self, *args, return_options=None, **kwargs):
Expand All @@ -39,7 +49,7 @@ def _proposals_batch(self):
if iteration > self.max_iterations:
raise RuntimeError("Exceeded maximum iterations in proposals_batch")
proposals = self.http_client.get_request(
f"{url}?filter={quote(dumps(filter))}", headers=HEADER_JSON
f"{url}?filters={quote(dumps(filter))}", headers=HEADER_JSON
)
if not proposals or len(proposals) == 0:
break
Expand All @@ -53,5 +63,13 @@ def proposals(self):
return generator if lazy else list(generator)


class SciCat:
def __new__(cls, *args, **kwargs):
token_prefix = kwargs.get("options", {}).get("token_prefix")
if token_prefix:
return SciCatNew(*args, **kwargs)
return SciCatLegacy(*args, **kwargs)


class SciCatAuthError(AuthError):
pass
93 changes: 12 additions & 81 deletions sdk/python/tests/test_scicat.py
Original file line number Diff line number Diff line change
@@ -1,92 +1,23 @@
from json import dumps
from unittest.mock import ANY, Mock, call, patch
from urllib.parse import quote
from unittest.mock import ANY, Mock, patch

import pytest

from scilog import SciCat

ADDRESS = "http://scicat"


@patch("requests.post")
@patch("requests.get")
@pytest.mark.parametrize(
"token_prefix",
"token_prefix, expected_class",
[
"",
None,
"Bearer ",
["", "SciCatLegacy"],
[None, "SciCatLegacy"],
["Bearer ", "SciCatNew"],
],
)
def test_get_proposals(mock_get, mock_post, token_prefix):
options = {
"username": f"username{token_prefix}",
"password": "password",
"login_path": f"{ADDRESS}/login",
"token_prefix": token_prefix,
}
headers = {"Content-type": "application/json", "Accept": "application/json"}
token = "token123"

scicat = SciCat(ADDRESS, options=options)
scicat.http_client.config = {}
mock_response = Mock()
mock_response.json.return_value = {"id": token}
mock_post.return_value = mock_response
list(scicat.proposals)
mock_post.assert_called_with(
options["login_path"],
json={"username": options["username"], "password": options["password"]},
headers=headers,
timeout=ANY,
verify=True,
def test_new(token_prefix, expected_class):
scicat = SciCat(
"http://scicat",
options={
"token_prefix": token_prefix,
},
)
filter = {"limits": {"skip": 0, "limit": 500}}
mock_get.assert_called_with(
f"{ADDRESS}/proposals?filter={quote(dumps(filter))}",
params=None,
headers={**headers, "Authorization": f"{token_prefix or ''}{token}"},
timeout=ANY,
verify=True,
)


@patch("scilog.scicat.SciCatRestAPI.get_request")
def test__proposals_batch(mock_get):
scicat = SciCat(ADDRESS)
scicat.http_client.config = {}

mock_get.side_effect = [[1, 2], [3, 4], []]
filters = [{"limits": {"skip": 0, "limit": 500}}, {"limits": {"skip": 500, "limit": 500}}]
for _ in scicat._proposals_batch():
continue

assert mock_get.call_count == 3

expected_calls = [
call(f"{ADDRESS}/proposals?filter={quote(dumps(filters[0]))}", headers=ANY),
call(f"{ADDRESS}/proposals?filter={quote(dumps(filters[1]))}", headers=ANY),
]
mock_get.assert_has_calls(expected_calls, any_order=False)


@patch("scilog.scicat.SciCatRestAPI.get_request")
@pytest.mark.parametrize(
"return_options",
[
None,
{"lazy": True},
{"lazy": False},
],
)
def test_proposals(mock_get, return_options):
scicat = SciCat(ADDRESS, return_options=return_options)
scicat.http_client.config = {}
mock_get.side_effect = [[1, 2], [3, 4], []]
proposals = [1, 2, 3, 4]
scicat_proposals = scicat.proposals
for i, p in enumerate(scicat_proposals):
assert p == proposals[i]
lazy = return_options.get("lazy", False) if return_options else False
assert len(list(scicat_proposals)) == (0 if lazy else len(proposals))
assert scicat.__class__.__name__ == expected_class
47 changes: 47 additions & 0 deletions sdk/python/tests/test_scicat_legacy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from unittest.mock import ANY, Mock, patch

import pytest

from scilog import SciCat


@patch("requests.post")
@patch("requests.get")
@pytest.mark.parametrize(
"token_prefix",
[
"",
None,
],
)
def test_get_proposals(mock_get, mock_post, token_prefix):
address = "http://scicat"
options = {
"username": f"username{token_prefix}",
"password": "password",
"login_path": f"{address}/login",
"token_prefix": token_prefix,
}
headers = {"Content-type": "application/json", "Accept": "application/json"}
token = "token123"

scicat = SciCat(address, options=options)
mock_response = Mock()
mock_response.json.return_value = {"id": token}
mock_post.return_value = mock_response
scicat.http_client.config = {}
scicat.proposals
mock_post.assert_called_with(
options["login_path"],
json={"username": options["username"], "password": options["password"]},
headers=headers,
timeout=ANY,
verify=True,
)
mock_get.assert_called_with(
f"{address}/proposals",
params=None,
headers={**headers, "Authorization": f"{token_prefix or ''}{token}"},
timeout=ANY,
verify=True,
)
87 changes: 87 additions & 0 deletions sdk/python/tests/test_scicat_new.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from json import dumps
from unittest.mock import ANY, Mock, call, patch
from urllib.parse import quote

import pytest

from scilog import SciCat

ADDRESS = "http://scicat"

OPTIONS = {
"username": f"username",
"password": "password",
"login_path": f"{ADDRESS}/login",
"token_prefix": "Bearer ",
}


@pytest.fixture()
def scicat():
scicat = SciCat(ADDRESS, options=OPTIONS)
scicat.http_client.config = {}
return scicat


@patch("requests.post")
@patch("requests.get")
def test_get_proposals(mock_get, mock_post, scicat):
headers = {"Content-type": "application/json", "Accept": "application/json"}
token = "token123"
mock_response = Mock()
mock_response.json.return_value = {"id": token}
mock_post.return_value = mock_response
list(scicat.proposals)
mock_post.assert_called_with(
OPTIONS["login_path"],
json={"username": OPTIONS["username"], "password": OPTIONS["password"]},
headers=headers,
timeout=ANY,
verify=True,
)
filter = {"limits": {"skip": 0, "limit": 500}}
mock_get.assert_called_with(
f"{ADDRESS}/proposals?filters={quote(dumps(filter))}",
params=None,
headers={**headers, "Authorization": f"{OPTIONS['token_prefix']}{token}"},
timeout=ANY,
verify=True,
)


@patch("scilog.scicat.SciCatRestAPI.get_request")
def test__proposals_batch(mock_get, scicat):

mock_get.side_effect = [[1, 2], [3, 4], []]
filters = [{"limits": {"skip": 0, "limit": 500}}, {"limits": {"skip": 500, "limit": 500}}]
for _ in scicat._proposals_batch():
continue

assert mock_get.call_count == 3

expected_calls = [
call(f"{ADDRESS}/proposals?filters={quote(dumps(filters[0]))}", headers=ANY),
call(f"{ADDRESS}/proposals?filters={quote(dumps(filters[1]))}", headers=ANY),
]
mock_get.assert_has_calls(expected_calls, any_order=False)


@patch("scilog.scicat.SciCatRestAPI.get_request")
@pytest.mark.parametrize(
"return_options",
[
None,
{"lazy": True},
{"lazy": False},
],
)
def test_proposals(mock_get, return_options):
scicat = SciCat(ADDRESS, options=OPTIONS, return_options=return_options)
scicat.http_client.config = {}
mock_get.side_effect = [[1, 2], [3, 4], []]
proposals = [1, 2, 3, 4]
scicat_proposals = scicat.proposals
for i, p in enumerate(scicat_proposals):
assert p == proposals[i]
lazy = return_options.get("lazy", False) if return_options else False
assert len(list(scicat_proposals)) == (0 if lazy else len(proposals))