Skip to content

Commit

Permalink
Merge pull request #55 from crowdin/create_with_fetch_all
Browse files Browse the repository at this point in the history
create with_fetch_all functional
  • Loading branch information
andrii-bodnar authored Oct 31, 2022
2 parents 2fd61c7 + 1854f22 commit 491f876
Show file tree
Hide file tree
Showing 30 changed files with 258 additions and 68 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Crowdin API is a full-featured RESTful API that helps you to integrate localizat
## Table of Contents
* [Installation](#installation)
* [Quick Start](#quick-start)
* [Fetch all records](#fetch-all-records)
* [Seeking Assistance](#seeking-assistance)
* [Contributing](#contributing)
* [License](#license)
Expand Down Expand Up @@ -98,6 +99,22 @@ my_file = client.source_files.add_file(__project_id__, storage['data']['id'], fi
print(my_file)
```

### Fetch all records

It is possible to fetch all records from paginatable methods (where we have limit and offset in arguments).

```python
from crowdin_api import CrowdinClient

client = CrowdinClient(token='__token__')

# get all projects
print(client.projects.with_fetch_all().list_projects())

# get projects but not more than 1000
print(client.projects.with_fetch_all(1000).list_projects())
```

## Seeking Assistance

If you find any problems or would like to suggest a feature, please read the [How can I contribute](https://github.com/crowdin/crowdin-api-client-python/blob/main/CONTRIBUTING.md#how-can-i-contribute) section in our contributing guidelines.
Expand Down
59 changes: 59 additions & 0 deletions crowdin_api/api_resources/abstract/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class BaseResource(metaclass=ABCMeta):
def __init__(self, requester: APIRequester, page_size=25):
self.requester = requester
self.page_size = page_size
self._flag_fetch_all = None
self._max_limit = None

def _get_page_params(self, page: int):
if page < 1:
Expand Down Expand Up @@ -37,3 +39,60 @@ def get_page_params(
raise ValueError("The limit must be greater than or equal to 1.")

return {"offset": offset, "limit": limit}

def with_fetch_all(self, max_limit: Optional[int] = None):
self._max_limit = max_limit
self._flag_fetch_all = True
return self

def _get_entire_data(self, method: str, path: str, params: Optional[dict] = None):
if not self._flag_fetch_all:
return self.requester.request(
method=method,
path=path,
params=params,
)

contents = self._fetch_all(
method=method,
path=path,
params=params,
max_amount=self._max_limit
)
self._flag_fetch_all = False
self._max_limit = None
return contents

def _fetch_all(
self,
method: str,
path: str,
params: Optional[dict] = None,
max_amount: Optional[int] = None
) -> list:
limit = 500
offset = 0
join_data = []
if params is None:
params = {}

if max_amount and max_amount < limit:
limit = max_amount

while True:
params.update({"limit": limit, "offset": offset})

content = self.requester.request(method=method, path=path, params=params)
data = content.get("data", [])
data and join_data.extend(data)

if len(data) < limit or (max_amount and len(join_data) >= max_amount):
break
else:
offset += limit

if max_amount and max_amount < len(join_data) + limit:
limit = max_amount - len(join_data)

content["data"] = join_data
return content
113 changes: 113 additions & 0 deletions crowdin_api/api_resources/abstract/tests/test_resources.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from unittest import mock
from unittest.mock import Mock

import pytest
from crowdin_api.api_resources.abstract.resources import BaseResource
from crowdin_api.requester import APIRequester
Expand Down Expand Up @@ -31,3 +34,113 @@ def test_get_page_params_invalid_params(self, kwargs, base_absolut_url):
resource = BaseResource(requester=APIRequester(base_url=base_absolut_url))
with pytest.raises(ValueError):
resource.get_page_params(**kwargs)

@pytest.mark.parametrize(
"in_param,out_param",
(
({"max_limit": None}, None),
({"max_limit": 0}, 0),
({"max_limit": 1}, 1),
({"max_limit": 100}, 100),
),
)
def test_with_fetch_all(self, in_param, out_param, base_absolut_url):
resource = BaseResource(requester=APIRequester(base_url=base_absolut_url))

resource.with_fetch_all(**in_param)

assert resource._max_limit == out_param
assert resource._flag_fetch_all is True

@pytest.mark.parametrize(
"incoming_data, request_data",
(
(
{"method": "get", "path": ""},
{"method": "get", "path": "", "params": None},
),
(
{"method": "get", "path": "test", "params": "params"},
{"method": "get", "path": "test", "params": "params"},
),
),
)
@mock.patch("crowdin_api.requester.APIRequester.request")
def test__get_list(self, m_request, incoming_data, request_data, base_absolut_url):
m_request.return_value = "response"

resource = BaseResource(requester=APIRequester(base_url=base_absolut_url))

assert resource._get_entire_data(**incoming_data) == "response"
m_request.assert_called_once_with(**request_data)

@pytest.mark.parametrize(
"max_limit, incoming_data, request_data",
(
(
None,
{"method": "get", "path": ""},
{"method": "get", "path": "", "params": None, "max_amount": None},
),
(
0,
{"method": "get", "path": "test", "params": "params"},
{"method": "get", "path": "test", "params": "params", "max_amount": 0},
),
(
1,
{"method": "get", "path": "test", "params": "params"},
{"method": "get", "path": "test", "params": "params", "max_amount": 1},
),
),
)
def test__get_list_with__flag_fetch_all(
self,
incoming_data,
max_limit,
request_data,
base_absolut_url
):
resource = BaseResource(requester=APIRequester(base_url=base_absolut_url))
resource.with_fetch_all(max_limit=max_limit)

resource._fetch_all = Mock(return_value="response")

assert resource._flag_fetch_all is True
assert resource._get_entire_data(**incoming_data) == "response"
assert resource._flag_fetch_all is False
assert resource._max_limit is None
resource._fetch_all.assert_called_once_with(**request_data)

@pytest.mark.parametrize(
"incoming_data, expected_result",
(
(
{"method": "get", "path": ""},
{"data": [None] * 1}
),
(
{"method": "get", "path": ""},
{"data": [None] * 499}
),
(
{"method": "get", "path": "", "params": None, "max_amount": 0},
{"data": []}
),
(
{"method": "get", "path": "", "params": None, "max_amount": 1},
{"data": [None] * 1}
),
(
{"method": "get", "path": "", "params": None, "max_amount": 499},
{"data": [None] * 499}
),
),
)
@mock.patch("crowdin_api.requester.APIRequester.request")
def test__fetch_all(self, m_request, incoming_data, expected_result, base_absolut_url):
m_request.return_value = expected_result
resource = BaseResource(requester=APIRequester(base_url=base_absolut_url))

testing_result = resource._fetch_all(**incoming_data)
assert testing_result == expected_result
4 changes: 2 additions & 2 deletions crowdin_api/api_resources/bundles/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def list_bundles(

params = self.get_page_params(offset=offset, limit=limit)

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_bundles_path(projectId=projectId),
params=params,
Expand Down Expand Up @@ -146,7 +146,7 @@ def get_bundle_list_files(

params = self.get_page_params(offset=offset, limit=limit)

return self.requester.request(
return self._get_entire_data(
method="get",
path=f"{self.get_bundles_path(projectId=projectId, bundleId=bundleId)}/files",
params=params,
Expand Down
2 changes: 1 addition & 1 deletion crowdin_api/api_resources/dictionaries/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def list_dictionaries(
params = self.get_page_params(page=page, offset=offset, limit=limit)
params["languageIds"] = None if languageIds is None else ",".join(languageIds)

return self.requester.request(
return self._get_entire_data(
method="get",
path=f"projects/{projectId}/dictionaries",
params=params,
Expand Down
2 changes: 1 addition & 1 deletion crowdin_api/api_resources/distributions/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def list_distributions(
https://developer.crowdin.com/api/v2/#operation/api.projects.distributions.getMany
"""

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_distributions_path(projectId=projectId),
params=self.get_page_params(offset=offset, limit=limit),
Expand Down
6 changes: 3 additions & 3 deletions crowdin_api/api_resources/glossaries/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def list_glossaries(
https://developer.crowdin.com/api/v2/#operation/api.glossaries.getMany
"""

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_glossaries_path(),
params=self.get_page_params(page=page, offset=offset, limit=limit),
Expand Down Expand Up @@ -229,7 +229,7 @@ def list_terms(

params.update(self.get_page_params(offset=offset, limit=limit))

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_terms_path(glossaryId=glossaryId),
params=params,
Expand Down Expand Up @@ -354,7 +354,7 @@ def list_concepts(
https://developer.crowdin.com/api/v2/#operation/api.glossaries.concepts.getMany
"""

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_concepts_path(glossaryId=glossaryId),
params=self.get_page_params(offset=offset, limit=limit),
Expand Down
2 changes: 1 addition & 1 deletion crowdin_api/api_resources/groups/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def list_groups(
params = {"parentId": parentId}
params.update(self.get_page_params(offset=offset, limit=limit))

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_groups_path(),
params=params,
Expand Down
2 changes: 1 addition & 1 deletion crowdin_api/api_resources/labels/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def list_labels(
https://developer.crowdin.com/api/v2/#operation/api.projects.labels.getMany
"""

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_labels_path(projectId=projectId),
params=self.get_page_params(page=page, offset=offset, limit=limit),
Expand Down
2 changes: 1 addition & 1 deletion crowdin_api/api_resources/languages/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def list_supported_languages(
https://developer.crowdin.com/api/v2/#operation/api.languages.getMany
"""

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_languages_path(),
params=self.get_page_params(page=page, offset=offset, limit=limit),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def list_mts(self, limit: Optional[int] = None, offset: Optional[int] = None):
https://developer.crowdin.com/api/v2/#operation/api.mts.getMany
"""

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_mts_path(),
params=self.get_page_params(offset=offset, limit=limit),
Expand Down
2 changes: 1 addition & 1 deletion crowdin_api/api_resources/projects/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def list_projects(
params = {"userId": userId, "hasManagerAccess": hasManagerAccess}
params.update(self.get_page_params(page=page, offset=offset, limit=limit))

return self.requester.request(method="get", path=self.get_projects_path(), params=params)
return self._get_entire_data(method="get", path=self.get_projects_path(), params=params)

def add_project(self, request_data: Dict):
"""
Expand Down
2 changes: 1 addition & 1 deletion crowdin_api/api_resources/reports/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def list_report_settings_template(
https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.reports.settings-templates.getMany
"""

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_report_settings_templates_path(projectId=projectId),
params=self.get_page_params(offset=offset, limit=limit),
Expand Down
4 changes: 2 additions & 2 deletions crowdin_api/api_resources/screenshots/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def list_screenshots(
https://developer.crowdin.com/api/v2/#operation/api.projects.screenshots.getMany
"""

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_screenshots_path(projectId=projectId),
params=self.get_page_params(page=page, offset=offset, limit=limit),
Expand Down Expand Up @@ -153,7 +153,7 @@ def list_tags(
https://developer.crowdin.com/api/v2/#operation/api.projects.screenshots.tags.getMany
"""

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_tags_path(projectId=projectId, screenshotId=screenshotId),
params=self.get_page_params(page=page, offset=offset, limit=limit),
Expand Down
8 changes: 4 additions & 4 deletions crowdin_api/api_resources/source_files/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def list_project_branches(
params = {"name": name}
params.update(self.get_page_params(page=page, offset=offset, limit=limit))

return self.requester.request(
return self._get_entire_data(
method="get", path=self.get_branch_path(projectId=projectId), params=params
)

Expand Down Expand Up @@ -159,7 +159,7 @@ def list_directories(
}
params.update(self.get_page_params(page=page, offset=offset, limit=limit))

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_directory_path(projectId=projectId),
params=params,
Expand Down Expand Up @@ -270,7 +270,7 @@ def list_files(
}
params.update(self.get_page_params(page=page, offset=offset, limit=limit))

return self.requester.request(
return self._get_entire_data(
method="get", path=self.get_file_path(projectId=projectId), params=params
)

Expand Down Expand Up @@ -441,7 +441,7 @@ def list_file_revisions(
https://developer.crowdin.com/api/v2/#operation/api.projects.files.revisions.getMany
"""

return self.requester.request(
return self._get_entire_data(
method="get",
path=self.get_file_revisions_path(projectId=projectId, fileId=fileId),
params=self.get_page_params(page=page, offset=offset, limit=limit),
Expand Down
Loading

0 comments on commit 491f876

Please sign in to comment.