Skip to content

Commit 9f30606

Browse files
authored
Merge pull request #1 from splitio/new_sdk_version
new sdk version
2 parents 26ce715 + a80e5e7 commit 9f30606

7 files changed

Lines changed: 75 additions & 78 deletions

File tree

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
0.0.1
22
- First release. Up to date with spec 0.5.1 and python sdk 0.0.6
3+
0.1.0
4+
- Up to date with spec 0.8.0 and python sdk 0.8.1. Using split client 10.2.0

README.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,27 @@ This SDK is compatible with Python 3 and higher.
1010
## Getting started
1111
### Pip Installation
1212
```python
13-
pip install split-openfeature==0.0.1
13+
pip install split-openfeature==1.0.0
1414
```
1515
### Configure it
1616
Below is a simple example that describes using the Split Provider. Please see the [OpenFeature Documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api) for details on how to use the OpenFeature SDK.
1717

1818
```python
19-
from open_feature import open_feature_api
19+
from openfeature import api
2020
from split_openfeature import SplitProvider
2121

22-
open_feature_api.set_provider(SplitProvider(api_key="YOUR_API_KEY"))
22+
api.set_provider(SplitProvider(api_key="YOUR_API_KEY"))
2323
```
2424

2525
If you are more familiar with Split or want access to other initialization options, you can provide a Split `client` to the constructor. See the [Split Java SDK Documentation](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK) for more information.
2626
```python
27-
from open_feature import open_feature_api
27+
from openfeature import api
2828
from split_openfeature import SplitProvider
2929
from splitio import get_factory
3030

3131
factory = get_factory("YOUR_API_KEY", config=config_file)
3232
factory.block_until_ready(5)
33-
open_feature_api.set_provider(SplitProvider(client=factory.client()))
33+
api.set_provider(SplitProvider(client=factory.client()))
3434
```
3535
where config_file is the Split config file you want to use
3636

