Skip to content

Commit 8e51d3e

Browse files
authored
Merge branch 'main' into feat/client/integrate-context-engine
2 parents 1734178 + be7ad72 commit 8e51d3e

File tree

4 files changed

+64
-18
lines changed

4 files changed

+64
-18
lines changed

Flagsmith.Engine/Segment/Evaluator.cs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ private static FlagResult<FeatureMetadataT> GetFlagResult<_, FeatureMetadataT>(E
240240
Enabled = featureContext.Enabled,
241241
Value = variant.Value,
242242
Metadata = featureContext.Metadata,
243-
Reason = $"SPLIT; weight={weight}",
243+
Reason = FormattableString.Invariant($"SPLIT; weight={weight}"),
244244
};
245245
break;
246246
}
@@ -336,7 +336,7 @@ private static bool LongOperations(long contextValue, Condition condition)
336336
long conditionValue;
337337
try
338338
{
339-
conditionValue = Convert.ToInt64(condition.Value.String);
339+
conditionValue = InvariantConvert.ToInt64(condition.Value);
340340
}
341341
catch (FormatException)
342342
{
@@ -358,12 +358,13 @@ private static bool IntOperations(long contextValue, Condition condition)
358358
{
359359
switch (condition.Operator)
360360
{
361-
case Operator.Equal: return contextValue == Convert.ToInt32(condition.Value.String);
362-
case Operator.NotEqual: return contextValue != Convert.ToInt32(condition.Value.String);
363-
case Operator.GreaterThan: return contextValue > Convert.ToInt32(condition.Value.String);
364-
case Operator.GreaterThanInclusive: return contextValue >= Convert.ToInt32(condition.Value.String);
365-
case Operator.LessThan: return contextValue < Convert.ToInt32(condition.Value.String);
366-
case Operator.LessThanInclusive: return contextValue <= Convert.ToInt32(condition.Value.String);
361+
case Constants.Equal: return traitValue == InvariantConvert.ToInt32(condition.Value);
362+
case Constants.NotEqual: return traitValue != InvariantConvert.ToInt32(condition.Value);
363+
case Constants.GreaterThan: return traitValue > InvariantConvert.ToInt32(condition.Value);
364+
case Constants.GreaterThanInclusive: return traitValue >= InvariantConvert.ToInt32(condition.Value);
365+
case Constants.LessThan: return traitValue < InvariantConvert.ToInt32(condition.Value);
366+
case Constants.LessThanInclusive: return traitValue <= InvariantConvert.ToInt32(condition.Value);
367+
case Constants.In: return condition.Value.Split(',').Contains(traitValue.ToString());
367368
default: throw new ArgumentException("Invalid Operator");
368369
}
369370
}
@@ -372,12 +373,13 @@ private static bool DoubleOperations(double contextValue, Condition condition)
372373
{
373374
switch (condition.Operator)
374375
{
375-
case Operator.Equal: return contextValue == Convert.ToDouble(condition.Value.String);
376-
case Operator.NotEqual: return contextValue != Convert.ToDouble(condition.Value.String);
377-
case Operator.GreaterThan: return contextValue > Convert.ToDouble(condition.Value.String);
378-
case Operator.GreaterThanInclusive: return contextValue >= Convert.ToDouble(condition.Value.String);
379-
case Operator.LessThan: return contextValue < Convert.ToDouble(condition.Value.String);
380-
case Operator.LessThanInclusive: return contextValue <= Convert.ToDouble(condition.Value.String);
376+
case Constants.Equal: return traitValue == InvariantConvert.ToDouble(condition.Value);
377+
case Constants.NotEqual: return traitValue != InvariantConvert.ToDouble(condition.Value);
378+
case Constants.GreaterThan: return traitValue > InvariantConvert.ToDouble(condition.Value);
379+
case Constants.GreaterThanInclusive: return traitValue >= InvariantConvert.ToDouble(condition.Value);
380+
case Constants.LessThan: return traitValue < InvariantConvert.ToDouble(condition.Value);
381+
case Constants.LessThanInclusive: return traitValue <= InvariantConvert.ToDouble(condition.Value);
382+
case Constants.In: return false;
381383
default: throw new ArgumentException("Invalid Operator");
382384
}
383385
}

Flagsmith.Engine/Segment/Models/SegmentConditionModel.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Text.RegularExpressions;
22
using System;
3+
using System.Globalization;
34
using Newtonsoft.Json;
45
namespace FlagsmithEngine.Segment.Models
56
{
@@ -21,10 +22,10 @@ public bool EvaluateModulo(string traitValue)
2122
string[] parts = this.Value.Split('|');
2223
if (parts.Length != 2) { return false; }
2324

24-
double divisor = Convert.ToDouble(parts[0]);
25-
double remainder = Convert.ToDouble(parts[1]);
25+
double divisor = Convert.ToDouble(parts[0], CultureInfo.InvariantCulture);
26+
double remainder = Convert.ToDouble(parts[1], CultureInfo.InvariantCulture);
2627

27-
return Convert.ToDouble(traitValue) % divisor == remainder;
28+
return Convert.ToDouble(traitValue, CultureInfo.InvariantCulture) % divisor == remainder;
2829
}
2930
catch (FormatException)
3031
{
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Globalization;
3+
4+
namespace FlagsmithEngine.Utils
5+
{
6+
/// <summary>
7+
/// Provides culture-invariant conversion methods for numeric types.
8+
/// </summary>
9+
/// <remarks>
10+
/// Ensures consistent numeric parsing across all system locales by using InvariantCulture.
11+
/// Prevents locale-specific decimal separator issues (e.g., "1.23" vs "1,23").
12+
/// </remarks>
13+
internal static class InvariantConvert
14+
{
15+
/// <summary>
16+
/// Converts the string representation of a number to its 32-bit signed integer equivalent
17+
/// using culture-invariant formatting.
18+
/// </summary>
19+
/// <param name="value">A string containing a number to convert.</param>
20+
/// <returns>A 32-bit signed integer equivalent to the number in value.</returns>
21+
public static int ToInt32(string value) =>
22+
Convert.ToInt32(value, CultureInfo.InvariantCulture);
23+
24+
/// <summary>
25+
/// Converts the string representation of a number to its 64-bit signed integer equivalent
26+
/// using culture-invariant formatting.
27+
/// </summary>
28+
/// <param name="value">A string containing a number to convert.</param>
29+
/// <returns>A 64-bit signed integer equivalent to the number in value.</returns>
30+
public static long ToInt64(string value) =>
31+
Convert.ToInt64(value, CultureInfo.InvariantCulture);
32+
33+
/// <summary>
34+
/// Converts the string representation of a number to its double-precision floating-point equivalent
35+
/// using culture-invariant formatting.
36+
/// </summary>
37+
/// <param name="value">A string containing a number to convert.</param>
38+
/// <returns>A double-precision floating-point number equivalent to the numeric value in value.</returns>
39+
public static double ToDouble(string value) =>
40+
Convert.ToDouble(value, CultureInfo.InvariantCulture);
41+
}
42+
}

Flagsmith.EngineTest/Unit/Traits/TraitSchemaTest.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Globalization;
34
using System.Text;
45
using Xunit;
56
using Newtonsoft.Json.Linq;
@@ -30,7 +31,7 @@ public void TestTraitToJobject(TraitModel trait)
3031
var jObject = JObject.FromObject(trait);
3132
Assert.Equal(trait.TraitKey, jObject["trait_key"].Value<string>());
3233
var valueToken = jObject["trait_value"];
33-
var value = valueToken.Type != JTokenType.Null ? Convert.ChangeType(valueToken.Value<string>(), trait.TraitValue.GetType()) : null;
34+
var value = valueToken.Type != JTokenType.Null ? Convert.ChangeType(valueToken.Value<string>(), trait.TraitValue.GetType(), CultureInfo.InvariantCulture) : null;
3435
Assert.Equal(trait.TraitValue, value);
3536
}
3637
public static IEnumerable<object[]> TestTraitToJobjectData() =>

0 commit comments

Comments
 (0)