Skip to content

Add internal RulesEngine evaluate API surface#6966

Merged
ajpallares merged 82 commits into
mainfrom
pallares/sdk-4308-ios-rules-engine-api-surface
Jun 10, 2026
Merged

Add internal RulesEngine evaluate API surface#6966
ajpallares merged 82 commits into
mainfrom
pallares/sdk-4308-ios-rules-engine-api-surface

Conversation

@ajpallares

@ajpallares ajpallares commented Jun 9, 2026

Copy link
Copy Markdown
Member

Motivation

Expose the minimal entry point for the JSON Logic rules engine so SDK call sites can evaluate predicates. Resolves SDK-4308.

Description

  • Makes the existing Value public
  • Adds a new RulesEngine.evaluate(predicate:variables:) public method to the RulesEngineInternal module, with failures surfaced as RulesEngine.EvaluationError. evaluate takes the predicate as a JSON string plus a native [String: Value] scope and returns Result<Bool, RulesEngine.EvaluationError>.
  • No SDK wiring yet.

Note on API tests

The public APIs of RulesEngineInternal are not pinned by API tests because they are not meant for public usage. The SDK should never leak any of these APIs in its public API (enforced by #6778)


Note

Low Risk
Changes are confined to the internal rules module and add API surface without SDK integration; evaluation behavior is covered by new and updated tests.

Overview
Exposes a minimal RulesEngineInternal entry point so future SDK code can evaluate JSON Logic predicates without reaching into the evaluator directly.

Public API: RulesEngine and Value are now public, along with RulesEngineLogger and RulesEngine.setLogger. RuleError is replaced by public RulesEngine.EvaluationError (adds an unknown case and CustomStringConvertible descriptions). New RulesEngine.evaluate(predicate:variables:) accepts predicate JSON as a String and a [String: Value] scope, returning Result<Bool, EvaluationError>.

Parsing: Value+JSON is compiled into the engine target (moved out of test-only helpers) and is the production path for predicate strings; parse failures map to EvaluationError.parse.

Tests / project: Adds RulesEngineEvaluateTests for the new API; existing tests and operators are updated to use RulesEngine.EvaluationError. Xcode project references are renamed/rewired (RulesEngine.swift, EvaluationError.swift, RulesEngineEvaluate.swift). No SDK call-site wiring in this PR.

Reviewed by Cursor Bugbot for commit 157f86d. Bugbot is set up for automated code reviews on this repo. Configure here.

ajpallares and others added 30 commits June 2, 2026 14:56
…tures

Convert the hand-written operator unit tests for operators available at the
string+array level into declarative JSON predicate fixtures sharing khepri's
conformance format, run by a shared runner that auto-discovers every file in
PredicateFixtures/. `expected` is a khepri-compatible superset (Bool or
{ "error": ... }) plus optional `description` and `expectedWarnings`.

Adds a test-only Value: Decodable conformance so predicates and variables
decode straight into the engine's value model; production Value is unchanged.
Tests that can't be expressed as predicate->Bool stay in Swift; ValueTests
untouched. Test-only change.

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 40901e5)
Load the in-repo fixtures once and feed the decoded cases straight into
the parameterized test, instead of collecting IDs and re-reading every
file to find the matching case per ID. PredicateConformanceFixtureCase
gains Identifiable + Sendable (for the arguments) and a
CustomTestStringConvertible extension so each case still displays by id.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Asserting an exact warning count coupled the fixtures to engine
internals. Match warnings by substring only (count-agnostic); an empty
`contains` now asserts that no warning is emitted, preserving the
"does not warn" cases. The three count-only fixtures gain the
"missing variable" substring they actually emit.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the khepri single-file plumbing (defaultFixtureURL, fixtureURL,
the no-arg loadCases(), the env var, and the unused fixtureNotFound
error). This PR only runs the in-repo PredicateFixtures/ directory; the
khepri conformance loader lands with its test in the conformance PR.

Co-authored-by: Cursor <cursoragent@cursor.com>
Implements the json-logic-js some/all iteration predicates. Operator coverage
is expressed as JSON predicate fixtures (some.json / all.json) run by the
shared fixture runner introduced downstack, replacing hand-written unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit d11529c)
Implements the json-logic-js min/max operators (variadic flat list, ±∞ for
empty input, NaN propagation for non-numeric operands). Coverage is expressed
as JSON predicate fixtures (min.json / max.json) run by the shared fixture
runner, replacing hand-written unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit e73eabe)
Completes the json-logic-js iteration family (none/map/filter/reduce) on top
of some/all. Coverage is expressed as JSON predicate fixtures
(none.json / map.json / filter.json / reduce.json) run by the shared fixture
runner, replacing hand-written unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 46d6925)
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	RevenueCat.xcodeproj/project.pbxproj
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	RevenueCat.xcodeproj/project.pbxproj
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	RevenueCat.xcodeproj/project.pbxproj
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	RevenueCat.xcodeproj/project.pbxproj
Expresses the "empty-string key resolves to the whole scope and is not
missing" case as a `{"!!":{"missing":[""]}}` -> false fixture; the string
coercion the other missing fixtures use can't distinguish [] from [""].
Bumps the pinned fixture count to 237. Keeps the corpus in sync with the
purchases-android counterpart.