@@ -39,10 +39,10 @@ After the initial setup you can use OpenFeature according to their [documentatio
3939

4040
One important note is that the Split Provider **requires a targeting key** to be set. Often times this should be set when evaluating the value of a flag by [setting an EvaluationContext](https://docs.openfeature.dev/docs/reference/concepts/evaluation-context) which contains the targeting key. An example flag evaluation is
4141
```python
42-
from open_feature import open_feature_api
43-
from open_feature.evaluation_context.evaluation_context import EvaluationContext
42+
from openfeature import api
43+
from openfeature.evaluation_context import EvaluationContext
4444

45-
client = open_feature_api.get_client("CLIENT_NAME")
45+
client = api.get_client("CLIENT_NAME")
4646

4747
context = EvaluationContext(targeting_key="TARGETING_KEY")
4848
value = client.get_boolean_value("FLAG_NAME", False, context)
@@ -54,10 +54,8 @@ client.context = context
5454
```
5555
or at the OpenFeatureAPI level
5656
```python
57-
from open_feature.open_feature_evaluation_context import set_api_evaluation_context
58-
5957
context = EvaluationContext(targeting_key="TARGETING_KEY")
60-
set_api_evaluation_context(context)
58+
api.set_evaluation_context(context)
6159
````
6260
If the context was set at the client or api level, it is not required to provide it during flag evaluation.
6361

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
openfeature_sdk==0.0.6
2-
splitio_client==9.1.3
1+
openfeature_sdk==0.8.1
2+
splitio_client==10.2.0

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setuptools.setup(
77
name="split_openfeature",
8-
version="0.0.1",
8+
version="0.1.0",
99
author="Robert Grassian",
1010
author_email="robert.grassian@split.io",
1111
description="The official Python Split Provider for OpenFeature",
Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import typing
22
from json import JSONDecodeError
33

4-
from open_feature.hooks.hook import Hook
5-
from open_feature.provider import provider
6-
from open_feature.evaluation_context.evaluation_context import EvaluationContext
7-
from open_feature.exception import exceptions
8-
from open_feature.exception.error_code import ErrorCode
9-
from open_feature.flag_evaluation.reason import Reason
10-
from open_feature.flag_evaluation.flag_evaluation_details import FlagEvaluationDetails
4+
from openfeature.hook import Hook
5+
from openfeature.evaluation_context import EvaluationContext
6+
from openfeature.exception import ErrorCode, GeneralError, ParseError, OpenFeatureError, TargetingKeyMissingError
7+
from openfeature.flag_evaluation import Reason, FlagResolutionDetails
8+
from openfeature.provider import AbstractProvider, Metadata
119
from splitio import get_factory
1210
from splitio.exceptions import TimeoutException
1311
import json
1412

1513

16-
class SplitProvider(provider.AbstractProvider):
14+
class SplitProvider(AbstractProvider):
1715

1816
def __init__(self, api_key="", client=None):
1917
if api_key == "" and client is None:
@@ -23,13 +21,13 @@ def __init__(self, api_key="", client=None):
2321
try:
2422
factory.block_until_ready(1)
2523
except TimeoutException:
26-
raise exceptions.GeneralError("Error occurred initializing the client.")
24+
raise GeneralError("Error occurred initializing the client.")
2725
self.split_client = factory.client()
2826
else:
2927
self.split_client = client
3028

31-
def get_metadata(self) -> provider.Metadata:
32-
return provider.Metadata("Split")
29+
def get_metadata(self) -> Metadata:
30+
return Metadata("Split")
3331

3432
def get_provider_hooks(self) -> typing.List[Hook]:
3533
return []
@@ -39,93 +37,93 @@ def resolve_boolean_details(self, flag_key: str, default_value: bool,
3937
try:
4038
evaluated = self.evaluate_treatment(flag_key, evaluation_context)
4139
if SplitProvider.no_treatment(evaluated):
42-
return SplitProvider.construct_provider_evaluation(flag_key, default_value, evaluated, Reason.DEFAULT,
43-
ErrorCode.FLAG_NOT_FOUND)
40+
return SplitProvider.construct_flag_resolution(default_value, evaluated, Reason.DEFAULT,
41+
ErrorCode.FLAG_NOT_FOUND)
4442
evaluated_lower = evaluated.lower()
4543
if evaluated_lower in ["true", "on"]:
4644
value = True
4745
elif evaluated_lower in ["false", "off"]:
4846
value = False
4947
else:
50-
raise exceptions.ParseError("Could not convert treatment to boolean")
51-
return SplitProvider.construct_provider_evaluation(flag_key, value, evaluated)
52-
except exceptions.OpenFeatureError:
48+
raise ParseError("Could not convert treatment to boolean")
49+
return SplitProvider.construct_flag_resolution(value, evaluated)
50+
except OpenFeatureError:
5351
raise
5452
except Exception:
55-
raise exceptions.GeneralError("Error getting boolean evaluation")
53+
raise GeneralError("Error getting boolean evaluation")
5654

5755
def resolve_string_details(self, flag_key: str, default_value: str,
5856
evaluation_context: EvaluationContext = EvaluationContext()):
5957
try:
6058
evaluated = self.evaluate_treatment(flag_key, evaluation_context)
6159
if SplitProvider.no_treatment(evaluated):
62-
return SplitProvider.construct_provider_evaluation(flag_key, default_value, evaluated, Reason.DEFAULT,
63-
ErrorCode.FLAG_NOT_FOUND)
64-
return SplitProvider.construct_provider_evaluation(flag_key, evaluated, evaluated)
65-
except exceptions.OpenFeatureError:
60+
return SplitProvider.construct_flag_resolution(default_value, evaluated, Reason.DEFAULT,
61+
ErrorCode.FLAG_NOT_FOUND)
62+
return SplitProvider.construct_flag_resolution(evaluated, evaluated)
63+
except OpenFeatureError:
6664
raise
6765
except Exception:
68-
raise exceptions.GeneralError("Error getting boolean evaluation")
66+
raise GeneralError("Error getting boolean evaluation")
6967

7068
def resolve_integer_details(self, flag_key: str, default_value: int,
7169
evaluation_context: EvaluationContext = EvaluationContext()):
7270
try:
7371
evaluated = self.evaluate_treatment(flag_key, evaluation_context)
7472
if SplitProvider.no_treatment(evaluated):
75-
return SplitProvider.construct_provider_evaluation(flag_key, default_value, evaluated, Reason.DEFAULT,
76-
ErrorCode.FLAG_NOT_FOUND)
73+
return SplitProvider.construct_flag_resolution(default_value, evaluated, Reason.DEFAULT,
74+
ErrorCode.FLAG_NOT_FOUND)
7775
try:
7876
value = int(evaluated)
7977
except ValueError:
80-
raise exceptions.ParseError("Could not convert treatment to integer")
81-
return SplitProvider.construct_provider_evaluation(flag_key, value, evaluated)
82-
except exceptions.OpenFeatureError:
78+
raise ParseError("Could not convert treatment to integer")
79+
return SplitProvider.construct_flag_resolution(value, evaluated)
80+
except OpenFeatureError:
8381
raise
8482
except Exception:
85-
raise exceptions.GeneralError("Error getting boolean evaluation")
83+
raise GeneralError("Error getting boolean evaluation")
8684

8785
def resolve_float_details(self, flag_key: str, default_value: float,
8886
evaluation_context: EvaluationContext = EvaluationContext()):
8987
try:
9088
evaluated = self.evaluate_treatment(flag_key, evaluation_context)
9189
if SplitProvider.no_treatment(evaluated):
92-
return SplitProvider.construct_provider_evaluation(flag_key, default_value, evaluated, Reason.DEFAULT,
93-
ErrorCode.FLAG_NOT_FOUND)
90+
return SplitProvider.construct_flag_resolution(default_value, evaluated, Reason.DEFAULT,
91+
ErrorCode.FLAG_NOT_FOUND)
9492
try:
9593
value = float(evaluated)
9694
except ValueError:
97-
raise exceptions.ParseError("Could not convert treatment to float")
98-
return SplitProvider.construct_provider_evaluation(flag_key, value, evaluated)
99-
except exceptions.OpenFeatureError:
95+
raise ParseError("Could not convert treatment to float")
96+
return SplitProvider.construct_flag_resolution(value, evaluated)
97+
except OpenFeatureError:
10098
raise
10199
except Exception:
102-
raise exceptions.GeneralError("Error getting boolean evaluation")
100+
raise GeneralError("Error getting boolean evaluation")
103101

104102
def resolve_object_details(self, flag_key: str, default_value: dict,
105103
evaluation_context: EvaluationContext = EvaluationContext()):
106104
try:
107105
evaluated = self.evaluate_treatment(flag_key, evaluation_context)
108106
if SplitProvider.no_treatment(evaluated):
109-
return SplitProvider.construct_provider_evaluation(flag_key, default_value, evaluated, Reason.DEFAULT,
110-
ErrorCode.FLAG_NOT_FOUND)
107+
return SplitProvider.construct_flag_resolution(default_value, evaluated, Reason.DEFAULT,
108+
ErrorCode.FLAG_NOT_FOUND)
111109
value = json.loads(evaluated)
112-
return SplitProvider.construct_provider_evaluation(flag_key, value, evaluated)
110+
return SplitProvider.construct_flag_resolution(value, evaluated)
113111
except JSONDecodeError:
114-
raise exceptions.ParseError("Could not convert treatment to dict")
115-
except exceptions.OpenFeatureError:
112+
raise ParseError("Could not convert treatment to dict")
113+
except OpenFeatureError:
116114
raise
117115
except Exception:
118-
raise exceptions.GeneralError("Error getting boolean evaluation")
116+
raise GeneralError("Error getting boolean evaluation")
119117

120118
# *** --- Helpers --- ***
121119

122120
def evaluate_treatment(self, key: str, evaluation_context: EvaluationContext):
123121
if evaluation_context is None:
124-
raise exceptions.GeneralError("Evaluation Context must be provided for the Split Provider")
122+
raise GeneralError("Evaluation Context must be provided for the Split Provider")
125123

126124
targeting_key = evaluation_context.targeting_key
127125
if not targeting_key:
128-
raise exceptions.TargetingKeyMissingError("Missing targeting key")
126+
raise TargetingKeyMissingError("Missing targeting key")
129127

130128
attributes = SplitProvider.transform_context(evaluation_context)
131129
return self.split_client.get_treatment(targeting_key, key, attributes)
@@ -139,7 +137,6 @@ def no_treatment(treatment: str):
139137
return not treatment or treatment == "control"
140138

141139
@staticmethod
142-
def construct_provider_evaluation(key: str, value, variant: str, reason: Reason = Reason.TARGETING_MATCH,
143-
error_code: ErrorCode = None):
144-
return FlagEvaluationDetails(flag_key=key, value=value, reason=reason,
145-
error_code=error_code, variant=variant)
140+
def construct_flag_resolution(value, variant: str, reason: Reason = Reason.TARGETING_MATCH,
141+
error_code: ErrorCode = None):
142+
return FlagResolutionDetails(value=value, error_code=error_code, reason=reason, variant=variant)

tests/test_client.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import pytest
2-
from open_feature import open_feature_api
3-
from open_feature.evaluation_context.evaluation_context import EvaluationContext
4-
from open_feature.exception.error_code import ErrorCode
5-
from open_feature.flag_evaluation.reason import Reason
2+
from openfeature import api
3+
from openfeature.evaluation_context import EvaluationContext
4+
from openfeature.exception import ErrorCode
5+
from openfeature.flag_evaluation import Reason
66
from splitio import get_factory
77
from split_openfeature import SplitProvider
88

@@ -18,17 +18,18 @@ class TestClient(object):
1818

1919
@pytest.fixture
2020
def provider(self):
21-
factory = get_factory("localhost", config={"splitFile": "split.yaml"})
22-
factory.block_until_ready(5)
23-
return SplitProvider(client=factory.client())
21+
split_factory = get_factory("localhost", config={"splitFile": "split.yaml"})
22+
split_factory.block_until_ready(5)
23+
split_client = split_factory.client()
24+
return SplitProvider(client=split_client)
2425

2526
@pytest.fixture
2627
def set_provider(self, provider):
27-
open_feature_api.set_provider(provider)
28+
api.set_provider(provider)
2829

2930
@pytest.fixture
3031
def client(self, set_provider):
31-
return open_feature_api.get_client("Split Client")
32+
return api.get_client()
3233

3334
@pytest.fixture(autouse=True)
3435
def targeting_key(self, client):
@@ -108,7 +109,7 @@ def test_obj_split(self, client):
108109
assert result == {"key": "value"}
109110

110111
def test_get_metadata(self):
111-
assert open_feature_api.get_provider().get_metadata().name == "Split"
112+
assert api.get_provider_metadata().name == "Split"
112113

113114
def test_boolean_details(self, client):
114115
details = client.get_boolean_details(self.some_other_feature, True)

tests/test_split_provider.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
from pytest import fail
22
from mock import MagicMock
3-
from open_feature.exception import exceptions
4-
from open_feature.exception.error_code import ErrorCode
5-
from open_feature.evaluation_context.evaluation_context import EvaluationContext
3+
from openfeature.exception import ErrorCode, OpenFeatureError
4+
from openfeature.evaluation_context import EvaluationContext
65
from split_openfeature import SplitProvider
76

87

@@ -75,7 +74,7 @@ def test_boolean_error(self):
7574
try:
7675
self.provider.resolve_boolean_details(self.flag_name, False, self.eval_context)
7776
fail("Should have thrown an error casting string to boolean")
78-
except exceptions.OpenFeatureError as e:
77+
except OpenFeatureError as e:
7978
assert e.error_code == ErrorCode.PARSE_ERROR
8079
except Exception:
8180
fail("Unexpected exception occurred")
@@ -150,7 +149,7 @@ def test_int_error(self):
150149
try:
151150
self.provider.resolve_integer_details(self.flag_name, 100, self.eval_context)
152151
fail("Should have thrown an exception casting string to num")
153-
except exceptions.OpenFeatureError as e:
152+
except OpenFeatureError as e:
154153
assert e.error_code == ErrorCode.PARSE_ERROR
155154
except Exception:
156155
fail("Unexpected exception occurred")
@@ -159,7 +158,7 @@ def test_int_error(self):
159158
try:
160159
self.provider.resolve_integer_details(self.flag_name, 100, self.eval_context)
161160
fail("Should have thrown an exception casting string to int")
162-
except exceptions.OpenFeatureError as e:
161+
except OpenFeatureError as e:
163162
assert e.error_code == ErrorCode.PARSE_ERROR
164163
except Exception:
165164
fail("Unexpected exception occurred")
@@ -207,7 +206,7 @@ def test_float_error(self):
207206
try:
208207
self.provider.resolve_float_details(self.flag_name, 100.5, self.eval_context)
209208
fail("Should have thrown an exception casting string to float")
210-
except exceptions.OpenFeatureError as e:
209+
except OpenFeatureError as e:
211210
assert e.error_code == ErrorCode.PARSE_ERROR
212211
except Exception:
213212
fail("Unexpected exception occurred")
@@ -255,7 +254,7 @@ def test_obj_error(self):
255254
try:
256255
self.provider.resolve_object_details(self.flag_name, {"foo": "bar"}, self.eval_context)
257256
fail("Should have thrown an exception casting string to an object")
258-
except exceptions.OpenFeatureError as e:
257+
except OpenFeatureError as e:
259258
assert e.error_code == ErrorCode.PARSE_ERROR
260259
except Exception:
261260
fail("Unexpected exception occurred")

0 commit comments

Comments
 (0)