Skip to content

Commit cf3b561

Browse files
Fix missing filter evaluation (#158)
1 parent f026af0 commit cf3b561

File tree

2 files changed

+87
-1
lines changed

2 files changed

+87
-1
lines changed

src/feature-management/src/featureManager.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,12 @@ export class FeatureManager implements IFeatureManager {
103103
const contextWithFeatureName = { featureName: featureFlag.id, parameters: clientFilter.parameters };
104104
if (matchedFeatureFilter === undefined) {
105105
console.warn(`Feature filter ${clientFilter.name} is not found.`);
106-
return false;
106+
if (requirementType === "All") {
107+
// A missing filter means we cannot satisfy all conditions.
108+
return false;
109+
}
110+
// For "Any", skip the missing filter and continue evaluating.
111+
continue;
107112
}
108113
if (await matchedFeatureFilter.evaluate(contextWithFeatureName, appContext) === shortCircuitEvaluationResult) {
109114
return shortCircuitEvaluationResult;

src/feature-management/test/featureManager.test.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,85 @@ describe("feature manager", () => {
248248
});
249249

250250
it("should override default filters with custom filters");
251+
252+
it("should skip missing filter and continue evaluating when requirement type is Any", () => {
253+
const jsonObject = {
254+
"feature_management": {
255+
"feature_flags": [
256+
{
257+
"id": "MissingFilterAny",
258+
"enabled": true,
259+
"conditions": {
260+
"requirement_type": "Any",
261+
"client_filters": [
262+
{
263+
"name": "UnregisteredFilter",
264+
"parameters": {}
265+
},
266+
{
267+
"name": "Microsoft.Targeting",
268+
"parameters": {
269+
"Audience": {
270+
"Users": [ "Alice" ]
271+
}
272+
}
273+
}
274+
]
275+
}
276+
},
277+
{
278+
"id": "MissingFilterAll",
279+
"enabled": true,
280+
"conditions": {
281+
"requirement_type": "All",
282+
"client_filters": [
283+
{
284+
"name": "UnregisteredFilter",
285+
"parameters": {}
286+
},
287+
{
288+
"name": "Microsoft.Targeting",
289+
"parameters": {
290+
"Audience": {
291+
"Users": [ "Alice" ]
292+
}
293+
}
294+
}
295+
]
296+
}
297+
},
298+
{
299+
"id": "AllMissingFiltersAny",
300+
"enabled": true,
301+
"conditions": {
302+
"requirement_type": "Any",
303+
"client_filters": [
304+
{
305+
"name": "UnregisteredFilter1",
306+
"parameters": {}
307+
},
308+
{
309+
"name": "UnregisteredFilter2",
310+
"parameters": {}
311+
}
312+
]
313+
}
314+
}
315+
]
316+
}
317+
};
318+
319+
const provider = new ConfigurationObjectFeatureFlagProvider(jsonObject);
320+
const featureManager = new FeatureManager(provider);
321+
return Promise.all([
322+
// Any: missing filter is skipped, targeting filter matches Alice => enabled
323+
expect(featureManager.isEnabled("MissingFilterAny", {userId: "Alice"})).eventually.eq(true),
324+
// Any: missing filter is skipped, targeting filter does not match Bob => disabled
325+
expect(featureManager.isEnabled("MissingFilterAny", {userId: "Bob"})).eventually.eq(false),
326+
// All: missing filter means cannot satisfy all conditions => disabled
327+
expect(featureManager.isEnabled("MissingFilterAll", {userId: "Alice"})).eventually.eq(false),
328+
// Any: all filters are missing => disabled (no filter can match)
329+
expect(featureManager.isEnabled("AllMissingFiltersAny", {userId: "Alice"})).eventually.eq(false)
330+
]);
331+
});
251332
});

0 commit comments

Comments
 (0)