Skip to content

Commit eda6fc5

Browse files
author
Dave Johnston
authored
Merge branch 'main' into SWAT-8089
2 parents 058ebcc + d241f37 commit eda6fc5

File tree

6 files changed

+99
-37
lines changed

6 files changed

+99
-37
lines changed

README.md

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Harness Feature Flag Python SDK
1+
Python SDK For Harness Feature Flags
22
========================
33

44
[![pypi](https://img.shields.io/pypi/v/harness-featureflags.svg)](https://pypi.python.org/pypi/harness-featureflags)
@@ -13,10 +13,10 @@ Harness Feature Flag Python SDK
1313

1414
## Intro
1515

16-
Harness Feature Flags (FF) is a feature management solution that enables users to change the software’s functionality, without deploying new code. FF uses feature flags to hide code or behaviours without having to ship new versions of the software. A feature flag is like a powerful if statement.
17-
* For more information, see https://harness.io/products/feature-flags/
18-
* To read more, see https://ngdocs.harness.io/category/vjolt35atg-feature-flags
19-
* To sign up, https://app.harness.io/auth/#/signup/
16+
Use this README to get started with our Feature Flags (FF) SDK for Python. This guide outlines the basics of getting started with the SDK and provides a full code sample for you to try out.
17+
This sample doesn’t include configuration options, for in depth steps and configuring the SDK, for example, disabling streaming or using our Relay Proxy, see the [Python SDK Reference](https://ngdocs.harness.io/article/hwoxb6x2oe-python-sdk-reference).
18+
19+
For a sample FF Python SDK project, see our test [test Golang project](examples/getting_started/getting_started.py).
2020

2121
![FeatureFlags](https://github.com/harness/ff-python-server-sdk/raw/main/docs/images/ff-gui.png)
2222

@@ -29,20 +29,22 @@ Harness Feature Flags (FF) is a feature management solution that enables users t
2929

3030

3131
## Quickstart
32-
The Feature Flag SDK provides a client that connects to the feature flag service, and fetches the value
33-
of featue flags. The following section provides an example of how to install the SDK and initalize it from
34-
an application.
35-
This quickstart assumes you have followed the instructions to [setup a Feature Flag project and have created a flag called `harnessappdemodarkmode` and created a server API Key](https://ngdocs.harness.io/article/1j7pdkqh7j-create-a-feature-flag#step_1_create_a_project).
32+
To follow along with our test code sample, make sure you’ve:
3633

34+
- [Created a Feature Flag on the Harness Platform](https://ngdocs.harness.io/article/1j7pdkqh7j-create-a-feature-flag) called harnessappdemodarkmode
35+
- [Created a server SDK key and made a copy of it](https://ngdocs.harness.io/article/1j7pdkqh7j-create-a-feature-flag#step_3_create_an_sdk_key)
36+
-
3737
### Install the SDK
3838
Install the python SDK using pip
3939
```python
4040
python -m pip install harness-featureflags
4141
```
4242

43-
### A Simple Example
44-
Here is a complete example that will connect to the feature flag service and report the flag value every 10 seconds until the connection is closed.
45-
Any time a flag is toggled from the feature flag service you will receive the updated value.
43+
### Code Sample
44+
The following is a complete code example that you can use to test the `harnessappdemodarkmode` Flag you created on the Harness Platform. When you run the code it will:
45+
- Connect to the FF service.
46+
- Report the value of the Flag every 10 seconds until the connection is closed. Every time the harnessappdemodarkmode Flag is toggled on or off on the Harness Platform, the updated value is reported.
47+
- Close the SDK.
4648

4749
```python
4850
from featureflags.client import CfClient
@@ -63,7 +65,7 @@ def main():
6365
client = CfClient(apiKey)
6466

6567
# Create a target (different targets can get different results based on rules. This include a custom attribute 'location')
66-
target = Target(identifier='mytarget', name="FriendlyName", attributes={"location": "emea"}
68+
target = Target(identifier='pythonSDK', name="PythonSDK", attributes={"location": "emea"})
6769

6870
# Loop forever reporting the state of the flag
6971
while True:
@@ -85,9 +87,9 @@ $ export FF_API_KEY=<your key here>
8587
$ python3 examples/getting_started/getting_started.py
8688
```
8789

88-
### Running with docker
90+
### Running the example with Docker
8991
If you dont have the right version of python installed locally, or dont want to install the dependancies you can
90-
use docker to quicky get started
92+
use docker to quickly get started
9193

9294
```bash
9395
# Install the package
@@ -99,13 +101,13 @@ docker run -e PYTHONPATH=/app/local -e FF_API_KEY=$FF_API_KEY -v $(pwd):/app -w
99101

100102
### Additional Reading
101103

102-
Further examples and config options are in the further reading section:
103-
104-
[Further Reading](docs/further_reading.md)
104+
For further examples and config options, see the [Python SDK Reference](https://ngdocs.harness.io/article/hwoxb6x2oe-python-sdk-reference).
105105

106+
For more information about Feature Flags, see our [Feature Flags documentation](https://ngdocs.harness.io/article/0a2u2ppp8s-getting-started-with-feature-flags).
106107

107108
-------------------------
108109
[Harness](https://www.harness.io/) is a feature management platform that helps teams to build better software and to
109110
test features quicker.
110111

111112
-------------------------
113+

docs/further_reading.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,43 @@
22

33
Covers advanced topics (different config options and scenarios)
44

5+
## Configuration Options
6+
The following configuration options are available to control the behaviour of the SDK.
7+
You can pass the configuration in as options when the SDK client is created.
8+
```python
9+
# Create a Feature Flag Client
10+
client = CfClient(apiKey,
11+
with_base_url("https://config.ff.harness.io/api/1.0"),
12+
with_events_url("https://events.ff.harness.io/api/1.0"),
13+
with_stream_enabled(True),
14+
with_analytics_enabled(True),
15+
Config(pull_interval=60))
16+
```
17+
18+
| Name | Config Option | Description | default |
19+
|-----------------|----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------|
20+
| baseUrl | with_base_url("https://config.ff.harness.io/api/1.0") | the URL used to fetch feature flag evaluations. You should change this when using the Feature Flag proxy to http://localhost:7000 | https://config.ff.harness.io/api/1.0 |
21+
| eventsUrl | with_events_url("https://events.ff.harness.io/api/1.0"), | the URL used to post metrics data to the feature flag service. You should change this when using the Feature Flag proxy to http://localhost:7000 | https://events.ff.harness.io/api/1.0 |
22+
| pollInterval | Config(pull_interval=60) | when running in stream mode, the interval in seconds that we poll for changes. | 60 |
23+
| enableStream | with_stream_enabled(True), | Enable streaming mode. | true |
24+
| enableAnalytics | with_analytics_enabled(True) | Enable analytics. Metrics data is posted every 60s | true |
25+
| pollInterval | with_poll_interval(120) | When running in stream mode, the interval in seconds that we poll for changes. | 60 |
26+
27+
## Logging Configuration
28+
The SDK provides a logger that wraps the standard python logging package. You can import and use it with:
29+
```python
30+
from featureflags.util import log
31+
log.info("Hello, World!")
32+
```
33+
34+
If you want to change the default log level, you can use the standard logging levels
35+
```python
36+
from featureflags.util import log
37+
import logging
38+
39+
log.setLevel(logging.WARN)
40+
```
41+
542
## Recommended reading
643

744
[Feature Flag Concepts](https://ngdocs.harness.io/article/7n9433hkc0-cf-feature-flag-overview)

examples/getting_started/getting_started.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@
55
import os
66
import time
77

8+
89
# API Key
9-
apiKey = os.getenv('FF_API_KEY', "c9b3f14f-6336-4d23-83b4-73f29d1ebeeb")
10+
apiKey = os.getenv('FF_API_KEY', "")
1011

1112
# Flag Name
1213
flagName = os.getenv('FF_FLAG_NAME', "harnessappdemodarkmode")
1314

1415
def main():
16+
log.info("Harness SDK Getting Started")
1517
# Create a Feature Flag Client
1618
client = CfClient(apiKey)
1719

20+
1821
# Create a target (different targets can get different results based on rules)
19-
target = Target(identifier='mytarget', name="FriendlyName", attributes={"location": "emea"})
22+
target = Target(identifier='pythonSDK', name="PythonSDK", attributes={"location": "emea"})
2023

2124
# Loop forever reporting the state of the flag
2225
while True:

featureflags/client.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323

2424
class CfClient(object):
2525
def __init__(
26-
self, sdk_key: str, *options: Callable, config: Optional[Config] = None
26+
self, sdk_key: str,
27+
*options: Callable,
28+
config: Optional[Config] = None
2729
):
2830
self._client: Optional[Client] = None
2931
self._auth_token: Optional[str] = None
@@ -97,7 +99,7 @@ def authenticate(self):
9799
self._auth_token = response.auth_token
98100

99101
decoded = decode(self._auth_token, options={
100-
"verify_signature": False})
102+
"verify_signature": False})
101103
self._environment_id = decoded["environment"]
102104
self._cluster = decoded["clusterIdentifier"]
103105
if not self._cluster:
@@ -115,33 +117,38 @@ def authenticate(self):
115117
def bool_variation(self, identifier: str, target: Target,
116118
default: bool) -> bool:
117119
variation = self._evaluator.evaluate(identifier, target)
118-
self._analytics.enqueue(target, identifier, variation)
120+
if self._config.enable_analytics:
121+
self._analytics.enqueue(target, identifier, variation)
119122
return variation.bool(default)
120123

121124
def int_variation(self, identifier: str, target: Target,
122125
default: int) -> int:
123126
variation = self._evaluator.evaluate(identifier, target)
124-
self._analytics.enqueue(target, identifier, variation)
127+
if self._config.enable_analytics:
128+
self._analytics.enqueue(target, identifier, variation)
125129
return variation.int(default)
126130

127131
def number_variation(self, identifier: str, target: Target,
128132
default: float) -> float:
129133
variation = self._evaluator.evaluate(
130134
identifier, target)
131-
self._analytics.enqueue(target, identifier, variation)
135+
if self._config.enable_analytics:
136+
self._analytics.enqueue(target, identifier, variation)
132137
return variation.number(default)
133138

134139
def string_variation(self, identifier: str, target: Target,
135140
default: str) -> str:
136141
variation = self._evaluator.evaluate(
137142
identifier, target)
138-
self._analytics.enqueue(target, identifier, variation)
143+
if self._config.enable_analytics:
144+
self._analytics.enqueue(target, identifier, variation)
139145
return variation.string(default)
140146

141147
def json_variation(self, identifier: str, target: Target,
142148
default: Dict[str, Any]) -> Dict[str, Any]:
143149
variation = self._evaluator.evaluate(identifier, target)
144-
self._analytics.enqueue(target, identifier, variation)
150+
if self._config.enable_analytics:
151+
self._analytics.enqueue(target, identifier, variation)
145152
return variation.json(default)
146153

147154
def close(self):

featureflags/config.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616

1717
class Config(object):
1818
def __init__(
19-
self,
20-
base_url: str = BASE_URL,
21-
events_url: str = EVENTS_URL,
22-
pull_interval: int = PULL_INTERVAL,
23-
persist_interval: int = PERSIST_INTERVAL,
24-
events_sync_interval: int = EVENTS_SYNC_INTERVAL,
25-
cache: Cache = None,
26-
store: object = None,
27-
enable_stream: bool = True,
28-
enable_analytics: bool = True
19+
self,
20+
base_url: str = BASE_URL,
21+
events_url: str = EVENTS_URL,
22+
pull_interval: int = PULL_INTERVAL,
23+
persist_interval: int = PERSIST_INTERVAL,
24+
events_sync_interval: int = EVENTS_SYNC_INTERVAL,
25+
cache: Cache = None,
26+
store: object = None,
27+
enable_stream: bool = True,
28+
enable_analytics: bool = True
2929
):
3030
self.base_url = base_url
3131
self.events_url = events_url
@@ -69,3 +69,10 @@ def func(config: Config) -> None:
6969
config.enable_analytics = value
7070

7171
return func
72+
73+
74+
def with_pull_interval(value: int) -> Callable:
75+
def func(config: Config) -> None:
76+
config.pull_interval = value
77+
78+
return func

featureflags/polling.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ def __init__(self, client: AuthenticatedClient, config: Config,
2828

2929
def run(self):
3030
if not self.__running:
31+
if self.__config.pull_interval < 60:
32+
log.warning("Pull Interval must be greater than or equal "
33+
"to 60 seconds, was: " +
34+
str(self.__config.pull_interval) +
35+
" setting to 60")
36+
self.__config.pull_interval = 60
3137
log.info("Starting PollingProcessor with request interval: " +
3238
str(self.__config.pull_interval))
3339
self.__running = True

0 commit comments

Comments
 (0)