Skip to content

Commit ecf38fc

Browse files
CopilotJuroUhlar
andauthored
Address review feedback: EventEdge.source literal, EdgeRequest IP validation, sync.sh fallback, /edge tests
Co-authored-by: JuroUhlar <11249993+JuroUhlar@users.noreply.github.com>
1 parent c93a704 commit ecf38fc

6 files changed

Lines changed: 105 additions & 7 deletions

File tree

‎docs/EventEdge.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Name | Type | Description | Notes
1818
**vpn** | **bool** | VPN or other anonymizing service has been used when sending the request. | [optional]
1919
**vpn_confidence** | [**VpnConfidence**](VpnConfidence.md) | | [optional]
2020
**vpn_methods** | [**VpnMethods**](VpnMethods.md) | | [optional]
21-
**source** | [**EventSource**](EventSource.md) | |
21+
**source** | **str** | Identifies how the event was generated. Always `edge` for events generated by the Automation Intelligence API (`/edge` endpoint). |
2222

2323
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
2424

‎fingerprint_server_sdk/models/edge_request.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import re # noqa: F401
1919
from typing import Annotated, Any, ClassVar, Optional
2020

21-
from pydantic import BaseModel, ConfigDict, Field, StrictStr
21+
from pydantic import BaseModel, ConfigDict, Field, StrictStr, model_validator
2222
from typing_extensions import Self
2323

2424
from fingerprint_server_sdk.models.edge_request_headers_inner import EdgeRequestHeadersInner
@@ -67,6 +67,13 @@ class EdgeRequest(BaseModel):
6767
protected_namespaces=(),
6868
)
6969

70+
@model_validator(mode='after')
71+
def check_at_least_one_ip_address(self) -> Self:
72+
"""Validates that at least one of `ipv4_address` or `ipv6_address` is set"""
73+
if self.ipv4_address is None and self.ipv6_address is None:
74+
raise ValueError('At least one of `ipv4_address` or `ipv6_address` must be provided')
75+
return self
76+
7077
def to_str(self) -> str:
7178
"""Returns the string representation of the model using alias"""
7279
return pprint.pformat(self.model_dump(by_alias=True))

‎fingerprint_server_sdk/models/event_edge.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616
import json
1717
import pprint
1818
import re # noqa: F401
19-
from typing import Any, ClassVar, Optional
19+
from typing import Any, ClassVar, Literal, Optional
2020

2121
from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictInt, StrictStr
2222
from typing_extensions import Self
2323

