Skip to content

Commit 49923c8

Browse files
mariohdrsempe
authored andcommitted
feat(audit-logs):api logs endpoints
1 parent 1819864 commit 49923c8

File tree

8 files changed

+311
-0
lines changed

8 files changed

+311
-0
lines changed

lago_python_client/api_logs/__init__.py

Whitespace-only changes.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from typing import ClassVar, Type
2+
3+
from ..base_client import BaseClient
4+
from ..mixins import (
5+
FindAllCommandMixin,
6+
FindCommandMixin,
7+
)
8+
from ..models.api_log import ApiLogResponse
9+
10+
11+
class ApiLogClient(
12+
FindAllCommandMixin[ApiLogResponse],
13+
FindCommandMixin[ApiLogResponse],
14+
BaseClient,
15+
):
16+
API_RESOURCE: ClassVar[str] = "api_logs"
17+
RESPONSE_MODEL: ClassVar[Type[ApiLogResponse]] = ApiLogResponse
18+
ROOT_NAME: ClassVar[str] = "api_log"

lago_python_client/client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from .webhooks.clients import WebhookClient
2727
from .webhook_endpoints.clients import WebhookEndpointClient
2828
from .activity_logs.clients import ActivityLogClient
29+
from .api_logs.clients import ApiLogClient
2930

3031
try:
3132
from typing import Final
@@ -169,3 +170,7 @@ def webhook_endpoints(self) -> WebhookEndpointClient:
169170
@callable_cached_property
170171
def activity_logs(self) -> ActivityLogClient:
171172
return ActivityLogClient(self.base_api_url, self.api_key)
173+
174+
@callable_cached_property
175+
def api_logs(self) -> ApiLogClient:
176+
return ApiLogClient(self.base_api_url, self.api_key)

lago_python_client/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .activity_log import ActivityLog as ActivityLog, ActivityLogResponse as ActivityLogResponse
2+
from .api_log import ApiLog as ApiLog, ApiLogResponse as ApiLogResponse
23
from .applied_coupon import AppliedCoupon as AppliedCoupon
34
from .billable_metric import (
45
BillableMetric as BillableMetric,

lago_python_client/models/api_log.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from typing import Optional
2+
3+
from lago_python_client.base_model import BaseModel
4+
5+
from ..base_model import BaseResponseModel
6+
7+
8+
class ApiLog(BaseModel):
9+
api_version: str
10+
client: str
11+
http_method: str
12+
http_status: int
13+
request_body: dict
14+
logged_at: str
15+
request_origin: str
16+
created_at: str
17+
request_path: str
18+
request_response: Optional[dict]
19+
request_id: str
20+
21+
22+
class ApiLogResponse(BaseResponseModel):
23+
request_id: str
24+
client: str
25+
http_method: str
26+
http_status: int
27+
request_origin: str
28+
request_path: str
29+
request_body: dict
30+
request_response: Optional[dict]
31+
api_version: str
32+
logged_at: str
33+
created_at: str

tests/fixtures/api_log.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"api_log": {
3+
"request_id": "8fae2f0e-fe8e-44d3-bbf7-1c552eba3a24",
4+
"client": "LagoClient.0.0.0",
5+
"http_method": "post",
6+
"http_status": 200,
7+
"request_origin": "https://request-origin.com",
8+
"request_path": "/api/v1/billable_metrics",
9+
"request_body": {
10+
"billable_metric": {
11+
"name": "Storage",
12+
"code": "storage",
13+
"aggregation_type": "sum_agg",
14+
"description": "GB of storage used in my application",
15+
"recurring": false,
16+
"rounding_function": "round",
17+
"rounding_precision": 2,
18+
"field_name": "gb",
19+
"weighted_interval": "seconds"
20+
}
21+
},
22+
"request_response": {
23+
"billable_metric": {
24+
"lago_id": "4caa4455-07f2-4760-a697-f2644005eb43",
25+
"name": "Storage",
26+
"code": "storage",
27+
"description": "GB of storage used in my application",
28+
"aggregation_type": "sum_agg",
29+
"weighted_interval": "seconds",
30+
"recurring": false,
31+
"rounding_function": "round",
32+
"rounding_precision": 2,
33+
"created_at": "2025-06-20T14:34:25Z",
34+
"field_name": "gb",
35+
"expression": null,
36+
"active_subscriptions_count": 0,
37+
"draft_invoices_count": 0,
38+
"plans_count": 0,
39+
"filters": []
40+
}
41+
},
42+
"api_version": "v1",
43+
"logged_at": "2025-06-20T14:34:25Z",
44+
"created_at": "2025-06-20T14:34:25Z"
45+
}
46+
}

