From df05d7ebfcb26ea5cf6a46881dc558e6c84908ea Mon Sep 17 00:00:00 2001 From: mcnulty-fp Date: Mon, 10 Aug 2026 20:47:32 +0000 Subject: [PATCH 1/6] feat: sync OpenAPI schema to v3.6.0 --- .changeset/add-event-source-field.md | 5 ++ .changeset/add-labels-v3.md | 5 ++ .openapi-generator/FILES | 2 + .schema-version | 2 +- README.md | 1 + README.pypi.md | 1 + docs/Event.md | 1 + docs/EventSource.md | 13 +++++ fingerprint_server_sdk/__init__.py | 2 + fingerprint_server_sdk/models/__init__.py | 1 + fingerprint_server_sdk/models/event.py | 4 ++ fingerprint_server_sdk/models/event_source.py | 48 +++++++++++++++++ res/fingerprint-server-api.yaml | 19 +++++++ test/mocks/errors/400_edge_ip_required.json | 6 +++ test/mocks/errors/400_edge_unknown_field.json | 6 +++ .../errors/400_request_read_timeout.json | 6 +++ test/mocks/errors/413_payload_too_large.json | 6 +++ test/mocks/events/get_event_200.json | 1 + .../get_event_200_with_unknown_field.json | 1 + test/mocks/events/get_event_ruleset_200.json | 1 + .../events/get_event_with_bot_info_200.json | 1 + .../mocks/events/get_event_with_edge_200.json | 51 +++++++++++++++++++ .../events/search/get_event_search_200.json | 1 + 23 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 .changeset/add-event-source-field.md create mode 100644 .changeset/add-labels-v3.md create mode 100644 docs/EventSource.md create mode 100644 fingerprint_server_sdk/models/event_source.py create mode 100644 test/mocks/errors/400_edge_ip_required.json create mode 100644 test/mocks/errors/400_edge_unknown_field.json create mode 100644 test/mocks/errors/400_request_read_timeout.json create mode 100644 test/mocks/errors/413_payload_too_large.json create mode 100644 test/mocks/events/get_event_with_edge_200.json diff --git a/.changeset/add-event-source-field.md b/.changeset/add-event-source-field.md new file mode 100644 index 00000000..98947d7e --- /dev/null +++ b/.changeset/add-event-source-field.md @@ -0,0 +1,5 @@ +--- +'@fingerprint/python-sdk': minor +--- + +Add `source` field to `Event` and `EdgeResponse` to identify how the event was generated (`device` or `edge`) diff --git a/.changeset/add-labels-v3.md b/.changeset/add-labels-v3.md new file mode 100644 index 00000000..e126c61a --- /dev/null +++ b/.changeset/add-labels-v3.md @@ -0,0 +1,5 @@ +--- +'@fingerprint/python-sdk': patch +--- + +**events**: Add `labels` signal to v3 schema \ No newline at end of file diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index ee60f952..fe9e7dbb 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -17,6 +17,7 @@ docs/EventRuleAction.md docs/EventRuleActionAllow.md docs/EventRuleActionBlock.md docs/EventSearch.md +docs/EventSource.md docs/EventUpdate.md docs/FingerprintApi.md docs/FontPreferences.md @@ -87,6 +88,7 @@ fingerprint_server_sdk/models/event_rule_action.py fingerprint_server_sdk/models/event_rule_action_allow.py fingerprint_server_sdk/models/event_rule_action_block.py fingerprint_server_sdk/models/event_search.py +fingerprint_server_sdk/models/event_source.py fingerprint_server_sdk/models/event_update.py fingerprint_server_sdk/models/font_preferences.py fingerprint_server_sdk/models/geolocation.py diff --git a/.schema-version b/.schema-version index 684a941c..c03b60b0 100644 --- a/.schema-version +++ b/.schema-version @@ -1 +1 @@ -v3.5.1 \ No newline at end of file +v3.6.0 \ No newline at end of file diff --git a/README.md b/README.md index 4353c82d..aeac8954 100644 --- a/README.md +++ b/README.md @@ -327,6 +327,7 @@ Class | Method | HTTP request | Description - [EventRuleActionAllow](docs/EventRuleActionAllow.md) - [EventRuleActionBlock](docs/EventRuleActionBlock.md) - [EventSearch](docs/EventSearch.md) + - [EventSource](docs/EventSource.md) - [EventUpdate](docs/EventUpdate.md) - [FontPreferences](docs/FontPreferences.md) - [Geolocation](docs/Geolocation.md) diff --git a/README.pypi.md b/README.pypi.md index 473ec1aa..03278276 100644 --- a/README.pypi.md +++ b/README.pypi.md @@ -323,6 +323,7 @@ Class | Method | HTTP request | Description - [EventRuleActionAllow](https://github.com/fingerprintjs/python-sdk/blob/main/docs/EventRuleActionAllow.md) - [EventRuleActionBlock](https://github.com/fingerprintjs/python-sdk/blob/main/docs/EventRuleActionBlock.md) - [EventSearch](https://github.com/fingerprintjs/python-sdk/blob/main/docs/EventSearch.md) + - [EventSource](https://github.com/fingerprintjs/python-sdk/blob/main/docs/EventSource.md) - [EventUpdate](https://github.com/fingerprintjs/python-sdk/blob/main/docs/EventUpdate.md) - [FontPreferences](https://github.com/fingerprintjs/python-sdk/blob/main/docs/FontPreferences.md) - [Geolocation](https://github.com/fingerprintjs/python-sdk/blob/main/docs/Geolocation.md) diff --git a/docs/Event.md b/docs/Event.md index 578aedfe..93434207 100644 --- a/docs/Event.md +++ b/docs/Event.md @@ -6,6 +6,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **event_id** | **str** | Unique identifier of the user's request. The first portion of the event_id is a unix epoch milliseconds timestamp. | **timestamp** | **int** | Timestamp of the event with millisecond precision in Unix time. | +**source** | [**EventSource**](EventSource.md) | | [optional] **incremental_identification_status** | [**IncrementalIdentificationStatus**](IncrementalIdentificationStatus.md) | | [optional] **linked_id** | **str** | A customer-provided id that was sent with the request. | [optional] **environment_id** | **str** | Environment Id of the event. | [optional] diff --git a/docs/EventSource.md b/docs/EventSource.md new file mode 100644 index 00000000..562419a4 --- /dev/null +++ b/docs/EventSource.md @@ -0,0 +1,13 @@ +# EventSource +Identifies how the event was generated. +- `device` - the event was generated by the JS agent or a mobile SDK running on an end-user device. +- `edge` - the event was generated by the Automation Intelligence API (`/edge` endpoint), analyzing a request intercepted at the edge. + + +## Enum + +* `DEVICE` (value: `'device'`) +* `EDGE` (value: `'edge'`) + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + diff --git a/fingerprint_server_sdk/__init__.py b/fingerprint_server_sdk/__init__.py index e84d31e0..e2aff003 100644 --- a/fingerprint_server_sdk/__init__.py +++ b/fingerprint_server_sdk/__init__.py @@ -59,6 +59,7 @@ 'EventRuleActionAllow', 'EventRuleActionBlock', 'EventSearch', + 'EventSource', 'EventUpdate', 'FontPreferences', 'Geolocation', @@ -145,6 +146,7 @@ from fingerprint_server_sdk.models.event_rule_action_allow import EventRuleActionAllow from fingerprint_server_sdk.models.event_rule_action_block import EventRuleActionBlock from fingerprint_server_sdk.models.event_search import EventSearch +from fingerprint_server_sdk.models.event_source import EventSource from fingerprint_server_sdk.models.event_update import EventUpdate from fingerprint_server_sdk.models.font_preferences import FontPreferences from fingerprint_server_sdk.models.geolocation import Geolocation diff --git a/fingerprint_server_sdk/models/__init__.py b/fingerprint_server_sdk/models/__init__.py index 126c4941..eba59cff 100644 --- a/fingerprint_server_sdk/models/__init__.py +++ b/fingerprint_server_sdk/models/__init__.py @@ -29,6 +29,7 @@ from fingerprint_server_sdk.models.event_rule_action_allow import EventRuleActionAllow from fingerprint_server_sdk.models.event_rule_action_block import EventRuleActionBlock from fingerprint_server_sdk.models.event_search import EventSearch +from fingerprint_server_sdk.models.event_source import EventSource from fingerprint_server_sdk.models.event_update import EventUpdate from fingerprint_server_sdk.models.font_preferences import FontPreferences from fingerprint_server_sdk.models.geolocation import Geolocation diff --git a/fingerprint_server_sdk/models/event.py b/fingerprint_server_sdk/models/event.py index d13ecb81..9badf776 100644 --- a/fingerprint_server_sdk/models/event.py +++ b/fingerprint_server_sdk/models/event.py @@ -25,6 +25,7 @@ from fingerprint_server_sdk.models.bot_result import BotResult from fingerprint_server_sdk.models.browser_details import BrowserDetails from fingerprint_server_sdk.models.event_rule_action import EventRuleAction +from fingerprint_server_sdk.models.event_source import EventSource from fingerprint_server_sdk.models.identification import Identification from fingerprint_server_sdk.models.incremental_identification_status import ( IncrementalIdentificationStatus, @@ -57,6 +58,7 @@ class Event(BaseModel): timestamp: StrictInt = Field( description='Timestamp of the event with millisecond precision in Unix time.' ) + source: Optional[EventSource] = None incremental_identification_status: Optional[IncrementalIdentificationStatus] = None linked_id: Optional[StrictStr] = Field( default=None, description='A customer-provided id that was sent with the request.' @@ -259,6 +261,7 @@ class Event(BaseModel): __properties: ClassVar[list[str]] = [ 'event_id', 'timestamp', + 'source', 'incremental_identification_status', 'linked_id', 'environment_id', @@ -424,6 +427,7 @@ def from_dict(cls, obj: Optional[dict[str, Any]]) -> Optional[Self]: { 'event_id': obj.get('event_id'), 'timestamp': obj.get('timestamp'), + 'source': obj.get('source'), 'incremental_identification_status': obj.get('incremental_identification_status'), 'linked_id': obj.get('linked_id'), 'environment_id': obj.get('environment_id'), diff --git a/fingerprint_server_sdk/models/event_source.py b/fingerprint_server_sdk/models/event_source.py new file mode 100644 index 00000000..63622771 --- /dev/null +++ b/fingerprint_server_sdk/models/event_source.py @@ -0,0 +1,48 @@ +""" +Server API +Fingerprint Server API allows you to get, search, and update Events in a server environment. It can be used for data exports, decision-making, and data analysis scenarios. +Server API is intended for server-side usage, it's not intended to be used from the client side, whether it's a browser or a mobile device. +The API also supports collection of Automation Intelligence for requests to your server in edge, pre-origin, or middleware contexts. + +The version of the OpenAPI document: 4 +Contact: support@fingerprint.com +Generated by OpenAPI Generator (https://openapi-generator.tech) + +Do not edit the class manually. +""" # noqa: E501 + +from __future__ import annotations + +import json +from enum import Enum + +from typing_extensions import Self + + +class EventSource(str, Enum): + """ + Identifies how the event was generated. - `device` - the event was generated by the JS agent or a mobile SDK running on an end-user device. - `edge` - the event was generated by the Automation Intelligence API (`/edge` endpoint), analyzing a request intercepted at the edge. + """ + + """ + allowed enum values + """ + DEVICE = 'device' + EDGE = 'edge' + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of EventSource from a JSON string""" + return cls(json.loads(json_str)) + + @classmethod + def _missing_(cls, value: object) -> Self: + """Accept unknown enum values gracefully.""" + if not isinstance(value, str): + raise ValueError(f'{value!r} is not a valid {cls.__name__}') + obj = str.__new__(cls, value) + obj._name_ = str(value) + obj._value_ = value + # cache it so the same value won't trigger `_missing_` again + cls._value2member_map_[value] = obj + return obj diff --git a/res/fingerprint-server-api.yaml b/res/fingerprint-server-api.yaml index 2bac3291..f20cd1d4 100644 --- a/res/fingerprint-server-api.yaml +++ b/res/fingerprint-server-api.yaml @@ -1164,6 +1164,19 @@ components: format: int64 examples: - 1708102555327 + EventSource: + type: string + description: > + Identifies how the event was generated. + + - `device` - the event was generated by the JS agent or a mobile SDK + running on an end-user device. + + - `edge` - the event was generated by the Automation Intelligence API + (`/edge` endpoint), analyzing a request intercepted at the edge. + enum: + - device + - edge Url: type: string examples: @@ -3070,6 +3083,12 @@ components: - android - ios - browser + source: + $ref: '#/components/schemas/EventSource' + x-platforms: + - android + - ios + - browser incremental_identification_status: $ref: '#/components/schemas/IncrementalIdentificationStatus' x-platforms: diff --git a/test/mocks/errors/400_edge_ip_required.json b/test/mocks/errors/400_edge_ip_required.json new file mode 100644 index 00000000..8f055f8f --- /dev/null +++ b/test/mocks/errors/400_edge_ip_required.json @@ -0,0 +1,6 @@ +{ + "error": { + "code": "request_cannot_be_parsed", + "message": "at least one of ipv4_address or ipv6_address is required" + } +} diff --git a/test/mocks/errors/400_edge_unknown_field.json b/test/mocks/errors/400_edge_unknown_field.json new file mode 100644 index 00000000..c0a153ef --- /dev/null +++ b/test/mocks/errors/400_edge_unknown_field.json @@ -0,0 +1,6 @@ +{ + "error": { + "code": "request_cannot_be_parsed", + "message": "request body contains an unknown field \"unknown\"" + } +} \ No newline at end of file diff --git a/test/mocks/errors/400_request_read_timeout.json b/test/mocks/errors/400_request_read_timeout.json new file mode 100644 index 00000000..89daaca8 --- /dev/null +++ b/test/mocks/errors/400_request_read_timeout.json @@ -0,0 +1,6 @@ +{ + "error": { + "code": "request_read_timeout", + "message": "request read timeout" + } +} \ No newline at end of file diff --git a/test/mocks/errors/413_payload_too_large.json b/test/mocks/errors/413_payload_too_large.json new file mode 100644 index 00000000..00a1c6f9 --- /dev/null +++ b/test/mocks/errors/413_payload_too_large.json @@ -0,0 +1,6 @@ +{ + "error": { + "code": "payload_too_large", + "message": "payload too large" + } +} diff --git a/test/mocks/events/get_event_200.json b/test/mocks/events/get_event_200.json index 8c40bcd6..6d6c726e 100644 --- a/test/mocks/events/get_event_200.json +++ b/test/mocks/events/get_event_200.json @@ -3,6 +3,7 @@ "tags": {}, "timestamp": 1708102555327, "event_id": "1708102555327.NLOjmg", + "source": "device", "incremental_identification_status": "completed", "url": "https://www.example.com/login?hope{this{works[!", "ip_address": "61.127.217.15", diff --git a/test/mocks/events/get_event_200_with_unknown_field.json b/test/mocks/events/get_event_200_with_unknown_field.json index b87f4864..bdbf8715 100644 --- a/test/mocks/events/get_event_200_with_unknown_field.json +++ b/test/mocks/events/get_event_200_with_unknown_field.json @@ -3,6 +3,7 @@ "tags": {}, "timestamp": 1708102555327, "event_id": "1708102555327.NLOjmg", + "source": "device", "incremental_identification_status": "completed", "url": "https://www.example.com/login?hope{this{works[!", "ip_address": "61.127.217.15", diff --git a/test/mocks/events/get_event_ruleset_200.json b/test/mocks/events/get_event_ruleset_200.json index b312bfe5..1e5337c0 100644 --- a/test/mocks/events/get_event_ruleset_200.json +++ b/test/mocks/events/get_event_ruleset_200.json @@ -3,6 +3,7 @@ "tags": {}, "timestamp": 1708102555327, "event_id": "1708102555327.NLOjmg", + "source": "device", "incremental_identification_status": "completed", "url": "https://www.example.com/login?hope{this{works[!", "ip_address": "61.127.217.15", diff --git a/test/mocks/events/get_event_with_bot_info_200.json b/test/mocks/events/get_event_with_bot_info_200.json index 5b5427aa..b44a687a 100644 --- a/test/mocks/events/get_event_with_bot_info_200.json +++ b/test/mocks/events/get_event_with_bot_info_200.json @@ -3,6 +3,7 @@ "tags": {}, "timestamp": 1708102555327, "event_id": "1708102555327.NLOjmg", + "source": "device", "incremental_identification_status": "completed", "url": "https://www.example.com/login?hope{this{works[!", "ip_address": "61.127.217.15", diff --git a/test/mocks/events/get_event_with_edge_200.json b/test/mocks/events/get_event_with_edge_200.json new file mode 100644 index 00000000..387c86eb --- /dev/null +++ b/test/mocks/events/get_event_with_edge_200.json @@ -0,0 +1,51 @@ +{ + "event_id": "1758130560902.8tRtrH", + "timestamp": 1758130560902, + "source": "edge", + "url": "https://example.com/login?foo=bar", + "bot_info": { + "category": "ai_agent", + "provider": "OpenAI", + "provider_url": "https://openai.com", + "name": "ChatGPT Agent", + "identity": "signed", + "confidence": "high" + }, + "ip_info": { + "v4": { + "address": "104.210.139.192", + "geolocation": { + "accuracy_radius": 20, + "latitude": 29.42412, + "longitude": -98.49363, + "postal_code": "78205", + "timezone": "America/Chicago", + "city_name": "San Antonio", + "country_code": "US", + "country_name": "United States", + "continent_code": "NA", + "continent_name": "North America", + "subdivisions": [ + { + "iso_code": "TX", + "name": "Texas" + } + ] + }, + "asn": "8075", + "asn_name": "Microsoft Corporation", + "asn_network": "104.208.0.0/13", + "asn_type": "hosting", + "datacenter_result": true, + "datacenter_name": "Microsoft Azure" + } + }, + "proxy": false, + "proxy_confidence": "high", + "vpn": false, + "vpn_confidence": "medium", + "vpn_methods": { + "public_vpn": false, + "relay": false + } +} \ No newline at end of file diff --git a/test/mocks/events/search/get_event_search_200.json b/test/mocks/events/search/get_event_search_200.json index ca87827b..daad06b0 100644 --- a/test/mocks/events/search/get_event_search_200.json +++ b/test/mocks/events/search/get_event_search_200.json @@ -5,6 +5,7 @@ "tags": {}, "timestamp": 1708102555327, "event_id": "1708102555327.NLOjmg", + "source": "device", "incremental_identification_status": "completed", "url": "https://www.example.com/login?hope{this{works[!", "ip_address": "61.127.217.15", From 3ad1e75c15d41fc2a8e4312c264b7538c93f1232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eray=20Ayd=C4=B1n?= Date: Wed, 12 Aug 2026 16:37:48 +0300 Subject: [PATCH 2/6] chore: remove unneeded changeset --- .changeset/add-labels-v3.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/add-labels-v3.md diff --git a/.changeset/add-labels-v3.md b/.changeset/add-labels-v3.md deleted file mode 100644 index e126c61a..00000000 --- a/.changeset/add-labels-v3.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@fingerprint/python-sdk': patch ---- - -**events**: Add `labels` signal to v3 schema \ No newline at end of file From 289979719d073ea15ddd22462079336e123e2e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eray=20Ayd=C4=B1n?= Date: Wed, 12 Aug 2026 16:38:09 +0300 Subject: [PATCH 3/6] chore: reword changeset for `source` property --- .changeset/add-event-source-field.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/add-event-source-field.md b/.changeset/add-event-source-field.md index 98947d7e..f2bc404b 100644 --- a/.changeset/add-event-source-field.md +++ b/.changeset/add-event-source-field.md @@ -2,4 +2,4 @@ '@fingerprint/python-sdk': minor --- -Add `source` field to `Event` and `EdgeResponse` to identify how the event was generated (`device` or `edge`) +Add `source` field to `Event` to identify how the event was generated (`device` or `edge`) From e43afef13ec70859b9aaa329b66a171d57fea23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eray=20Ayd=C4=B1n?= Date: Wed, 12 Aug 2026 16:42:04 +0300 Subject: [PATCH 4/6] test: remove unnecessary mock files --- test/mocks/errors/400_edge_ip_required.json | 6 --- test/mocks/errors/400_edge_unknown_field.json | 6 --- .../errors/400_request_read_timeout.json | 6 --- test/mocks/errors/413_payload_too_large.json | 6 --- .../mocks/events/get_event_with_edge_200.json | 51 ------------------- 5 files changed, 75 deletions(-) delete mode 100644 test/mocks/errors/400_edge_ip_required.json delete mode 100644 test/mocks/errors/400_edge_unknown_field.json delete mode 100644 test/mocks/errors/400_request_read_timeout.json delete mode 100644 test/mocks/errors/413_payload_too_large.json delete mode 100644 test/mocks/events/get_event_with_edge_200.json diff --git a/test/mocks/errors/400_edge_ip_required.json b/test/mocks/errors/400_edge_ip_required.json deleted file mode 100644 index 8f055f8f..00000000 --- a/test/mocks/errors/400_edge_ip_required.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "error": { - "code": "request_cannot_be_parsed", - "message": "at least one of ipv4_address or ipv6_address is required" - } -} diff --git a/test/mocks/errors/400_edge_unknown_field.json b/test/mocks/errors/400_edge_unknown_field.json deleted file mode 100644 index c0a153ef..00000000 --- a/test/mocks/errors/400_edge_unknown_field.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "error": { - "code": "request_cannot_be_parsed", - "message": "request body contains an unknown field \"unknown\"" - } -} \ No newline at end of file diff --git a/test/mocks/errors/400_request_read_timeout.json b/test/mocks/errors/400_request_read_timeout.json deleted file mode 100644 index 89daaca8..00000000 --- a/test/mocks/errors/400_request_read_timeout.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "error": { - "code": "request_read_timeout", - "message": "request read timeout" - } -} \ No newline at end of file diff --git a/test/mocks/errors/413_payload_too_large.json b/test/mocks/errors/413_payload_too_large.json deleted file mode 100644 index 00a1c6f9..00000000 --- a/test/mocks/errors/413_payload_too_large.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "error": { - "code": "payload_too_large", - "message": "payload too large" - } -} diff --git a/test/mocks/events/get_event_with_edge_200.json b/test/mocks/events/get_event_with_edge_200.json deleted file mode 100644 index 387c86eb..00000000 --- a/test/mocks/events/get_event_with_edge_200.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "event_id": "1758130560902.8tRtrH", - "timestamp": 1758130560902, - "source": "edge", - "url": "https://example.com/login?foo=bar", - "bot_info": { - "category": "ai_agent", - "provider": "OpenAI", - "provider_url": "https://openai.com", - "name": "ChatGPT Agent", - "identity": "signed", - "confidence": "high" - }, - "ip_info": { - "v4": { - "address": "104.210.139.192", - "geolocation": { - "accuracy_radius": 20, - "latitude": 29.42412, - "longitude": -98.49363, - "postal_code": "78205", - "timezone": "America/Chicago", - "city_name": "San Antonio", - "country_code": "US", - "country_name": "United States", - "continent_code": "NA", - "continent_name": "North America", - "subdivisions": [ - { - "iso_code": "TX", - "name": "Texas" - } - ] - }, - "asn": "8075", - "asn_name": "Microsoft Corporation", - "asn_network": "104.208.0.0/13", - "asn_type": "hosting", - "datacenter_result": true, - "datacenter_name": "Microsoft Azure" - } - }, - "proxy": false, - "proxy_confidence": "high", - "vpn": false, - "vpn_confidence": "medium", - "vpn_methods": { - "public_vpn": false, - "relay": false - } -} \ No newline at end of file From 771c44a8e376de81182a63c80bd38a0ac05002a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eray=20Ayd=C4=B1n?= Date: Wed, 12 Aug 2026 16:46:55 +0300 Subject: [PATCH 5/6] test: add unknown enum value coverage for source --- test/test_unknown_enum_values.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/test_unknown_enum_values.py b/test/test_unknown_enum_values.py index 2ff63a1c..b979586d 100644 --- a/test/test_unknown_enum_values.py +++ b/test/test_unknown_enum_values.py @@ -5,6 +5,7 @@ from fingerprint_server_sdk import Event from fingerprint_server_sdk.models.bot_info import BotInfo +from fingerprint_server_sdk.models.event_source import EventSource from fingerprint_server_sdk.models.proxy_details import ProxyDetails from fingerprint_server_sdk.models.sdk import SDK @@ -82,6 +83,24 @@ def test_event_with_unknown_rare_device_percentile_bucket(self) -> None: self.assertIsInstance(event, Event) self.assertEqual(event.rare_device_percentile_bucket, 'unknown-value') + def test_event_with_unknown_source(self) -> None: + """Unknown source value should be accepted and preserved.""" + data = self._load_event_json() + data['source'] = 'unknown-value' + + event = Event.from_json(json.dumps(data)) + self.assertIsInstance(event, Event) + self.assertEqual(event.source, 'unknown-value') + + def test_event_without_source(self) -> None: + """Event should deserialize when the optional source field is absent.""" + data = self._load_event_json() + del data['source'] + + event = Event.from_json(json.dumps(data)) + self.assertIsInstance(event, Event) + self.assertIsNone(event.source) + def test_proxy_details_with_unknown_proxy_type(self) -> None: """ProxyDetails model should accept unknown proxy_type directly.""" details = ProxyDetails.from_dict({'proxy_type': 'unknown-value', 'last_seen_at': 123}) @@ -120,6 +139,7 @@ def test_event_with_multiple_unknown_enum_values(self) -> None: data['proxy_confidence'] = 'unknown-value' data['tampering_confidence'] = 'unknown-value' data['rare_device_percentile_bucket'] = 'unknown-value' + data['source'] = 'unknown-value' event = Event.from_json(json.dumps(data)) self.assertIsInstance(event, Event) @@ -130,6 +150,7 @@ def test_event_with_multiple_unknown_enum_values(self) -> None: self.assertEqual(event.proxy_confidence, 'unknown-value') self.assertEqual(event.tampering_confidence, 'unknown-value') self.assertEqual(event.rare_device_percentile_bucket, 'unknown-value') + self.assertEqual(event.source, 'unknown-value') def test_known_enum_values_still_work(self) -> None: """Known enum values should continue to work as before.""" @@ -139,6 +160,16 @@ def test_known_enum_values_still_work(self) -> None: self.assertIsInstance(event, Event) self.assertEqual(event.proxy_details.proxy_type, 'residential') self.assertEqual(event.sdk.platform, 'js') + self.assertEqual(event.source, EventSource.DEVICE) + + def test_event_with_edge_source(self) -> None: + """The edge source value should be deserialized into the enum member.""" + data = self._load_event_json() + data['source'] = 'edge' + + event = Event.from_json(json.dumps(data)) + self.assertIsInstance(event, Event) + self.assertEqual(event.source, EventSource.EDGE) if __name__ == '__main__': From 93d06d3f819a9b06ba089573a2959fcd4e903044 Mon Sep 17 00:00:00 2001 From: Eray AYDIN Date: Wed, 12 Aug 2026 17:14:38 +0300 Subject: [PATCH 6/6] test: guard del for source field Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- test/test_unknown_enum_values.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_unknown_enum_values.py b/test/test_unknown_enum_values.py index b979586d..9020f0db 100644 --- a/test/test_unknown_enum_values.py +++ b/test/test_unknown_enum_values.py @@ -95,7 +95,7 @@ def test_event_with_unknown_source(self) -> None: def test_event_without_source(self) -> None: """Event should deserialize when the optional source field is absent.""" data = self._load_event_json() - del data['source'] + data.pop('source', None) event = Event.from_json(json.dumps(data)) self.assertIsInstance(event, Event)