2424
from fingerprint_server_sdk.models.bot_info import BotInfo
25-
from fingerprint_server_sdk.models.event_source import EventSource
2625
from fingerprint_server_sdk.models.ip_info import IPInfo
2726
from fingerprint_server_sdk.models.proxy_confidence import ProxyConfidence
2827
from fingerprint_server_sdk.models.proxy_details import ProxyDetails
@@ -65,7 +64,9 @@ class EventEdge(BaseModel):
6564
)
6665
vpn_confidence: Optional[VpnConfidence] = None
6766
vpn_methods: Optional[VpnMethods] = None
68-
source: EventSource
67+
source: Literal['edge'] = Field(
68+
description='Identifies how the event was generated. Always `edge` for events generated by the Automation Intelligence API (`/edge` endpoint).'
69+
)
6970
__properties: ClassVar[list[str]] = [
7071
'event_id',
7172
'timestamp',

‎sync.sh‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,21 @@ set -euo pipefail
33

44
defaultBaseUrl="https://fingerprintjs.github.io/fingerprint-pro-server-api-openapi"
55
# Flattened Event (single object, source optional). start/end stay a date|int oneOf.
6-
schemaUrl="${1:-$defaultBaseUrl/schemas/fingerprint-server-api-v4-flat.yaml}"
6+
flatSchemaUrl="$defaultBaseUrl/schemas/fingerprint-server-api-v4-flat.yaml"
7+
fallbackSchemaUrl="$defaultBaseUrl/schemas/fingerprint-server-api-v4.yaml"
8+
schemaUrl="${1:-$flatSchemaUrl}"
79
examplesBaseUrl="${2:-$defaultBaseUrl/examples}"
810

911
mkdir -p ./res
1012

11-
curl -fSL --retry 3 -o ./res/fingerprint-server-api.yaml "$schemaUrl"
13+
if ! curl -fSL --retry 3 -o ./res/fingerprint-server-api.yaml "$schemaUrl"; then
14+
if [ -z "${1:-}" ]; then
15+
echo "Failed to download $schemaUrl, falling back to $fallbackSchemaUrl" >&2
16+
curl -fSL --retry 3 -o ./res/fingerprint-server-api.yaml "$fallbackSchemaUrl"
17+
else
18+
exit 1
19+
fi
20+
fi
1221

1322
examples=(
1423
'events/search/get_event_search_200.json'

‎test/mocks/edge/post_edge_200.json‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"event_id": "1700000000000.AbCdEf",
3+
"timestamp": 1700000000000,
4+
"source": "edge",
5+
"ip_info": {
6+
"v4": {
7+
"address": "34.162.244.71"
8+
}
9+
}
10+
}

‎test/test_fingerprint_api.py‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
BotInfoIdentity,
1010
Configuration,
1111
ConflictException,
12+
EdgeRequest,
13+
EdgeRequestHeadersInner,
1214
ErrorCode,
1315
ErrorResponse,
1416
Event,
17+
EventEdge,
1518
EventSearch,
1619
EventUpdate,
1720
ForbiddenException,
@@ -83,6 +86,74 @@ def delete_visitor_path(visitor_id, region: Region = Region.US):
8386
base = Configuration.get_host(region)
8487
return f'{base}/visitors/{visitor_id}'
8588

89+
@staticmethod
90+
def get_edge_path(region: Region = Region.US):
91+
base = Configuration.get_host(region)
92+
return f'{base}/edge'
93+
94+
def test_analyze_request_for_automation_intelligence(self) -> None:
95+
"""Test case for analyze_request_for_automation_intelligence
96+
97+
Collect Automation Intelligence.
98+
"""
99+
mock_pool = MockPoolManager(self)
100+
self.api.api_client.rest_client.pool_manager = mock_pool
101+
edge_request = EdgeRequest(
102+
headers=[EdgeRequestHeadersInner(name='Host', value='example.com')],
103+
method='GET',
104+
url='https://example.com/login',
105+
ipv4_address='34.162.244.71',
106+
)
107+
mock_pool.expect_request(
108+
'POST',
109+
TestFingerprintApi.get_edge_path(),
110+
headers=self.request_headers,
111+
preload_content=True,
112+
timeout=None,
113+
body=(
114+
'{"headers": [{"name": "Host", "value": "example.com"}], '
115+
'"method": "GET", "url": "https://example.com/login", '
116+
'"ipv4_address": "34.162.244.71"}'
117+
),
118+
response_data_file='edge/post_edge_200.json',
119+
)
120+
121+
event_edge_response = self.api.analyze_request_for_automation_intelligence(edge_request)
122+
self.assertIsInstance(event_edge_response, EventEdge)
123+
self.assertEqual(event_edge_response.source, 'edge')
124+
125+
def test_analyze_request_for_automation_intelligence_bad_request(self) -> None:
126+
"""Test case for analyze_request_for_automation_intelligence with 400 Bad Request"""
127+
mock_pool = MockPoolManager(self)
128+
self.api.api_client.rest_client.pool_manager = mock_pool
129+
edge_request = EdgeRequest(
130+
headers=[EdgeRequestHeadersInner(name='Host', value='example.com')],
131+
method='GET',
132+
url='https://example.com/login',
133+
ipv4_address='34.162.244.71',
134+
)
135+
mock_pool.expect_request(
136+
'POST',
137+
TestFingerprintApi.get_edge_path(),
138+
headers=self.request_headers,
139+
preload_content=True,
140+
timeout=None,
141+
body=(
142+
'{"headers": [{"name": "Host", "value": "example.com"}], '
143+
'"method": "GET", "url": "https://example.com/login", '
144+
'"ipv4_address": "34.162.244.71"}'
145+
),
146+
response_status_code=400,
147+
response_data_file='errors/400_request_body_invalid.json',
148+
)
149+
150+
with self.assertRaises(BadRequestException) as context:
151+
self.api.analyze_request_for_automation_intelligence(edge_request)
152+
153+
self.assertEqual(context.exception.status, 400)
154+
self.assertIsInstance(context.exception.data, ErrorResponse)
155+
self.assertEqual(context.exception.data.error.code, ErrorCode.REQUEST_CANNOT_BE_PARSED)
156+
86157
def test_delete_visitor_data(self) -> None:
87158
"""Test case for delete_visitor_data
88159

0 commit comments

Comments
 (0)