tests/fixtures/api_log_index.json

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
{
2+
"api_logs": [
3+
{
4+
"request_id": "8fae2f0e-fe8e-44d3-bbf7-1c552eba3a24",
5+
"client": "LagoClient.0.0.0",
6+
"http_method": "post",
7+
"http_status": 200,
8+
"request_origin": "https://request-origin.com",
9+
"request_path": "/api/v1/billable_metrics",
10+
"request_body": {
11+
"billable_metric": {
12+
"name": "Storage",
13+
"code": "storage",
14+
"aggregation_type": "sum_agg",
15+
"description": "GB of storage used in my application",
16+
"recurring": false,
17+
"rounding_function": "round",
18+
"rounding_precision": 2,
19+
"field_name": "gb",
20+
"weighted_interval": "seconds"
21+
}
22+
},
23+
"request_response": {
24+
"billable_metric": {
25+
"lago_id": "4caa4455-07f2-4760-a697-f2644005eb43",
26+
"name": "Storage",
27+
"code": "storage",
28+
"description": "GB of storage used in my application",
29+
"aggregation_type": "sum_agg",
30+
"weighted_interval": "seconds",
31+
"recurring": false,
32+
"rounding_function": "round",
33+
"rounding_precision": 2,
34+
"created_at": "2025-06-20T14:34:25Z",
35+
"field_name": "gb",
36+
"expression": null,
37+
"active_subscriptions_count": 0,
38+
"draft_invoices_count": 0,
39+
"plans_count": 0,
40+
"filters": []
41+
}
42+
},
43+
"api_version": "v1",
44+
"logged_at": "2025-06-20T14:34:25Z",
45+
"created_at": "2025-06-20T14:34:25Z"
46+
},
47+
{
48+
"request_id": "65ec835e-43f4-40ad-a4bd-da663349d583",
49+
"client": "LagoClient.0.0.0",
50+
"http_method": "post",
51+
"http_status": 422,
52+
"request_origin": "https://request-origin.com",
53+
"request_path": "/api/v1/billable_metrics",
54+
"request_body": {
55+
"billable_metric": {
56+
"name": "Storage",
57+
"code": "storage",
58+
"aggregation_type": "sum_agg",
59+
"description": "GB of storage used in my application",
60+
"recurring": false,
61+
"expression": "round((ended_at - started_at) * units)",
62+
"rounding_function": "round",
63+
"rounding_precision": 2,
64+
"field_name": "gb",
65+
"weighted_interval": "seconds",
66+
"filters": [
67+
{
68+
"key": {
69+
"value": "<Error: Too many levels of nesting to fake this schema>"
70+
},
71+
"values": {
72+
"value": "<Error: Too many levels of nesting to fake this schema>"
73+
}
74+
},
75+
{
76+
"key": {
77+
"value": "<Error: Too many levels of nesting to fake this schema>"
78+
},
79+
"values": {
80+
"value": "<Error: Too many levels of nesting to fake this schema>"
81+
}
82+
}
83+
]
84+
}
85+
},
86+
"request_response": {
87+
"status": "422",
88+
"error": "Unprocessable Entity",
89+
"code": "validation_errors",
90+
"error_details": {
91+
"expression": [
92+
"invalid_expression"
93+
]
94+
}
95+
},
96+
"api_version": "v1",
97+
"logged_at": "2025-06-20T14:17:13Z",
98+
"created_at": "2025-06-20T14:17:13Z"
99+
}
100+
],
101+
"meta": {
102+
"current_page": 1,
103+
"next_page": null,
104+
"prev_page": null,
105+
"total_pages": 1,
106+
"total_count": 2
107+
}
108+
}

