Skip to content

Commit 13fbbad

Browse files
authored
feat: support flag metadata (#1476)
- Adds functionality to return flag metadata as part of the response. Signed-off-by: Aasif Khan <[email protected]>
1 parent 26b0b1a commit 13fbbad

File tree

5 files changed

+255
-8
lines changed

5 files changed

+255
-8
lines changed

core/pkg/evaluator/json.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,13 @@ func (je *Resolver) evaluateVariant(ctx context.Context, reqID string, flagKey s
327327
metadata[SelectorMetadataKey] = selector
328328
}
329329

330+
for key, value := range flag.Metadata {
331+
// If value is not nil or empty, copy to metadata
332+
if value != nil {
333+
metadata[key] = value
334+
}
335+
}
336+
330337
if flag.State == Disabled {
331338
je.Logger.DebugWithID(reqID, fmt.Sprintf("requested flag is disabled: %s", flagKey))
332339
return "", flag.Variants, model.ErrorReason, metadata, errors.New(model.FlagDisabledErrorCode)
@@ -460,11 +467,28 @@ func configToFlags(log *logger.Logger, config string, newFlags *Flags) error {
460467
return fmt.Errorf("transposing evaluators: %w", err)
461468
}
462469

463-
err = json.Unmarshal([]byte(transposedConfig), &newFlags)
470+
var configData ConfigWithMetadata
471+
err = json.Unmarshal([]byte(transposedConfig), &configData)
464472
if err != nil {
465473
return fmt.Errorf("unmarshalling provided configurations: %w", err)
466474
}
467475

476+
// Assign the flags from the unmarshalled config to the newFlags struct
477+
newFlags.Flags = configData.Flags
478+
479+
// Assign metadata as a map to each flag's metadata
480+
for key, flag := range newFlags.Flags {
481+
if flag.Metadata == nil {
482+
flag.Metadata = make(map[string]interface{})
483+
}
484+
for metaKey, metaValue := range configData.Metadata {
485+
if _, exists := flag.Metadata[metaKey]; !exists {
486+
flag.Metadata[metaKey] = metaValue
487+
}
488+
}
489+
newFlags.Flags[key] = flag
490+
}
491+
468492
return validateDefaultVariants(newFlags)
469493
}
470494

core/pkg/evaluator/json_model.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ type Evaluators struct {
1010
Evaluators map[string]json.RawMessage `json:"$evaluators"`
1111
}
1212

13+
type ConfigWithMetadata struct {
14+
Flags map[string]model.Flag `json:"flags"`
15+
Metadata map[string]interface{} `json:"metadata"`
16+
}
17+
1318
type Flags struct {
1419
Flags map[string]model.Flag `json:"flags"`
1520
}

core/pkg/evaluator/json_test.go

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,6 +1184,222 @@ func TestState_Evaluator(t *testing.T) {
11841184
expectedError: true,
11851185
expectedResync: false,
11861186
},
1187+
"flag metadata": {
1188+
inputState: `
1189+
{
1190+
"flags": {
1191+
"fibAlgo": {
1192+
"variants": {
1193+
"recursive": "recursive",
1194+
"memo": "memo",
1195+
"loop": "loop",
1196+
"binet": "binet"
1197+
},
1198+
"metadata": {
1199+
"id": 1
1200+
},
1201+
"defaultVariant": "recursive",
1202+
"state": "ENABLED",
1203+
"targeting": {
1204+
"if": [
1205+
{
1206+
"$ref": "emailWithFaas"
1207+
}, "binet", null
1208+
]
1209+
}
1210+
}
1211+
},
1212+
"$evaluators": {
1213+
"emailWithFaas": {
1214+
"in": ["@faas.com", {
1215+
"var": ["email"]
1216+
}]
1217+
}
1218+
}
1219+
}
1220+
`,
1221+
inputSyncType: sync.ALL,
1222+
expectedOutputState: `
1223+
{
1224+
"flags": {
1225+
"fibAlgo": {
1226+
"variants": {
1227+
"recursive": "recursive",
1228+
"memo": "memo",
1229+
"loop": "loop",
1230+
"binet": "binet"
1231+
},
1232+
"metadata": {
1233+
"id": 1
1234+
},
1235+
"defaultVariant": "recursive",
1236+
"state": "ENABLED",
1237+
"source":"",
1238+
"selector":"",
1239+
"targeting": {
1240+
"if": [
1241+
{
1242+
"in": ["@faas.com", {
1243+
"var": ["email"]
1244+
}]
1245+
}, "binet", null
1246+
]
1247+
}
1248+
}
1249+
},
1250+
"flagSources":null
1251+
}
1252+
`,
1253+
},
1254+
"flagSet and flag metadata": {
1255+
inputState: `
1256+
{
1257+
"flags": {
1258+
"fibAlgo": {
1259+
"variants": {
1260+
"recursive": "recursive",
1261+
"memo": "memo",
1262+
"loop": "loop",
1263+
"binet": "binet"
1264+
},
1265+
"metadata": {
1266+
"id": "sso/dev",
1267+
"version": "1.0.0"
1268+
},
1269+
"defaultVariant": "recursive",
1270+
"state": "ENABLED",
1271+
"targeting": {
1272+
"if": [
1273+
{
1274+
"$ref": "emailWithFaas"
1275+
}, "binet", null
1276+
]
1277+
}
1278+
}
1279+
},
1280+
"metadata": {
1281+
"flagSetId": "test",
1282+
"flagSetVersion": "1"
1283+
},
1284+
"$evaluators": {
1285+
"emailWithFaas": {
1286+
"in": ["@faas.com", {
1287+
"var": ["email"]
1288+
}]
1289+
}
1290+
}
1291+
}
1292+
`,
1293+
inputSyncType: sync.ALL,
1294+
expectedOutputState: `
1295+
{
1296+
"flags": {
1297+
"fibAlgo": {
1298+
"variants": {
1299+
"recursive": "recursive",
1300+
"memo": "memo",
1301+
"loop": "loop",
1302+
"binet": "binet"
1303+
},
1304+
"metadata": {
1305+
"id": "sso/dev",
1306+
"version": "1.0.0",
1307+
"flagSetId": "test",
1308+
"flagSetVersion": "1"
1309+
},
1310+
"defaultVariant": "recursive",
1311+
"state": "ENABLED",
1312+
"source":"",
1313+
"selector":"",
1314+
"targeting": {
1315+
"if": [
1316+
{
1317+
"in": ["@faas.com", {
1318+
"var": ["email"]
1319+
}]
1320+
}, "binet", null
1321+
]
1322+
}
1323+
}
1324+
},
1325+
"flagSources":null
1326+
}
1327+
`,
1328+
},
1329+
"flag metadata priority": {
1330+
inputState: `
1331+
{
1332+
"flags": {
1333+
"fibAlgo": {
1334+
"variants": {
1335+
"recursive": "recursive",
1336+
"memo": "memo",
1337+
"loop": "loop",
1338+
"binet": "binet"
1339+
},
1340+
"metadata": {
1341+
"id": "sso/dev",
1342+
"version": "1.0.0"
1343+
},
1344+
"defaultVariant": "recursive",
1345+
"state": "ENABLED",
1346+
"targeting": {
1347+
"if": [
1348+
{
1349+
"$ref": "emailWithFaas"
1350+
}, "binet", null
1351+
]
1352+
}
1353+
}
1354+
},
1355+
"metadata": {
1356+
"id": "test",
1357+
"flagSetVersion": "1"
1358+
},
1359+
"$evaluators": {
1360+
"emailWithFaas": {
1361+
"in": ["@faas.com", {
1362+
"var": ["email"]
1363+
}]
1364+
}
1365+
}
1366+
}
1367+
`,
1368+
inputSyncType: sync.ALL,
1369+
expectedOutputState: `
1370+
{
1371+
"flags": {
1372+
"fibAlgo": {
1373+
"variants": {
1374+
"recursive": "recursive",
1375+
"memo": "memo",
1376+
"loop": "loop",
1377+
"binet": "binet"
1378+
},
1379+
"metadata": {
1380+
"id": "sso/dev",
1381+
"version": "1.0.0",
1382+
"flagSetVersion": "1"
1383+
},
1384+
"defaultVariant": "recursive",
1385+
"state": "ENABLED",
1386+
"source":"",
1387+
"selector":"",
1388+
"targeting": {
1389+
"if": [
1390+
{
1391+
"in": ["@faas.com", {
1392+
"var": ["email"]
1393+
}]
1394+
}, "binet", null
1395+
]
1396+
}
1397+
}
1398+
},
1399+
"flagSources":null
1400+
}
1401+
`,
1402+
},
11871403
}
11881404

