-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathsplits.py
More file actions
208 lines (171 loc) · 9.73 KB
/
Copy pathsplits.py
File metadata and controls
208 lines (171 loc) · 9.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
"""Splits API module."""
import logging
import json
from splitio.api import APIException, headers_from_metadata
from splitio.api.commons import build_fetch, FetchOptions
from splitio.api.client import HttpClientException
from splitio.models.telemetry import HTTPExceptionsAndLatencies
from splitio.util.time import utctime_ms
from splitio.spec import SPEC_VERSION
from splitio.sync import util
_LOGGER = logging.getLogger(__name__)
_SPEC_1_1 = "1.1"
_PROXY_CHECK_INTERVAL_MILLISECONDS_SS = 24 * 60 * 60 * 1000
class SplitsAPIBase(object): # pylint: disable=too-few-public-methods
"""Class that uses an httpClient to communicate with the splits API."""
def __init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer):
"""
Class constructor.
:param client: HTTP Client responsble for issuing calls to the backend.
:type client: HttpClient
:param sdk_key: User sdk_key token.
:type sdk_key: string
:param sdk_metadata: SDK version & machine name & IP.
:type sdk_metadata: splitio.client.util.SdkMetadata
"""
self._client = client
self._sdk_key = sdk_key
self._metadata = headers_from_metadata(sdk_metadata)
self._telemetry_runtime_producer = telemetry_runtime_producer
self._client.set_telemetry_data(HTTPExceptionsAndLatencies.SPLIT, self._telemetry_runtime_producer)
self._spec_version = SPEC_VERSION
self._last_proxy_check_timestamp = 0
self.clear_storage = False
self._old_spec_since = None
def _check_last_proxy_check_timestamp(self, since):
if self._spec_version == _SPEC_1_1 and ((utctime_ms() - self._last_proxy_check_timestamp) >= _PROXY_CHECK_INTERVAL_MILLISECONDS_SS):
_LOGGER.info("Switching to new Feature flag spec (%s) and fetching.", SPEC_VERSION);
self._spec_version = SPEC_VERSION
self._old_spec_since = since
def _check_old_spec_since(self, change_number):
if self._spec_version == _SPEC_1_1 and self._old_spec_since is not None:
since = self._old_spec_since
self._old_spec_since = None
return since
return change_number
class SplitsAPI(SplitsAPIBase): # pylint: disable=too-few-public-methods
"""Class that uses an httpClient to communicate with the splits API."""
def __init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer):
"""
Class constructor.
:param client: HTTP Client responsble for issuing calls to the backend.
:type client: HttpClient
:param sdk_key: User sdk_key token.
:type sdk_key: string
:param sdk_metadata: SDK version & machine name & IP.
:type sdk_metadata: splitio.client.util.SdkMetadata
"""
SplitsAPIBase.__init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer)
def fetch_splits(self, change_number, rbs_change_number, fetch_options):
"""
Fetch feature flags from backend.
:param change_number: Last known timestamp of a split modification.
:type change_number: int
:param rbs_change_number: Last known timestamp of a rule based segment modification.
:type rbs_change_number: int
:param fetch_options: Fetch options for getting feature flag definitions.
:type fetch_options: splitio.api.commons.FetchOptions
:return: Json representation of a splitChanges response.
:rtype: dict
"""
try:
self._check_last_proxy_check_timestamp(change_number)
change_number = self._check_old_spec_since(change_number)
if self._spec_version == _SPEC_1_1:
fetch_options = FetchOptions(fetch_options.cache_control_headers, fetch_options.change_number,
None, fetch_options.sets, self._spec_version)
rbs_change_number = None
query, extra_headers = build_fetch(change_number, fetch_options, self._metadata, rbs_change_number)
response = self._client.get(
'sdk',
'splitChanges',
self._sdk_key,
extra_headers=extra_headers,
query=query,
)
if 200 <= response.status_code < 300:
if self._spec_version == _SPEC_1_1:
return util.convert_to_new_spec(json.loads(response.body))
self.clear_storage = self._last_proxy_check_timestamp != 0
self._last_proxy_check_timestamp = 0
parsed = json.loads(response.body)
if 'rbs' not in parsed:
parsed['rbs'] = {"d": [], "s": -1, "t": -1}
return parsed
else:
if response.status_code == 414:
_LOGGER.error('Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.')
if self._client.is_sdk_endpoint_overridden() and response.status_code == 400 and self._spec_version == SPEC_VERSION:
_LOGGER.warning('Detected proxy response error, changing spec version from %s to %s and re-fetching.', self._spec_version, _SPEC_1_1)
self._spec_version = _SPEC_1_1
self._last_proxy_check_timestamp = utctime_ms()
return self.fetch_splits(change_number, None, FetchOptions(fetch_options.cache_control_headers, fetch_options.change_number,
None, fetch_options.sets, self._spec_version))
raise APIException(response.body, response.status_code)
except HttpClientException as exc:
_LOGGER.error('Error fetching feature flags because an exception was raised by the HTTPClient')
_LOGGER.debug('Error: ', exc_info=True)
raise APIException('Feature flags not fetched correctly.') from exc
class SplitsAPIAsync(SplitsAPIBase): # pylint: disable=too-few-public-methods
"""Class that uses an httpClient to communicate with the splits API."""
def __init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer):
"""
Class constructor.
:param client: HTTP Client responsble for issuing calls to the backend.
:type client: HttpClient
:param sdk_key: User sdk_key token.
:type sdk_key: string
:param sdk_metadata: SDK version & machine name & IP.
:type sdk_metadata: splitio.client.util.SdkMetadata
"""
SplitsAPIBase.__init__(self, client, sdk_key, sdk_metadata, telemetry_runtime_producer)
async def fetch_splits(self, change_number, rbs_change_number, fetch_options):
"""
Fetch feature flags from backend.
:param change_number: Last known timestamp of a split modification.
:type change_number: int
:param rbs_change_number: Last known timestamp of a rule based segment modification.
:type rbs_change_number: int
:param fetch_options: Fetch options for getting feature flag definitions.
:type fetch_options: splitio.api.commons.FetchOptions
:return: Json representation of a splitChanges response.
:rtype: dict
"""
try:
self._check_last_proxy_check_timestamp(change_number)
change_number = self._check_old_spec_since(change_number)
if self._spec_version == _SPEC_1_1:
fetch_options = FetchOptions(fetch_options.cache_control_headers, fetch_options.change_number,
None, fetch_options.sets, self._spec_version)
rbs_change_number = None
query, extra_headers = build_fetch(change_number, fetch_options, self._metadata, rbs_change_number)
response = await self._client.get(
'sdk',
'splitChanges',
self._sdk_key,
extra_headers=extra_headers,
query=query,
)
if 200 <= response.status_code < 300:
if self._spec_version == _SPEC_1_1:
return util.convert_to_new_spec(json.loads(response.body))
self.clear_storage = self._last_proxy_check_timestamp != 0
self._last_proxy_check_timestamp = 0
parsed = json.loads(response.body)
if 'rbs' not in parsed:
parsed['rbs'] = {"d": [], "s": -1, "t": -1}
return parsed
else:
if response.status_code == 414:
_LOGGER.error('Error fetching feature flags; the amount of flag sets provided are too big, causing uri length error.')
if self._client.is_sdk_endpoint_overridden() and response.status_code == 400 and self._spec_version == SPEC_VERSION:
_LOGGER.warning('Detected proxy response error, changing spec version from %s to %s and re-fetching.', self._spec_version, _SPEC_1_1)
self._spec_version = _SPEC_1_1
self._last_proxy_check_timestamp = utctime_ms()
return await self.fetch_splits(change_number, None, FetchOptions(fetch_options.cache_control_headers, fetch_options.change_number,
None, fetch_options.sets, self._spec_version))
raise APIException(response.body, response.status_code)
except HttpClientException as exc:
_LOGGER.error('Error fetching feature flags because an exception was raised by the HTTPClient')
_LOGGER.debug('Error: ', exc_info=True)
raise APIException('Feature flags not fetched correctly.') from exc