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

feat: add targeting schema, improve def. schema #120

Merged
merged 14 commits into from
Jan 8, 2024
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ gen-rust: install-buf guard-GOPATH

gen-schema-json: install-yq
yq eval -o=json json/flagd-definitions.yaml > json/flagd-definitions.json
yq eval -o=json json/targeting.yaml > json/targeting.json

ajv-validate-flagd-schema:
@if ! npm ls ajv-cli; then npm ci; fi
npx ajv compile -s json/flagd-definitions.json
npx ajv compile -s json/targeting.json
# load the targeting json so flagd-definitions.json can reference it
npx ajv compile -r json/targeting.json -s json/flagd-definitions.json
241 changes: 240 additions & 1 deletion json/examples/full.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "../flagd-definitions.json",
"flags": {
"myBoolFlag": {
"state": "ENABLED",
Expand Down Expand Up @@ -35,6 +36,244 @@
}
},
"defaultVariant": "object1"
},
"fractional-flag": {
"state": "ENABLED",
"variants": {
"clubs": "clubs",
"diamonds": "diamonds",
"hearts": "hearts",
"spades": "spades",
"wild": "wild"
},
"defaultVariant": "wild",
"targeting": {
"fractional": [
{ "var": "user.name" },
["clubs", 25],
["diamonds", 25],
["hearts", 25],
["spades", 25]
]
}
},
"shorthand-fractional-flag": {
"state": "ENABLED",
"variants": {
"clubs": "clubs",
"diamonds": "diamonds",
"hearts": "hearts",
"spades": "spades",
"wild": "wild"
},
"defaultVariant": "wild",
"targeting": {
"fractional": [
["clubs", 25],
["diamonds", 25],
["hearts", 25],
["spades", 25]
]
}
},
"starts-ends-flag": {
"state": "ENABLED",
"variants": {
"prefix": "prefix",
"postfix": "postfix",
"none": "none"
},
"defaultVariant": "none",
"targeting": {
"if": [
{
"starts_with": [{ "var": "id" }, "abc"]
},
"prefix",
{
"if": [
{
"ends_with": [{ "var": "id" }, "xyz"]
},
"postfix",
{
"if": [
{
"ends_with": [{ "var": "id" }, 3]
},
"fail",
"none"
]
}
]
}
]
}
},
"equal-greater-lesser-version-flag": {
"state": "ENABLED",
"variants": {
"equal": "equal",
"greater": "greater",
"lesser": "lesser",
"none": "none"
},
"defaultVariant": "none",
"targeting": {
"if": [
{
"sem_ver": [{ "var": "version" }, "=", "2.0.0"]
},
"equal",
{
"if": [
{
"sem_ver": [{ "var": "version" }, ">", "2.0.0"]
},
"greater",
{
"if": [
{
"sem_ver": [{ "var": "version" }, "<", "2.0.0"]
},
"lesser",
{
"if": [
{
"sem_ver": [{ "var": "version" }, "=", "2.0.0.0"]
},
"fail",
null
]
}
]
}
]
}
]
}
},
"major-minor-version-flag": {
"state": "ENABLED",
"variants": {
"minor": "minor",
"major": "major",
"none": "none"
},
"defaultVariant": "none",
"targeting": {
"if": [
{
"sem_ver": [{ "var": "version" }, "~", "3.0.0"]
},
"minor",
{
"if": [
{
"sem_ver": [{ "var": "version" }, "^", "3.0.0"]
},
"major",
"none"
]
}
]
}
},
"test-cat": {
"state": "ENABLED",
"variants": {
"minor": "minor",
"major": "major",
"none": "none"
},
"defaultVariant": "none",
"targeting": {
"cat": ["1", "@"]
}
},
"context-aware": {
"state": "ENABLED",
"variants": {
"internal": "INTERNAL",
"external": "EXTERNAL"
},
"defaultVariant": "external",
"targeting": {
"if": [
{
"and": [
{
"==": [
{
"var": ["fn"]
},
"Sulisław"
]
},
{
"==": [
{
"var": ["ln"]
},
"Świętopełk"
]
},
{
"==": [
{
"var": ["age"]
},
29
]
},
{
"==": [
{
"var": ["customer"]
},
false
]
}
]
},
"internal",
"external"
]
}
},
"timestamp-flag": {
"state": "ENABLED",
"variants": {
"past": -1,
"future": 1,
"none": 0
},
"defaultVariant": "none",
"targeting": {
"if": [
{
">": [{ "var": "$flagd.timestamp" }, { "var": "time" }]
},
"past",
{
"if": [
{
"<": [{ "var": "$flagd.timestamp" }, { "var": "time" }]
},
"future",
"none"
]
}
]
}
},
"wrong-flag": {
"state": "ENABLED",
"variants": {
"one": "uno",
"two": "dos"
},
"defaultVariant": "one"
}
}
}
}
37 changes: 26 additions & 11 deletions json/flagd-definitions.json
beeme1mr marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
{
"$id": "https://flagd.dev/schema/flagd-definitions.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "flagd Flag Configuration",
"description": "Defines flags for use in flagd, including typed variants and rules",
"type": "object",
"properties": {
"flags": {
"title": "Flags",
"description": "Top-level flags object. All flags are defined here.",
"type": "object",
"$comment": "flag objects are one of the 4 flag types defined in $defs",
"additionalProperties": false,
Expand All @@ -13,50 +16,62 @@
"oneOf": [
{
"title": "Boolean flag",
"description": "A flag associated with boolean values",
"description": "A flag having boolean values.",
"$ref": "#/$defs/booleanFlag"
},
{
"title": "String flag",
"description": "A flag associated with string values",
"description": "A flag having string values.",
"$ref": "#/$defs/stringFlag"
},
{
"title": "Numeric flag",
"description": "A flag associated with numeric values",
"description": "A flag having numeric values.",
"$ref": "#/$defs/numberFlag"
},
{
"title": "Object flag",
"description": "A flag associated with arbitrary object values",
"description": "A flag having arbitrary object values.",
"$ref": "#/$defs/objectFlag"
}
]
}
}
},
"$evaluators": {
"title": "Evaluators",
"description": "Reusable targeting rules that can be referenced with \"$ref\": \"myRule\" in multiple flags.",
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^.{1,}$": {
"$comment": "this relative ref means that targeting.json MUST be in the same dir, or available on the same HTTP path",
"$ref": "https://flagd.dev/schema/targeting.json#/$defs/targeting"
}
}
}
},
"$defs": {
"flag": {
"title": "Flag Base",
"description": "Base object for all flags",
"$comment": "base flag object; no title/description here, allows for better UX, keep it in the overrides",
"type": "object",
"properties": {
"state": {
"description": "Indicates whether the flag is functional. Disabled flags are treated as if they don't exist",
"title": "Flag State",
"description": "Indicates whether the flag is functional. Disabled flags are treated as if they don't exist.",
"type": "string",
"enum": [
"ENABLED",
"DISABLED"
]
},
"defaultVariant": {
"description": "The variant to serve if no dynamic targeting applies",
"title": "Default Variant",
"description": "The variant to serve if no dynamic targeting applies (including if the targeting returns null).",
"type": "string"
},
"targeting": {
"type": "object",
"description": "JsonLogic expressions to be used for dynamic evaluation. The \"context\" is passed as the data. Rules must resolve one of the defined variants, or the \"defaultVariant\" will be used."
"$ref": "https://flagd.dev/schema/targeting.json#/$defs/targeting"
}
},
"required": [
Expand Down Expand Up @@ -124,7 +139,7 @@
}
}
},
"$comment": "Merge the variants with the base flag to build our typed flags",
"$comment": "merge the variants with the base flag to build our typed flags",
"booleanFlag": {
"allOf": [
{
Expand Down
Loading
Loading