Co-authored-by: Cursor <cursoragent@cursor.com>
Adds fixtures for the empty-segment dot-path splits, var default-vs-null-leaf
behavior, and the non-numeric missing_some threshold (7 new cases), bumping
the pinned count to 244. Also adds the var-null-path Swift test so the
non-fixturizable cases match the purchases-android counterpart. Keeps the
fixture corpus byte-identical across both repos.

Co-authored-by: Cursor <cursoragent@cursor.com>
…-logic-iteration-operators

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
…json-logic-min-max-operators

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
…on-logic-iteration-mapping-operators

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	Tests/RulesEngineInternalTests/PredicateFixtureTests.swift
ajpallares and others added 3 commits June 9, 2026 15:06
Exposes the JSON Logic engine's evaluate entry point (and Value / RuleError) at the RulesEngineInternal module level for SDK call sites. No SDK wiring yet. SDK-4308.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add doc comments to public Value enum cases and RuleError.description so missing_docs passes under --strict in CI and pre-commit.

Co-authored-by: Cursor <cursoragent@cursor.com>
@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Jun 9, 2026

Copy link
Copy Markdown

❌ CI Job Failed — run-test-ios-26

View Build

ajpallares and others added 11 commits June 10, 2026 12:54
Nest the error type under the RulesEngine namespace and update all module and test references.

Co-authored-by: Cursor <cursoragent@cursor.com>
Stops misreporting unexpected errors as parse failures, and trims evaluate's doc comment.

Co-authored-by: Cursor <cursoragent@cursor.com>
Match the file name to the RulesEngine namespace it declares.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
fromJSONObject already throws EvaluationError, so the rethrow catch is unnecessary.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Brings back the lock-guarded `LoggerStorage` so the default logger can be
replaced (host-SDK wiring will expose this in a follow-up PR), while keeping
everything internal to the module.

Co-authored-by: Cursor <cursoragent@cursor.com>
Lets SDK call sites install their own logger via the module-internal public surface.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares changed the base branch from main to pallares/rules-task-local-logger June 10, 2026 11:40
@ajpallares ajpallares marked this pull request as ready for review June 10, 2026 11:45
@ajpallares ajpallares requested a review from a team as a code owner June 10, 2026 11:45
Comment thread RevenueCat.xcodeproj/project.pbxproj Outdated
709B11FAA2FC6B4929062F25 /* RulesEngineInternal.swift */,
709B11FAA2FC6B4929062F25 /* RulesEngine.swift */,
9AA7D8F9033399B294E4D4DB /* Logger.swift */,
7F1B03A2D59CC0822C02186A /* RuleError.swift */,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file was deleted but is still listed here


@testable import RulesEngineInternal

final class RulesEngineEvaluateTests: XCTestCase {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could use Swift testing for this now? Ofc fine to not use it :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh true! Better to be consistent in the module. Done in 2566ff4

Thanks!

ajpallares and others added 2 commits June 10, 2026 16:01
Rename the stale RuleError.swift reference to EvaluationError.swift and
register RulesEngineEvaluateTests.swift in the RulesEngineInternalTests
target, fixing the Danger project-sync failure.

Co-authored-by: Cursor <cursoragent@cursor.com>
Addresses review feedback on #6966 suggesting Swift Testing for the new
test file, matching the style of PredicateFixtureTests.

Co-authored-by: Cursor <cursoragent@cursor.com>
Base automatically changed from pallares/rules-task-local-logger to main June 10, 2026 15:49
ajpallares and others added 2 commits June 10, 2026 17:49
Value+JSON.swift moved from the test helpers to the RulesEngineInternal
module, but the project still compiled it from the old test path. Point
the reference at the module source and also register the previously
untracked RulesEngineEvaluate.swift in the module target.

Co-authored-by: Cursor <cursoragent@cursor.com>
…-rules-engine-api-surface

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	RulesEngineInternal/Logger.swift
#	RulesEngineInternal/RulesEngine.swift
@ajpallares

Copy link
Copy Markdown
Member Author

@RCGitBot please test

@ajpallares ajpallares merged commit 99f74c3 into main Jun 10, 2026
42 of 43 checks passed
@ajpallares ajpallares deleted the pallares/sdk-4308-ios-rules-engine-api-surface branch June 10, 2026 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants