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
1 change: 1 addition & 0 deletions src/dualentry_cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""DualEntry CLI - command-line interface for DualEntry accounting."""

__version__ = "0.1.0"
USER_AGENT = f"dualentry-cli/{__version__}"
4 changes: 4 additions & 0 deletions src/dualentry_cli/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import keyring
import typer

from dualentry_cli import USER_AGENT

_SERVICE_NAME = "dualentry-cli"
_KEY_NAME_API_KEY = "api_key"

Expand Down Expand Up @@ -86,6 +88,7 @@ def _authorize(api_url: str, redirect_uri: str, code_challenge: str, state: str)
"code_challenge_method": "S256",
"state": state,
},
headers={"User-Agent": USER_AGENT},
timeout=30.0,
)
try:
Expand All @@ -110,6 +113,7 @@ def _exchange_code(api_url: str, code: str, code_verifier: str, redirect_uri: st
"code_verifier": code_verifier,
"redirect_uri": redirect_uri,
},
headers={"User-Agent": USER_AGENT},
timeout=30.0,
)
try:
Expand Down
7 changes: 6 additions & 1 deletion src/dualentry_cli/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import httpx

from dualentry_cli import USER_AGENT


class APIError(Exception):
def __init__(self, status_code: int, detail: str):
Expand All @@ -21,7 +23,10 @@ def __init__(self, api_url: str, *, api_key: str):
self._base_url = f"{self._api_url}/public/v2"
self._client = httpx.Client(
base_url=self._base_url,
headers={"X-API-KEY": api_key},
headers={
"X-API-KEY": api_key,
"User-Agent": USER_AGENT,
},
timeout=30.0,
)

Expand Down
11 changes: 11 additions & 0 deletions tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ def test_authorize_returns_url(self):
assert url == "https://auth.example.com/authorize?state=abc"
assert route.called

@respx.mock
def test_authorize_sends_user_agent(self):
from dualentry_cli import USER_AGENT
from dualentry_cli.auth import _authorize

route = respx.post("https://api.dualentry.com/public/v2/oauth/authorize/").mock(
return_value=httpx.Response(200, json={"authorization_url": "https://auth.example.com/authorize"})
)
_authorize("https://api.dualentry.com", "http://localhost:9876/callback", "challenge", "state")
assert route.calls[0].request.headers["User-Agent"] == USER_AGENT


class TestExchangeCode:
@respx.mock
Expand Down
7 changes: 7 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ def test_sets_api_key_header(self):
client = DualEntryClient(api_url="https://api.dualentry.com", api_key="org_live_xxxx_secret")
assert client._client.headers["X-API-KEY"] == "org_live_xxxx_secret"

def test_sets_user_agent_header(self):
from dualentry_cli import USER_AGENT
from dualentry_cli.client import DualEntryClient

client = DualEntryClient(api_url="https://api.dualentry.com", api_key="test_key")
assert client._client.headers["User-Agent"] == USER_AGENT

@respx.mock
def test_get_request(self):
from dualentry_cli.client import DualEntryClient
Expand Down
Loading