feat: Add public IConditionEvaluator interface for standalone condition evaluation - #29
Conversation
…on evaluation Implements #24 - Add IConditionEvaluator interface in TriasDev.Templify.Conditionals namespace - Add ConditionEvaluator implementation wrapping internal ConditionalEvaluator - Support Dictionary, JSON string, and IEvaluationContext inputs - Include sync and async methods (EvaluateAsync wraps sync for API compatibility) - Add CreateContext methods for batch evaluation scenarios - Add null parameter validation with ArgumentNullException - Add 26 unit tests covering all methods and edge cases
There was a problem hiding this comment.
Pull request overview
This PR adds a public API for standalone condition evaluation by exposing the internal condition evaluation engine through a new IConditionEvaluator interface and ConditionEvaluator implementation.
Key Changes:
- New public
IConditionEvaluatorinterface with synchronous and asynchronous evaluation methods ConditionEvaluatorclass that wraps the internalConditionalEvaluator- Support for Dictionary, JSON string, and
IEvaluationContextinputs with context creation for batch operations - Comprehensive test suite with 26 unit tests covering all public methods and edge cases
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| TriasDev.Templify/Conditionals/IConditionEvaluator.cs | Defines the public interface for condition evaluation with multiple overloads supporting Dictionary, JSON, and context-based inputs |
| TriasDev.Templify/Conditionals/ConditionEvaluator.cs | Implements the interface by wrapping the internal ConditionalEvaluator with proper null validation and async method support |
| TriasDev.Templify.Tests/ConditionEvaluatorTests.cs | Provides comprehensive test coverage for all public methods, including null validation, JSON parsing, and batch evaluation scenarios |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Task<bool> EvaluateAsync(string expression, Dictionary<string, object> data); | ||
|
|
||
| /// <summary> | ||
| /// Asynchronously evaluates a conditional expression against JSON data. | ||
| /// </summary> | ||
| /// <param name="expression">The expression to evaluate.</param> | ||
| /// <param name="jsonData">A JSON string representing the data object.</param> | ||
| /// <returns>A task that resolves to true if the condition is met; otherwise, false.</returns> | ||
| /// <exception cref="ArgumentNullException">Thrown when <paramref name="expression"/> or <paramref name="jsonData"/> is null.</exception> | ||
| /// <exception cref="System.Text.Json.JsonException">Thrown when JSON is invalid or root is not an object.</exception> | ||
| Task<bool> EvaluateAsync(string expression, string jsonData); | ||
|
|
||
| /// <summary> | ||
| /// Asynchronously evaluates a conditional expression using a pre-created evaluation context. | ||
| /// </summary> | ||
| /// <param name="expression">The expression to evaluate.</param> | ||
| /// <param name="context">The evaluation context containing variable data.</param> | ||
| /// <returns>A task that resolves to true if the condition is met; otherwise, false.</returns> | ||
| /// <exception cref="ArgumentNullException">Thrown when <paramref name="expression"/> or <paramref name="context"/> is null.</exception> | ||
| Task<bool> EvaluateAsync(string expression, IEvaluationContext context); |
There was a problem hiding this comment.
[nitpick] The async methods lack CancellationToken parameters, which is a best practice for async APIs. While the current implementation uses Task.FromResult (which completes synchronously), adding CancellationToken support would:
- Follow .NET async/await best practices
- Make the API future-proof if true async operations are added later
- Allow callers to cancel long-running operations
Consider adding overloads with CancellationToken cancellationToken = default parameters to all EvaluateAsync methods. Example:
Task<bool> EvaluateAsync(string expression, Dictionary<string, object> data, CancellationToken cancellationToken = default);The implementation can check cancellationToken.ThrowIfCancellationRequested() before calling the sync method.
| #region EvaluateAsync | ||
|
|
||
| [Fact] | ||
| public async Task EvaluateAsync_WithDictionary_ReturnsCorrectResult() | ||
| { | ||
| Dictionary<string, object> data = new() { ["IsActive"] = true }; | ||
|
|
||
| bool result = await _evaluator.EvaluateAsync("IsActive", data); | ||
|
|
||
| Assert.True(result); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task EvaluateAsync_WithJson_ReturnsCorrectResult() | ||
| { | ||
| string json = """{"IsActive": true}"""; | ||
|
|
||
| bool result = await _evaluator.EvaluateAsync("IsActive", json); | ||
|
|
||
| Assert.True(result); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task EvaluateAsync_WithContext_ReturnsCorrectResult() | ||
| { | ||
| Dictionary<string, object> data = new() { ["IsActive"] = true }; | ||
| IEvaluationContext context = _evaluator.CreateContext(data); | ||
|
|
||
| bool result = await _evaluator.EvaluateAsync("IsActive", context); | ||
|
|
||
| Assert.True(result); | ||
| } | ||
|
|
There was a problem hiding this comment.
The EvaluateAsync methods lack explicit null parameter validation tests. While these methods delegate to the synchronous Evaluate methods which do validate parameters, it's a best practice to have explicit tests for each public API method to ensure they throw ArgumentNullException for null parameters.
Consider adding tests like:
[Fact]
public async Task EvaluateAsync_WithNullExpression_ThrowsArgumentNullException()
{
var data = new Dictionary<string, object> { ["Key"] = "Value" };
await Assert.ThrowsAsync<ArgumentNullException>(() => _evaluator.EvaluateAsync(null!, data));
}Similar tests should be added for all three EvaluateAsync overloads with null parameters.
- Add CancellationToken parameter to all EvaluateAsync methods - Add cancellationToken.ThrowIfCancellationRequested() in implementation - Add null validation tests for EvaluateAsync methods - Add cancellation token test for EvaluateAsync
Summary
IConditionEvaluatorinterface inTriasDev.Templify.ConditionalsnamespaceConditionEvaluatorimplementation wrapping internalConditionalEvaluatorIEvaluationContextinputsEvaluateAsyncwraps sync for API compatibility)CreateContextmethods for batch evaluation scenariosArgumentNullExceptionCloses #24
Test plan
ConditionEvaluatorpass