Skip to content

Commit

Permalink
feat: standalone targeting, fractional shorthand (#168)
Browse files Browse the repository at this point in the history
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: #165

---------

Signed-off-by: Todd Baert <[email protected]>
  • Loading branch information
toddbaert authored Jul 8, 2024
1 parent 1ba4b03 commit 6211e3a
Show file tree
Hide file tree
Showing 35 changed files with 530 additions and 650 deletions.
37 changes: 27 additions & 10 deletions json/flagd_definitions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down
6 changes: 3 additions & 3 deletions json/flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -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"
}
}
}
Expand All @@ -71,7 +71,7 @@
"type": "string"
},
"targeting": {
"$ref": "./targeting.json#/definitions/targeting"
"$ref": "./targeting.json"
}
},
"required": [
Expand Down
6 changes: 3 additions & 3 deletions json/flags.yaml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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,
Expand All @@ -56,7 +56,7 @@ definitions:
if the targeting returns null).
type: string
targeting:
$ref: "./targeting.json#/definitions/targeting"
$ref: "./targeting.json"
required:
- state
- defaultVariant
Expand Down
30 changes: 13 additions & 17 deletions json/targeting.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
{
Expand Down Expand Up @@ -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": [
{
Expand Down
Loading

0 comments on commit 6211e3a

Please sign in to comment.