Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Support for feature properties #23

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
= CHANGELOG

==== 1.1.0

- updated to support feature properties
- updated to rev the dependencies. Minimum support of Python 3.7 means
upgrading the urllib to 2.x and SSE client library
2 changes: 1 addition & 1 deletion development.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ This SDK provides two edge clients (a Streaming one based on urllib3's `sseclien

== Repo issues
- needs reporting on how much test coverage we have
- needs beta testing!

3 changes: 2 additions & 1 deletion example/build.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
#!/bin/sh
docker build --no-cache -t featurehub/python-example:1.0 .
#docker build --no-cache -t featurehub/python-example:1.0 .
docker build -t featurehub/python-example:1.0 .
2 changes: 1 addition & 1 deletion example/config.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
edge_url = 'https://zjbisc.demo.featurehub.io'
edge_url = 'http://zjbisc.demo.featurehub.io'
client_eval_key = 'default/9b71f803-da79-4c04-8081-e5c0176dda87/CtVlmUHirgPd9Qz92Y0IQauUMUv3Wb*4dacoo47oYp6hSFFjVkG'
3 changes: 3 additions & 0 deletions example/load.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh
kind load docker-image featurehub/python-example:1.0 --name featurehub-cluster
#featurehub-cluster
2 changes: 1 addition & 1 deletion example/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def create_app(config=None):

fh_config = FeatureHubConfig(edge_url, [client_eval_key])
# to use polling
fh_config.use_polling_edge_service()
fh_config.use_polling_edge_service(1)
# it takes a parameter uses the environment variable FEATUREHUB_POLL_INTERVAL if set

print("starting featurehub")
Expand Down
2 changes: 1 addition & 1 deletion example/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Flask[async]==2.0.3
flask-cors==3.0.10
#git+https://github.com/featurehub-io/featurehub-python-sdk@main#egg=featurehub-sdk
git+https://github.com/featurehub-io/featurehub-python-sdk@feature/sdk-extensions#egg=featurehub-sdk


4 changes: 4 additions & 0 deletions featurehub_sdk/client_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ def locked(self):
def id(self):
return ""

@property
def feature_properties(self) -> dict[str,str]:
return {}

@property
def get_version(self) -> int:
return -1
Expand Down
2 changes: 0 additions & 2 deletions featurehub_sdk/featurehub_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ def __update_feature_state(self, feature_state):
# if feature is in the dictionary, check if version has changed
elif feature_state.get('version') < holder.get_version:
return
elif feature_state.get('version') == holder.get_version and feature_state.get('value') == holder.get_value:
return

holder.set_feature_state(feature_state)

Expand Down
9 changes: 9 additions & 0 deletions featurehub_sdk/fh_state_base_holder.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ def __get_value(self, feature_type: Optional[str]) -> Union[None, bool, str, flo

return state.get('value')

@property
def feature_properties(self) -> dict[str, str]:
state = self._top_feature_state_holder()._feature_state()

if state is None or state.get('fp') is None:
return {}

return state.get('fp')

def with_context(self, ctx: ClientContext) -> FeatureState:
return FeatureStateHolder(self._key, self._repo, None, self, ctx)

Expand Down
2 changes: 1 addition & 1 deletion featurehub_sdk/strategy_matchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Optional, List, Dict
import re

from semver import cmp
from nodesemver import cmp
import math
from murmurhash2 import murmurhash3
from ipaddress import ip_address, ip_network
Expand Down
2 changes: 1 addition & 1 deletion featurehub_sdk/test/test_apply_feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def test_should_be_false_if_no_strategies_match_context(self):
self.assertIsNone(found.value)
ctx.get_attr.assert_called_once()

def test_should_not_match_percentage_and_should_match_field(self):
def test_should_match_conditional(self):
ctx = MagicMock()
ctx.default_percentage_key = 'userkey-value'
ctx.get_attr.return_value = 'ponsonby'
Expand Down
51 changes: 51 additions & 0 deletions featurehub_sdk/test/test_feature_properties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import json
from unittest import TestCase

from unittest.mock import MagicMock

from featurehub_sdk.fh_state_base_holder import FeatureStateHolder

from featurehub_sdk.client_context import InternalFeatureRepository


class FeaturePropertiesTest(TestCase):
repo: InternalFeatureRepository

def setUp(self) -> None:
self.repo = MagicMock()

def test_feature_returns_empty_with_no_feature_state(self):
fs = FeatureStateHolder('key', self.repo)

self.assertEqual(fs.feature_properties, {})

def test_feature_returns_empty_dict_with_no_value_in_feature_state(self):
fs = FeatureStateHolder('key', self.repo)
data = '''{
"id": "227dc2e8-59e8-424a-b510-328ef52010f7",
"key": "key",
"l": true,
"version": 28,
"type": "STRING",
"value": "orange"
}'''
json_data = json.loads(data)
fs.set_feature_state(json_data)

self.assertEqual(fs.feature_properties, {})

def test_feature_returns_dict_when_properties_are_present(self):
fs = FeatureStateHolder('key', self.repo)
data = '''{
"id": "227dc2e8-59e8-424a-b510-328ef52010f7",
"key": "key",
"l": true,
"version": 28,
"type": "STRING",
"value": "orange",
"fp": {"category": "shoes", "appName": "conga", "portfolio": "fish"}
}'''
json_data = json.loads(data)
fs.set_feature_state(json_data)

self.assertEqual(fs.feature_properties, {"category": "shoes", "appName": "conga", "portfolio": "fish"})
2 changes: 1 addition & 1 deletion featurehub_sdk/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sdk_version="1.0.0"
sdk_version="1.1.0"
7 changes: 3 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
urllib3==1.26.*
sseclient-py==1.7.*
urllib3==2.2.*
sseclient-py==1.8.*
murmurhash2==0.2.*
node_semver==0.8.*

node_semver==0.9.*
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
packages=['featurehub_sdk'],
long_description=long_description,
long_description_content_type='text/markdown',
install_requires=['urllib3==1.26.*',
'sseclient-py==1.7.*',
install_requires=['urllib3==2.2.*',
'sseclient-py==1.8.*',
'murmurhash2==0.2.*',
'node_semver==0.8.*'],
'node_semver==0.9.*'],
)
Loading