-
Notifications
You must be signed in to change notification settings - Fork 406
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs(feature_flags): snippets split, improved, and lint (#2222)
Co-authored-by: Ruben Fonseca <fonseka@gmail.com>
- Loading branch information
1 parent
099487e
commit ef09c8b
Showing
36 changed files
with
860 additions
and
521 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
AWSTemplateFormatVersion: "2010-09-09" | ||
Description: Lambda Powertools for Python Feature flags sample template | ||
Resources: | ||
FeatureStoreApp: | ||
Type: AWS::AppConfig::Application | ||
Properties: | ||
Description: "AppConfig Application for feature toggles" | ||
Name: product-catalogue | ||
|
||
FeatureStoreDevEnv: | ||
Type: AWS::AppConfig::Environment | ||
Properties: | ||
ApplicationId: !Ref FeatureStoreApp | ||
Description: "Development Environment for the App Config Store" | ||
Name: dev | ||
|
||
FeatureStoreConfigProfile: | ||
Type: AWS::AppConfig::ConfigurationProfile | ||
Properties: | ||
ApplicationId: !Ref FeatureStoreApp | ||
Name: features | ||
LocationUri: "hosted" | ||
|
||
HostedConfigVersion: | ||
Type: AWS::AppConfig::HostedConfigurationVersion | ||
Properties: | ||
ApplicationId: !Ref FeatureStoreApp | ||
ConfigurationProfileId: !Ref FeatureStoreConfigProfile | ||
Description: 'A sample hosted configuration version' | ||
Content: | | ||
{ | ||
"premium_features": { | ||
"default": false, | ||
"rules": { | ||
"customer tier equals premium": { | ||
"when_match": true, | ||
"conditions": [ | ||
{ | ||
"action": "EQUALS", | ||
"key": "tier", | ||
"value": "premium" | ||
} | ||
] | ||
} | ||
} | ||
}, | ||
"ten_percent_off_campaign": { | ||
"default": false | ||
} | ||
} | ||
ContentType: 'application/json' | ||
|
||
ConfigDeployment: | ||
Type: AWS::AppConfig::Deployment | ||
Properties: | ||
ApplicationId: !Ref FeatureStoreApp | ||
ConfigurationProfileId: !Ref FeatureStoreConfigProfile | ||
ConfigurationVersion: !Ref HostedConfigVersion | ||
DeploymentStrategyId: "AppConfig.AllAtOnce" | ||
EnvironmentId: !Ref FeatureStoreDevEnv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from typing import Any | ||
|
||
from botocore.config import Config | ||
from jmespath.functions import Functions, signature | ||
|
||
from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags | ||
from aws_lambda_powertools.utilities.typing import LambdaContext | ||
|
||
boto_config = Config(read_timeout=10, retries={"total_max_attempts": 2}) | ||
|
||
|
||
# Custom JMESPath functions | ||
class CustomFunctions(Functions): | ||
@signature({"types": ["object"]}) | ||
def _func_special_decoder(self, features): | ||
# You can add some logic here | ||
return features | ||
|
||
|
||
custom_jmespath_options = {"custom_functions": CustomFunctions()} | ||
|
||
|
||
app_config = AppConfigStore( | ||
environment="dev", | ||
application="product-catalogue", | ||
name="features", | ||
max_age=120, | ||
envelope="special_decoder(features)", # using a custom function defined in CustomFunctions Class | ||
sdk_config=boto_config, | ||
jmespath_options=custom_jmespath_options, | ||
) | ||
|
||
feature_flags = FeatureFlags(store=app_config) | ||
|
||
|
||
def lambda_handler(event: dict, context: LambdaContext): | ||
apply_discount: Any = feature_flags.evaluate(name="ten_percent_off_campaign", default=False) | ||
|
||
price: Any = event.get("price") | ||
|
||
if apply_discount: | ||
# apply 10% discount to product | ||
price = price * 0.9 | ||
|
||
return {"price": price} |
11 changes: 11 additions & 0 deletions
11
examples/feature_flags/src/appconfig_provider_options_features.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"logging": { | ||
"level": "INFO", | ||
"sampling_rate": 0.1 | ||
}, | ||
"features": { | ||
"ten_percent_off_campaign": { | ||
"default": true | ||
} | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
examples/feature_flags/src/appconfig_provider_options_payload.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"product": "laptop", | ||
"price": 1000 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from typing import Any | ||
|
||
from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags | ||
from aws_lambda_powertools.utilities.typing import LambdaContext | ||
|
||
app_config = AppConfigStore(environment="dev", application="comments", name="config") | ||
|
||
feature_flags = FeatureFlags(store=app_config) | ||
|
||
|
||
def lambda_handler(event: dict, context: LambdaContext): | ||
# Get customer's tier from incoming request | ||
ctx = {"tier": event.get("tier", "standard")} | ||
|
||
# Evaluate `has_premium_features` based on customer's tier | ||
premium_features: Any = feature_flags.evaluate(name="premium_features", context=ctx, default=[]) | ||
|
||
return {"Premium features enabled": premium_features} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"premium_features": { | ||
"boolean_type": false, | ||
"default": [], | ||
"rules": { | ||
"customer tier equals premium": { | ||
"when_match": [ | ||
"no_ads", | ||
"no_limits", | ||
"chat" | ||
], | ||
"conditions": [ | ||
{ | ||
"action": "EQUALS", | ||
"key": "tier", | ||
"value": "premium" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"username": "lessa", | ||
"tier": "premium", | ||
"basked_id": "random_id" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"conditions": [ | ||
{ | ||
"action": "EQUALS", | ||
"key": "tier", | ||
"value": "premium" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import json | ||
from typing import Any, Dict | ||
|
||
import boto3 | ||
from botocore.exceptions import ClientError | ||
|
||
from aws_lambda_powertools.utilities.feature_flags.base import StoreProvider | ||
from aws_lambda_powertools.utilities.feature_flags.exceptions import ( | ||
ConfigurationStoreError, | ||
) | ||
|
||
|
||
class S3StoreProvider(StoreProvider): | ||
def __init__(self, bucket_name: str, object_key: str): | ||
# Initialize the client to your custom store provider | ||
|
||
super().__init__() | ||
|
||
self.bucket_name = bucket_name | ||
self.object_key = object_key | ||
self.client = boto3.client("s3") | ||
|
||
def _get_s3_object(self) -> Dict[str, Any]: | ||
# Retrieve the object content | ||
parameters = {"Bucket": self.bucket_name, "Key": self.object_key} | ||
|
||
try: | ||
response = self.client.get_object(**parameters) | ||
return json.loads(response["Body"].read().decode()) | ||
except ClientError as exc: | ||
raise ConfigurationStoreError("Unable to get S3 Store Provider configuration file") from exc | ||
|
||
def get_configuration(self) -> Dict[str, Any]: | ||
return self._get_s3_object() | ||
|
||
@property | ||
def get_raw_configuration(self) -> Dict[str, Any]: | ||
return self._get_s3_object() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,37 @@ | ||
from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags | ||
from aws_lambda_powertools.utilities.typing import LambdaContext | ||
|
||
app_config = AppConfigStore(environment="dev", application="product-catalogue", name="features") | ||
|
||
feature_flags = FeatureFlags(store=app_config) | ||
|
||
|
||
def lambda_handler(event, context): | ||
# Get customer's tier from incoming request | ||
def lambda_handler(event: dict, context: LambdaContext): | ||
""" | ||
This feature flag is enabled under the following conditions: | ||
- Start date: December 25th, 2022 at 12:00:00 PM EST | ||
- End date: December 31st, 2022 at 11:59:59 PM EST | ||
- Timezone: America/New_York | ||
Rule condition to be evaluated: | ||
"conditions": [ | ||
{ | ||
"action": "SCHEDULE_BETWEEN_DATETIME_RANGE", | ||
"key": "CURRENT_DATETIME", | ||
"value": { | ||
"START": "2022-12-25T12:00:00", | ||
"END": "2022-12-31T23:59:59", | ||
"TIMEZONE": "America/New_York" | ||
} | ||
} | ||
] | ||
""" | ||
|
||
# Checking if the Christmas discount is enable | ||
xmas_discount = feature_flags.evaluate(name="christmas_discount", default=False) | ||
|
||
if xmas_discount: | ||
# Enable special discount on christmas: | ||
pass | ||
return {"message": "The Christmas discount is enabled."} | ||
|
||
return {"message": "The Christmas discount is not enabled."} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from typing import Any | ||
|
||
from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags | ||
from aws_lambda_powertools.utilities.typing import LambdaContext | ||
|
||
app_config = AppConfigStore( | ||
environment="dev", application="product-catalogue", name="features", envelope="feature_flags" | ||
) | ||
|
||
feature_flags = FeatureFlags(store=app_config) | ||
|
||
|
||
def lambda_handler(event: dict, context: LambdaContext): | ||
apply_discount: Any = feature_flags.evaluate(name="ten_percent_off_campaign", default=False) | ||
|
||
price: Any = event.get("price") | ||
|
||
if apply_discount: | ||
# apply 10% discount to product | ||
price = price * 0.9 | ||
|
||
return {"price": price} |
11 changes: 11 additions & 0 deletions
11
examples/feature_flags/src/extracting_envelope_features.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"logging": { | ||
"level": "INFO", | ||
"sampling_rate": 0.1 | ||
}, | ||
"features": { | ||
"ten_percent_off_campaign": { | ||
"default": true | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"product": "laptop", | ||
"price": 1000 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"premium_feature": { | ||
"default": false, | ||
"rules": { | ||
"customer tier equals premium": { | ||
"when_match": true, | ||
"conditions": [ | ||
{ | ||
"action": "EQUALS", | ||
"key": "tier", | ||
"value": "premium" | ||
} | ||
] | ||
} | ||
} | ||
}, | ||
"non_boolean_premium_feature": { | ||
"default": [], | ||
"rules": { | ||
"customer tier equals premium": { | ||
"when_match": ["remove_limits", "remove_ads"], | ||
"conditions": [ | ||
{ | ||
"action": "EQUALS", | ||
"key": "tier", | ||
"value": "premium" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
examples/feature_flags/src/getting_all_enabled_features.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from aws_lambda_powertools.event_handler import APIGatewayRestResolver | ||
from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags | ||
from aws_lambda_powertools.utilities.typing import LambdaContext | ||
|
||
app = APIGatewayRestResolver() | ||
|
||
app_config = AppConfigStore(environment="dev", application="product-catalogue", name="features") | ||
|
||
feature_flags = FeatureFlags(store=app_config) | ||
|
||
|
||
@app.get("/products") | ||
def list_products(): | ||
# getting fields from request | ||
# https://awslabs.github.io/aws-lambda-powertools-python/latest/core/event_handler/api_gateway/#accessing-request-details | ||
json_body = app.current_event.json_body | ||
headers = app.current_event.headers | ||
|
||
ctx = {**headers, **json_body} | ||
|
||
# getting price from payload | ||
price: float = float(json_body.get("price")) | ||
percent_discount: int = 0 | ||
|
||
# all_features is evaluated to ["premium_features", "geo_customer_campaign", "ten_percent_off_campaign"] | ||
all_features: list[str] = feature_flags.get_enabled_features(context=ctx) | ||
|
||
if "geo_customer_campaign" in all_features: | ||
# apply 20% discounts for customers in NL | ||
percent_discount += 20 | ||
|
||
if "ten_percent_off_campaign" in all_features: | ||
# apply additional 10% for all customers | ||
percent_discount += 10 | ||
|
||
price = price * (100 - percent_discount) / 100 | ||
|
||
return {"price": price} | ||
|
||
|
||
def lambda_handler(event: dict, context: LambdaContext): | ||
return app.resolve(event, context) |
Oops, something went wrong.