Skip to content

Commit

Permalink
Blacklist → denylist, whitelist → allowlist (#20)
Browse files Browse the repository at this point in the history
Since this is an incompatible change, also bump up version to 2.0.0
(major version change, to signal incompatible API).
  • Loading branch information
lupino3 authored Jun 12, 2020
1 parent 1a2e1f3 commit 0742b20
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 95 deletions.
4 changes: 2 additions & 2 deletions FeatureFlags.PowerShell.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
<package>
<metadata>
<id>FeatureFlags.PowerShell</id>
<version>1.0.1</version>
<version>2.0.0</version>
<title>PowerShell Feature Flags</title>
<authors>Andrea Spadaccini,Nick Hara</authors>
<owners>Andrea Spadaccini</owners>
<license type="expression">MIT</license>
<projectUrl>https://github.com/microsoft/PowerShell-FeatureFlags/</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>PowerShell module containing a Feature Flags implementation based on a local config file.</description>
<releaseNotes>Fixed bug with the "probability" condition (issue #18).</releaseNotes>
<releaseNotes>Renamed blacklist to denylist, whitelist to allowlist. Incompatible with 1.0.x.</releaseNotes>
<copyright>Copyright 2020 Microsoft</copyright>
<repository type="git" url="https://github.com/microsoft/PowerShell-FeatureFlags.git" branch="master" />
</metadata>
Expand Down
Binary file modified FeatureFlags.psd1
Binary file not shown.
30 changes: 15 additions & 15 deletions FeatureFlags.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -219,24 +219,24 @@ function Test-FeatureConditions
)
# Conditions are evaluated in the order they are presented in the configuration file.
foreach ($condition in $conditions) {
# Each condition object can have only one of the whitelist, blacklist or probability
# Each condition object can have only one of the allowlist, denylist or probability
# attributes set. This invariant is enforced by the JSON schema, which uses the "oneof"
# strategy to choose between whitelist, blacklist or probability and, for each of these
# strategy to choose between allowlist, denylist or probability and, for each of these
# condition types, only allows the homonym attribute to be set.
if ($null -ne $condition.whitelist) {
Write-Verbose "Checking the whitelist condition"
# The predicate must match any of the regexes in the whitelist in order to
# consider the whitelist condition satisfied.
$matchesWhitelist = Test-RegexList $predicate @($condition.whitelist)
if (-not $matchesWhitelist) {
if ($null -ne $condition.allowlist) {
Write-Verbose "Checking the allowlist condition"
# The predicate must match any of the regexes in the allowlist in order to
# consider the allowlist condition satisfied.
$matchesallowlist = Test-RegexList $predicate @($condition.allowlist)
if (-not $matchesallowlist) {
return $false
}
} elseif ($null -ne $condition.blacklist) {
Write-Verbose "Checking the blacklist condition"
# The predicate must not match all of the regexes in the blacklist in order to
# consider the blacklist condition satisfied.
$matchesBlacklist = Test-RegexList $predicate @($condition.blacklist)
if ($matchesBlacklist) {
} elseif ($null -ne $condition.denylist) {
Write-Verbose "Checking the denylist condition"
# The predicate must not match all of the regexes in the denylist in order to
# consider the denylist condition satisfied.
$matchesdenylist = Test-RegexList $predicate @($condition.denylist)
if ($matchesdenylist) {
return $false
}
} elseif ($null -ne $condition.probability) {
Expand All @@ -250,7 +250,7 @@ function Test-FeatureConditions
return $false
}
} else {
throw "${condition} is not a supported condition type (blacklist, whitelist or probability)."
throw "${condition} is not a supported condition type (denylist, allowlist or probability)."
}
}
return $true
Expand Down
46 changes: 23 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ Imagine to have a feature flag configuration file called `features.json`:
{
"stages": {
"test": [
{"whitelist": ["test.*", "dev.*"]}
{"allowlist": ["test.*", "dev.*"]}
],
"canary": [
{"whitelist": ["prod-canary"]}
{"allowlist": ["prod-canary"]}
],
"prod": [
{"whitelist": ["prod.*"]},
{"blacklist": ["prod-canary"]}
{"allowlist": ["prod.*"]},
{"denylist": ["prod-canary"]}
]
},
"features": {
Expand Down Expand Up @@ -122,13 +122,13 @@ An example lifecycle of a feature flag might be the following:

Here is how these example stages could be implemented:

* Stage 1 can be implemented with a `blacklist` condition with value `.*`.
* Stages 2 and 3 can be implemented with `whitelist` conditions.
* Stage 1 can be implemented with a `denylist` condition with value `.*`.
* Stages 2 and 3 can be implemented with `allowlist` conditions.
* Stages 4 and 5 can be implemented with `probability` conditions.

## Conditions

There are two types of conditions: *deterministic* (whitelist and blacklist,
There are two types of conditions: *deterministic* (allowlist and denylist,
regex-based) and *probabilistic* (probability, expressed as a number between
0 and 1). Conditions can be repeated if multiple instances are required.

Expand All @@ -138,9 +138,9 @@ in the configuration file, for the feature to be considered enabled.
If any condition is not met, evaluation of conditions stops and the feature
is considered disabled.

### Whitelist
### Allow list

The `whitelist` condition allows to specify a list of regular expressions; if the
The `allowlist` condition allows to specify a list of regular expressions; if the
predicate matches any of the expressions, then the condition is met and the evaluation
moves to the next condition, if there is any.

Expand All @@ -150,9 +150,9 @@ unintended matches, it's recommended to always anchor the regex.

So, for example, `"^storage$"` will only match `"storage"` and not `"storage1"`.

### Blacklist
### Deny list

The `blacklist` condition is analogous to the whitelist condition, except that if
The `denylist` condition is analogous to the allowlist condition, except that if
the predicate matches any of the expressions the condition is considered not met
and the evaluation stops.

Expand All @@ -172,25 +172,25 @@ the following example:
```json
{
"stages": {
"whitelist-first": [
{"whitelist": ["storage.*"]},
"allowlist-first": [
{"allowlist": ["storage.*"]},
{"probability": 0.1}
],
"probability-first": [
{"probability": 0.1}
{"whitelist": ["storage.*"]},
{"allowlist": ["storage.*"]},
]
}
}
```

The first stage definition, `whitelist-first`, will evaluate the `probability` condition
only if the predicate first passes the whitelist.
The first stage definition, `allowlist-first`, will evaluate the `probability` condition
only if the predicate first passes the allowlist.

The second stage definition, `probability-first`, will instead first evaluate
the `probability` condition, and then apply the whitelist.
the `probability` condition, and then apply the allowlist.

Assuming there are predicates that do not match the whitelist, the second stage definition
Assuming there are predicates that do not match the allowlist, the second stage definition
is more restrictive than the first one, leading to fewer positive evaluations of the
feature flag.

Expand Down Expand Up @@ -231,19 +231,19 @@ for comments. Don't add comments to your feature flag configuration file.
],
// Examples of deterministic stages.
"all-storage": [
{"whitelist": [".*Storage.*"]},
{"allowlist": [".*Storage.*"]},
],
"storage-except-important": [
{"whitelist": [".*Storage.*"]},
{"blacklist": [".*StorageImportant.*"]},
{"allowlist": [".*Storage.*"]},
{"denylist": [".*StorageImportant.*"]},
],
// Example of mixed roll-out stage.
// This stage will match on predicates containing the word "Storage"
// but not the word "StorageImportant", and then will consider the feature
// enabled in 50% of the cases.
"50-percent-storage-except-StorageImportant": [
{"whitelist": [".*Storage.*"]},
{"blacklist": ["StorageImportant"]},
{"allowlist": [".*Storage.*"]},
{"denylist": ["StorageImportant"]},
{"probability": 0.5},
],
},
Expand Down
8 changes: 4 additions & 4 deletions examples/features.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"stages": {
"test": [
{"whitelist": ["test.*", "dev.*"]}
{"allowlist": ["test.*", "dev.*"]}
],
"canary": [
{"whitelist": ["prod-canary"]}
{"allowlist": ["prod-canary"]}
],
"prod": [
{"whitelist": ["prod.*"]},
{"blacklist": ["prod-canary"]}
{"allowlist": ["prod.*"]},
{"denylist": ["prod-canary"]}
]
},
"features": {
Expand Down
19 changes: 10 additions & 9 deletions featureflags.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,30 @@
"required": ["stages"],
"additionalProperties": false,
"definitions": {
"whitelist": {
"allowlist": {
"type": "object",
"properties": {
"whitelist": {
"allowlist": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["whitelist"],
"required": ["allowlist"],
"additionalProperties": false
},
"blacklist": {
"denylist": {
"type": "object",
"properties": {
"blacklist": {
"denylist": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["blacklist"],
"required": ["denylist"],
"additionalProperties": false
},
"probability": {
Expand All @@ -70,11 +70,12 @@
"conditions": {
"type": "array",
"items": {
"oneOf": [{
"$ref": "#/definitions/whitelist"
"oneOf": [
{
"$ref": "#/definitions/allowlist"
},
{
"$ref": "#/definitions/blacklist"
"$ref": "#/definitions/denylist"
},
{
"$ref": "#/definitions/probability"
Expand Down
Loading

0 comments on commit 0742b20

Please sign in to comment.