Skip to content

Commit f44715b

Browse files
authored
✨ Select whether to use backup or not when fetching from the API (#14)
1 parent 4bd174f commit f44715b

File tree

5 files changed

+53
-16
lines changed

5 files changed

+53
-16
lines changed

src/gitmojis/cli/__init__.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,20 @@ def get_commands() -> list[click.Command]:
1818
name="gitmojis",
1919
commands=get_commands(),
2020
)
21+
@click.option(
22+
"--use-backup",
23+
is_flag=True,
24+
help="Use the backup to fetch data if the API request fails.",
25+
)
2126
@click.version_option(
2227
package_name="python-gitmojis",
2328
prog_name="gitmojis",
2429
)
2530
@click.pass_context
26-
def gitmojis_cli(context: click.Context) -> None:
31+
def gitmojis_cli(context: click.Context, use_backup: bool) -> None:
2732
"""Command-line interface for managing the official Gitmoji guide."""
2833
# Initialize the context object
2934
context.ensure_object(dict)
3035

3136
# Pass the current state of the Gitmoji guide to the group context
32-
context.obj["guide"] = fetch_guide()
37+
context.obj["guide"] = fetch_guide(use_backup=use_backup)

src/gitmojis/core.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,30 @@
33
import requests
44

55
from . import defaults
6-
from .exceptions import ResponseJsonError
6+
from .exceptions import ApiRequestError, ResponseJsonError
77
from .model import Gitmoji, Guide
88

99

10-
def fetch_guide() -> Guide:
10+
def fetch_guide(*, use_backup: bool = False) -> Guide:
1111
"""Fetch the Gitmoji guide from the official Gitmoji API.
1212
1313
This function sends a GET request to the Gitmoji API to retrieve the current state
1414
of the Gitmoji guide. If the request is successful and contains the expected JSON
1515
data, a `Guide` object is returned. If the expected JSON data is not present, a
16-
`ResponseJsonError` is raised. In case of an HTTP error during the request (e.g.,
17-
connection error, timeout), the function falls back to loading a local copy of the
18-
Gitmoji guide.
16+
`ResponseJsonError` is raised. In case of an HTTP error during the request
17+
(e.g., connection error, timeout), and if `use_backup` is set to `True`, the function
18+
falls back to loading a local copy of the Gitmoji guide. Otherwise, it raises an
19+
`ApiRequestError`.
20+
21+
Args:
22+
use_backup: A flag indicating whether to use a local backup in case of a request
23+
failure. Defaults to `False`.
1924
2025
Returns:
2126
A `Guide` object representing the current state of the Gitmoji API.
2227
2328
Raises:
29+
ApiRequestError: If the API request fails and `use_backup` is `False`.
2430
ResponseJsonError: If the API response doesn't contain the expected JSON data or
2531
if there is an error loading the local backup of the Gitmoji guide.
2632
"""
@@ -29,9 +35,12 @@ def fetch_guide() -> Guide:
2935

3036
if (gitmojis_json := response.json().get(defaults.GITMOJI_API_KEY)) is None:
3137
raise ResponseJsonError
32-
except requests.RequestException:
33-
with defaults.GITMOJI_API_PATH.open(encoding="UTF-8") as f:
34-
gitmojis_json = json.load(f)
38+
except requests.RequestException as exc_info:
39+
if use_backup:
40+
with defaults.GITMOJI_API_PATH.open(encoding="UTF-8") as f:
41+
gitmojis_json = json.load(f)
42+
else:
43+
raise ApiRequestError from exc_info
3544

3645
guide = Guide(gitmojis=[Gitmoji(**gitmoji_json) for gitmoji_json in gitmojis_json])
3746

src/gitmojis/exceptions.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ def __init__(self, message: str | None = None) -> None:
55
super().__init__(message or getattr(self.__class__, "message", ""))
66

77

8-
class ApiError(GitmojisException):
9-
pass
8+
class ApiRequestError(GitmojisException):
9+
message = "request to get the Gitmoji data from the API failed"
1010

1111

12-
class ResponseJsonError(ApiError):
12+
class ResponseJsonError(ApiRequestError):
1313
message = "unsupported format of the JSON data returned by the API"

tests/test_cli.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ def command(context):
4848
assert result.exit_code == 0
4949

5050

51+
def test_gitmojis_cli_passes_use_backup_option_to_fetch_guide(mocker, cli_runner):
52+
fetch_guide = mocker.patch("gitmojis.cli.fetch_guide", return_value=Guide())
53+
54+
@click.command()
55+
@click.pass_context
56+
def command(context):
57+
pass
58+
59+
gitmojis_cli.add_command(command)
60+
61+
cli_runner.invoke(gitmojis_cli, ["--use-backup", "command"])
62+
63+
assert fetch_guide.call_args.kwargs == {"use_backup": True}
64+
65+
5166
def test_sync_command_dumps_api_data_to_backup_file(tmp_path, mocker, cli_runner):
5267
# Mock the backup file as empty file
5368
gitmoji_api_path = tmp_path / "gitmojis.json"

tests/test_core.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from gitmojis import defaults
55
from gitmojis.core import fetch_guide
6-
from gitmojis.exceptions import ResponseJsonError
6+
from gitmojis.exceptions import ApiRequestError, ResponseJsonError
77
from gitmojis.model import Guide
88

99

@@ -31,13 +31,21 @@ def test_fetch_guide_raises_error_if_gitmoji_api_key_not_in_response_json(mocker
3131
fetch_guide()
3232

3333

34-
def test_fetch_guide_fall_back_to_backup_data_if_request_error(mocker):
34+
def test_fetch_guide_falls_back_to_backup_data_if_request_error_and_using_backup(mocker): # fmt: skip
3535
mocker.patch("pathlib.Path.open", mocker.mock_open(read_data="[]"))
3636
mocker.patch("requests.get", side_effect=requests.RequestException)
3737

3838
json_load = mocker.patch("json.load")
3939

40-
guide = fetch_guide()
40+
guide = fetch_guide(use_backup=True)
4141

4242
assert json_load.called
4343
assert guide == Guide(gitmojis=[])
44+
45+
46+
def test_fetch_guide_raises_error_if_request_error_and_not_using_backup(mocker):
47+
mocker.patch("requests.get", side_effect=requests.RequestException)
48+
49+
with pytest.raises(ApiRequestError) as exc_info:
50+
fetch_guide(use_backup=False)
51+
assert isinstance(exc_info.value.__cause__, requests.RequestException)

0 commit comments

Comments
 (0)