-
Notifications
You must be signed in to change notification settings - Fork 433
Feature request: Time based feature flags actions #1666
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
Closed
+1,800
−205
Closed
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
930e853
feature: time conditions
65ac67f
feature: time conditions
5b41a62
Update aws_lambda_powertools/utilities/feature_flags/time_conditions.py
ran-isenberg 4042651
Update aws_lambda_powertools/utilities/feature_flags/time_conditions.py
ran-isenberg 623d527
fixed all tests, added tests
76489d6
fixed all tests, added tests
e1f48a5
even better
ab4d1db
fix(feature_flags): subtle bugs on enum manipulation
heitorlessa 17c4eb0
chore: use built-in datetime utc tz over 3P
heitorlessa 81bbf7a
cr fixes
6760fbb
add schema validation for SCHEDULE_BETWEEN_TIME_RANGE action
cef82c4
added schema validation for datetime range
6f534d6
finished validation
7f07c24
pre cr changes
e88a268
first cr fixes
707a27d
chore: re-wrote time based actions to reduce size
rubenfonseca 00e47a3
fix tests and typo
41d63d6
no mo' fixture
03ad6ae
chore: change strptime with fromisoformat
rubenfonseca 2dfbfc3
fix: tests under py3.9
rubenfonseca 0b77d7f
chore: add initial documentation
rubenfonseca 2e6519d
cr fix
d550d03
fix(docs): added use cases to documentation
rubenfonseca ebe5abb
fix: improve performance by removing one more srptime
rubenfonseca b1462e7
cr fix
62dd956
chore: support non UTC timestamps everywhere
rubenfonseca b1d6ff2
fix: updated doc examples
rubenfonseca beec3ad
docs: update docs
rubenfonseca 8b0e14e
chore: add more doc strings
rubenfonseca f789ee5
fixed exampe, added example
c07d1c7
update changelog with latest changes
4b81046
fix poetry.lock conflict
18859f5
fix: handle time conditions across the day boundary
rubenfonseca 3739ab6
fix: clarified condition
rubenfonseca 540659d
chore(deps-dev): bump mypy-boto3-lambda from 1.26.18 to 1.26.49 (#1832)
dependabot[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
73 changes: 73 additions & 0 deletions
73
aws_lambda_powertools/utilities/feature_flags/time_conditions.py
This file contains hidden or 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,73 @@ | ||
from datetime import datetime, tzinfo | ||
from typing import Dict, Optional | ||
|
||
from dateutil.tz import gettz | ||
|
||
from .schema import HOUR_MIN_SEPARATOR, TimeValues | ||
rubenfonseca marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
def _get_now_from_timezone(timezone: Optional[tzinfo]) -> datetime: | ||
""" | ||
Returns now in the specified timezone. Defaults to UTC if not present. | ||
At this stage, we already validated that the passed timezone string is valid, so we assume that | ||
gettz() will return a tzinfo object. | ||
""" | ||
timezone = gettz("UTC") if timezone is None else timezone | ||
return datetime.now(timezone) | ||
|
||
|
||
def compare_days_of_week(action: str, values: Dict) -> bool: | ||
timezone_name = values.get(TimeValues.TIMEZONE.value, "UTC") | ||
|
||
# %A = Weekday as locale’s full name. | ||
current_day = _get_now_from_timezone(gettz(timezone_name)).strftime("%A").upper() | ||
|
||
days = values.get(TimeValues.DAYS.value, []) | ||
return current_day in days | ||
|
||
|
||
def compare_datetime_range(action: str, values: Dict) -> bool: | ||
timezone_name = values.get(TimeValues.TIMEZONE.value, "UTC") | ||
timezone = gettz(timezone_name) | ||
current_time: datetime = _get_now_from_timezone(timezone) | ||
|
||
start_date_str = values.get(TimeValues.START.value, "") | ||
end_date_str = values.get(TimeValues.END.value, "") | ||
|
||
# Since start_date and end_date doesn't include timezone information, we mark the timestamp | ||
# with the same timezone as the current_time. This way all the 3 timestamps will be on | ||
# the same timezone. | ||
start_date = datetime.fromisoformat(start_date_str).replace(tzinfo=timezone) | ||
end_date = datetime.fromisoformat(end_date_str).replace(tzinfo=timezone) | ||
return start_date <= current_time <= end_date | ||
|
||
|
||
def compare_time_range(action: str, values: Dict) -> bool: | ||
timezone_name = values.get(TimeValues.TIMEZONE.value, "UTC") | ||
current_time: datetime = _get_now_from_timezone(gettz(timezone_name)) | ||
|
||
start_hour, start_min = values.get(TimeValues.START.value, "").split(HOUR_MIN_SEPARATOR) | ||
end_hour, end_min = values.get(TimeValues.END.value, "").split(HOUR_MIN_SEPARATOR) | ||
|
||
start_time = current_time.replace(hour=int(start_hour), minute=int(start_min)) | ||
end_time = current_time.replace(hour=int(end_hour), minute=int(end_min)) | ||
|
||
if int(end_hour) < int(start_hour): | ||
# When the end hour is smaller than start hour, it means we are crossing a day's boundary. | ||
# In this case we need to assert that current_time is **either** on one side or the other side of the boundary | ||
# | ||
# ┌─────┐ ┌─────┐ ┌─────┐ | ||
# │20.00│ │00.00│ │04.00│ | ||
# └─────┘ └─────┘ └─────┘ | ||
# ───────────────────────────────────────────┬─────────────────────────────────────────▶ | ||
# ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ | ||
# │ │ │ | ||
# │ either this area │ │ or this area | ||
# │ │ │ | ||
# └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ | ||
# │ | ||
|
||
return (start_time <= current_time) or (current_time <= end_time) | ||
else: | ||
# In normal circumstances, we need to assert **both** conditions | ||
return start_time <= current_time <= end_time |
This file contains hidden or 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
This file contains hidden or 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,21 @@ | ||
{ | ||
"christmas_discount": { | ||
"default": false, | ||
"rules": { | ||
"enable discount during christmas": { | ||
"when_match": true, | ||
"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" | ||
} | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} |
This file contains hidden or 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,14 @@ | ||
from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags | ||
|
||
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 | ||
xmas_discount = feature_flags.evaluate(name="christmas_discount", default=False) | ||
|
||
if xmas_discount: | ||
# Enable special discount on christmas: | ||
pass |
This file contains hidden or 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,16 @@ | ||
from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags | ||
|
||
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 | ||
ctx = {"tier": event.get("tier", "standard")} | ||
|
||
weekend_premium_discount = feature_flags.evaluate(name="weekend_premium_discount", default=False, context=ctx) | ||
|
||
if weekend_premium_discount: | ||
# Enable special discount for premium members on weekends | ||
pass |
This file contains hidden or 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": "rubefons", | ||
"tier": "premium", | ||
"basked_id": "random_id" | ||
} |
This file contains hidden or 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,28 @@ | ||
{ | ||
"weekend_premium_discount": { | ||
"default": false, | ||
"rules": { | ||
"customer tier equals premium and its time for a discount": { | ||
"when_match": true, | ||
"conditions": [ | ||
{ | ||
"action": "EQUALS", | ||
"key": "tier", | ||
"value": "premium" | ||
}, | ||
{ | ||
"action": "SCHEDULE_BETWEEN_DAYS_OF_WEEK", | ||
"key": "CURRENT_DAY_OF_WEEK", | ||
"value": { | ||
"DAYS": [ | ||
"SATURDAY", | ||
"SUNDAY" | ||
], | ||
"TIMEZONE": "America/New_York" | ||
} | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} |
This file contains hidden or 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,13 @@ | ||
from aws_lambda_powertools.utilities.feature_flags import AppConfigStore, FeatureFlags | ||
|
||
app_config = AppConfigStore(environment="dev", application="product-catalogue", name="features") | ||
|
||
feature_flags = FeatureFlags(store=app_config) | ||
|
||
|
||
def lambda_handler(event, context): | ||
is_happy_hour = feature_flags.evaluate(name="happy_hour", default=False) | ||
|
||
if is_happy_hour: | ||
# Apply special discount | ||
pass |
21 changes: 21 additions & 0 deletions
21
examples/feature_flags/src/timebased_happyhour_features.json
This file contains hidden or 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,21 @@ | ||
{ | ||
"happy_hour": { | ||
"default": false, | ||
"rules": { | ||
"is happy hour": { | ||
"when_match": true, | ||
"conditions": [ | ||
{ | ||
"action": "SCHEDULE_BETWEEN_TIME_RANGE", | ||
"key": "CURRENT_TIME", | ||
"value": { | ||
"START": "17:00", | ||
"END": "19:00", | ||
"TIMEZONE": "Europe/Copenhagen" | ||
} | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or 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
491 changes: 490 additions & 1 deletion
491
tests/functional/feature_flags/test_schema_validation.py
Large diffs are not rendered by default.
Oops, something went wrong.
533 changes: 533 additions & 0 deletions
533
tests/functional/feature_flags/test_time_based_actions.py
Large diffs are not rendered by default.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.