tests/test_api_logs_client.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import os
2+
3+
import pytest
4+
from pytest_httpx import HTTPXMock
5+
6+
from urllib.parse import urlencode
7+
8+
from lago_python_client.client import Client
9+
from lago_python_client.exceptions import LagoApiError
10+
11+
ENDPOINT = "https://api.getlago.com/api/v1/api_logs"
12+
13+
14+
def mock_response(fixture_path):
15+
this_dir = os.path.dirname(os.path.abspath(__file__))
16+
data_path = os.path.join(this_dir, fixture_path)
17+
18+
with open(data_path, "rb") as response:
19+
return response.read()
20+
21+
22+
def test_valid_find_api_log_request(httpx_mock: HTTPXMock):
23+
client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d")
24+
request_id = "8fae2f0e-fe8e-44d3-bbf7-1c552eba3a24"
25+
26+
httpx_mock.add_response(
27+
method="GET",
28+
url=ENDPOINT + f"/{request_id}",
29+
content=mock_response("fixtures/api_log.json"),
30+
)
31+
response = client.api_logs.find(request_id)
32+
33+
assert response.request_id == request_id
34+
assert response.client == "LagoClient.0.0.0"
35+
36+
37+
def test_invalid_find_api_log_request(httpx_mock: HTTPXMock):
38+
client = Client(api_key="invalid")
39+
request_id = "invalid"
40+
41+
httpx_mock.add_response(
42+
method="GET",
43+
url=ENDPOINT + f"/{request_id}",
44+
status_code=404,
45+
content=b"",
46+
)
47+
48+
with pytest.raises(LagoApiError):
49+
client.api_logs.find(request_id)
50+
51+
52+
def test_valid_find_all_api_log_request(httpx_mock: HTTPXMock):
53+
client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d")
54+
55+
httpx_mock.add_response(
56+
method="GET",
57+
url=ENDPOINT,
58+
content=mock_response("fixtures/api_log_index.json"),
59+
)
60+
response = client.api_logs.find_all()
61+
62+
assert response["api_logs"][0].request_id == "8fae2f0e-fe8e-44d3-bbf7-1c552eba3a24"
63+
assert response["api_logs"][1].request_id == "65ec835e-43f4-40ad-a4bd-da663349d583"
64+
assert response["meta"]["current_page"] == 1
65+
66+
67+
def test_valid_find_all_api_log_request_with_options(httpx_mock: HTTPXMock):
68+
client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d")
69+
options = {"per_page": 1, "page": 1}
70+
71+
httpx_mock.add_response(
72+
method="GET",
73+
url=ENDPOINT + f"?{urlencode(options)}",
74+
content=mock_response("fixtures/api_log_index.json"),
75+
)
76+
response = client.api_logs.find_all(options)
77+
78+
assert response["api_logs"][1].request_id == "65ec835e-43f4-40ad-a4bd-da663349d583"
79+
assert response["meta"]["current_page"] == 1
80+
81+
82+
def test_invalid_find_all_api_log_request(httpx_mock: HTTPXMock):
83+
client = Client(api_key="invalid")
84+
85+
httpx_mock.add_response(
86+
method="GET",
87+
url=ENDPOINT,
88+
status_code=404,
89+
content=b"",
90+
)
91+
92+
with pytest.raises(LagoApiError):
93+
client.api_logs.find_all()
94+
95+
96+
def test_invalid_operations_for_api_log_request():
97+
client = Client(api_key="886fe239-927d-4072-ab72-6dd345e8dd0d")
98+
assert not hasattr(client.api_logs, "create")
99+
assert not hasattr(client.api_logs, "update")
100+
assert not hasattr(client.api_logs, "destroy")

0 commit comments

Comments
 (0)