diff --git a/.changeset/add-event-source-field.md b/.changeset/add-event-source-field.md new file mode 100644 index 00000000..f2bc404b --- /dev/null +++ b/.changeset/add-event-source-field.md @@ -0,0 +1,5 @@ +--- +'@fingerprint/python-sdk': minor +--- + +Add `source` field to `Event` to identify how the event was generated (`device` or `edge`) 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/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/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", diff --git a/test/test_unknown_enum_values.py b/test/test_unknown_enum_values.py index 2ff63a1c..9020f0db 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() + data.pop('source', None) + + 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__':