feat: add targeting validation (#1146)
- adds targeting validation - updates sample json files - updates schemas modules to most recent release Closes #1140 Signed-off-by: Best Olunusi <olunusibest@gmail.com> Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
This commit is contained in:
parent
2adfa573a2
commit
b727dd00c5
|
|
@ -12,7 +12,7 @@ require (
|
||||||
github.com/fsnotify/fsnotify v1.7.0
|
github.com/fsnotify/fsnotify v1.7.0
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/open-feature/open-feature-operator/apis v0.2.38-0.20231117101310-726a7f714906
|
github.com/open-feature/open-feature-operator/apis v0.2.38-0.20231117101310-726a7f714906
|
||||||
github.com/open-feature/schemas v0.2.8
|
github.com/open-feature/schemas v0.2.9-0.20240115143711-4a0be4f48816
|
||||||
github.com/prometheus/client_golang v1.18.0
|
github.com/prometheus/client_golang v1.18.0
|
||||||
github.com/robfig/cron v1.2.0
|
github.com/robfig/cron v1.2.0
|
||||||
github.com/rs/cors v1.10.1
|
github.com/rs/cors v1.10.1
|
||||||
|
|
|
||||||
|
|
@ -640,6 +640,8 @@ github.com/open-feature/open-feature-operator/apis v0.2.38-0.20231117101310-726a
|
||||||
github.com/open-feature/open-feature-operator/apis v0.2.38-0.20231117101310-726a7f714906/go.mod h1:YtdYEXJHo9iKwmchxbwGGsmu33FbugMQ8lKGarA9UYM=
|
github.com/open-feature/open-feature-operator/apis v0.2.38-0.20231117101310-726a7f714906/go.mod h1:YtdYEXJHo9iKwmchxbwGGsmu33FbugMQ8lKGarA9UYM=
|
||||||
github.com/open-feature/schemas v0.2.8 h1:oA75hJXpOd9SFgmNI2IAxWZkwzQPUDm7Jyyh3q489wM=
|
github.com/open-feature/schemas v0.2.8 h1:oA75hJXpOd9SFgmNI2IAxWZkwzQPUDm7Jyyh3q489wM=
|
||||||
github.com/open-feature/schemas v0.2.8/go.mod h1:vj+rfTsOLlh5PtGGkAbitnJmFPYuTHXTjOy13kzNgKQ=
|
github.com/open-feature/schemas v0.2.8/go.mod h1:vj+rfTsOLlh5PtGGkAbitnJmFPYuTHXTjOy13kzNgKQ=
|
||||||
|
github.com/open-feature/schemas v0.2.9-0.20240115143711-4a0be4f48816 h1:8F+uUDbw/Qa192HXfxfoLf/RkVAhFLImTH6uYzhaLz8=
|
||||||
|
github.com/open-feature/schemas v0.2.9-0.20240115143711-4a0be4f48816/go.mod h1:5tZlsJkdP1CiBHWrGUbDQxpCAVNTG851yAbmUrfji1M=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
|
|
||||||
|
|
@ -400,16 +400,38 @@ func getFlagdProperties(context map[string]any) (flagdProperties, bool) {
|
||||||
return p, true
|
return p, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (je *JSON) loadAndCompileSchema() *gojsonschema.Schema {
|
||||||
|
schemaLoader := gojsonschema.NewSchemaLoader()
|
||||||
|
|
||||||
|
// compile dependency schema
|
||||||
|
targetingSchemaLoader := gojsonschema.NewStringLoader(schema.Targeting)
|
||||||
|
if err := schemaLoader.AddSchemas(targetingSchemaLoader); err != nil {
|
||||||
|
je.Logger.Warn(fmt.Sprintf("error adding Targeting schema: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile root schema
|
||||||
|
flagdDefinitionsLoader := gojsonschema.NewStringLoader(schema.FlagdDefinitions)
|
||||||
|
compiledSchema, err := schemaLoader.Compile(flagdDefinitionsLoader)
|
||||||
|
if err != nil {
|
||||||
|
je.Logger.Warn(fmt.Sprintf("error compiling FlagdDefinitions schema: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return compiledSchema
|
||||||
|
}
|
||||||
|
|
||||||
// configToFlags convert string configurations to flags and store them to pointer newFlags
|
// configToFlags convert string configurations to flags and store them to pointer newFlags
|
||||||
func (je *JSON) configToFlags(config string, newFlags *Flags) error {
|
func (je *JSON) configToFlags(config string, newFlags *Flags) error {
|
||||||
schemaLoader := gojsonschema.NewStringLoader(schema.FlagdDefinitions)
|
compiledSchema := je.loadAndCompileSchema()
|
||||||
|
|
||||||
flagStringLoader := gojsonschema.NewStringLoader(config)
|
flagStringLoader := gojsonschema.NewStringLoader(config)
|
||||||
|
|
||||||
result, err := gojsonschema.Validate(schemaLoader, flagStringLoader)
|
result, err := compiledSchema.Validate(flagStringLoader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error validating json schema: %w", err)
|
je.Logger.Warn(fmt.Sprintf("failed to execute JSON schema validation: %s", err))
|
||||||
} else if !result.Valid() {
|
} else if !result.Valid() {
|
||||||
return fmt.Errorf("JSON schema validation failed: %s", buildErrorString(result.Errors()))
|
je.Logger.Warn(fmt.Sprintf(
|
||||||
|
"flag definition does not conform to the schema; validation errors: %s", buildErrorString(result.Errors()),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
transposedConfig, err := je.transposeEvaluators(config)
|
transposedConfig, err := je.transposeEvaluators(config)
|
||||||
|
|
|
||||||
|
|
@ -973,6 +973,112 @@ func TestState_Evaluator(t *testing.T) {
|
||||||
inputSyncType: sync.ALL,
|
inputSyncType: sync.ALL,
|
||||||
expectedError: true,
|
expectedError: true,
|
||||||
},
|
},
|
||||||
|
"invalid targeting": {
|
||||||
|
inputState: `
|
||||||
|
{
|
||||||
|
"flags": {
|
||||||
|
"fibAlgo": {
|
||||||
|
"variants": {
|
||||||
|
"recursive": "recursive",
|
||||||
|
"memo": "memo",
|
||||||
|
"loop": "loop",
|
||||||
|
"binet": "binet"
|
||||||
|
},
|
||||||
|
"defaultVariant": "recursive",
|
||||||
|
"state": "ENABLED",
|
||||||
|
"source":"",
|
||||||
|
"targeting": {
|
||||||
|
"if": [
|
||||||
|
{
|
||||||
|
"in": ["@faas.com", {
|
||||||
|
"var": ["email"]
|
||||||
|
}]
|
||||||
|
}, "binet", "null", "loop"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isColorYellow": {
|
||||||
|
"state": "ENABLED",
|
||||||
|
"variants": {
|
||||||
|
"on": true,
|
||||||
|
"off": false
|
||||||
|
},
|
||||||
|
"defaultVariant": "off",
|
||||||
|
"source":"",
|
||||||
|
"targeting": {
|
||||||
|
"if": [
|
||||||
|
{
|
||||||
|
"==": [
|
||||||
|
{
|
||||||
|
"varr": ["color"]
|
||||||
|
},
|
||||||
|
"yellow"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"on",
|
||||||
|
"off",
|
||||||
|
"none"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flagSources":null
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
inputSyncType: sync.ALL,
|
||||||
|
expectedError: false,
|
||||||
|
expectedOutputState: `
|
||||||
|
{
|
||||||
|
"flags": {
|
||||||
|
"fibAlgo": {
|
||||||
|
"variants": {
|
||||||
|
"recursive": "recursive",
|
||||||
|
"memo": "memo",
|
||||||
|
"loop": "loop",
|
||||||
|
"binet": "binet"
|
||||||
|
},
|
||||||
|
"defaultVariant": "recursive",
|
||||||
|
"state": "ENABLED",
|
||||||
|
"source":"",
|
||||||
|
"targeting": {
|
||||||
|
"if": [
|
||||||
|
{
|
||||||
|
"in": ["@faas.com", {
|
||||||
|
"var": ["email"]
|
||||||
|
}]
|
||||||
|
}, "binet", "null", "loop"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isColorYellow": {
|
||||||
|
"state": "ENABLED",
|
||||||
|
"variants": {
|
||||||
|
"on": true,
|
||||||
|
"off": false
|
||||||
|
},
|
||||||
|
"defaultVariant": "off",
|
||||||
|
"source":"",
|
||||||
|
"targeting": {
|
||||||
|
"if": [
|
||||||
|
{
|
||||||
|
"==": [
|
||||||
|
{
|
||||||
|
"varr": ["color"]
|
||||||
|
},
|
||||||
|
"yellow"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"on",
|
||||||
|
"off",
|
||||||
|
"none"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flagSources":null
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
"empty evaluator": {
|
"empty evaluator": {
|
||||||
inputState: `
|
inputState: `
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,7 @@
|
||||||
{
|
{
|
||||||
"==": [
|
"==": [
|
||||||
{
|
{
|
||||||
"var": [
|
"var": ["color"]
|
||||||
"color"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"yellow"
|
"yellow"
|
||||||
]
|
]
|
||||||
|
|
@ -81,7 +79,9 @@
|
||||||
"if": [
|
"if": [
|
||||||
{
|
{
|
||||||
"$ref": "emailWithFaas"
|
"$ref": "emailWithFaas"
|
||||||
}, "binet", null
|
},
|
||||||
|
"binet",
|
||||||
|
null
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -100,35 +100,27 @@
|
||||||
"$ref": "emailWithFaas"
|
"$ref": "emailWithFaas"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fractionalEvaluation": [
|
"fractional": [
|
||||||
"email",
|
{ "var": "email" },
|
||||||
[
|
["red", 25],
|
||||||
"red",
|
["blue", 25],
|
||||||
25
|
["green", 25],
|
||||||
],
|
["yellow", 25]
|
||||||
[
|
|
||||||
"blue",
|
|
||||||
25
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"green",
|
|
||||||
25
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"yellow",
|
|
||||||
25
|
|
||||||
]
|
]
|
||||||
]
|
},
|
||||||
}, null
|
null
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"$evaluators": {
|
"$evaluators": {
|
||||||
"emailWithFaas": {
|
"emailWithFaas": {
|
||||||
"in": ["@faas.com", {
|
"in": [
|
||||||
|
"@faas.com",
|
||||||
|
{
|
||||||
"var": ["email"]
|
"var": ["email"]
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,8 @@ flags:
|
||||||
targeting:
|
targeting:
|
||||||
if:
|
if:
|
||||||
- "$ref": emailWithFaas
|
- "$ref": emailWithFaas
|
||||||
- fractionalEvaluation:
|
- fractional:
|
||||||
|
- var:
|
||||||
- email
|
- email
|
||||||
- - red
|
- - red
|
||||||
- 25
|
- 25
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,7 @@
|
||||||
{
|
{
|
||||||
"==": [
|
"==": [
|
||||||
{
|
{
|
||||||
"var": [
|
"var": ["color"]
|
||||||
"color"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"yellow"
|
"yellow"
|
||||||
]
|
]
|
||||||
|
|
@ -81,7 +79,9 @@
|
||||||
"if": [
|
"if": [
|
||||||
{
|
{
|
||||||
"$ref": "emailWithFaas"
|
"$ref": "emailWithFaas"
|
||||||
}, "binet", null
|
},
|
||||||
|
"binet",
|
||||||
|
null
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -100,35 +100,27 @@
|
||||||
"$ref": "emailWithFaas"
|
"$ref": "emailWithFaas"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fractionalEvaluation": [
|
"fractional": [
|
||||||
"email",
|
{ "var": "email" },
|
||||||
[
|
["red", 25],
|
||||||
"red",
|
["blue", 25],
|
||||||
25
|
["green", 25],
|
||||||
],
|
["yellow", 25]
|
||||||
[
|
|
||||||
"blue",
|
|
||||||
25
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"green",
|
|
||||||
25
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"yellow",
|
|
||||||
25
|
|
||||||
]
|
]
|
||||||
]
|
},
|
||||||
}, null
|
null
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"$evaluators": {
|
"$evaluators": {
|
||||||
"emailWithFaas": {
|
"emailWithFaas": {
|
||||||
"in": ["@faas.com", {
|
"in": [
|
||||||
|
"@faas.com",
|
||||||
|
{
|
||||||
"var": ["email"]
|
"var": ["email"]
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,8 @@ flags:
|
||||||
targeting:
|
targeting:
|
||||||
if:
|
if:
|
||||||
- "$ref": emailWithFaas
|
- "$ref": emailWithFaas
|
||||||
- fractionalEvaluation:
|
- fractional:
|
||||||
|
- var:
|
||||||
- email
|
- email
|
||||||
- - red
|
- - red
|
||||||
- 25
|
- 25
|
||||||
|
|
|
||||||
2
schemas
2
schemas
|
|
@ -1 +1 @@
|
||||||
Subproject commit f3e419c5ea676b6e0a4384b1ba3c9ad43b047eed
|
Subproject commit 4a0be4f48816ea0ac83d909ae58b8dcf5acda4b8
|
||||||
Loading…
Reference in New Issue