11891405
for name, tt := range tests {

core/pkg/model/flag.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ package model
33
import "encoding/json"
44

55
type Flag struct {
6-
State string `json:"state"`
7-
DefaultVariant string `json:"defaultVariant"`
8-
Variants map[string]any `json:"variants"`
9-
Targeting json.RawMessage `json:"targeting,omitempty"`
10-
Source string `json:"source"`
11-
Selector string `json:"selector"`
6+
State string `json:"state"`
7+
DefaultVariant string `json:"defaultVariant"`
8+
Variants map[string]any `json:"variants"`
9+
Targeting json.RawMessage `json:"targeting,omitempty"`
10+
Source string `json:"source"`
11+
Selector string `json:"selector"`
12+
Metadata map[string]interface{} `json:"metadata,omitempty"`
1213
}
1314

1415
type Evaluators struct {

core/pkg/store/flags.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ type Flags struct {
2121
mx sync.RWMutex
2222
Flags map[string]model.Flag `json:"flags"`
2323
FlagSources []string
24-
SourceMetadata map[string]SourceDetails
24+
SourceMetadata map[string]SourceDetails `json:"sourceMetadata,omitempty"`
25+
Metadata map[string]interface{} `json:"metadata,omitempty"`
2526
}
2627

2728
type SourceDetails struct {

0 commit comments

Comments
 (0)