From 6211e3a5ea413809c2818fbe54ba1b6769ac0f54 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Mon, 8 Jul 2024 08:56:01 -0400 Subject: [PATCH] feat: standalone targeting, fractional shorthand (#168) This PR does 2 things: - Allows for fractional targeting shorthand where no weight (assumes weight = 1) - Allows the targeting.json schema to be used alone, not just as part of the flag definition schema Non functionally, it restructures our testing so that tests pertaining to `targeting.json` exist in the `test/targeting/` dir, while tests pertaining to the `flags.json` exist in the `test/flags` dir. Fixes: https://github.com/open-feature/flagd-schemas/issues/165 --------- Signed-off-by: Todd Baert --- json/flagd_definitions_test.go | 37 ++- json/flags.json | 6 +- json/flags.yaml | 6 +- json/targeting.json | 30 +- json/targeting.yaml | 299 ++++++++++-------- .../{ => flags}/negative/empty-variants.json | 2 +- .../{ => flags}/negative/malformed-flag.json | 2 +- .../negative/missing-variants.json | 3 +- .../mixed-variant-types.ffconfig.json | 2 +- .../negative/no-default-variant.json | 2 +- .../negative/state-set-incorrectly.json | 2 +- .../negative/with-invalid-targeting.json | 16 + .../positive/example-simple.flagd.json | 1 + .../{ => flags}/positive/example.flagd.json | 2 +- .../flags/positive/with-valid-targeting.json | 26 ++ .../fractional-invalid-bucketing.json | 26 -- .../fractional-invalid-weighting.json | 25 -- .../negative/invalid-ends-with-param.json | 23 -- json/test/negative/invalid-flagd-props.json | 23 -- .../negative/invalid-starts-with-param.json | 23 -- .../sem-ver-invalid-range-specifier.json | 23 -- .../sem-ver-invalid-ver-expression.json | 23 -- json/test/positive/basic-json-ops.json | 74 ----- json/test/positive/custom-ops.json | 161 ---------- json/test/positive/if-shorthand.json | 71 ----- .../fractional-invalid-bucketing.json | 9 + .../fractional-invalid-weighting.json | 8 + .../negative/invalid-ends-with-param.json | 9 + .../negative/invalid-flagd-props.json | 9 + .../negative/invalid-starts-with-param.json | 9 + .../sem-ver-invalid-range-specifier.json | 9 + .../sem-ver-invalid-ver-expression.json | 9 + .../targeting/positive/basic-json-ops.json | 61 ++++ json/test/targeting/positive/custom-ops.json | 114 +++++++ .../test/targeting/positive/if-shorthand.json | 35 ++ 35 files changed, 530 insertions(+), 650 deletions(-) rename json/test/{ => flags}/negative/empty-variants.json (77%) rename json/test/{ => flags}/negative/malformed-flag.json (84%) rename json/test/{ => flags}/negative/missing-variants.json (73%) rename json/test/{ => flags}/negative/mixed-variant-types.ffconfig.json (82%) rename json/test/{ => flags}/negative/no-default-variant.json (79%) rename json/test/{ => flags}/negative/state-set-incorrectly.json (82%) create mode 100644 json/test/flags/negative/with-invalid-targeting.json rename json/test/{ => flags}/positive/example-simple.flagd.json (95%) rename json/test/{ => flags}/positive/example.flagd.json (98%) create mode 100644 json/test/flags/positive/with-valid-targeting.json delete mode 100644 json/test/negative/fractional-invalid-bucketing.json delete mode 100644 json/test/negative/fractional-invalid-weighting.json delete mode 100644 json/test/negative/invalid-ends-with-param.json delete mode 100644 json/test/negative/invalid-flagd-props.json delete mode 100644 json/test/negative/invalid-starts-with-param.json delete mode 100644 json/test/negative/sem-ver-invalid-range-specifier.json delete mode 100644 json/test/negative/sem-ver-invalid-ver-expression.json delete mode 100644 json/test/positive/basic-json-ops.json delete mode 100644 json/test/positive/custom-ops.json delete mode 100644 json/test/positive/if-shorthand.json create mode 100644 json/test/targeting/negative/fractional-invalid-bucketing.json create mode 100644 json/test/targeting/negative/fractional-invalid-weighting.json create mode 100644 json/test/targeting/negative/invalid-ends-with-param.json create mode 100644 json/test/targeting/negative/invalid-flagd-props.json create mode 100644 json/test/targeting/negative/invalid-starts-with-param.json create mode 100644 json/test/targeting/negative/sem-ver-invalid-range-specifier.json create mode 100644 json/test/targeting/negative/sem-ver-invalid-ver-expression.json create mode 100644 json/test/targeting/positive/basic-json-ops.json create mode 100644 json/test/targeting/positive/custom-ops.json create mode 100644 json/test/targeting/positive/if-shorthand.json diff --git a/json/flagd_definitions_test.go b/json/flagd_definitions_test.go index 158b71c..672ab8d 100644 --- a/json/flagd_definitions_test.go +++ b/json/flagd_definitions_test.go @@ -13,34 +13,51 @@ import ( "github.com/xeipuuv/gojsonschema" ) -var compiledSchema *gojsonschema.Schema +var compiledFlagDefinitionSchema *gojsonschema.Schema +var compiledTargetingSchema *gojsonschema.Schema func init() { - schemaLoader := gojsonschema.NewSchemaLoader() - schemaLoader.AddSchemas(gojsonschema.NewStringLoader(flagd_definitions.TargetingSchema)) + flagDefinitionSchemaLoader := gojsonschema.NewSchemaLoader() + flagDefinitionSchemaLoader.AddSchemas(gojsonschema.NewStringLoader(flagd_definitions.TargetingSchema)) + targetingSchemaLoader := gojsonschema.NewSchemaLoader() var err error - compiledSchema, err = schemaLoader.Compile(gojsonschema.NewStringLoader(flagd_definitions.FlagSchema)) + compiledFlagDefinitionSchema, err = flagDefinitionSchemaLoader.Compile(gojsonschema.NewStringLoader(flagd_definitions.FlagSchema)) + compiledTargetingSchema, err = targetingSchemaLoader.Compile(gojsonschema.NewStringLoader(flagd_definitions.TargetingSchema)) if err != nil { message := fmt.Errorf("err: %v", err) log.Fatal(message) } } -func TestPositiveParsing(t *testing.T) { - if err := walkPath(true, "./test/positive"); err != nil { +func TestPositiveFlagParsing(t *testing.T) { + if err := walkPath(true, "./test/flags/positive", compiledFlagDefinitionSchema); err != nil { t.Error(err) t.FailNow() } } -func TestNegativeParsing(t *testing.T) { - if err := walkPath(false, "./test/negative"); err != nil { +func TestNegativeFlagParsing(t *testing.T) { + if err := walkPath(false, "./test/flags/negative", compiledFlagDefinitionSchema); err != nil { t.Error(err) t.FailNow() } } -func walkPath(shouldPass bool, root string) error { +func TestPositiveTargetingParsing(t *testing.T) { + if err := walkPath(true, "./test/targeting/positive", compiledTargetingSchema); err != nil { + t.Error(err) + t.FailNow() + } +} + +func TestNegativeTargetingParsing(t *testing.T) { + if err := walkPath(false, "./test/targeting/negative", compiledTargetingSchema); err != nil { + t.Error(err) + t.FailNow() + } +} + +func walkPath(shouldPass bool, root string, schema *gojsonschema.Schema) error { return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -56,7 +73,7 @@ func walkPath(shouldPass bool, root string) error { flagStringLoader := gojsonschema.NewStringLoader(string(file)) - p, err := compiledSchema.Validate(flagStringLoader) + p, err := schema.Validate(flagStringLoader) if err != nil { return err } diff --git a/json/flags.json b/json/flags.json index a6a09a7..b440b36 100644 --- a/json/flags.json +++ b/json/flags.json @@ -2,7 +2,7 @@ "$id": "https://flagd.dev/schema/v0/flags.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", + "description": "Defines flags for use in flagd, including typed variants and rules.", "type": "object", "properties": { "flags": { @@ -46,7 +46,7 @@ "patternProperties": { "^.{1,}$": { "$comment": "this relative ref means that targeting.json MUST be in the same dir, or available on the same HTTP path", - "$ref": "./targeting.json#/definitions/targeting" + "$ref": "./targeting.json" } } } @@ -71,7 +71,7 @@ "type": "string" }, "targeting": { - "$ref": "./targeting.json#/definitions/targeting" + "$ref": "./targeting.json" } }, "required": [ diff --git a/json/flags.yaml b/json/flags.yaml index 3a18693..04a3dc1 100644 --- a/json/flags.yaml +++ b/json/flags.yaml @@ -1,7 +1,7 @@ $id: "https://flagd.dev/schema/v0/flags.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 +description: Defines flags for use in flagd, including typed variants and rules. type: object properties: flags: @@ -35,7 +35,7 @@ properties: "^.{1,}$": $comment: this relative ref means that targeting.json MUST be in the same dir, or available on the same HTTP path - $ref: "./targeting.json#/definitions/targeting" + $ref: "./targeting.json" definitions: flag: $comment: base flag object; no title/description here, allows for better UX, @@ -56,7 +56,7 @@ definitions: if the targeting returns null). type: string targeting: - $ref: "./targeting.json#/definitions/targeting" + $ref: "./targeting.json" required: - state - defaultVariant diff --git a/json/targeting.json b/json/targeting.json index 3818343..5df5b6d 100644 --- a/json/targeting.json +++ b/json/targeting.json @@ -2,24 +2,20 @@ "$id": "https://flagd.dev/schema/v0/targeting.json", "$schema": "http://json-schema.org/draft-07/schema#", "title": "flagd Targeting", - "description": "Defines targeting logic for flagd; a extension of JSONLogic, including purpose-built feature-flagging operations.", + "description": "Defines targeting logic for flagd; a extension of JSONLogic, including purpose-built feature-flagging operations. Note that this schema applies to top-level objects; no additional properties are supported, including \"$schema\", which means built-in JSON-schema support is not possible in editors. Please use flags.json (which imports this schema) for a rich editor experience.", "type": "object", - "definitions": { - "targeting": { - "title": "Targeting", - "description": "An expression returning a value which is coerced to a string to be used as a targeting key, or null (to fall back to defaultVariant). If targeting returns a value which is not a variant key, it's considered an error.", - "anyOf": [ - { - "$comment": "we need this to support empty targeting", - "type": "object", - "additionalProperties": false, - "properties": {} - }, - { - "$ref": "#/definitions/anyRule" - } - ] + "anyOf": [ + { + "$comment": "we need this to support empty targeting", + "type": "object", + "additionalProperties": false, + "properties": {} }, + { + "$ref": "#/definitions/anyRule" + } + ], + "definitions": { "primitive": { "oneOf": [ { @@ -462,7 +458,7 @@ "$comment": "if we remove the \"sum to 100\" restriction, update the descriptions below!", "description": "Distribution for all possible variants, with their associated weighting out of 100.", "type": "array", - "minItems": 2, + "minItems": 1, "maxItems": 2, "items": [ { diff --git a/json/targeting.yaml b/json/targeting.yaml index 5e726c8..dee4490 100644 --- a/json/targeting.yaml +++ b/json/targeting.yaml @@ -1,41 +1,44 @@ $id: "https://flagd.dev/schema/v0/targeting.json" $schema: http://json-schema.org/draft-07/schema# title: flagd Targeting -description: Defines targeting logic for flagd; a extension of JSONLogic, including - purpose-built feature-flagging operations. +description: + Defines targeting logic for flagd; a extension of JSONLogic, including + purpose-built feature-flagging operations. Note that this schema applies + to top-level objects; no additional properties are supported, including "$schema", + which means built-in JSON-schema support is not possible in editors. + Please use flags.json (which imports this schema) for a rich editor experience. type: object +anyOf: + - $comment: we need this to support empty targeting + type: object + additionalProperties: false + properties: {} + - $ref: "#/definitions/anyRule" definitions: - targeting: - title: Targeting - description: An expression returning a value which is coerced to a string to be - used as a targeting key, or null (to fall back to defaultVariant). If targeting - returns a value which is not a variant key, it's considered an error. - anyOf: - - $comment: we need this to support empty targeting - type: object - additionalProperties: false - properties: {} - - $ref: "#/definitions/anyRule" primitive: oneOf: - - description: When returned from rules, a null value "exits", the targeting, - and the "defaultValue" is returned, with the reason indicating the targeting - did not match. - type: 'null' - - description: When returned from rules, booleans are converted to strings ("true"/"false"), - and used to as keys to retrieve the associated value from the "variants" object. - Be sure that the returned string is present as a key in the variants! - type: boolean - - description: When returned from rules, the behavior of numbers is not defined. - type: number - - description: When returned from rules, strings are used to as keys to retrieve - the associated value from the "variants" object. Be sure that the returned - string is present as a key in the variants!. - type: string - - description: When returned from rules, strings are used to as keys to retrieve - the associated value from the "variants" object. Be sure that the returned - string is present as a key in the variants!. - type: array + - description: + When returned from rules, a null value "exits", the targeting, + and the "defaultValue" is returned, with the reason indicating the targeting + did not match. + type: "null" + - description: + When returned from rules, booleans are converted to strings ("true"/"false"), + and used to as keys to retrieve the associated value from the "variants" object. + Be sure that the returned string is present as a key in the variants! + type: boolean + - description: When returned from rules, the behavior of numbers is not defined. + type: number + - description: + When returned from rules, strings are used to as keys to retrieve + the associated value from the "variants" object. Be sure that the returned + string is present as a key in the variants!. + type: string + - description: + When returned from rules, strings are used to as keys to retrieve + the associated value from the "variants" object. Be sure that the returned + string is present as a key in the variants!. + type: array varRule: title: Var Operation description: Retrieve data from the provided data object. @@ -44,34 +47,33 @@ definitions: properties: var: anyOf: - - type: string - description: flagd automatically injects "$flagd.timestamp" (unix epoch) - and "$flagd.flagKey" (the key of the flag in evaluation) into the context. - pattern: "^\\$flagd\\.((timestamp)|(flagKey))$" - - not: - $comment: this is a negated (not) match of "$flagd.{some-key}", which is faster and more compatible that a negative lookahead regex - type: string - description: flagd automatically injects "$flagd.timestamp" (unix epoch) and "$flagd.flagKey" - (the key of the flag in evaluation) into the context. - pattern: "^\\$flagd\\..*$" - - type: array - $comment: this is to support the form of var with a default... there seems to be a bug here, where ajv gives a warning (not an error) because maxItems doesn't equal the number of entries in items, though this is valid in this case - minItems: 1 - items: - - type: string - additionalItems: - anyOf: - - type: - 'null' - - type: - boolean - - type: - string - - type: - number + - type: string + description: + flagd automatically injects "$flagd.timestamp" (unix epoch) + and "$flagd.flagKey" (the key of the flag in evaluation) into the context. + pattern: "^\\$flagd\\.((timestamp)|(flagKey))$" + - not: + $comment: this is a negated (not) match of "$flagd.{some-key}", which is faster and more compatible that a negative lookahead regex + type: string + description: + flagd automatically injects "$flagd.timestamp" (unix epoch) and "$flagd.flagKey" + (the key of the flag in evaluation) into the context. + pattern: "^\\$flagd\\..*$" + - type: array + $comment: this is to support the form of var with a default... there seems to be a bug here, where ajv gives a warning (not an error) because maxItems doesn't equal the number of entries in items, though this is valid in this case + minItems: 1 + items: + - type: string + additionalItems: + anyOf: + - type: "null" + - type: boolean + - type: string + - type: number missingRule: title: Missing Operation - description: Takes an array of data keys to search for (same format as var). Returns + description: + Takes an array of data keys to search for (same format as var). Returns an array of any keys that are missing from the data object, or an empty array. type: object additionalProperties: false @@ -82,7 +84,8 @@ definitions: type: string missingSomeRule: title: Missing-Some Operation - description: Takes a minimum number of data keys that are required, and an array + description: + Takes a minimum number of data keys that are required, and an array of keys to search for (same format as var or missing). Returns an empty array if the minimum is met, or an array of the missing keys otherwise. type: object @@ -93,10 +96,10 @@ definitions: maxItems: 2 type: array items: - - type: number - - type: array - items: - type: string + - type: number + - type: array + items: + type: string binaryOrTernaryOp: type: array minItems: 2 @@ -109,16 +112,19 @@ definitions: properties: substr: title: Substring Operation - description: Get a portion of a string. Give a positive start position to return everything beginning at that index. + description: + Get a portion of a string. Give a positive start position to return everything beginning at that index. Give a negative start position to work backwards from the end of the string, then return everything. Give a positive length to express how many characters to return. $ref: "#/definitions/binaryOrTernaryOp" "<": - title: Less-Than/Between Operation. Can be used to test that one value is + title: + Less-Than/Between Operation. Can be used to test that one value is between two others. $ref: "#/definitions/binaryOrTernaryOp" "<=": - title: Less-Than-Or-Equal-To/Between Operation. Can be used to test that one + title: + Less-Than-Or-Equal-To/Between Operation. Can be used to test that one value is between two others. $ref: "#/definitions/binaryOrTernaryOp" binaryOp: @@ -135,7 +141,7 @@ definitions: properties: "if": title: "If Operator" - description: "The if statement takes 1 or more arguments: a condition (\"if\"), what to do if its true (\"then\", optional, defaults to returning true), and what to do if its false (\"else\", optional, defaults to returning false). Note that the else condition can be used as an else-if statement by adding additional arguments." + description: 'The if statement takes 1 or more arguments: a condition ("if"), what to do if its true ("then", optional, defaults to returning true), and what to do if its false ("else", optional, defaults to returning false). Note that the else condition can be used as an else-if statement by adding additional arguments.' $ref: "#/definitions/variadicOp" "==": title: Lose Equality Operation @@ -161,7 +167,8 @@ definitions: $ref: "#/definitions/binaryOp" "%": title: Modulo Operation - description: Finds the remainder after the first argument is divided by the + description: + Finds the remainder after the first argument is divided by the second argument. $ref: "#/definitions/binaryOp" "/": @@ -169,37 +176,43 @@ definitions: $ref: "#/definitions/binaryOp" map: title: Map Operation - description: Perform an action on every member of an array. Note, that inside + description: + Perform an action on every member of an array. Note, that inside the logic being used to map, var operations are relative to the array element being worked on. $ref: "#/definitions/binaryOp" filter: title: Filter Operation - description: Keep only elements of the array that pass a test. Note, that + description: + Keep only elements of the array that pass a test. Note, that inside the logic being used to filter, var operations are relative to the array element being worked on. $ref: "#/definitions/binaryOp" all: title: All Operation - description: Perform a test on each member of that array, returning true if + description: + Perform a test on each member of that array, returning true if all pass. Inside the test code, var operations are relative to the array element being tested. $ref: "#/definitions/binaryOp" none: title: None Operation - description: Perform a test on each member of that array, returning true if + description: + Perform a test on each member of that array, returning true if none pass. Inside the test code, var operations are relative to the array element being tested. $ref: "#/definitions/binaryOp" some: title: Some Operation - description: Perform a test on each member of that array, returning true if + description: + Perform a test on each member of that array, returning true if some pass. Inside the test code, var operations are relative to the array element being tested. $ref: "#/definitions/binaryOp" in: title: In Operation - description: If the second argument is an array, tests that the first argument + description: + If the second argument is an array, tests that the first argument is a member of the array. $ref: "#/definitions/binaryOp" reduceRule: @@ -208,7 +221,8 @@ definitions: properties: reduce: title: Reduce Operation - description: Combine all the elements in an array into a single value, like + description: + Combine all the elements in an array into a single value, like adding up a list of numbers. Note, that inside the logic being used to reduce, var operations only have access to an object with a "current" and a "accumulator". type: array @@ -229,17 +243,18 @@ definitions: properties: "*": title: Multiplication Operation - description: Multiplication; associative, will accept and unlimited amount + description: + Multiplication; associative, will accept and unlimited amount of arguments. $ref: "#/definitions/associativeOp" unaryOp: anyOf: - - type: array - minItems: 1 - maxItems: 1 - items: - "$ref": "#/definitions/args" - - "$ref": "#/definitions/args" + - type: array + minItems: 1 + maxItems: 1 + items: + "$ref": "#/definitions/args" + - "$ref": "#/definitions/args" unaryRule: title: Unary Operation description: Any primitive JSONLogic operation with 1 operands. @@ -266,12 +281,14 @@ definitions: properties: or: title: Or Operation - description: Simple boolean test, with 1 or more arguments. At a more sophisticated + description: + Simple boolean test, with 1 or more arguments. At a more sophisticated level, "or" returns the first truthy argument, or the last argument. $ref: "#/definitions/variadicOp" and: - title: '' - description: Simple boolean test, with 1 or more arguments. At a more sophisticated + title: "" + description: + Simple boolean test, with 1 or more arguments. At a more sophisticated level, "and" returns the first falsy argument, or the last argument. $ref: "#/definitions/variadicOp" "+": @@ -291,18 +308,20 @@ definitions: $ref: "#/definitions/variadicOp" merge: title: Merge Operation - description: Takes one or more arrays, and merges them into one array. If + description: + Takes one or more arrays, and merges them into one array. If arguments aren't arrays, they get cast to arrays. $ref: "#/definitions/variadicOp" cat: title: Concatenate Operation - description: Concatenate all the supplied arguments. Note that this is not + description: + Concatenate all the supplied arguments. Note that this is not a join or implode operation, there is no “glue” string. $ref: "#/definitions/variadicOp" stringCompareArg: oneOf: - - type: string - - $ref: "#/definitions/anyRule" + - type: string + - $ref: "#/definitions/anyRule" stringCompareArgs: type: array minItems: 2 @@ -323,7 +342,8 @@ definitions: $ref: "#/definitions/stringCompareArgs" semVerString: title: Semantic Version String - description: A string representing a valid semantic version expression as per + description: + A string representing a valid semantic version expression as per https://semver.org/. type: string pattern: "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" @@ -333,55 +353,60 @@ definitions: properties: sem_ver: title: Semantic Version Operation - description: 'Attribute matches a semantic version condition. Accepts "npm-style" + description: + 'Attribute matches a semantic version condition. Accepts "npm-style" range specifiers: "=", "!=", ">", "<", ">=", "<=", "~" (match minor version), "^" (match major version).' type: array minItems: 3 maxItems: 3 items: - - oneOf: - - $ref: "#/definitions/semVerString" - - $ref: "#/definitions/varRule" - - description: 'Range specifiers: "=", "!=", ">", "<", ">=", "<=", "~" (match - minor version), "^" (match major version).' - enum: - - "=" - - "!=" - - ">" - - "<" - - ">=" - - "<=" - - "~" - - "^" - - oneOf: - - $ref: "#/definitions/semVerString" - - $ref: "#/definitions/varRule" + - oneOf: + - $ref: "#/definitions/semVerString" + - $ref: "#/definitions/varRule" + - description: + 'Range specifiers: "=", "!=", ">", "<", ">=", "<=", "~" (match + minor version), "^" (match major version).' + enum: + - "=" + - "!=" + - ">" + - "<" + - ">=" + - "<=" + - "~" + - "^" + - oneOf: + - $ref: "#/definitions/semVerString" + - $ref: "#/definitions/varRule" fractionalWeightArg: $comment: if we remove the "sum to 100" restriction, update the descriptions below! - description: Distribution for all possible variants, with their associated weighting + description: + Distribution for all possible variants, with their associated weighting out of 100. type: array - minItems: 2 + minItems: 1 maxItems: 2 items: - - description: If this bucket is randomly selected, this string is used to as - a key to retrieve the associated value from the "variants" object. - type: string - - description: Weighted distribution for this variant key (must sum to 100). - type: number + - description: + If this bucket is randomly selected, this string is used to as + a key to retrieve the associated value from the "variants" object. + type: string + - description: Weighted distribution for this variant key (must sum to 100). + type: number fractionalOp: type: array minItems: 3 $comment: there seems to be a bug here, where ajv gives a warning (not an error) because maxItems doesn't equal the number of entries in items, though this is valid in this case items: - - description: Bucketing value used in pseudorandom assignment; should be unique - and stable for each subject of flag evaluation. Defaults to a concatenation - of the flagKey and targetingKey. - $ref: "#/definitions/anyRule" - - $ref: "#/definitions/fractionalWeightArg" - - $ref: "#/definitions/fractionalWeightArg" + - description: + Bucketing value used in pseudorandom assignment; should be unique + and stable for each subject of flag evaluation. Defaults to a concatenation + of the flagKey and targetingKey. + $ref: "#/definitions/anyRule" + - $ref: "#/definitions/fractionalWeightArg" + - $ref: "#/definitions/fractionalWeightArg" additionalItems: $ref: "#/definitions/fractionalWeightArg" fractionalShorthandOp: @@ -397,8 +422,8 @@ definitions: title: Fractional Operation description: Deterministic, pseudorandom fractional distribution. oneOf: - - $ref: "#/definitions/fractionalOp" - - $ref: "#/definitions/fractionalShorthandOp" + - $ref: "#/definitions/fractionalOp" + - $ref: "#/definitions/fractionalShorthandOp" reference: additionalProperties: false type: object @@ -410,20 +435,20 @@ definitions: type: string args: oneOf: - - $ref: "#/definitions/reference" - - $ref: "#/definitions/anyRule" - - $ref: "#/definitions/primitive" + - $ref: "#/definitions/reference" + - $ref: "#/definitions/anyRule" + - $ref: "#/definitions/primitive" anyRule: anyOf: - - $ref: "#/definitions/varRule" - - $ref: "#/definitions/missingRule" - - $ref: "#/definitions/missingSomeRule" - - $ref: "#/definitions/binaryRule" - - $ref: "#/definitions/binaryOrTernaryRule" - - $ref: "#/definitions/associativeRule" - - $ref: "#/definitions/unaryRule" - - $ref: "#/definitions/variadicRule" - - $ref: "#/definitions/reduceRule" - - $ref: "#/definitions/stringCompareRule" - - $ref: "#/definitions/ruleSemVer" - - $ref: "#/definitions/fractionalRule" + - $ref: "#/definitions/varRule" + - $ref: "#/definitions/missingRule" + - $ref: "#/definitions/missingSomeRule" + - $ref: "#/definitions/binaryRule" + - $ref: "#/definitions/binaryOrTernaryRule" + - $ref: "#/definitions/associativeRule" + - $ref: "#/definitions/unaryRule" + - $ref: "#/definitions/variadicRule" + - $ref: "#/definitions/reduceRule" + - $ref: "#/definitions/stringCompareRule" + - $ref: "#/definitions/ruleSemVer" + - $ref: "#/definitions/fractionalRule" diff --git a/json/test/negative/empty-variants.json b/json/test/flags/negative/empty-variants.json similarity index 77% rename from json/test/negative/empty-variants.json rename to json/test/flags/negative/empty-variants.json index a0ed458..2b312de 100644 --- a/json/test/negative/empty-variants.json +++ b/json/test/flags/negative/empty-variants.json @@ -1,5 +1,5 @@ { - "$schema": "../../flags.json", + "$schema": "../../../flags.json", "flags": { "myBoolFlag": { "state": "ENABLED", diff --git a/json/test/negative/malformed-flag.json b/json/test/flags/negative/malformed-flag.json similarity index 84% rename from json/test/negative/malformed-flag.json rename to json/test/flags/negative/malformed-flag.json index 2dfe73c..df52d24 100644 --- a/json/test/negative/malformed-flag.json +++ b/json/test/flags/negative/malformed-flag.json @@ -1,5 +1,5 @@ { - "$schema": "../../flags.json", + "$schema": "../../../flags.json", "flags": { "myBoolFlag": { "state": "ENABLED", diff --git a/json/test/negative/missing-variants.json b/json/test/flags/negative/missing-variants.json similarity index 73% rename from json/test/negative/missing-variants.json rename to json/test/flags/negative/missing-variants.json index 6d4b803..b88ad6f 100644 --- a/json/test/negative/missing-variants.json +++ b/json/test/flags/negative/missing-variants.json @@ -1,6 +1,5 @@ { - "$schema": "../../flags.json", - + "$schema": "../../../flags.json", "flags": { "myBoolFlag": { "state": "ENABLED", diff --git a/json/test/negative/mixed-variant-types.ffconfig.json b/json/test/flags/negative/mixed-variant-types.ffconfig.json similarity index 82% rename from json/test/negative/mixed-variant-types.ffconfig.json rename to json/test/flags/negative/mixed-variant-types.ffconfig.json index 4becc76..ea26663 100644 --- a/json/test/negative/mixed-variant-types.ffconfig.json +++ b/json/test/flags/negative/mixed-variant-types.ffconfig.json @@ -1,5 +1,5 @@ { - "$schema": "../../flags.json", + "$schema": "../../../flags.json", "flags": { "myBoolFlag": { "state": "ENABLED", diff --git a/json/test/negative/no-default-variant.json b/json/test/flags/negative/no-default-variant.json similarity index 79% rename from json/test/negative/no-default-variant.json rename to json/test/flags/negative/no-default-variant.json index bd57238..58c1335 100644 --- a/json/test/negative/no-default-variant.json +++ b/json/test/flags/negative/no-default-variant.json @@ -1,5 +1,5 @@ { - "$schema": "../../flags.json", + "$schema": "../../../flags.json", "flags": { "myBoolFlag": { "state": "ENABLED", diff --git a/json/test/negative/state-set-incorrectly.json b/json/test/flags/negative/state-set-incorrectly.json similarity index 82% rename from json/test/negative/state-set-incorrectly.json rename to json/test/flags/negative/state-set-incorrectly.json index 08e7e39..021918e 100644 --- a/json/test/negative/state-set-incorrectly.json +++ b/json/test/flags/negative/state-set-incorrectly.json @@ -1,5 +1,5 @@ { - "$schema": "../../flags.json", + "$schema": "../../../flags.json", "flags": { "myBoolFlag": { "state": "WILL-FAIL", diff --git a/json/test/flags/negative/with-invalid-targeting.json b/json/test/flags/negative/with-invalid-targeting.json new file mode 100644 index 0000000..ba78ce0 --- /dev/null +++ b/json/test/flags/negative/with-invalid-targeting.json @@ -0,0 +1,16 @@ +{ + "$schema": "../../../flags.json", + "flags": { + "targetedFlag": { + "state": "ENABLED", + "variants": { + "on": true, + "off": false + }, + "defaultVariant": "on", + "targeting": { + "not-a-real-rule": [1, 2] + } + } + } +} diff --git a/json/test/positive/example-simple.flagd.json b/json/test/flags/positive/example-simple.flagd.json similarity index 95% rename from json/test/positive/example-simple.flagd.json rename to json/test/flags/positive/example-simple.flagd.json index c2c7ddf..9d2dd60 100644 --- a/json/test/positive/example-simple.flagd.json +++ b/json/test/flags/positive/example-simple.flagd.json @@ -1,4 +1,5 @@ { + "$schema": "../../../flags.json", "flags": { "myBoolFlag": { "state": "ENABLED", diff --git a/json/test/positive/example.flagd.json b/json/test/flags/positive/example.flagd.json similarity index 98% rename from json/test/positive/example.flagd.json rename to json/test/flags/positive/example.flagd.json index afe580a..8f66a70 100644 --- a/json/test/positive/example.flagd.json +++ b/json/test/flags/positive/example.flagd.json @@ -1,5 +1,5 @@ { - "$schema": "../../flags.json", + "$schema": "../../../flags.json", "flags": { "myBoolFlag": { "state": "ENABLED", diff --git a/json/test/flags/positive/with-valid-targeting.json b/json/test/flags/positive/with-valid-targeting.json new file mode 100644 index 0000000..1a33e4e --- /dev/null +++ b/json/test/flags/positive/with-valid-targeting.json @@ -0,0 +1,26 @@ +{ + "$schema": "../../../flags.json", + "flags": { + "targetedFlag": { + "state": "ENABLED", + "variants": { + "on": true, + "off": false + }, + "defaultVariant": "on", + "targeting": { + "if": [ + { + "ends_with": [ + { + "var": "email" + }, + "@company.com" + ] + }, + "on" + ] + } + } + } +} diff --git a/json/test/negative/fractional-invalid-bucketing.json b/json/test/negative/fractional-invalid-bucketing.json deleted file mode 100644 index df02f5d..0000000 --- a/json/test/negative/fractional-invalid-bucketing.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "../../flags.json", - "$comments": "tests that an invalid bucking value is invalid", - "flags": { - "fractional-invalid-bucketing": { - "state": "ENABLED", - "variants": { - "clubs": "clubs", - "diamonds": "diamonds", - "hearts": "hearts", - "spades": "spades", - "none": "none" - }, - "defaultVariant": "none", - "targeting": { - "fractional": [ - "invalid", - ["clubs", 25], - ["diamonds", 25], - ["hearts", 25], - ["spades", 25] - ] - } - } - } -} diff --git a/json/test/negative/fractional-invalid-weighting.json b/json/test/negative/fractional-invalid-weighting.json deleted file mode 100644 index 4178c3f..0000000 --- a/json/test/negative/fractional-invalid-weighting.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "$schema": "../../flags.json", - "$comments": "tests that an invalid weight value is invalid", - "flags": { - "fractional-invalid-weighting": { - "state": "ENABLED", - "variants": { - "clubs": "clubs", - "diamonds": "diamonds", - "hearts": "hearts", - "spades": "spades", - "none": "none" - }, - "defaultVariant": "none", - "targeting": { - "fractional": [ - ["clubs", 25], - ["diamonds", "25"], - ["hearts", 25], - ["spades", 25] - ] - } - } - } -} diff --git a/json/test/negative/invalid-ends-with-param.json b/json/test/negative/invalid-ends-with-param.json deleted file mode 100644 index eab00e5..0000000 --- a/json/test/negative/invalid-ends-with-param.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "../../flags.json", - "$comments": "tests that an an int is not a valid ends_with param", - "flags": { - "invalid-ends-with-param": { - "state": "ENABLED", - "variants": { - "prefix": 1, - "postfix": 2 - }, - "defaultVariant": "none", - "targeting": { - "if": [ - { - "ends_with": [{ "var": "id" }, 0] - }, - "postfix", - "prefix" - ] - } - } - } -} diff --git a/json/test/negative/invalid-flagd-props.json b/json/test/negative/invalid-flagd-props.json deleted file mode 100644 index 53d8823..0000000 --- a/json/test/negative/invalid-flagd-props.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "../../flags.json", - "$comments": "tests that an unsupported $flagd property is invalid", - "flags": { - "invalid-flagd-props": { - "state": "ENABLED", - "variants": { - "yes": "1", - "no": "2" - }, - "defaultVariant": "none", - "targeting": { - "if": [ - { - "==": [{ "var": "$flagd.invalidProp" }, { "var": "someprop" }] - }, - "yes", - "no" - ] - } - } - } -} diff --git a/json/test/negative/invalid-starts-with-param.json b/json/test/negative/invalid-starts-with-param.json deleted file mode 100644 index fbf5058..0000000 --- a/json/test/negative/invalid-starts-with-param.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "../../flags.json", - "$comments": "tests that an an int is not a valid starts_with param", - "flags": { - "invalid-starts-with-param": { - "state": "ENABLED", - "variants": { - "prefix": 1, - "postfix": 2 - }, - "defaultVariant": "none", - "targeting": { - "if": [ - { - "starts_with": [{ "var": "id" }, 0] - }, - "prefix", - "postfix" - ] - } - } - } -} diff --git a/json/test/negative/sem-ver-invalid-range-specifier.json b/json/test/negative/sem-ver-invalid-range-specifier.json deleted file mode 100644 index b23b838..0000000 --- a/json/test/negative/sem-ver-invalid-range-specifier.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "../../flags.json", - "$comments": "tests that an invalid range specifier is invalid", - "flags": { - "sem-ver-invalid-range-specifier": { - "state": "ENABLED", - "variants": { - "equal": "equal", - "not": "not" - }, - "defaultVariant": "none", - "targeting": { - "if": [ - { - "sem_ver": [{ "var": "version" }, "*", "2.0.0"] - }, - "equal", - "not" - ] - } - } - } -} diff --git a/json/test/negative/sem-ver-invalid-ver-expression.json b/json/test/negative/sem-ver-invalid-ver-expression.json deleted file mode 100644 index 75de5d6..0000000 --- a/json/test/negative/sem-ver-invalid-ver-expression.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "../../flags.json", - "$comments": "tests that an invalid version is invalid", - "flags": { - "sem-ver-invalid-ver-expression": { - "state": "ENABLED", - "variants": { - "equal": "equal", - "not": "not" - }, - "defaultVariant": "none", - "targeting": { - "if": [ - { - "sem_ver": [{ "var": "version" }, "=", "2.0.0.0"] - }, - "equal", - "not" - ] - } - } - } -} diff --git a/json/test/positive/basic-json-ops.json b/json/test/positive/basic-json-ops.json deleted file mode 100644 index f1239b7..0000000 --- a/json/test/positive/basic-json-ops.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "$schema": "../../flags.json", - "flags": { - "basic-json-ops": { - "state": "ENABLED", - "variants": { - "on": true, - "off": false - }, - "defaultVariant": "on", - "targeting": { - "*" : [ - {"if" : [false, "false", false, "false", true, "true"] }, - {"if" : [ true, "yes", "no" ]}, - {"if" : [ true, "yes" ]}, - {"if" : [ true ]}, - {"==" : [1, 1]}, - {"===" : [1, 1]}, - {"!=" : [1, 2]}, - {"!==" : [1, 2]}, - {"!": [true]}, - {"!": true}, - {"!!": ["0"] }, - {"or": [true, false]}, - {"or":[false, 0, "a"]}, - {"and": [true, true]}, - {"and":[true, "a", 3]}, - {">" : [2, 1]}, - {">=" : [1, 1]}, - {"<" : [1, 2]}, - {"<=" : [1, 1]}, - {"<" : [1, 2, 3]}, - {"<" : [1, 4, 3]}, - {"<=" : [1, 2, 3]}, - {"<=" : [1, 1, 3]}, - {"max":[1,2,3]}, - {"min":[1,2,3]}, - {"+":[4,2]}, - {"+":[2,2,2,2,2]}, - {"+" : [ "3.14" ]}, - {"/":[4,2]}, - {"*":[2,2,2,2,2]}, - {"-": [ 2 ]}, - {"-":[4, 2]}, - {"%": [101,2]}, - {"map":[ - {"var":"integers"}, - {"*":[{"var":""},2]} - ]}, - {"filter":[ - {"var":"integers"}, - {"%":[{"var":""},2]} - ]}, - {"reduce":[ - {"var":"integers"}, - {"+":[{"var":"current"}, {"var":"accumulator"}]}, - 0 - ]}, - {"all" : [ [1,2,3], {">":[{"var":""}, 0]} ]}, - {"some" : [ [-1,0,1], {">":[{"var":""}, 0]} ]}, - {"none" : [ [-3,-2,-1], {">":[{"var":""}, 0]} ]}, - {"some" : [ {"var":"pies"}, {"==":[{"var":"filling"}, "apple"]} ]}, - {"merge":[ [1,2], [3,4] ]}, - {"merge":[ 1, 2, [3,4] ]}, - {"in":[ "Ringo", ["John", "Paul", "George", "Ringo"] ]}, - {"in":["Spring", "Springfield"]}, - {"cat": ["I love", " pie"]}, - {"substr": ["jsonlogic", 4]}, - {"substr": ["jsonlogic", 1, 3]} - ] - } - } - } -} diff --git a/json/test/positive/custom-ops.json b/json/test/positive/custom-ops.json deleted file mode 100644 index eae7ad8..0000000 --- a/json/test/positive/custom-ops.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "$schema": "../../flags.json", - "$comments": "tests that all the custom ops work", - "flags": { - "fractional-flag": { - "state": "ENABLED", - "variants": { - "clubs": "clubs", - "diamonds": "diamonds", - "hearts": "hearts", - "spades": "spades", - "wild": "wild" - }, - "defaultVariant": "wild", - "targeting": { - "fractional": [ - { "cat": [ - { "var": "$flagd.flagKey" }, - { "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", - null - ] - } - ] - } - }, - "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", - 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" - ] - } - ] - } - }, - "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" - ] - } - ] - } - } - } -} diff --git a/json/test/positive/if-shorthand.json b/json/test/positive/if-shorthand.json deleted file mode 100644 index 88b1da8..0000000 --- a/json/test/positive/if-shorthand.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "$schema": "../../flags.json", - "$comments": "tests that all the variants of 'if' work (bugfix)", - "flags": { - "if-shorthand-1": { - "state": "ENABLED", - "variants": { - "true": true, - "false": false - }, - "defaultVariant": "true", - "targeting": { - "if": [ - { - "===": [ - { - "var": "env" - }, - "production" - ] - } - ] - } - }, - "if-shorthand-2": { - "state": "ENABLED", - "variants": { - "true": true, - "false": false - }, - "defaultVariant": "true", - "targeting": { - "if": [ - { - "===": [ - { - "var": "env" - }, - "production" - ] - }, "true" - ] - } - }, - "if-shorthand-ref": { - "state": "ENABLED", - "variants": { - "true": true, - "false": false - }, - "defaultVariant": "true", - "targeting": { - "if": [ - { - "$ref": "env-production" - } - ] - } - } - }, - "$evaluators": { - "env-production": { - "===": [ - { - "var": "env" - }, - "production" - ] - } - } -} diff --git a/json/test/targeting/negative/fractional-invalid-bucketing.json b/json/test/targeting/negative/fractional-invalid-bucketing.json new file mode 100644 index 0000000..9ed172f --- /dev/null +++ b/json/test/targeting/negative/fractional-invalid-bucketing.json @@ -0,0 +1,9 @@ +{ + "fractional": [ + "invalid", + ["clubs", 25], + ["diamonds", 25], + ["hearts", 25], + ["spades", 25] + ] +} \ No newline at end of file diff --git a/json/test/targeting/negative/fractional-invalid-weighting.json b/json/test/targeting/negative/fractional-invalid-weighting.json new file mode 100644 index 0000000..bdcd10e --- /dev/null +++ b/json/test/targeting/negative/fractional-invalid-weighting.json @@ -0,0 +1,8 @@ +{ + "fractional": [ + ["clubs", 25], + ["diamonds", "25"], + ["hearts", 25], + ["spades", 25] + ] +} \ No newline at end of file diff --git a/json/test/targeting/negative/invalid-ends-with-param.json b/json/test/targeting/negative/invalid-ends-with-param.json new file mode 100644 index 0000000..2d1e457 --- /dev/null +++ b/json/test/targeting/negative/invalid-ends-with-param.json @@ -0,0 +1,9 @@ +{ + "if": [ + { + "ends_with": [{ "var": "id" }, 0] + }, + "postfix", + "prefix" + ] +} \ No newline at end of file diff --git a/json/test/targeting/negative/invalid-flagd-props.json b/json/test/targeting/negative/invalid-flagd-props.json new file mode 100644 index 0000000..c89f529 --- /dev/null +++ b/json/test/targeting/negative/invalid-flagd-props.json @@ -0,0 +1,9 @@ +{ + "if": [ + { + "==": [{ "var": "$flagd.invalidProp" }, { "var": "someprop" }] + }, + "yes", + "no" + ] +} \ No newline at end of file diff --git a/json/test/targeting/negative/invalid-starts-with-param.json b/json/test/targeting/negative/invalid-starts-with-param.json new file mode 100644 index 0000000..805aa14 --- /dev/null +++ b/json/test/targeting/negative/invalid-starts-with-param.json @@ -0,0 +1,9 @@ +{ + "if": [ + { + "starts_with": [{ "var": "id" }, 0] + }, + "prefix", + "postfix" + ] +} \ No newline at end of file diff --git a/json/test/targeting/negative/sem-ver-invalid-range-specifier.json b/json/test/targeting/negative/sem-ver-invalid-range-specifier.json new file mode 100644 index 0000000..32998f5 --- /dev/null +++ b/json/test/targeting/negative/sem-ver-invalid-range-specifier.json @@ -0,0 +1,9 @@ +{ + "if": [ + { + "sem_ver": [{ "var": "version" }, "*", "2.0.0"] + }, + "equal", + "not" + ] +} \ No newline at end of file diff --git a/json/test/targeting/negative/sem-ver-invalid-ver-expression.json b/json/test/targeting/negative/sem-ver-invalid-ver-expression.json new file mode 100644 index 0000000..7af1374 --- /dev/null +++ b/json/test/targeting/negative/sem-ver-invalid-ver-expression.json @@ -0,0 +1,9 @@ +{ + "if": [ + { + "sem_ver": [{ "var": "version" }, "=", "2.0.0.0"] + }, + "equal", + "not" + ] +} \ No newline at end of file diff --git a/json/test/targeting/positive/basic-json-ops.json b/json/test/targeting/positive/basic-json-ops.json new file mode 100644 index 0000000..dcbd358 --- /dev/null +++ b/json/test/targeting/positive/basic-json-ops.json @@ -0,0 +1,61 @@ +{ + "*" : [ + {"if" : [false, "false", false, "false", true, "true"] }, + {"if" : [ true, "yes", "no" ]}, + {"if" : [ true, "yes" ]}, + {"if" : [ true ]}, + {"==" : [1, 1]}, + {"===" : [1, 1]}, + {"!=" : [1, 2]}, + {"!==" : [1, 2]}, + {"!": [true]}, + {"!": true}, + {"!!": ["0"] }, + {"or": [true, false]}, + {"or":[false, 0, "a"]}, + {"and": [true, true]}, + {"and":[true, "a", 3]}, + {">" : [2, 1]}, + {">=" : [1, 1]}, + {"<" : [1, 2]}, + {"<=" : [1, 1]}, + {"<" : [1, 2, 3]}, + {"<" : [1, 4, 3]}, + {"<=" : [1, 2, 3]}, + {"<=" : [1, 1, 3]}, + {"max":[1,2,3]}, + {"min":[1,2,3]}, + {"+":[4,2]}, + {"+":[2,2,2,2,2]}, + {"+" : [ "3.14" ]}, + {"/":[4,2]}, + {"*":[2,2,2,2,2]}, + {"-": [ 2 ]}, + {"-":[4, 2]}, + {"%": [101,2]}, + {"map":[ + {"var":"integers"}, + {"*":[{"var":""},2]} + ]}, + {"filter":[ + {"var":"integers"}, + {"%":[{"var":""},2]} + ]}, + {"reduce":[ + {"var":"integers"}, + {"+":[{"var":"current"}, {"var":"accumulator"}]}, + 0 + ]}, + {"all" : [ [1,2,3], {">":[{"var":""}, 0]} ]}, + {"some" : [ [-1,0,1], {">":[{"var":""}, 0]} ]}, + {"none" : [ [-3,-2,-1], {">":[{"var":""}, 0]} ]}, + {"some" : [ {"var":"pies"}, {"==":[{"var":"filling"}, "apple"]} ]}, + {"merge":[ [1,2], [3,4] ]}, + {"merge":[ 1, 2, [3,4] ]}, + {"in":[ "Ringo", ["John", "Paul", "George", "Ringo"] ]}, + {"in":["Spring", "Springfield"]}, + {"cat": ["I love", " pie"]}, + {"substr": ["jsonlogic", 4]}, + {"substr": ["jsonlogic", 1, 3]} + ] +} diff --git a/json/test/targeting/positive/custom-ops.json b/json/test/targeting/positive/custom-ops.json new file mode 100644 index 0000000..99c7ecd --- /dev/null +++ b/json/test/targeting/positive/custom-ops.json @@ -0,0 +1,114 @@ +{ + "*": [ + { + "fractional": [ + { "cat": [ + { "var": "$flagd.flagKey" }, + { "var": "user.name" } + ]}, + ["clubs", 25], + ["diamonds", 25], + ["hearts", 25], + ["spades", 25] + ] + }, + { + "fractional": [ + [ "heads" ], + [ "tails", 1 ] + ] + }, + { + "fractional": [ + ["clubs", 25], + ["diamonds", 25], + ["hearts", 25], + ["spades", 25] + ] + }, + { + "fractional": [ + ["clubs", 25], + ["diamonds", 25], + ["hearts", 25], + ["spades", 25] + ] + }, + { + "if": [ + { + "starts_with": [{ "var": "id" }, "abc"] + }, + "prefix", + { + "if": [ + { + "ends_with": [{ "var": "id" }, "xyz"] + }, + "postfix", + null + ] + } + ] + }, + { + "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", + null + ] + } + ] + } + ] + }, + { + "if": [ + { + "sem_ver": [{ "var": "version" }, "~", "3.0.0"] + }, + "minor", + { + "if": [ + { + "sem_ver": [{ "var": "version" }, "^", "3.0.0"] + }, + "major", + "none" + ] + } + ] + }, + { + "if": [ + { + ">": [{ "var": "$flagd.timestamp" }, { "var": "time" }] + }, + "past", + { + "if": [ + { + "<": [{ "var": "$flagd.timestamp" }, { "var": "time" }] + }, + "future", + "none" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/json/test/targeting/positive/if-shorthand.json b/json/test/targeting/positive/if-shorthand.json new file mode 100644 index 0000000..8fc1476 --- /dev/null +++ b/json/test/targeting/positive/if-shorthand.json @@ -0,0 +1,35 @@ +{ + "*": [ + { + "if": [ + { + "===": [ + { + "var": "env" + }, + "production" + ] + } + ] + }, + { + "if": [ + { + "===": [ + { + "var": "env" + }, + "production" + ] + }, "true" + ] + }, + { + "if": [ + { + "$ref": "some-ref" + } + ] + } + ] +} \ No newline at end of file